Esempio n. 1
0
def evaluate(solution):
    x = solution["parameters"]

    m = 3

    coll = CollGaussRadau_Right(num_nodes=m, tleft=0.0, tright=1.0)
    # coll = CollGaussLobatto(num_nodes=m, tleft=0.0, tright=1.0)
    # coll = EquidistantNoLeft(num_nodes=m, tleft=0.0, tright=1.0)

    Q = coll.Qmat[1:, 1:]

    var = [x['x'+str(j)] for j in range(1, m + 1)]
    # var = [x['x' + str(j) + 'r'] + 1j * x['x' + str(j) + 'i'] for j in range(1, m + 1)]

    Qd = np.diag(var)

    # THIS WORKS REALLY WELL! No need to take imaginary parts in x, though (found minimum has zero imaginary parts)
    k = 0
    obj_val = 0.0
    for i in range(-8, 8):
        for l in range(-8, 8):
            k += 1
            lamdt = -10 ** i + 1j * 10 ** l
            R = lamdt * np.linalg.inv(np.eye(m) - lamdt * Qd).dot(Q - Qd)
            rhoR = max(abs(np.linalg.eigvals(np.linalg.matrix_power(R, 1))))
            obj_val += rhoR

    obj_val /= k

    solution["metrics"] = {}
    solution["metrics"]["rho"] = obj_val
    return solution
Esempio n. 2
0
def evaluate(solution):
    x = solution["parameters"]

    m = 5

    coll = CollGaussRadau_Right(num_nodes=m, tleft=0.0, tright=1.0)
    Q = coll.Qmat[1:, 1:]

    var = [x['x' + str(j)] for j in range(1, m)]
    # var = [x['x' + str(j) + 'r'] + 1j * x['x' + str(j) + 'i'] for j in range(1, m + 1)]

    Qd = np.zeros((m, m))
    Qd[1, 0] = var[0]
    Qd[2, 1] = var[1]
    Qd[3, 2] = var[2]
    Qd[4, 3] = var[3]

    # THIS WORKS REALLY WELL! No need to take imaginary parts in x, though (found minimum has zero imaginary parts)
    k = 0
    obj_val = 0.0
    for i in range(-8, 1):
        for l in range(-8, 1):
            k += 1
            lamdt = -10**i + 1j * 10**l
            R = lamdt * np.linalg.inv(np.eye(m) - lamdt * Qd).dot(Q - Qd)
            rhoR = max(abs(np.linalg.eigvals(R)))
            obj_val += rhoR

    obj_val /= k

    solution["metrics"] = {}
    solution["metrics"]["rho"] = obj_val
    return solution
Esempio n. 3
0
def evaluate(solution):
    x = solution["parameters"]

    m = 5

    coll = CollGaussRadau_Right(num_nodes=m, tleft=0.0, tright=1.0)
    Q = coll.Qmat[1:, 1:]

    Qd = np.array([[x['x11'], x['x12'], x['x13'], x['x14'], x['x15']],
                   [0.0, x['x22'], x['x23'], x['x24'], x['x25']],
                   [0.0, 0.0, x['x33'], x['x34'], x['x35']],
                   [0.0, 0.0, 0.0, x['x44'], x['x45']],
                   [0.0, 0.0, 0.0, 0.0, x['x55']]])

    k = 0
    obj_val = 0.0
    for i in range(-8, 8):
        for l in range(-8, 8):
            k += 1
            lamdt = -10**i + 1j * 10**l
            R = lamdt * np.linalg.inv(np.eye(m) - lamdt * Qd).dot(Q - Qd)
            rhoR = max(abs(np.linalg.eigvals(R)))
            obj_val += rhoR

    obj_val /= k

    solution["metrics"] = {}
    solution["metrics"]["rho"] = obj_val
    return solution
def iterate(x, k_impl, k_expl, _u0, N):

    k = k_impl + k_expl
    coll = CollGaussRadau_Right(num_nodes, 0, 1)
    Q = coll.Qmat[1:, 1:]
    C = identity(num_nodes*N) -  dt *  scipy.sparse.kron(Q, sp.diags(k))
    u0 = np.concatenate((_u0, _u0, _u0), axis=None)
    u = np.concatenate((_u0, _u0, _u0), axis=None) 

    print("u", u)
    Qdmat = np.zeros_like(Q)
    np.fill_diagonal(Qdmat, x)



    
    QD_expl = np.zeros(Q.shape)

    for m in range(coll.num_nodes):
        QD_expl[m, 0:m] = coll.delta_m[0:m]


    mat0_i =[]
    mat1_i =[]
    mat2_i =[]

    for i in k_impl:
        #hier wuerde jetzt von RL fuer jedes kappa ein Qd gewaehlt werden
        mat0_i = np.concatenate((mat0_i, x[0]*i), axis=None)  
        mat1_i = np.concatenate((mat1_i, x[1]*i), axis=None)  
        mat2_i = np.concatenate((mat2_i, x[2]*i), axis=None)  


    mat_i = sp.diags(np.concatenate((mat0_i, mat1_i, mat2_i), axis=None)  )


    Pinv_impl = np.linalg.inv(
        np.eye(coll.num_nodes*N) -  dt * ( mat_i   +   scipy.sparse.kron(QD_expl, sp.diags(k_expl))  )    ,
    )


    residual = u0 - C @ u

    done = False
    err = False
    niter = 0
    while not done and not niter >= 50 and not err:
        niter += 1
        u = np.squeeze( np.array( u + Pinv_impl @ (u0 - C @ u) ))
        residual = np.squeeze( np.array( u0 - C @ u ))

        norm_res = np.linalg.norm(residual, np.inf)
        print(norm_res)
        if np.isnan(norm_res) or np.isinf(norm_res):
            niter = 51
            break
        done = norm_res < restol
    print("niter", niter)
    return niter, u
Esempio n. 5
0
def calc_default_initial_residual(M, lam, dt):
    coll = CollGaussRadau_Right(M, 0, 1)
    Q = coll.Qmat[1:, 1:]

    return ((np.ones(coll.num_nodes, dtype=np.complex128))
            - (np.eye(coll.num_nodes)
               - lam * dt * Q)
            @ (np.ones(coll.num_nodes, dtype=np.complex128)))
