Beispiel #1
0
def bicpd(T, R, fixed_factor, options):
    """
    Practically the same as tricpd, but this function keeps the some factor fixed during all the computations. This
    function is to be used as part of the tensor train cpd.
    """

    # INITIALIZE RELEVANT VARIABLES

    # Extract all variable from the class of options.
    initialization = options.initialization
    refine = options.refine
    symm = options.symm
    display = options.display
    tol_mlsvd = options.tol_mlsvd
    bi_method = options.bi_method_parameters[0]
    if type(tol_mlsvd) == list:
        tol_mlsvd = tol_mlsvd[1]

    # Set the other variables.
    m, n, p = T.shape
    Tsize = norm(T)
    ordering = [0, 1, 2]

    # Test consistency of dimensions and rank.
    aux.consistency(R, (m, n, p), options)

    # COMPRESSION STAGE

    if display > 0:
        print(
            '-----------------------------------------------------------------------------------------------'
        )
        print('Computing MLSVD of T')

    # Compute compressed version of T with the MLSVD. We have that T = (U1, U2, U3)*S.
    if display > 2 or display < -1:
        S, U, T1, sigmas, best_error = cmpr.mlsvd(T, Tsize, R, options)
    else:
        S, U, T1, sigmas = cmpr.mlsvd(T, Tsize, R, options)
    R1, R2, R3 = S.shape
    U1, U2, U3 = U

    # When the tensor is symmetric we want S to have equal dimensions.
    if symm:
        R_min = min(R1, R2, R3)
        R1, R2, R3 = R_min, R_min, R_min
        S = S[:R_min, :R_min, :R_min]
        U1, U2, U3 = U1[:, :R_min], U2[:, :R_min], U3[:, :R_min]

    if display > 0:
        if (R1, R2, R3) == (m, n, p):
            if tol_mlsvd == -1:
                print('    No compression and no truncation requested by user')
                print('    Working with dimensions', T.shape)
            else:
                print('    No compression detected')
                print('    Working with dimensions', T.shape)
        else:
            print('    Compression detected')
            print('    Compressing from', T.shape, 'to', S.shape)
        if display > 2:
            print('    Compression relative error = {:7e}'.format(best_error))

    # GENERATION OF STARTING POINT STAGE

    # Generate initial to start dGN.
    if display > 2 or display < -1:
        [X, Y, Z], init_error = init.starting_point(T, Tsize, S, U, R,
                                                    ordering, options)
    else:
        [X, Y, Z] = init.starting_point(T, Tsize, S, U, R, ordering, options)

    # Discard the factor computed in start_point and use the previous one. Then project it on the compressed space.
    if fixed_factor[1] == 0:
        X = dot(U1.T, fixed_factor[0])
        X = [X, 0]
    elif fixed_factor[1] == 1:
        Y = dot(U2.T, fixed_factor[0])
        Y = [Y, 1]
    elif fixed_factor[1] == 2:
        Z = dot(U3.T, fixed_factor[0])
        Z = [Z, 2]

    if display > 0:
        print(
            '-----------------------------------------------------------------------------------------------'
        )
        if type(initialization) == list:
            print('Type of initialization: fixed + user')
        else:
            print('Type of initialization: fixed +', initialization)
        if display > 2:
            if fixed_factor[1] == 0:
                S_init = cnv.cpd2tens([X[0], Y, Z])
            elif fixed_factor[1] == 1:
                S_init = cnv.cpd2tens([X, Y[0], Z])
            elif fixed_factor[1] == 2:
                S_init = cnv.cpd2tens([X, Y, Z[0]])
            S1_init = cnv.unfold(S_init, 1)
            init_error = mlinalg.compute_error(T, Tsize, S1_init, [U1, U2, U3],
                                               (R1, R2, R3))
            print(
                '    Initial guess relative error = {:5e}'.format(init_error))

    # DAMPED GAUSS-NEWTON STAGE

    if display > 0:
        print(
            '-----------------------------------------------------------------------------------------------'
        )
        print('Computing CPD of T')

    # Compute the approximated tensor in coordinates with dGN or ALS.
    if bi_method == 'als':
        factors, step_sizes_main, errors_main, improv_main, gradients_main, stop_main = \
            als.als(S, [X, Y, Z], R, options)
    else:
        factors, step_sizes_main, errors_main, improv_main, gradients_main, stop_main = \
            gn.dGN(S, [X, Y, Z], R, options)
    X, Y, Z = factors

    # FINAL WORKS

    # Use the orthogonal transformations to obtain the CPD of T.
    if fixed_factor[1] == 0:
        Y = dot(U2, Y)
        Z = dot(U3, Z)
    elif fixed_factor[1] == 1:
        X = dot(U1, X)
        Z = dot(U3, Z)
    elif fixed_factor[1] == 2:
        X = dot(U1, X)
        Y = dot(U2, Y)

    # Compute error.
    T1_approx = empty(T1.shape)
    if fixed_factor[1] == 0:
        T1_approx = cnv.cpd2unfold1(T1_approx, [fixed_factor[0], Y, Z])
    elif fixed_factor[1] == 1:
        T1_approx = cnv.cpd2unfold1(T1_approx, [X, fixed_factor[0], Z])
    elif fixed_factor[1] == 2:
        T1_approx = cnv.cpd2unfold1(T1_approx, [X, Y, fixed_factor[0]])

    # Save and display final information.
    step_sizes_refine = array([0])
    errors_refine = array([0])
    improv_refine = array([0])
    gradients_refine = array([0])
    stop_refine = 5
    output = aux.output_info(T1, Tsize, T1_approx, step_sizes_main,
                             step_sizes_refine, errors_main, errors_refine,
                             improv_main, improv_refine, gradients_main,
                             gradients_refine, stop_main, stop_refine, options)

    if display > 0:
        print(
            '==============================================================================================='
        )
        print('Final results of bicpd')
        if refine:
            print('    Number of steps =', output.num_steps)
        else:
            print('    Number of steps =', output.num_steps)
        print('    Relative error =', output.rel_error)
        acc = float('%.6e' % Decimal(output.accuracy))
        print('    Accuracy = ', acc, '%')

    return X, Y, Z, output
