Exemplo n.º 1
0
def super_diagonal_tensor(shape, distr='ones', values=None):
    """ Generates a tensor of any dimension with random or specified numbers across the super-diagonal and zeros elsewhere
    
    Parameters
    ----------
    shape : tuple
        Specifies the dimensions of the tensor
        ``len(shape)`` defines the order of the tensor, whereas its values specify sizes of dimensions of the tensor.
    distr : str, optional
        Specifies the random generation using a class of the numpy.random module
    values : list
        Array of values on the super-diagonal of a tensor

    Returns
    -------
    Tensor
        Generated tensor according to the parameters specified
    """
    if not isinstance(shape, tuple):
        raise TypeError("Parameter `shape` should be passed as a tuple!")
    
    if shape[1:] != shape[:-1]:
            raise ValueError("All values in `shape` should have the same value!")
    inds = shape[0]
    data = np.zeros(shape)
    
    if values is None:
        values = _predefined_distr(distr, inds)
    if len(values) != inds:
        raise ValueError("Dimension mismatch! The specified values do not match "
                         "the specified shape of the tensor provided ({} != {})".format(len(values), inds))
    values = np.asarray(values).flatten()
    np.fill_diagonal(data, values)
    return Tensor(array=data)
Exemplo n.º 2
0
def dense_tensor(shape, distr='uniform', distr_type=0, fxdind=None):
    """ Generates a dense tensor of any dimension and fills it accordingly
    
    Parameters
    ----------
    shape : tuple
        Specifies the dimensions of the tensor
    distr : str, optional
        Specifies the random generation using a class of the numpy.random module
    distr_type : int, optional
        Number of indices to not fix. 0 will be applied globally, 1 will apply to fibers, 2 to slices, etc.
    
    Returns
    -------
    Tensor
        Generated tensor according to the parameters specified
    """

    # fxdind: fixed indices
    if distr_type == 0:
        data = _predefined_distr(distr, shape)
    else:
        data = np.random.uniform(size=shape)
        raise NotImplementedError('Not implemented in dataset (basic) class')
    return Tensor(array=data)
Exemplo n.º 3
0
def sparse_tensor(shape, distr='uniform', distr_type=0, fxdind=None, pct=0.05):
    """ Generates a sparse tensor of any dimension and fills it accordingly
    
    Parameters
    ----------
    shape : tuple
        Specifies the dimensions of the tensor
    distr : str, optional
        Specifies the random generation using a class of the numpy.random module
    distr_type : int, optional
        Number of indices to not fix. 0 will be applied globally, 1 will apply to fibers, 2 to slices, etc.
    pct : float, optional
        Percentage of the dataset to be filled
    
    Returns
    -------
    Tensor
        Generated tensor according to the parameters specified
    """
    data_size = np.product(shape)
    if distr_type == 0:
        number_non_zero_values = int(data_size * pct)
        data = np.zeros(data_size)
        index = np.random.randint(low=0, high=data_size, size=number_non_zero_values)
        data[index] = _predefined_distr(distr, number_non_zero_values)
        data = data.reshape(shape)
    else:
        raise NotImplementedError('Not implemented in dataset (basic) class')

    return Tensor(array=data)
Exemplo n.º 4
0
def super_symmetric_tensor(shape, tensor=None):
    """ Generates a tensor of equal dimensions with random or specified numbers, with a specified tensor.

    Parameters
    ----------
    shape : tuple
        Specifies the dimensions of the tensor
        ``len(shape)`` defines the order of the tensor, whereas its values specify sizes of dimensions of the tensor.
    tensor : Tensor, optional
        Input tensor to be symmetricised

    Returns
    -------
    Tensor
        Generated tensor according to the parameters specified
    """

    dims = len(shape)
    inds = itertools.permutations(np.arange(dims))
    inds = np.array(list(inds))
    data = np.zeros(shape)
    if tensor is None:
        tensor = dense_tensor(shape)
    for i, _ in enumerate(inds):
        data = data + np.transpose(tensor.data, tuple(inds[i, :]))
    return Tensor(array=data)