Esempio n. 6
0
 def __init__(
     self,
     M,
     lambda_real_interval,
     lambda_imag_interval,
 ):
     super().__init__()
     self.M = M
     self.coll = CollGaussRadau_Right(M, 0, 1)
     self.Q = self.coll.Qmat[1:, 1:]
     self.lambda_real_interval = lambda_real_interval
     self.lambda_imag_interval = lambda_imag_interval
Esempio n. 7
0
def evaluate(solution):
    x = solution["parameters"]

    m = 5
    coll = CollGaussRadau_Right(num_nodes=m, tleft=0.0, tright=1.0)
    Q = coll.Qmat[1:, 1:]

    var = [x['x'+str(j)] for j in range(1, m + 1)]

    obj_val = max(abs(np.linalg.eigvals(np.eye(m) - np.diag(var).dot(Q))))

    solution["metrics"] = {}
    solution["metrics"]["rho"] = obj_val
    return solution
Esempio n. 8
0
def evaluate(solution):
    x = solution["parameters"]

    m = 3

    coll = CollGaussRadau_Right(num_nodes=m, tleft=0.0, tright=1.0)
    # coll = CollGaussLobatto(num_nodes=m, tleft=0.0, tright=1.0)
    # coll = EquidistantNoLeft(num_nodes=m, tleft=0.0, tright=1.0)
    # print(coll.nodes)
    # exit()
    Q = coll.Qmat[1:, 1:]

    var = [x['x'+str(j)] for j in range(1, m + 1)]
    # var = [x['x' + str(j) + 'r'] + 1j * x['x' + str(j) + 'i'] for j in range(1, m + 1)]

    Qd = np.diag(var)

    nsteps = 4

    E = np.zeros((nsteps, nsteps))
    np.fill_diagonal(E[1:, :], 1)

    Ea = E.copy()
    # Ea[0, -1] = 0.125
    # Ea = np.zeros(E.shape)
    # Da, Va = np.linalg.eig(Ea)
    # Via = np.linalg.inv(Va)

    N = np.zeros((m, m))
    N[:, -1] = 1

    # THIS WORKS REALLY WELL! No need to take imaginary parts in x, though (found minimum has zero imaginary parts)
    k = 0
    obj_val = 0.0
    for i in range(-8, 8):
        for l in range(-8, 8):
            k += 1
            lamdt = -10 ** i + 1j * 10 ** l
            Rf = np.linalg.inv(np.eye(nsteps * m) - lamdt * np.kron(np.eye(nsteps), Qd)).dot(lamdt * np.kron(np.eye(nsteps), Q - Qd) + np.kron(E, N))
            Rc = np.linalg.inv(np.eye(nsteps * m) - lamdt * np.kron(np.eye(nsteps), Qd) - np.kron(E, N)).dot(lamdt * np.kron(np.eye(nsteps), Q - Qd))
            rhoR = max(abs(np.linalg.eigvals(Rc.dot(Rf))))
            obj_val += rhoR

    obj_val /= k

    solution["metrics"] = {}
    solution["metrics"]["rho"] = obj_val
    return solution
Esempio n. 9
0
def iterate(x, k, _u0):

    coll = CollGaussRadau_Right(num_nodes, 0, 1)
    Q = coll.Qmat[1:, 1:]
    C = identity(num_nodes*N) -  dt *  scipy.sparse.kron(Q, sp.diags(k))
    u0 = np.concatenate((_u0, _u0, _u0), axis=None)
    u = np.concatenate((_u0, _u0, _u0), axis=None) 

    Qdmat = np.zeros_like(Q)
    np.fill_diagonal(Qdmat, x)


    mat0 =[]
    mat1 =[]
    mat2 =[]
    for i in k:
        #hier wuerde jetzt von RL fuer jedes kappa ein Qd gewaehlt werden
        mat0 = np.concatenate((mat0, x[0]*i), axis=None)  
        mat1 = np.concatenate((mat1, x[1]*i), axis=None)  
        mat2 = np.concatenate((mat2, x[2]*i), axis=None)  


    mat = sp.diags(np.concatenate((mat0, mat1, mat2), axis=None)  )

    Pinv = np.linalg.inv(
        np.eye(coll.num_nodes*N) -  dt * mat,
    )

    residual = u0 - C @ u

    done = False
    err = False
    niter = 0
    while not done and not niter >= 50 and not err:
        niter += 1
        u = np.squeeze( np.array( u + Pinv @ (u0 - C @ u) ))
        residual = np.squeeze( np.array( u0 - C @ u ))
        norm_res = np.linalg.norm(residual, np.inf)
        if np.isnan(norm_res) or np.isinf(norm_res):
            niter = 51
            break
        done = norm_res < restol
    return niter, u
Esempio n. 10
0
def main():
    """
    A simple test program to compute the order of accuracy in time
    """

    # initialize problem parameters
    problem_params = {}
    problem_params['nu'] = 0.1  # diffusion coefficient
    problem_params['freq'] = 4  # frequency for the test value
    problem_params['nvars'] = 16383  # number of DOFs in space

    # instantiate problem
    prob = heat1d(problem_params=problem_params, dtype_u=mesh, dtype_f=mesh)

    # instantiate collocation class, relative to the time interval [0,1]
    coll = CollGaussRadau_Right(num_nodes=3, tleft=0, tright=1)

    # assemble list of dt
    dt_list = [0.1 / 2**p for p in range(0, 4)]

    # run accuracy test for all dt
    results = run_accuracy_check(prob=prob, coll=coll, dt_list=dt_list)

    # get order of accuracy
    order = get_accuracy_order(results)

    f = open('step_1_D_out.txt', 'w')
    for l in range(len(order)):
        out = 'Expected order: %2i -- Computed order %4.3f' % (5, order[l])
        f.write(out + '\n')
        print(out)
    f.close()

    # visualize results
    plot_accuracy(results)

    assert os.path.isfile('step_1_accuracy_test_coll.png')

    assert all(
        np.isclose(order, 2 * coll.num_nodes - 1, rtol=0.4)
    ), "ERROR: did not get order of accuracy as expected, got %s" % order
Esempio n. 11
0
    def __init__(
        self,
        M,
        lambda_real_interval,
        lambda_imag_interval,
        batch_size,
        rng_key,
    ):
        super().__init__()
        self.M = M
        self.coll = CollGaussRadau_Right(M, 0, 1)
        self.Q = self.coll.Qmat[1:, 1:]
        self.lambda_real_interval = lambda_real_interval
        self.lambda_imag_interval = lambda_imag_interval
        self.batch_size = batch_size
        self.rng_key = rng_key

        self.lam_real_low = self.lambda_real_interval[0]
        self.lam_real_high = self.lambda_real_interval[1]

        self.lam_imag_low = self.lambda_imag_interval[0]
        self.lam_imag_high = self.lambda_imag_interval[1]