Beispiel #2
0
def tricpd(T, R, options):
    """
    Given a tensor T and a rank R, this function computes an approximated CPD of T with rank R. This function is called
    when the user sets method = 'dGN'.

    Inputs
    ------
    T: float array
    R: int
    options: class
    
    Outputs
    -------
    factors: list of float 2D arrays
    output: class
        This class contains all information needed about the computations made. We summarize these information below.
            num_steps: the total number of steps (iterations) the dGN function used at the two runs.
            accuracy: the accuracy of the solution, which is defined by the formula 100*(1 - rel_error). 0 means 0% of 
                      accuracy (worst case) and 100 means 100% of accuracy (best case).
            rel_error: relative error |T - T_approx|/|T| of the approximation computed. 
            step_sizes: array with the distances between consecutive computed points at each iteration.
            errors: array with the absolute errors of the approximating tensor at each iteration.
            improv: array with the differences between consecutive absolute errors.
            gradients: array with the gradient of the error function at each iteration. We expect that these gradients 
                       converges to zero as we keep iterating since the objective point is a local minimum.
            stop: it is a list of two integers. The first integer indicates why the dGN stopped at the first run, and
                  the second integer indicates why the dGN stopped at the second run (refinement stage). Check the 
                  functions mlsvd and dGN for more information. 
    """

    # INITIALIZE RELEVANT VARIABLES

    # Verify if T is sparse, in which case it will be given as a list with the data.
    if type(T) == list:
        T_orig = deepcopy(T)
        T = deepcopy(T_orig)
        dims_orig = T_orig[2]
    else:
        dims_orig = T.shape
    L = len(dims_orig)

    # Set options.
    initialization = options.initialization
    refine = options.refine
    symm = options.symm
    display = options.display
    tol_mlsvd = options.tol_mlsvd
    method = options.method
    if type(tol_mlsvd) == list:
        tol_mlsvd = tol_mlsvd[0]

    # Change ordering of indexes to improve performance if possible.
    T, ordering = aux.sort_dims(T)
    if type(T) == list:
        Tsize = norm(T[0])
        dims = T[2]
        # If T is sparse, we must use the classic method, and tol_mlsvd is set to the default 1e-16 in the case the
        # user requested -1 or 0.
        if tol_mlsvd < 0:
            tol_mlsvd = 1e-16
            if type(tol_mlsvd) == list:
                options.tol_mlsvd[0] = 1e-16
            else:
                options.tol_mlsvd = 1e-16
    else:
        Tsize = norm(T)
        dims = T.shape

    # COMPRESSION STAGE

    if display > 0:
        print(
            '-----------------------------------------------------------------------------------------------'
        )
        print('Computing MLSVD')

    # Compute compressed version of T with the MLSVD. We have that T = (U_1, ..., U_L)*S.
    if display > 2 or display < -1:
        S, U, T1, sigmas, best_error = cmpr.mlsvd(T, Tsize, R, options)
    else:
        S, U, T1, sigmas = cmpr.mlsvd(T, Tsize, R, options)
    dims_cmpr = S.shape

    # When the tensor is symmetric we want S to have equal dimensions.
    if symm:
        R_min = min(dims_cmpr)
        dims_cmpr = [R_min for l in range(L)]
        dims_cmpr_slices = tuple(slice(R_min) for l in range(L))
        S = S[dims_cmpr_slices]
        U = [U[l][:, :R_min] for l in range(L)]

    if display > 0:
        if dims_cmpr == dims:
            if tol_mlsvd == -1:
                print('    No compression and no truncation requested by user')
                print('    Working with dimensions', dims)
            else:
                print('    No compression detected')
                print('    Working with dimensions', dims)
        else:
            print('    Compression detected')
            print('    Compressing from', dims, 'to', S.shape)
        if display > 2:
            print('    Compression relative error = {:7e}'.format(best_error))

    # GENERATION OF STARTING POINT STAGE

    # Generate initial to start dGN.
    if display > 2 or display < -1:
        init_factors, init_error = init.starting_point(T, Tsize, S, U, R,
                                                       ordering, options)
    else:
        init_factors = init.starting_point(T, Tsize, S, U, R, ordering,
                                           options)

    if display > 0:
        print(
            '-----------------------------------------------------------------------------------------------'
        )
        if type(initialization) == list:
            print('Type of initialization: user')
        else:
            print('Type of initialization:', initialization)
        if display > 2:
            print(
                '    Initial guess relative error = {:5e}'.format(init_error))

    # DAMPED GAUSS-NEWTON STAGE

    if display > 0:
        print(
            '-----------------------------------------------------------------------------------------------'
        )
        print('Computing CPD')

    # Compute the approximated tensor in coordinates with dGN or ALS.
    if method == 'als':
        factors, step_sizes_main, errors_main, improv_main, gradients_main, stop_main = \
            als.als(S, init_factors, R, options)
    else:
        factors, step_sizes_main, errors_main, improv_main, gradients_main, stop_main = \
            gn.dGN(S, init_factors, R, options)

    # Use the orthogonal transformations to work in the original space.
    for l in range(L):
        factors[l] = dot(U[l], factors[l])

    # REFINEMENT STAGE

    # If T is sparse, no refinement is made.
    if type(T) == list:
        refine = False

    if refine:
        if display > 0:
            print()
            print(
                '==============================================================================================='
            )
            print('Computing refinement of solution')

        if display > 2:
            T1_approx = empty(T1.shape)
            T1_approx = cnv.cpd2unfold1(T1_approx, factors)
            init_error = crt.fastnorm(T1, T1_approx) / Tsize
            print(
                '    Initial guess relative error = {:5e}'.format(init_error))

        if display > 0:
            print(
                '-----------------------------------------------------------------------------------------------'
            )
            print('Computing CPD')

        if method == 'als':
            factors, step_sizes_refine, errors_refine, improv_refine, gradients_refine, stop_refine = \
                als.als(T, factors, R, options)
        else:
            factors, step_sizes_refine, errors_refine, improv_refine, gradients_refine, stop_refine = \
                gn.dGN(T, factors, R, options)

    else:
        step_sizes_refine = array([0])
        errors_refine = array([0])
        improv_refine = array([0])
        gradients_refine = array([0])
        stop_refine = 8

    # FINAL WORKS

    # Compute error.
    if type(T1) == ndarray:
        T1_approx = empty(T1.shape)
        T1_approx = cnv.cpd2unfold1(T1_approx, factors)

        # Go back to the original dimension ordering.
        factors = aux.unsort_dims(factors, ordering)

        # Save and display final informations.
        output = aux.output_info(T1, Tsize, T1_approx, step_sizes_main,
                                 step_sizes_refine, errors_main, errors_refine,
                                 improv_main, improv_refine, gradients_main,
                                 gradients_refine, stop_main, stop_refine,
                                 options)
    else:
        # Go back to the original dimension ordering.
        factors = aux.unsort_dims(factors, ordering)

        # Save and display final informations.
        output = aux.output_info(T_orig, Tsize, factors, step_sizes_main,
                                 step_sizes_refine, errors_main, errors_refine,
                                 improv_main, improv_refine, gradients_main,
                                 gradients_refine, stop_main, stop_refine,
                                 options)

    if display > 0:
        print(
            '==============================================================================================='
        )
        print('Final results')
        if refine:
            print('    Number of steps =', output.num_steps)
        else:
            print('    Number of steps =', output.num_steps)
        print('    Relative error =', output.rel_error)
        acc = float('%.6e' % Decimal(output.accuracy))
        print('    Accuracy = ', acc, '%')

    return factors, output