Exemplo n.º 5
0
def is_toeplitz_tensor(tensor, modes=None):
    """ Checks if ``tensor`` has Toeplitz structure

    Parameters
    ----------
    tensor : Tensor
        Input tensor to check

    Returns
    -------
        Boolean indicating if Toeplitz matrix
    """
    if tensor.order <= 2:
        return is_toeplitz_matrix(tensor.data)
    if modes is None:
        modes = [0, 1]
    sz = np.asarray(tensor.shape)
    availmodes = np.setdiff1d(np.arange(len(sz)), modes)
    for idx, mode in enumerate(availmodes):
        dim = sz[mode]
        #  Go through each dim
        for i in range(dim):
            t = tensor.access(i, mode)
            t = Tensor(t)
            if not (is_toeplitz_tensor(t)):
                print("Wrong slice: \n{}\n{}".format(t, (i, idx)))
                return False
    return True
Exemplo n.º 6
0
    def test_init_fmat(self):
        """ Tests for _init_fmat method """
        np.random.seed(0)
        shape = (4, 5, 6)
        size = reduce(lambda x, y: x * y, shape)
        tensor = Tensor(np.random.randn(size).reshape(shape))
        cpd = CPD()

        # ------ tests on getting factor matrices of the correct shape
        for rank_value in range(min(tensor.shape) - 1, max(tensor.shape) + 2):
            rank = (rank_value, )
            fmat = cpd._init_fmat(tensor=tensor, rank=rank)
            for mode, mat in enumerate(fmat):
                assert mat.shape == (tensor.shape[mode], rank_value)

        # ------ tests for the type of initialisation
        # svd type initialisation should produce factor matrices with orthogonal columns
        rank = (min(tensor.shape) - 1, )
        cpd = CPD(init='svd')
        fmat = cpd._init_fmat(tensor=tensor, rank=rank)
        for mat in fmat:
            result = np.dot(mat.T, mat)
            true_result = np.eye(rank[0])
            np.testing.assert_almost_equal(result, true_result)

        # svd type initialisation but the `rank` is greater then one of the dimensions then you get random fmat
        # and there would be a runtime warning
        rank = (min(tensor.shape) + 1, )
        cpd = CPD(init='svd', verbose=True)
        with pytest.warns(RuntimeWarning):
            fmat = cpd._init_fmat(tensor=tensor, rank=rank)
        for mat in fmat:
            result_1 = np.dot(mat.T, mat)
            result_2 = np.eye(rank[0])
            # since each mat is randomly initialized it is not orthonormal
            with pytest.raises(AssertionError):
                np.testing.assert_almost_equal(result_1, result_2)

        # random type initialisation should produce factor matrices each of which is not orthonormal
        rank = (3, )
        cpd = CPD(init='random')
        fmat = cpd._init_fmat(tensor=tensor, rank=rank)
        for mat in fmat:
            result_1 = np.dot(mat.T, mat)
            result_2 = np.eye(rank[0])
            # since each mat is randomly initialized it is not orthonormal
            with pytest.raises(AssertionError):
                np.testing.assert_almost_equal(result_1, result_2)

        # unknown type of initialisation
        with pytest.raises(NotImplementedError):
            rank = (min(tensor.shape) - 1, )
            cpd = CPD(init='qwerty')
            cpd._init_fmat(tensor=tensor, rank=rank)
Exemplo n.º 7
0
def test_mape():
    """ Tests for mape """
    # ------ tests for 1-d case
    shape = (2, )
    size = shape[0]
    tensor_true = Tensor(np.arange(size).reshape(shape) + 1)
    tensor_pred = Tensor(np.arange(size).reshape(shape) * 2)
    true_mape = 0.5
    result = mape(tensor_true, tensor_pred)
    np.testing.assert_array_almost_equal(result, true_mape)

    # ------ tests for 2-d case
    shape = (2, 2)
    size = reduce(lambda x, y: x * y, shape)
    tensor_true = Tensor(np.arange(size).reshape(shape) + 1)
    tensor_pred = Tensor(np.arange(size).reshape(shape) * 2)
    true_mape = 0.4583333333333333
    result = mape(tensor_true, tensor_pred)
    np.testing.assert_array_almost_equal(result, true_mape)

    # ------ tests for 3-d case
    shape = (2, 2, 2)
    size = reduce(lambda x, y: x * y, shape)
    tensor_true = Tensor(np.arange(size).reshape(shape) + 1)
    tensor_pred = Tensor(np.arange(size).reshape(shape) * 2)
    true_mape = 0.5705357142857143
    result = mape(tensor_true, tensor_pred)
    np.testing.assert_array_almost_equal(result, true_mape)