Esempio n. 12
0
def compute_and_plot_specrad():
    """
    Compute and plot spectral radius of smoother iteration matrix for a whole range of eigenvalues
    """
    # setup_list = [('LU', 'to0'), ('LU', 'toinf'), ('IE', 'to0'), ('IE', 'toinf')]
    # setup_list = [('LU', 'to0'), ('LU', 'toinf')]
    # setup_list = [('IE', 'to0'), ('IE', 'toinf')]
    # setup_list = [('LU', 'toinf'), ('IE', 'toinf')]
    setup_list = [('IE', 'full'), ('LU', 'full')]
    # setup_list = [('EX', 'to0'), ('PIC', 'to0')]

    # set up plotting parameters
    params = {
        'legend.fontsize': 24,
        'figure.figsize': (12, 8),
        'axes.labelsize': 24,
        'axes.titlesize': 24,
        'xtick.labelsize': 24,
        'ytick.labelsize': 24,
        'lines.linewidth': 3
    }
    plt.rcParams.update(params)

    Nnodes = 3
    Nsteps = 4

    coll = CollGaussRadau_Right(Nnodes, 0, 1)
    Qmat = coll.Qmat[1:, 1:]

    Nmat = np.zeros((Nnodes, Nnodes))
    Nmat[:, -1] = 1

    Emat = np.zeros((Nsteps, Nsteps))
    np.fill_diagonal(Emat[1:, :], 1)

    for qd_type, conv_type in setup_list:

        if qd_type == 'LU':

            QT = coll.Qmat[1:, 1:].T
            [_, _, U] = LA.lu(QT, overwrite_a=True)
            QDmat = U.T

        elif qd_type == 'IE':

            QI = np.zeros(np.shape(coll.Qmat))
            for m in range(coll.num_nodes + 1):
                QI[m, 1:m + 1] = coll.delta_m[0:m]
            QDmat = QI[1:, 1:]

        elif qd_type == 'EE':

            QE = np.zeros(np.shape(coll.Qmat))
            for m in range(coll.num_nodes + 1):
                QE[m, 0:m] = coll.delta_m[0:m]
            QDmat = QE[1:, 1:]

        elif qd_type == 'PIC':

            QDmat = np.zeros(np.shape(coll.Qmat[1:, 1:]))

        elif qd_type == 'EX':

            QT = coll.Qmat[1:, 1:].T
            [_, _, U] = LA.lu(QT, overwrite_a=True)
            QDmat = np.tril(U.T, k=-1)
            print(QDmat)

        else:
            raise NotImplementedError('qd_type %s is not implemented' %
                                      qd_type)

        # lim_specrad = max(abs(np.linalg.eigvals(np.eye(Nnodes) - np.linalg.inv(QDmat).dot(Qmat))))
        # print('qd_type: %s -- lim_specrad: %6.4e -- conv_type: %s' % (qd_type, lim_specrad, conv_type))

        if conv_type == 'to0':

            ilim_left = -4
            ilim_right = 2
            rlim_left = 2
            rlim_right = -4

        elif conv_type == 'toinf':

            ilim_left = 0
            ilim_right = 11
            rlim_left = 6
            rlim_right = 0

        elif conv_type == 'full':

            ilim_left = -10
            ilim_right = 11
            rlim_left = 10
            rlim_right = -11

        else:
            raise NotImplementedError('conv_type %s is not implemented' %
                                      conv_type)

        ilam_list = 1j * np.logspace(ilim_left, ilim_right, 201)
        rlam_list = -1 * np.logspace(rlim_left, rlim_right, 201)

        assert (rlim_right - rlim_left + 1) % 5 == 0
        assert (ilim_right - ilim_left - 1) % 5 == 0
        assert (len(rlam_list) - 1) % 5 == 0
        assert (len(ilam_list) - 1) % 5 == 0

        Prho = np.zeros((len(rlam_list), len(ilam_list)))

        for idr, rlam in enumerate(rlam_list):
            for idi, ilam in enumerate(ilam_list):
                dxlam = rlam + ilam

                mat = np.linalg.inv(
                    np.eye(Nnodes * Nsteps) -
                    dxlam * np.kron(np.eye(Nsteps), QDmat)).dot(
                        dxlam * np.kron(np.eye(Nsteps), (Qmat - QDmat)) +
                        np.kron(Emat, Nmat))
                mat = np.linalg.matrix_power(mat, Nnodes)

                Prho[idr, idi] = max(abs(np.linalg.eigvals(mat)))

        print(np.amax(Prho))

        fig, ax = plt.subplots(figsize=(15, 10))

        ax.set_xticks([
            i + 0.5 for i in range(0, len(rlam_list), int(len(rlam_list) / 5))
        ])
        ax.set_xticklabels([
            r'-$10^{%d}$' % i for i in range(
                rlim_left, rlim_right, int((rlim_right - rlim_left + 1) / 5))
        ])
        ax.set_yticks([
            i + 0.5 for i in range(0, len(ilam_list), int(len(ilam_list) / 5))
        ])
        ax.set_yticklabels([
            r'$10^{%d}i$' % i for i in range(
                ilim_left, ilim_right, int((ilim_right - ilim_left - 1) / 5))
        ])

        cmap = plt.get_cmap('Reds')
        pcol = plt.pcolor(Prho.T,
                          cmap=cmap,
                          norm=LogNorm(vmin=1E-09, vmax=1E-00))

        plt.colorbar(pcol)

        plt.xlabel(r'$Re(\Delta t\lambda)$')
        plt.ylabel(r'$Im(\Delta t\lambda)$')

        fname = 'data/heatmap_smoother_' + conv_type + '_Nsteps' + str(Nsteps) + '_M' + \
                str(Nnodes) + '_' + qd_type + '.png'
        plt.savefig(fname,
                    rasterized=True,
                    transparent=True,
                    bbox_inches='tight')
Esempio n. 13
0
def compute_and_plot_specrad(Nnodes, lam):
    """
    Compute and plot the spectral radius of the smoother for different step-sizes

    Args:
        Nnodes: number of collocation nodes
        lam: test parameter representing the spatial problem
    """

    coll = CollGaussRadau_Right(Nnodes, 0, 1)
    Qmat = coll.Qmat[1:, 1:]

    # do LU decomposition of QT (St. Martin's trick)
    QT = coll.Qmat[1:, 1:].T
    [_, _, U] = LA.lu(QT, overwrite_a=True)
    QDmat = U.T

    Nmat = np.zeros((Nnodes, Nnodes))
    Nmat[:, -1] = 1

    Nsteps_list = [64, 256]
    color_list = ['red', 'blue']
    marker_list = ['s', 'o']

    setup_list = zip(Nsteps_list, color_list, marker_list)

    xlist = [0.1**i for i in range(11)]

    rc('font', **{"sans-serif": ["Arial"], "size": 24})
    plt.subplots(figsize=(15, 10))

    for Nsteps, color, marker in setup_list:

        Emat = np.zeros((Nsteps, Nsteps))
        np.fill_diagonal(Emat[1:, :], 1)

        Prho_list = []
        predict_list = []
        for x in xlist:

            mat = np.linalg.inv(
                np.eye(Nnodes * Nsteps) -
                x * lam * np.kron(np.eye(Nsteps), QDmat)).dot(
                    x * lam * np.kron(np.eye(Nsteps), (Qmat - QDmat)) +
                    np.kron(Emat, Nmat))

            Prho_list.append(max(abs(np.linalg.eigvals(mat))))
            # predict_list.append((1 + x) ** (1.0 - 1.0 / (Nnodes * Nsteps)) * x ** (1.0 / (Nnodes * Nsteps)))
            predict_list.append(x**(1.0 / (Nsteps)))

            if len(predict_list) > 1:
                print(x, predict_list[-1], Prho_list[-1],
                      Prho_list[-2] / Prho_list[-1],
                      predict_list[-2] / predict_list[-1])

        plt.loglog(xlist,
                   Prho_list,
                   linestyle='-',
                   linewidth=3,
                   color=color,
                   marker=marker,
                   markersize=10,
                   label='spectral radius, L=' + str(Nsteps))
        plt.loglog(xlist, [item for item in predict_list],
                   linestyle='--',
                   linewidth=2,
                   color=color,
                   marker=marker,
                   markersize=10,
                   label='estimate, L=' + str(Nsteps))

    ax = plt.gca()
    ax.invert_xaxis()

    plt.xlabel('time-step size')
    plt.ylabel('spectral radius')
    plt.legend(loc=3, numpoints=1)
    plt.grid()
    plt.ylim([1E-02, 1E01])

    if type(lam) is complex:
        fname = 'data/smoother_specrad_to0_L64+256_M' + str(
            Nnodes) + 'LU_imag.png'
    else:
        fname = 'data/smoother_specrad_to0_L64+256_M' + str(
            Nnodes) + 'LU_real.png'
    plt.savefig(fname, rasterized=True, transparent=True, bbox_inches='tight')
Esempio n. 14
0
 def __init__(self, M, dt):
     coll = CollGaussRadau_Right(M, 0, 1)
     self.Q = jnp.array(coll.Qmat[1:, 1:])
     self.M = M
     self.dt = dt
Esempio n. 15
0
    def __init__(
            self,
            M=None,
            dt=None,
            restol=None,
            prec=None,
            seed=None,
            lambda_real_interval=[-100, 0],
            lambda_imag_interval=[0, 0],
            lambda_real_interpolation_interval=None,
            norm_factor=1,
            residual_weight=0.5,
            step_penalty=0.1,
            reward_iteration_only=None,
            reward_strategy='iteration_only',
            collect_states=False,
            use_doubles=True,
            do_scale=True,
            example=1,  #0 = TestEquation
            nvars=1000,
            dtype=np.complex128,  #np.float64,
            model=None,
            params=None,
            nu=None,
            imex=True):

        self.np_random = None
        self.niter = None
        self.restol = restol
        self.dt = dt
        self.M = M
        self.coll = CollGaussRadau_Right(M, 0, 1)
        self.num_nodes = self.coll.num_nodes
        self.Q = self.coll.Qmat[1:, 1:]
        self.C = None
        self.lam = None
        #self.u0 = np.ones(M, dtype=np.complex128)
        self.old_res = None
        self.prec = prec
        self.initial_residual = None
        self.initial_residual_time = None

        if (example == 0):
            print("running TEST-Equation")
            self.nvars = 1

        elif (example == 1):
            print("running Heat-Equation")
            self.nvars = nvars

        self.nu = nu
        self.imex = imex
        self.model = model
        self.params = params
        self.example = example
        self.dtype = np.complex128
        self.prob = None
        self.linear = False
        self.lambda_real_interval = lambda_real_interval
        self.lambda_real_interval_reversed = list(
            reversed(lambda_real_interval))
        self.lambda_imag_interval = lambda_imag_interval
        self.lambda_real_interpolation_interval = \
            lambda_real_interpolation_interval

        self.norm_factor = norm_factor
        self.residual_weight = residual_weight
        self.step_penalty = step_penalty
        if reward_iteration_only is None:
            self.reward_strategy = reward_strategy.lower()
        elif reward_iteration_only:
            self.reward_strategy = 'iteration_only'
        else:
            self.reward_strategy = 'residual_change'
        self.collect_states = False  #collect_states
        self.do_scale = do_scale

        self.num_episodes = 0
        # self.rewards = []
        # self.episode_rewards = []
        # self.norm _resids = []
        # Setting the spaces: both are continuous, observation box
        # artificially bounded by some large numbers
        # note that because lambda can be complex, U can be complex,
        # i.e. the observation space should be complex
        self.observation_space = spaces.Box(
            low=-1E10,
            high=+1E10,
            shape=(M * self.nvars * 2, self.max_iters) if collect_states else
            (2, M * self.nvars),
            dtype=np.complex128,
        )
        # I read somewhere that the actions should be scaled to [-1,1],
        # values will be real.
        self.action_space = spaces.Box(
            low=-1.0,
            high=1.0,
            shape=(M, ),
            dtype=np.float64 if use_doubles else np.float32,
        )

        self.seed(seed)
        self.state = None
        if collect_states:
            self.old_states = np.zeros((M * 2, self.max_iters),
                                       dtype=np.complex128)