Exemplo n.º 8
0
def test_rmse():
    """ Tests for rmse """
    # ------ tests for 1-d case
    shape = (2, )
    size = shape[0]
    tensor_true = Tensor(np.arange(size).reshape(shape))
    tensor_pred = Tensor(np.arange(size).reshape(shape) * 2)
    true_rmse = 0.7071067811865476
    result = rmse(tensor_true, tensor_pred)
    np.testing.assert_array_almost_equal(result, true_rmse)

    # ------ tests for 2-d case
    shape = (2, 2)
    size = reduce(lambda x, y: x * y, shape)
    tensor_true = Tensor(np.arange(size).reshape(shape))
    tensor_pred = Tensor(np.arange(size).reshape(shape) * 2)
    true_rmse = 1.8708286933869707
    result = rmse(tensor_true, tensor_pred)
    np.testing.assert_array_almost_equal(result, true_rmse)

    # ------ tests for 3-d case
    shape = (2, 2, 2)
    size = reduce(lambda x, y: x * y, shape)
    tensor_true = Tensor(np.arange(size).reshape(shape))
    tensor_pred = Tensor(np.arange(size).reshape(shape) * 2)
    true_rmse = 4.183300132670378
    result = rmse(tensor_true, tensor_pred)
    np.testing.assert_array_almost_equal(result, true_rmse)
Exemplo n.º 9
0
def test_residual_rel_error():
    # ------ tests for 1-d case
    shape = (2, )
    size = shape[0]
    tensor_true = Tensor(np.arange(size).reshape(shape))
    tensor_pred = Tensor(np.arange(size).reshape(shape) * 2)
    true_res_rel_error = 1
    result = residual_rel_error(tensor_true, tensor_pred)
    np.testing.assert_array_almost_equal(result, true_res_rel_error)

    # ------ tests for 2-d case
    shape = (2, 2)
    size = reduce(lambda x, y: x * y, shape)
    tensor_true = Tensor(np.arange(size).reshape(shape))
    tensor_pred = Tensor(np.arange(size).reshape(shape) * 2)
    true_res_rel_error = 1
    result = residual_rel_error(tensor_true, tensor_pred)
    np.testing.assert_array_almost_equal(result, true_res_rel_error)

    # ------ tests for 3-d case
    shape = (2, 2, 2)
    size = reduce(lambda x, y: x * y, shape)
    tensor_true = Tensor(np.arange(size).reshape(shape))
    tensor_pred = Tensor(np.arange(size).reshape(shape) * 2)
    true_res_rel_error = 1
    result = residual_rel_error(tensor_true, tensor_pred)
    np.testing.assert_array_almost_equal(result, true_res_rel_error)
Exemplo n.º 10
0
def residual_tensor(tensor_orig, tensor_approx):
    """ Residual tensor

    Parameters
    ----------
    tensor_orig : Tensor
    tensor_approx : {Tensor, TensorCPD, TensorTKD, TensorTT}

    Returns
    -------
    residual : Tensor
    """
    if not isinstance(tensor_orig, Tensor):
        raise TypeError("Unknown data type of original tensor.\n"
                        "The available type for `tensor_A` is `Tensor`")
    # TODO: make use of direct subtraction of tensors
    if isinstance(tensor_approx, Tensor):
        residual = Tensor(tensor_orig.data - tensor_approx.data)
    elif isinstance(tensor_approx, BaseTensorTD):
        residual = Tensor(tensor_orig.data - tensor_approx.reconstruct().data)
    else:
        raise TypeError("Unknown data type of the approximation tensor!\n"
                        "The available types for `tensor_B` are `Tensor`,  `TensorCPD`,  `TensorTKD`,  `TensorTT`")
    return residual
Exemplo n.º 11
0
    def test_init(self):
        """ Tests for constructor of BaseCPD class """
        # Even though we can create such object we shouldn't do that
        default_params = dict(init='svd',
                              max_iter=50,
                              epsilon=10e-3,
                              tol=10e-5,
                              random_state=None,
                              verbose=False)

        # basically for coverage tests object of
        with pytest.raises(NotImplementedError):
            tensor = Tensor(np.arange(2))
            rank = 5
            keep_meta = 0
            base_cpd = BaseCPD(**default_params)
            base_cpd.decompose(tensor, rank, keep_meta)
        with pytest.raises(NotImplementedError):
            base_cpd = BaseCPD(**default_params)
            base_cpd.plot()