Esempio n. 16
0
def SDC():

    M = 9
    Mc = int((M + 1) / 2)
    # Mc = 1
    dt = 1.0
    lamb = -1.0
    tol = 1E-10

    coll = CollGaussRadau_Right(M, 0, 1)
    collc = CollGaussRadau_Right(Mc, 0, 1)
    collc2 = CollGaussRadau_Right(1, 0, 1)

    Q = coll.Qmat[1:,1:]
    Qc = collc.Qmat[1:,1:]
    Qc2 = collc2.Qmat[1:,1:]

    _, _, U = sp.linalg.lu(Q.T, overwrite_a=False)
    Qd = U.T
    _, _, U = sp.linalg.lu(Qc.T, overwrite_a=False)
    Qdc = U.T
    _, _, U = sp.linalg.lu(Qc2.T, overwrite_a=False)
    Qdc2 = U.T

    # Qd = np.zeros((M, M))
    # Qdc = np.zeros((Mc,Mc))

    I = get_transfer_matrix_Q(coll.nodes, collc.nodes)
    R = get_transfer_matrix_Q(collc.nodes, coll.nodes)

    Id = np.eye(M)
    #
    # print(I)
    # print(R)

    C = Id - dt * lamb * Q
    Cc = np.eye(Mc) - dt * lamb * Qc
    Cc2 = np.eye(1) - dt * lamb * Qc2
    P = Id - dt * lamb * Qd
    Pc = np.eye(Mc) - dt * lamb * Qdc
    Pc2 = np.eye(1) - dt * lamb * Qdc2
    Pinv = np.linalg.inv(P)
    Pcinv = np.linalg.inv(Pc)

    u0 = 1.0
    u = np.zeros(M, dtype=np.complex128)
    u[0] = u0

    res = C.dot(u) - np.ones(M) * u0
    k = 0
    while np.linalg.norm(res, np.inf) > tol and k < 100:
        u += Pinv.dot(np.ones(M) * u0 - C.dot(u))
        res = C.dot(u) - np.ones(M) * u0
        k += 1
        print(k, np.linalg.norm(res, np.inf))
    print()

    I2 = get_transfer_matrix_Q(collc.nodes, collc2.nodes)
    R2 = get_transfer_matrix_Q(collc2.nodes, collc.nodes)

    K = k
    E = np.zeros((K, K))
    np.fill_diagonal(E[1:, :], 1)

    S = np.kron(np.eye(K), P) - np.kron(E, P - C)

    Rfull = np.kron(np.eye(K), R)
    Ifull = np.kron(np.eye(K), I)
    R2full = np.kron(np.eye(K), R2)
    I2full = np.kron(np.eye(K), I2)
    # Sc = Rfull.dot(S).dot(Ifull)
    Sc = np.kron(np.eye(K), Pc) - np.kron(E, Pc - Cc)
    Sc2 = np.kron(np.eye(K), Pc2) - np.kron(E, Pc2 - Cc2)
    Scinv = np.linalg.inv(Sc)
    Sinv = np.linalg.inv(S)

    Sc2inv = np.linalg.inv(Sc2)

    Sdiaginv = np.linalg.inv(np.kron(np.eye(K), P))
    Scdiaginv = np.linalg.inv(np.kron(np.eye(K), Pc))
    Sc2diaginv = np.linalg.inv(np.kron(np.eye(K), Pc2))
    u0vec = np.kron(np.ones(K), np.ones(M) * u0)
    u = np.zeros(M * K, dtype=np.complex128)
    l = 0
    res = C.dot(u[-M:]) - np.ones(M) * u0
    while np.linalg.norm(res, np.inf) > tol and l < K:
        # u += Sainv.dot(u0vec - S.dot(u))
        u += Sdiaginv.dot(u0vec - S.dot(u))
        uH = Rfull.dot(u)
        uHold = uH.copy()
        rhsH = Rfull.dot(u0vec) + Sc.dot(uH) - Rfull.dot(S.dot(u))
        uH = Scinv.dot(rhsH)
        # uH += Scdiaginv.dot(rhsH - Sc.dot(uH))
        # uH2 = R2full.dot(uH)
        # uH2old = uH2.copy()
        # rhsH2 = R2full.dot(rhsH) + Sc2.dot(uH2) - R2full.dot(Sc.dot(uH))
        # uH2 = Sc2inv.dot(rhsH2)
        # uH += I2full.dot(uH2 - uH2old)
        # uH += Scdiaginv.dot(rhsH - Sc.dot(uH))
        u += Ifull.dot(uH - uHold)
        u += Sdiaginv.dot(u0vec - S.dot(u))
        res = C.dot(u[-M:]) - np.ones(M) * u0
        l += 1
        print(l, np.linalg.norm(res, np.inf))
    print()

    Ea = E.copy()
    Ea[0, -1] = 1E+00
    Sa = np.kron(np.eye(K), P) - np.kron(Ea, P - C)
    Sainv = np.linalg.inv(Sa)

    u0vec = np.kron(np.ones(K), np.ones(M) * u0)
    u = np.zeros(M * K, dtype=np.complex128)
    l = 0
    res = C.dot(u[-M:]) - np.ones(M) * u0
    while np.linalg.norm(res, np.inf) > tol and l < K:
        u += Sainv.dot(u0vec - S.dot(u))
        res = C.dot(u[-M:]) - np.ones(M) * u0
        l += 1
        print(l, np.linalg.norm(res, np.inf))
    print()

    Da, Va = np.linalg.eig(Ea)
    Da = np.diag(Da)
    Vainv = np.linalg.inv(Va)
    # print(Ea - Va.dot(Da).dot(Vainv))
    # exit()

    Dafull = np.kron(np.eye(K), P) - np.kron(Da, P - C)
    # Dafull = Ifull.dot(np.kron(np.eye(K), Pc) - np.kron(Da, Pc - Cc)).dot(Rfull)
    Dafull = np.kron(np.eye(K) - Da, np.eye(M) - P)
    DaPfull = np.kron(np.eye(K), P)
    Dafullinv = np.linalg.inv(Dafull)
    DaPfullinv = np.linalg.inv(DaPfull)
    Vafull = np.kron(Va, np.eye(M))
    Vafullinv = np.kron(Vainv, np.eye(M))

    u0vec = np.kron(np.ones(K), np.ones(M) * u0)
    u = np.zeros(M * K, dtype=np.complex128)
    l = 0
    res = C.dot(u[-M:]) - np.ones(M) * u0
    while np.linalg.norm(res, np.inf) > tol and l < K:
        rhs = Vafullinv.dot(u0vec - Sa.dot(u))
        # x = np.zeros(u.shape, dtype=np.complex128)
        # x += DaPfullinv.dot(rhs - Dafull.dot(x))
        # x += DaPfullinv.dot(rhs - Dafull.dot(x))
        # x += DaPfullinv.dot(rhs - Dafull.dot(x))
        # x += DaPfullinv.dot(rhs - Dafull.dot(x))
        # u += x
        u += Dafullinv.dot(rhs)
        u = Vafull.dot(u)
        res = C.dot(u[-M:]) - np.ones(M) * u0
        l += 1
        print(l, np.linalg.norm(res, np.inf))
    print()