Exemplo n.º 12
0
def super_diag_tensor(shape, values=None):
    """ Super-diagonal tensor of the specified `order`.

    Parameters
    ----------
    shape : tuple
        Desired shape of the tensor.
        ``len(shape)`` defines the order of the tensor, whereas its values specify sizes of dimensions of the tensor.
    values : np.ndarray
        Array of values on the super-diagonal of a tensor. By default contains only ones.
        Length of this vector defines Kryskal rank which is equal to ``shape[0]``.

    Returns
    -------
    tensor : Tensor
    """
    order = len(shape)
    rank = shape[0]

    if not isinstance(shape, tuple):
        raise TypeError("Parameter `shape` should be passed as a tuple!")
    if not all(mode_size == shape[0] for mode_size in shape):
        raise ValueError("All values in `shape` should have the same value!")

    if values is None:
        values = np.ones(rank)  # set default values
    elif isinstance(values, np.ndarray):
        if values.ndim != 1:
            raise ValueError("The `values` should be 1-dimensional numpy array!")
        if values.size != rank:
            raise ValueError("Dimension mismatch! Not enough or too many `values` for the specified `shape`:\n"
                             "{} != {} (values.size != shape[0])".format(values.size, rank))
    else:
        raise TypeError("The `values` should be passed as a numpy array!")

    core = np.zeros(shape)
    core[np.diag_indices(rank, ndim=order)] = values
    tensor = Tensor(core)
    return tensor
Exemplo n.º 13
0
 def _reconstruct(fmat_a, fmat_b, n_mat):
     """ Reconstruct the tensor and matrix after the coupled factorisation
     Parameters
     ----------
     fmat_a : List(np.ndarray)
         Multidimensional data obtained from the factorisation
     fmat_b : List(np.ndarray)
         Multidimensional data obtained from the factorisation
     n_mat : int
         Number of matrices provided to fuse
     Returns
     -------
     (core_tensor, lrecon) : np.ndarray or List(np.ndarray)
         Reconstructed tensor and list of matrices obtained from the factorisation
     """
     core_values = np.repeat(np.array([1]), fmat_a[0].shape[1])
     _r = (fmat_a[0].shape[1], )
     core_shape = _r * len(fmat_a)
     core_tensor = super_diag_tensor(core_shape, values=core_values)
     for mode, fmat in enumerate(fmat_a):
         core_tensor.mode_n_product(fmat, mode=mode, inplace=True)
     lrecon = [Tensor(fmat_a[i].dot(fmat_b[i].T)) for i in range(n_mat)]
     return core_tensor, lrecon
Exemplo n.º 14
0
def test_residual_tensor():
    """ Tests for computing/creating a residual tensor """
    true_default_mode_names = ['mode-0', 'mode-1', 'mode-2']

    # ------ tests for residual tensor with the Tensor
    array_3d = np.array([[[0,  1,  2,  3],
                          [4,  5,  6,  7],
                          [8,  9, 10, 11]],

                         [[12, 13, 14, 15],
                          [16, 17, 18, 19],
                          [20, 21, 22, 23]]])
    true_residual_data = np.zeros(array_3d.shape)
    tensor_1 = Tensor(array=array_3d)
    tensor_2 = Tensor(array=array_3d)
    residual = residual_tensor(tensor_orig=tensor_1, tensor_approx=tensor_2)
    assert isinstance(residual, Tensor)
    assert (residual.mode_names == true_default_mode_names)
    np.testing.assert_array_equal(residual.data, true_residual_data)

    # ------ tests for residual tensor with the TensorCPD
    array_3d = np.array([[[100., 250., 400., 550.],
                          [250., 650., 1050., 1450.],
                          [400., 1050., 1700., 2350.]],

                         [[250., 650., 1050., 1450.],
                          [650., 1925., 3200., 4475.],
                          [1050., 3200., 5350., 7500.]]]
                        )
    true_residual_data = np.zeros(array_3d.shape)
    tensor = Tensor(array=array_3d)
    ft_shape = (2, 3, 4)    # define shape of the tensor in full form
    R = 5                   # define Kryskal rank of a tensor in CP form
    core_values = np.ones(R)
    fmat = [np.arange(orig_dim * R).reshape(orig_dim, R)
            for orig_dim in ft_shape]
    tensor_cpd = TensorCPD(fmat=fmat, core_values=core_values)
    residual = residual_tensor(tensor_orig=tensor, tensor_approx=tensor_cpd)
    assert isinstance(residual, Tensor)
    assert (residual.mode_names == true_default_mode_names)
    np.testing.assert_array_equal(residual.data, true_residual_data)

    # ------ tests for residual tensor with the TensorTKD
    array_3d = np.array([[[378,   1346,   2314,   3282,   4250],
                          [1368,   4856,   8344,  11832,  15320],
                          [2358,   8366,  14374,  20382,  26390],
                          [3348,  11876,  20404,  28932,  37460]],

                         [[1458,   5146,   8834,  12522,  16210],
                          [5112,  17944,  30776,  43608,  56440],
                          [8766,  30742,  52718,  74694,  96670],
                          [12420,  43540,  74660, 105780, 136900]],

                         [[2538,   8946,  15354,  21762,  28170],
                          [8856,  31032,  53208,  75384,  97560],
                          [15174,  53118,  91062, 129006, 166950],
                          [21492,  75204, 128916, 182628, 236340]]])
    true_residual_data = np.zeros(array_3d.shape)
    tensor = Tensor(array=array_3d)
    ft_shape = (3, 4, 5)    # define shape of the tensor in full form
    ml_rank = (2, 3, 4)     # define multi-linear rank of a tensor in Tucker form
    core_size = reduce(lambda x, y: x * y, ml_rank)
    core_values = np.arange(core_size).reshape(ml_rank)
    fmat = [np.arange(ft_shape[mode] * ml_rank[mode]).reshape(ft_shape[mode],
                                                              ml_rank[mode]) for mode in range(len(ft_shape))]
    tensor_tkd = TensorTKD(fmat=fmat, core_values=core_values)
    residual = residual_tensor(tensor_orig=tensor, tensor_approx=tensor_tkd)
    assert isinstance(residual, Tensor)
    assert (residual.mode_names == true_default_mode_names)
    np.testing.assert_array_equal(residual.data, true_residual_data)

    # ------ tests for residual tensor with the TensorTT
    array_3d = np.array([[[300, 348, 396, 444, 492, 540],
                          [354, 411, 468, 525, 582, 639],
                          [408, 474, 540, 606, 672, 738],
                          [462, 537, 612, 687, 762, 837],
                          [516, 600, 684, 768, 852, 936]],

                         [[960, 1110, 1260, 1410, 1560, 1710],
                          [1230, 1425, 1620, 1815, 2010, 2205],
                          [1500, 1740, 1980, 2220, 2460, 2700],
                          [1770, 2055, 2340, 2625, 2910, 3195],
                          [2040, 2370, 2700, 3030, 3360, 3690]],

                         [[1620, 1872, 2124, 2376, 2628, 2880],
                          [2106, 2439, 2772, 3105, 3438, 3771],
                          [2592, 3006, 3420, 3834, 4248, 4662],
                          [3078, 3573, 4068, 4563, 5058, 5553],
                          [3564, 4140, 4716, 5292, 5868, 6444]],

                         [[2280, 2634, 2988, 3342, 3696, 4050],
                          [2982, 3453, 3924, 4395, 4866, 5337],
                          [3684, 4272, 4860, 5448, 6036, 6624],
                          [4386, 5091, 5796, 6501, 7206, 7911],
                          [5088, 5910, 6732, 7554, 8376, 9198]]])
    true_residual_data = np.zeros(array_3d.shape)
    tensor = Tensor(array=array_3d)
    r1, r2 = 2, 3
    I, J, K = 4, 5, 6
    core_1 = np.arange(I * r1).reshape(I, r1)
    core_2 = np.arange(r1 * J * r2).reshape(r1, J, r2)
    core_3 = np.arange(r2 * K).reshape(r2, K)
    core_values = [core_1, core_2, core_3]
    ft_shape = (I, J, K)
    tensor_tt = TensorTT(core_values=core_values)
    residual = residual_tensor(tensor_orig=tensor, tensor_approx=tensor_tt)
    assert isinstance(residual, Tensor)
    assert (residual.mode_names == true_default_mode_names)
    np.testing.assert_array_equal(residual.data, true_residual_data)

    # ------ tests that should FAIL for residual tensor due to wrong input type
    array_3d = np.array([[[0, 1, 2, 3],
                          [4, 5, 6, 7],
                          [8, 9, 10, 11]],

                         [[12, 13, 14, 15],
                          [16, 17, 18, 19],
                          [20, 21, 22, 23]]])
    tensor_1 = Tensor(array=array_3d)
    tensor_2 = array_3d
    with pytest.raises(TypeError):
        residual_tensor(tensor_orig=tensor_1, tensor_approx=tensor_2)

    tensor_1 = array_3d
    tensor_2 = Tensor(array=array_3d)
    with pytest.raises(TypeError):
        residual_tensor(tensor_orig=tensor_1, tensor_approx=tensor_2)