Esempio n. 17
0
    def step_(self, action):

        num_nodes = self.num_nodes
        dt = 0.01  #self.dt
        N = self.nvars
        restol = 1e-6
        L = 10
        nu = self.nu
        c = 1.
        dx = L / N
        x = np.arange(-L / 2, L / 2, dx)

        kx = np.arange(-L / 2, L / 2, dx)
        for i in range(0, len(kx)):
            kx[i] = 2 * np.pi / L * i

        ddx = 2 * np.pi * np.fft.fftfreq(N, d=dx)
        lap = -np.power(ddx, 2)

        k_impl = nu * lap
        k_expl = -c * ddx

        freq = 1
        _u0 = np.arange(-L / 2, L / 2, dx)
        omega = 2.0 * np.pi * freq
        _u0 = np.sin(omega * (x - c * 0)) * np.exp(0 * nu * omega**2)

        #_u0 = u_exact_ad(0)

        u0hat = np.fft.fft(_u0)

        k = k_impl + k_expl
        coll = CollGaussRadau_Right(num_nodes, 0, 1)
        Q = coll.Qmat[1:, 1:]
        C = identity(num_nodes * N) - dt * scipy.sparse.kron(Q, sp.diags(k))
        u0 = np.concatenate((_u0, _u0, _u0), axis=None)
        u = np.concatenate((_u0, _u0, _u0), axis=None)

        Qdmat = np.zeros_like(Q)
        np.fill_diagonal(Qdmat, x)

        QD_expl = np.zeros(Q.shape)

        for m in range(coll.num_nodes):
            QD_expl[m, 0:m] = coll.delta_m[0:m]

        iterationen = []
        j = 0
        it = 0
        for i in k:
            u0 = [u0hat[j], u0hat[j], u0hat[j]]
            u = [u0hat[j], u0hat[j], u0hat[j]]

            C = identity(self.num_nodes) - self.dt * self.Q * i
            mat0 = []
            mat1 = []
            mat2 = []
            mat0 = np.concatenate(
                (mat0, self.model(self.params, k_impl[j])[0][0] * k_impl[j]),
                axis=None)
            mat1 = np.concatenate(
                (mat1, self.model(self.params, k_impl[j])[0][1] * k_impl[j]),
                axis=None)
            mat2 = np.concatenate(
                (mat2, self.model(self.params, k_impl[j])[0][2] * k_impl[j]),
                axis=None)
            mat = sp.diags(np.concatenate((mat0, mat1, mat2), axis=None))

            Pinv = np.linalg.inv(
                np.eye(self.coll.num_nodes) - self.dt * mat -
                dt * QD_expl * k_expl[j], )

            residual = np.squeeze(np.array(u0 - C @ u))
            done = False
            err = False
            self.niter = 0

            while not done and not self.niter >= self.max_iters and not err:
                self.niter += 1

                u = np.squeeze(
                    np.array(u + Pinv @ np.squeeze(np.array((u0 - C @ u)))))

                residual = np.squeeze(np.array(u0 - C @ u))
                norm_res = np.linalg.norm(residual, np.inf)
                #print(norm_res)
                if np.isnan(norm_res) or np.isinf(norm_res):
                    self.niter = 51
                    break
                done = norm_res < self.restol
            #print(i, self.niter)
            it = max(it, self.niter)
            iterationen.append(self.niter)
            j = j + 1

        reward = -1

        done = True
        print("iterationen", iterationen)

        self.state = (u, residual)

        info = {
            'residual': norm_res,
            'niter': it,  #self.niter,#iterationen,
            'lam': self.lam,  #-k, #self.lam,
            'nvars': self.nvars,
            'nu': self.nu
        }

        return (self.state, reward, done, info)
Esempio n. 18
0
    def __init__(
        self,
        M=None,
        dt=None,
        restol=None,
        prec=None,
        seed=None,
        batch_size=None,
        lambda_real_interval=[-100, 0],
        lambda_imag_interval=[0, 0],
        lambda_real_interpolation_interval=None,
        norm_factor=1,
        residual_weight=0.5,
        step_penalty=0.1,
        reward_iteration_only=None,
        reward_strategy='iteration_only',
        collect_states=False,
        use_doubles=True,
        do_scale=True,
    ):

        self.rng_key = None
        self.niter = None
        self.restol = restol
        self.dt = dt
        self.M = M
        self.coll = CollGaussRadau_Right(M, 0, 1)
        self.Q = self.coll.Qmat[1:, 1:]
        self.C = None
        self.lam = None
        self.u0 = jnp.ones(M, dtype=jnp.complex128)
        self.old_res = None
        self.prec = prec
        self.initial_residual = None
        self.batch_size = batch_size

        self.lambda_real_interval = jnp.array(lambda_real_interval)
        self.lambda_real_interval_reversed = list(
            reversed(lambda_real_interval))
        self.lambda_imag_interval = jnp.array(lambda_imag_interval)
        self.lambda_real_interpolation_interval = \
            lambda_real_interpolation_interval

        self.norm_factor = norm_factor
        self.residual_weight = residual_weight
        self.step_penalty = step_penalty
        if reward_iteration_only is None:
            self.reward_strategy = reward_strategy.lower()
        elif reward_iteration_only:
            self.reward_strategy = 'iteration_only'
        else:
            self.reward_strategy = 'residual_change'
        self.collect_states = collect_states
        self.do_scale = do_scale

        self.num_episodes = 0
        # self.rewards = []
        # self.episode_rewards = []
        # self.norm_resids = []
        # Setting the spaces: both are continuous, observation box
        # artificially bounded by some large numbers
        # note that because lambda can be complex, U can be complex,
        # i.e. the observation space should be complex
        self.observation_space = spaces.Box(
            low=-1E10,
            high=+1E10,
            shape=((batch_size, M * 2, self.max_iters) if collect_states else
                   (batch_size, 2 * M)),
            # shape=(M * 2, self.max_iters) if collect_states else (2 * M + 1),
            dtype=jnp.complex128,
        )
        # I read somewhere that the actions should be scaled to [-1,1],
        # values will be real.
        self.action_space = spaces.Box(
            low=-1.0,
            high=1.0,
            shape=(batch_size, M),
            dtype=jnp.float64 if use_doubles else jnp.float32,
        )

        self.seed(seed)
        self.state = None
        if collect_states:
            self.old_states = jnp.zeros((batch_size, M * 2, self.max_iters),
                                        dtype=jnp.complex128)
        self._setup_jit()
Esempio n. 19
0
    def step(self, action):

        num_nodes = self.num_nodes
        dt = 0.01  #self.dt
        N = self.nvars
        restol = 1e-8
        L = 10
        nu = 10
        c = 1.
        dx = L / N
        x = np.arange(-L / 2, L / 2, dx)
        nu = self.nu

        kx = np.arange(-L / 2, L / 2, dx)
        for i in range(0, len(kx)):
            kx[i] = 2 * np.pi / L * i

        ddx = 2 * np.pi * np.fft.fftfreq(N, d=dx)
        lap = -np.power(ddx, 2)

        k_impl = nu * lap
        k_expl = -c * ddx

        freq = 1
        _u0 = np.arange(-L / 2, L / 2, dx)
        omega = 2.0 * np.pi * freq
        _u0 = np.sin(omega * (x - c * 0)) * np.exp(0 * nu * omega**2)

        #_u0 = u_exact_ad(0)

        u0hat = np.fft.fft(_u0)

        k = k_impl + k_expl
        coll = CollGaussRadau_Right(num_nodes, 0, 1)
        Q = coll.Qmat[1:, 1:]
        C = identity(num_nodes * N) - dt * scipy.sparse.kron(Q, sp.diags(k))
        u0 = np.concatenate((u0hat.copy(), u0hat.copy(), u0hat.copy()),
                            axis=None)
        u = np.concatenate((u0hat.copy(), u0hat.copy(), u0hat.copy()),
                           axis=None)

        #print("u", u)
        Qdmat = np.zeros_like(Q)
        MIN = [
            0.3203856825077055,
            0.1399680686269595,
            0.3716708461097372,
        ]
        np.fill_diagonal(Qdmat, MIN)

        QD_expl = np.zeros(Q.shape)

        for m in range(coll.num_nodes):
            QD_expl[m, 0:m] = coll.delta_m[0:m]

        #C = identity(self.num_nodes*self.nvars) -  self.dt *  scipy.sparse.kron(self.Q, sp.diags(k))

        if self.prec is None:

            if nu == 0:
                print("k_impl", k_impl)
            mat0 = []
            mat1 = []
            mat2 = []

            if self.imex:
                for i in k_impl:
                    #mat0 = np.concatenate((mat0, MIN[0]*i), axis=None)
                    #mat1 = np.concatenate((mat1, MIN[1]*i), axis=None)
                    #mat2 = np.concatenate((mat2, MIN[2]*i), axis=None)
                    mat0 = np.concatenate(
                        (mat0, self.model(self.params, i)[0][0] * i),
                        axis=None)
                    mat1 = np.concatenate(
                        (mat1, self.model(self.params, i)[0][1] * i),
                        axis=None)
                    mat2 = np.concatenate(
                        (mat2, self.model(self.params, i)[0][2] * i),
                        axis=None)
            else:
                for i in k:
                    #mat0 = np.concatenate((mat0, MIN[0]*i), axis=None)
                    #mat1 = np.concatenate((mat1, MIN[1]*i), axis=None)
                    #mat2 = np.concatenate((mat2, MIN[2]*i), axis=None)
                    mat0 = np.concatenate(
                        (mat0, self.model(self.params, i)[0][0] * i),
                        axis=None)
                    mat1 = np.concatenate(
                        (mat1, self.model(self.params, i)[0][1] * i),
                        axis=None)
                    mat2 = np.concatenate(
                        (mat2, self.model(self.params, i)[0][2] * i),
                        axis=None)

            mat = sp.diags(
                np.concatenate((mat0, mat1, mat2), axis=None)
            )  # +  scipy.sparse.kron(QD_expl, sp.diags(k_expl)) #sp.diags(np.concatenate((mat0_, mat1_, mat2_), axis=None)  )

        else:
            scaled_action = self._scale_action(action)
            Qdmat = self._get_prec(scaled_action=scaled_action)
            if self.imex:
                if self.prec.upper() == 'LU':
                    mat = scipy.sparse.kron(
                        Qdmat, sp.diags(k_impl)) + scipy.sparse.kron(
                            QD_expl, sp.diags(k_expl))
                else:
                    mat = scipy.sparse.kron(Qdmat, sp.diags(k_impl))
            else:
                mat = scipy.sparse.kron(Qdmat, sp.diags(k))

        Pinv = np.linalg.inv(
            np.eye(self.coll.num_nodes * self.nvars) - dt * mat, )

        residual = u0 - C @ u

        done = False
        err = False
        self.niter = 0

        while not done and not self.niter >= self.max_iters and not err:
            self.niter += 1
            #print(u.shape, u0.shape, Pinv.shape, C.shape)
            u = np.squeeze(np.array(u + Pinv @ (u0 - C @ u)))

            residual = np.squeeze(np.array(u0 - C @ u))
            norm_res = np.linalg.norm(residual, np.inf)
            print(norm_res)
            if np.isnan(norm_res) or np.isinf(norm_res):
                self.niter = 51
                break
            done = norm_res < self.restol

        u = u.reshape(self.coll.num_nodes, self.nvars)

        u__ = np.zeros_like(u)

        for i in range(len(u)):
            u__[i] = np.fft.ifft(u[i])

        reward = -1

        done = True

        _u = u.reshape(self.num_nodes, self.nvars)
        _res = residual.reshape(self.num_nodes, self.nvars)

        self.state = (u, residual)

        if self.collect_states and self.niter < 50:
            self.old_states[:, self.niter] = np.concatenate(
                (np.array([
                    np.linalg.norm(_u[i], np.inf)
                    for i in range(self.num_nodes)
                ]),
                 np.array([
                     np.linalg.norm(_res[i], np.inf)
                     for i in range(self.num_nodes)
                 ])))

        info = {
            'residual': norm_res,
            'niter': self.niter,
            'nvars': self.nvars,  #self.lam,
            'nu': self.nu
        }
        if self.collect_states:
            return (self.old_states, reward, done, info)
        else:
            return ((np.array([
                np.linalg.norm(_u[i], np.inf) for i in range(self.num_nodes)
            ]),
                     np.array([
                         np.linalg.norm(_res[i], np.inf)
                         for i in range(self.num_nodes)
                     ])), reward, done, info
                    )  #(self.state, reward, done, info)