Exemplo n.º 15
0
    def test_decompose(self):
        """ Tests for decompose method """
        # ------ tests for termination conditions
        captured_output = io.StringIO(
        )  # Create StringIO object for testing verbosity
        sys.stdout = captured_output  # and redirect stdout.
        np.random.seed(0)
        shape = (6, 7, 8)
        size = reduce(lambda x, y: x * y, shape)
        array_3d = np.random.randn(size).reshape(shape)
        tensor = Tensor(array_3d)
        rank = (2, )
        cpd = RandomisedCPD(verbose=True)

        # check for termination at max iter
        cpd.max_iter = 10
        cpd.epsilon = 0.01
        cpd.tol = 0.0001
        cpd.decompose(tensor=tensor, rank=rank)
        assert not cpd.converged
        assert len(cpd.cost) == cpd.max_iter
        assert cpd.cost[-1] > cpd.epsilon

        # Repeat cpd, test is self.cost is reset
        cpd.decompose(tensor=tensor, rank=rank)
        assert len(cpd.cost) == cpd.max_iter

        # check for termination when acceptable level of approximation is achieved
        cpd.max_iter = 20
        cpd.epsilon = 0.98
        cpd.tol = 0.0001
        cpd.decompose(tensor=tensor, rank=rank)
        assert not cpd.converged
        assert len(cpd.cost) < cpd.max_iter
        assert cpd.cost[-1] <= cpd.epsilon

        # check for termination at convergence
        cpd.max_iter = 20
        cpd.epsilon = 0.01
        cpd.tol = 0.03
        cpd.decompose(tensor=tensor, rank=rank)
        assert cpd.converged
        assert len(cpd.cost) < cpd.max_iter
        assert cpd.cost[-1] > cpd.epsilon

        assert captured_output.getvalue(
        ) != ''  # to check that something was actually printed

        # ------ tests for correct output type and values

        shape = (4, 5, 6)
        size = reduce(lambda x, y: x * y, shape)
        array_3d = np.arange(size, dtype='float32').reshape(shape)
        tensor = Tensor(array_3d)
        rank = (7, )

        cpd = RandomisedCPD(init='random',
                            max_iter=50,
                            epsilon=10e-3,
                            tol=10e-5)

        tensor_cpd = cpd.decompose(tensor=tensor, rank=rank)
        assert isinstance(tensor_cpd, TensorCPD)
        assert tensor_cpd.order == tensor.order
        assert tensor_cpd.rank == rank
        # check dimensionality of computed factor matrices
        for mode, fmat in enumerate(tensor_cpd.fmat):
            assert fmat.shape == (tensor.shape[mode], rank[0])

        tensor_rec = tensor_cpd.reconstruct()
        np.testing.assert_almost_equal(tensor_rec.data, tensor.data)

        # ------ tests that should FAIL due to wrong input type
        cpd = RandomisedCPD()
        # tensor should be Tensor class
        with pytest.raises(TypeError):
            shape = (5, 5, 5)
            size = reduce(lambda x, y: x * y, shape)
            incorrect_tensor = np.arange(size).reshape(shape)
            correct_rank = (2, )
            cpd.decompose(tensor=incorrect_tensor, rank=correct_rank)
        # rank should be a tuple
        with pytest.raises(TypeError):
            shape = (5, 5, 5)
            size = reduce(lambda x, y: x * y, shape)
            correct_tensor = Tensor(np.arange(size).reshape(shape))
            incorrect_rank = [2]
            cpd.decompose(tensor=correct_tensor, rank=incorrect_rank)
        # incorrect length of rank
        with pytest.raises(ValueError):
            shape = (5, 5, 5)
            size = reduce(lambda x, y: x * y, shape)
            correct_tensor = Tensor(np.arange(size).reshape(shape))
            incorrect_rank = (2, 3)
            cpd.decompose(tensor=correct_tensor, rank=incorrect_rank)
        # invalid sample size
        with pytest.raises(ValueError):
            cpd = RandomisedCPD(sample_size=0)
            shape = (5, 5, 5)
            size = reduce(lambda x, y: x * y, shape)
            correct_tensor = Tensor(np.arange(size).reshape(shape))
            incorrect_rank = (2, )
            cpd.decompose(tensor=correct_tensor, rank=incorrect_rank)