Esempio n. 20
0
def main():
    def rho(x):
        return max(abs(np.linalg.eigvals(np.eye(M) - np.diag([x[i] for i in range(M)]).dot(coll.Qmat[1:, 1:]))))

    M = 2

    coll = CollGaussRadau_Right(M, 0, 1)

    x0 = np.ones(M)
    d = opt.minimize(rho, x0, method='Nelder-Mead')
    print(d)

    numsteps = 800
    xdim = np.linspace(0, 8, numsteps)
    ydim = np.linspace(0, 13, numsteps)

    minfield = np.zeros((len(xdim), len(ydim)))

    for idx, x in enumerate(xdim):
        for idy, y in enumerate(ydim):
            minfield[idx, idy] = max(abs(np.linalg.eigvals(np.eye(M) - np.diag([x, y]).dot(coll.Qmat[1:, 1:]))))

    # Set up plotting parameters
    params = {'legend.fontsize': 20,
              'figure.figsize': (12, 8),
              'axes.labelsize': 20,
              'axes.titlesize': 20,
              'xtick.labelsize': 16,
              'ytick.labelsize': 16,
              'lines.linewidth': 3
              }
    plt.rcParams.update(params)
    matplotlib.style.use('classic')

    plt.figure()
    plt.pcolor(xdim, ydim, minfield.T, cmap='Reds', vmin=0, vmax=1)
    plt.text(d.x[0], d.x[1], 'X', horizontalalignment='center', verticalalignment='center')
    plt.xlim((min(xdim), max(xdim)))
    plt.ylim((min(ydim), max(ydim)))
    plt.xlabel('component 1')
    plt.ylabel('component 2')
    cbar = plt.colorbar()
    cbar.set_label('spectral radius')

    fname = 'data/parallelSDC_minimizer_full.png'
    plt.savefig(fname, rasterized=True, bbox_inches='tight')

    plt.figure()
    xdim_part = xdim[int(0.25 * numsteps):int(0.75 * numsteps) + 1]
    ydim_part = ydim[0:int(0.25 * numsteps)]
    minfield_part = minfield[int(0.25 * numsteps):int(0.75 * numsteps) + 1, 0:int(0.25 * numsteps)]
    plt.pcolor(xdim_part, ydim_part, minfield_part.T, cmap='Reds', vmin=0, vmax=1)
    plt.text(d.x[0], d.x[1], 'X', horizontalalignment='center', verticalalignment='center')
    plt.xlim((min(xdim_part), max(xdim_part)))
    plt.ylim((min(ydim_part), max(ydim_part)))
    plt.xlabel('component 1')
    plt.ylabel('component 2')
    cbar = plt.colorbar()
    cbar.set_label('spectral radius')

    fname = 'data/parallelSDC_minimizer_zoom.png'
    plt.savefig(fname, rasterized=True, bbox_inches='tight')
Esempio n. 21
0
def evaluate(solution):
    x = solution["parameters"]

    mf = 3

    collf = CollGaussRadau_Right(num_nodes=mf, tleft=0.0, tright=1.0)
    # coll = CollGaussLobatto(num_nodes=m, tleft=0.0, tright=1.0)
    # coll = EquidistantNoLeft(num_nodes=m, tleft=0.0, tright=1.0)

    Qf = collf.Qmat[1:, 1:]
    Idf = np.eye(mf)

    QT = Qf.T
    [_, _, U] = scipy.linalg.lu(QT, overwrite_a=True)
    Qdf = U.T

    mc = int((mf + 1) / 2)

    collc = CollGaussRadau_Right(num_nodes=mc, tleft=0.0, tright=1.0)
    # coll = CollGaussLobatto(num_nodes=m, tleft=0.0, tright=1.0)
    # coll = EquidistantNoLeft(num_nodes=m, tleft=0.0, tright=1.0)

    Qc = collc.Qmat[1:, 1:]
    Idc = np.eye(mc)

    QT = Qc.T
    [_, _, U] = scipy.linalg.lu(QT, overwrite_a=True)
    Qdc = U.T

    sum1r = x['x11r'] + x['x12r'] + x['x13r']
    sum2r = x['x21r'] + x['x22r'] + x['x23r']
    sum1i = x['x11i'] + x['x12i']
    sum2i = x['x21i'] + x['x22i']
    sum3i = x['x31i'] + x['x32i']

    if sum1r == 0.0 or sum2r == 0.0 or sum1i == 0.0 or sum2i == 0.0 or sum3i == 0.0:
        solution["metrics"] = {}
        solution["metrics"]["rho"] = 99
        return solution

    Tr = np.array([[x['x11r'] / sum1r, x['x12r'] / sum1r, x['x13r'] / sum1r],
                   [x['x21r'] / sum2r, x['x22r'] / sum2r, x['x23r'] / sum2r]])
    Ti = np.array([[x['x11i'] / sum1i, x['x12i'] / sum1i],
                   [x['x21i'] / sum2i, x['x22i'] / sum2i],
                   [x['x31i'] / sum3i, x['x32i'] / sum3i]])

    # THIS WORKS REALLY WELL! No need to take imaginary parts in x, though (found minimum has zero imaginary parts)
    k = 0
    obj_val = 0.0
    for i in range(-8, 8):
        for l in range(-8, 8):
            k += 1
            lamdt = -10**i + 1j * 10**l
            C = Idf - lamdt * Qf
            Pf = Idf - lamdt * Qdf
            Rf = Idf - np.linalg.inv(Pf).dot(C)
            Pc = Idc - lamdt * Qdc
            Rc = Idf - Ti.dot(np.linalg.inv(Pc)).dot(Tr).dot(C)
            R = Rf.dot(Rc)
            rhoR = max(abs(np.linalg.eigvals(R)))
            obj_val += rhoR

    obj_val /= k

    solution["metrics"] = {}
    solution["metrics"]["rho"] = obj_val
    return solution