Exemplo n.º 16
0
    def decompose(self, tenl, rank):
        """ Performs Direct fitting using ALS on a list of tensors of order 2 with respect to the specified ``rank``.

        Parameters
        ----------
        tenl : List(np.ndarray)
            List of np.ndarray of dimension 2 to be decomposed
        rank : tuple
            Desired Kruskal rank for the given ``tensor``. Should contain only one value.
            If it is greater then any of dimensions then random initialisation is used

        Returns
        -------
        fmat_u, fmat_s, fmat_v, reconstructed : Tuple(np.ndarray)
            fmat_u,fmat_s,fmat_v are PARAFAC2 representation of list of tensors
            reconstructed is the reconstruction of the original tensor directly using fmat_u, fmat_s, fmat_v

        Notes
        -----
        khatri-rao product should be of matrices in reversed order. But this will duplicate original data (e.g. images)
        Probably this has something to do with data ordering in Python and how it relates to kr product
        """
        if not isinstance(tenl, list):
            raise TypeError(
                "Parameter `tenl` should be a list of np.ndarray objects!")
        if not all(isinstance(m, np.ndarray) for m in tenl):
            raise TypeError(
                "Parameter `tenl` should be a list of np.ndarray objects!")
        if not isinstance(rank, tuple):
            raise TypeError("Parameter `rank` should be passed as a tuple!")
        if len(rank) != 1:
            raise ValueError(
                "Parameter `rank` should be tuple with only one value!")

        self.cost = []  # Reset cost every time when method decompose is called
        sz = np.array([t.shape for t in tenl])
        _m = list(sz[:, 1])
        if _m[1:] != _m[:-1]:
            raise ValueError("Tensors must be of shape I[k] x J")
        num_t = len(sz)
        mode_b = _m[0]

        # Initialisations
        cpd = CPD(max_iter=1)
        fmat_h, fmat_v, fmat_s, fmat_u = self._init_fmat(rank, sz)
        cpd_fmat = None
        for n_iter in range(self.max_iter):
            for k in range(num_t):
                p, _, q = svd(fmat_h.dot(fmat_s[:, :, k]).dot(fmat_v.T).dot(
                    tenl[k].T),
                              rank=rank[0])
                fmat_u[k] = q.T.dot(p.T)

            y = np.zeros((rank[0], mode_b, num_t))
            for k in range(num_t):
                y[:, :, k] = fmat_u[k].T.dot(tenl[k])
            fmat = [fmat_h, fmat_v, cpd_fmat]
            if n_iter == 0:
                fmat = None
            decomposed_cpd = cpd.decompose(Tensor(y), rank, factor_mat=fmat)
            fmat_h, fmat_v, cpd_fmat = decomposed_cpd.fmat
            cpd_fmat = cpd_fmat.dot(np.diag(decomposed_cpd._core_values))
            for k in range(num_t):
                fmat_s[:, :, k] = np.diag(cpd_fmat[k, :])

            reconstructed = [
                (fmat_u[k].dot(fmat_h).dot(fmat_s[:, :, k])).dot(fmat_v.T)
                for k in range(num_t)
            ]
            err = np.sum([
                np.sum((tenl[k] - reconstructed[k])**2) for k in range(num_t)
            ])
            self.cost.append(err)

            if self.verbose:
                print('Iter {}: relative error of approximation = {}'.format(
                    n_iter, self.cost[-1]))
            # Check termination conditions
            if self.cost[-1] <= self.epsilon:
                if self.verbose:
                    print(
                        'Relative error of approximation has reached the acceptable level: {}'
                        .format(self.cost[-1]))
                break
            if self.converged:
                if self.verbose:
                    print('Converged in {} iteration(s)'.format(len(
                        self.cost)))
                break
        if self.verbose and not self.converged and self.cost[-1] > self.epsilon:
            print('Maximum number of iterations ({}) has been reached. '
                  'Variation = {}'.format(self.max_iter,
                                          abs(self.cost[-2] - self.cost[-1])))

        # TODO: possibly make another structure
        return fmat_u, fmat_s, fmat_v, reconstructed