diff --git a/.gitignore b/.gitignore index 286c6e57..1112f476 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ build/ .ipynb_checkpoints htmlcov __pycache__ -.vs* \ No newline at end of file +.vs* +TestResults diff --git a/TensorToolbox/__init__.py b/TensorToolbox/__init__.py index 888f0ff1..7f8778f3 100644 --- a/TensorToolbox/__init__.py +++ b/TensorToolbox/__init__.py @@ -19,6 +19,7 @@ from TensorToolbox.cp_als import cp_als from TensorToolbox.import_data import import_data +from TensorToolbox.export_data import export_data import warnings def ignore_warnings(ignore=True): diff --git a/TensorToolbox/export_data.py b/TensorToolbox/export_data.py new file mode 100644 index 00000000..a7d626a7 --- /dev/null +++ b/TensorToolbox/export_data.py @@ -0,0 +1,103 @@ +# Copyright 2022 National Technology & Engineering Solutions of Sandia, +# LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the +# U.S. Government retains certain rights in this software. + +import numpy as np +import os +import TensorToolbox as ttb + +def export_data(data, filename, fmt_data=None, fmt_weights=None): + """ + Export tensor-related data to a file. + """ + # open file + fp = open(filename, 'w') + + if isinstance(data, ttb.tensor): + print('tensor', file=fp) + export_size(fp, data.shape) + export_array(fp, data.data, fmt_data) + + elif isinstance(data, ttb.sptensor): + print('sptensor', file=fp) + export_sparse_size(fp, data) + export_sparse_array(fp, data, fmt_data) + + elif isinstance(data, ttb.ktensor): + print('ktensor', file=fp) + export_size(fp, data.shape) + export_rank(fp, data) + export_weights(fp, data, fmt_weights) + for n in range(data.ndims): + print('matrix', file=fp) + export_size(fp, data.factor_matrices[n].shape) + export_factor(fp, data.factor_matrices[n], fmt_data) + """ + fprintf(fid, 'ktensor\n'); + export_size(fid, size(A)); + export_rank(fid, A); + export_lambda(fid, A.lambda, fmt_lambda); + for n = 1:length(size(A)) + fprintf(fid, 'matrix\n'); + export_size(fid, size(A.U{n})); + export_factor(fid, A.U{n}, fmt_data); + end + """ + + elif isinstance(data, np.ndarray): + print('matrix', file=fp) + export_size(fp, data.shape) + export_array(fp, data, fmt_data) + + else: + assert False, 'Invalid data type for export' + +def export_size(fp, shape): + # Export the size of something to a file + print(f'{len(shape)}', file=fp) # # of dimensions on one line + shape_str = ' '.join([str(d) for d in shape]) + print(f'{shape_str}', file=fp) # size of each dimensions on the next line + +def export_rank(fp, data): + # Export the rank of a ktensor to a file + print(f'{len(data.weights)}', file=fp) # ktensor rank on one line + +def export_weights(fp, data, fmt_weights): + # Export dense data that supports numel and linear indexing + if not fmt_weights: fmt_weights = '%.16e' + data.weights.tofile(fp, sep=' ', format=fmt_weights) + print(file=fp) + +def export_array(fp, data, fmt_data): + # Export dense data that supports numel and linear indexing + if not fmt_data: fmt_data = '%.16e' + data.tofile(fp, sep='\n', format=fmt_data) + print(file=fp) + +def export_factor(fp, data, fmt_data): + # Export dense data that supports numel and linear indexing + if not fmt_data: fmt_data = '%.16e' + for i in range(data.shape[0]): + row = data[i,:] + row.tofile(fp, sep=' ', format=fmt_data) + print(file=fp) + +def export_sparse_size(fp, A): + # Export the size of something to a file + print(f'{len(A.shape)}', file=fp) # # of dimensions on one line + shape_str = ' '.join([str(d) for d in A.shape]) + print(f'{shape_str}', file=fp) # size of each dimensions on the next line + print(f'{A.nnz}', file=fp) # number of nonzeros + +def export_sparse_array(fp, A, fmt_data): + # Export sparse array data in coordinate format + if not fmt_data: fmt_data = '%.16e' + # TODO: looping through all values may take a long time, can this be more efficient? + for i in range(A.nnz): + # 0-based indexing in package, 1-based indexing in file + subs = A.subs[i,:] + 1 + subs.tofile(fp, sep=' ', format="%d") + print(end=' ', file=fp) + val = A.vals[i][0] + val.tofile(fp, sep=' ', format=fmt_data) + print(file=fp) diff --git a/tests/data/matrix.tns b/tests/data/matrix.tns index 6f7c076d..3fcd3cc5 100644 --- a/tests/data/matrix.tns +++ b/tests/data/matrix.tns @@ -1,7 +1,11 @@ matrix 2 4 2 -1.0000000000000000e+00 5.0000000000000000e+00 -2.0000000000000000e+00 6.0000000000000000e+00 -3.0000000000000000e+00 7.0000000000000000e+00 -4.0000000000000000e+00 8.0000000000000000e+00 \ No newline at end of file +1.0000000000000000e+00 +5.0000000000000000e+00 +2.0000000000000000e+00 +6.0000000000000000e+00 +3.0000000000000000e+00 +7.0000000000000000e+00 +4.0000000000000000e+00 +8.0000000000000000e+00 \ No newline at end of file diff --git a/tests/data/sptensor_zero_based_index.tns b/tests/data/sptensor_zero_based_index.tns deleted file mode 100644 index 80d0de2c..00000000 --- a/tests/data/sptensor_zero_based_index.tns +++ /dev/null @@ -1,22 +0,0 @@ -sptensor indices-start-at-zero -3 -5 4 3 -18 -0 0 0 1.0000000000000000e+00 -0 2 2 2.0000000000000000e+00 -1 1 1 3.0000000000000000e+00 -1 2 0 4.0000000000000000e+00 -1 2 1 5.0000000000000000e+00 -1 2 2 6.0000000000000000e+00 -1 3 1 7.0000000000000000e+00 -2 0 0 8.0000000000000000e+00 -2 0 1 9.0000000000000000e+00 -2 2 0 1.0000000000000000e+01 -2 2 1 1.1000000000000000e+01 -2 3 0 1.2000000000000000e+01 -2 3 2 1.3000000000000000e+01 -3 0 0 1.4000000000000000e+01 -3 0 1 1.5000000000000000e+01 -3 2 0 1.6000000000000000e+01 -4 0 2 1.7000000000000000e+01 -4 3 2 1.8000000000000000e+01 diff --git a/tests/test_import_export_data.py b/tests/test_import_export_data.py index db1f26b0..bfc61861 100644 --- a/tests/test_import_export_data.py +++ b/tests/test_import_export_data.py @@ -8,50 +8,54 @@ import TensorToolbox as ttb @pytest.fixture() -def sample_tensor_2way(): - data = np.array([[1., 2., 3.], [4., 5., 6.]]) - shape = (2, 3) - params = {'data':data, 'shape': shape} - tensorInstance = ttb.tensor().from_data(data, shape) - return params, tensorInstance +def sample_tensor(): + # truth data + T = ttb.tensor.from_data(np.ones((3,3,3)), (3,3,3)) + return T @pytest.fixture() -def sample_tensor_3way(): - data = np.array([1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.]) - shape = (2, 3, 2) - params = {'data':np.reshape(data, np.array(shape), order='F'), 'shape': shape} - tensorInstance = ttb.tensor().from_data(data, shape) - return params, tensorInstance +def sample_sptensor(): + # truth data + subs = np.array([[0, 0, 0],[0, 2, 2],[1, 1, 1],[1, 2, 0],[1, 2, 1],[1, 2, 2], + [1, 3, 1],[2, 0, 0],[2, 0, 1],[2, 2, 0],[2, 2, 1],[2, 3, 0], + [2, 3, 2],[3, 0, 0],[3, 0, 1],[3, 2, 0],[4, 0, 2],[4, 3, 2]]) + vals = np.reshape(np.array(range(1,19)),(18,1)) + shape = (5, 4, 3) + S = ttb.sptensor().from_data(subs, vals, shape) + return S @pytest.fixture() -def sample_tensor_4way(): - data = np.arange(1, 82) - shape = (3, 3, 3, 3) - params = {'data':np.reshape(data, np.array(shape), order='F'), 'shape': shape} - tensorInstance = ttb.tensor().from_data(data, shape) - return params, tensorInstance +def sample_ktensor(): + # truth data + weights = np.array([3, 2]) + fm0 = np.array([[1., 5.], [2., 6.], [3., 7.], [4., 8.]]) + fm1 = np.array([[ 2., 7.], [ 3., 8.], [ 4., 9.], [ 5., 10.], [ 6., 11.]]) + fm2 = np.array([[3., 6.], [4., 7.], [5., 8.]]) + factor_matrices = [fm0, fm1, fm2] + K = ttb.ktensor.from_data(weights, factor_matrices) + return K + +@pytest.fixture() +def sample_array(): + # truth data + M = np.array([[1., 5.], [2., 6.], [3., 7.], [4., 8.]]) + return M @pytest.mark.indevelopment -def test_import_data_tensor(): +def test_import_data_tensor(sample_tensor): # truth data - T = ttb.tensor.from_data(np.ones((3,3,3)), (3,3,3)) + T = sample_tensor # imported data data_filename = os.path.join(os.path.dirname(__file__),'data','tensor.tns') X = ttb.import_data(data_filename) - assert X.shape == (3, 3, 3) assert T.isequal(X) @pytest.mark.indevelopment -def test_import_data_sptensor(): +def test_import_data_sptensor(sample_sptensor): # truth data - subs = np.array([[0, 0, 0],[0, 2, 2],[1, 1, 1],[1, 2, 0],[1, 2, 1],[1, 2, 2], - [1, 3, 1],[2, 0, 0],[2, 0, 1],[2, 2, 0],[2, 2, 1],[2, 3, 0], - [2, 3, 2],[3, 0, 0],[3, 0, 1],[3, 2, 0],[4, 0, 2],[4, 3, 2]]) - vals = np.reshape(np.array(range(1,19)),(18,1)) - shape = (5, 4, 3) - S = ttb.sptensor().from_data(subs, vals, shape) + S = sample_sptensor # imported data data_filename = os.path.join(os.path.dirname(__file__),'data','sptensor.tns') @@ -60,15 +64,10 @@ def test_import_data_sptensor(): assert S.isequal(X) @pytest.mark.indevelopment -def test_import_data_ktensor(): +def test_import_data_ktensor(sample_ktensor): # truth data - weights = np.array([3, 2]) - fm0 = np.array([[1., 5.], [2., 6.], [3., 7.], [4., 8.]]) - fm1 = np.array([[ 2., 7.], [ 3., 8.], [ 4., 9.], [ 5., 10.], [ 6., 11.]]) - fm2 = np.array([[3., 6.], [4., 7.], [5., 8.]]) - factor_matrices = [fm0, fm1, fm2] - K = ttb.ktensor.from_data(weights, factor_matrices) - + K = sample_ktensor + # imported data data_filename = os.path.join(os.path.dirname(__file__),'data','ktensor.tns') X = ttb.import_data(data_filename) @@ -76,33 +75,91 @@ def test_import_data_ktensor(): assert K.isequal(X) @pytest.mark.indevelopment -def test_import_data_array(): +def test_import_data_array(sample_array): # truth data - M = np.array([[1., 5.], [2., 6.], [3., 7.], [4., 8.]]) - print('\nM') - print(M) - + M = sample_array + # imported data data_filename = os.path.join(os.path.dirname(__file__),'data','matrix.tns') X = ttb.import_data(data_filename) - print('\nX') - print(X) assert (M == X).all() @pytest.mark.indevelopment -def test_export_data_tensor(): - pass +def test_export_data_tensor(sample_tensor): + # truth data + T = sample_tensor + + data_filename = os.path.join(os.path.dirname(__file__),'data','tensor.out') + ttb.export_data(T, data_filename) + + X = ttb.import_data(data_filename) + assert T.isequal(X) + os.unlink(data_filename) + + data_filename = os.path.join(os.path.dirname(__file__),'data','tensor_int.out') + ttb.export_data(T, data_filename, fmt_data='%d') + + X = ttb.import_data(data_filename) + assert T.isequal(X) + os.unlink(data_filename) @pytest.mark.indevelopment -def test_export_data_sptensor(): - pass +def test_export_data_sptensor(sample_sptensor): + # truth data + S = sample_sptensor + + # imported data + data_filename = os.path.join(os.path.dirname(__file__),'data','sptensor.out') + ttb.export_data(S, data_filename) + + X = ttb.import_data(data_filename) + assert S.isequal(X) + os.unlink(data_filename) + + data_filename = os.path.join(os.path.dirname(__file__),'data','sptensor_int.out') + ttb.export_data(S, data_filename, fmt_data='%d') + + X = ttb.import_data(data_filename) + assert S.isequal(X) + os.unlink(data_filename) @pytest.mark.indevelopment -def test_export_data_ktensor(): - pass +def test_export_data_ktensor(sample_ktensor): + # truth data + K = sample_ktensor + + # imported data + data_filename = os.path.join(os.path.dirname(__file__),'data','ktensor.out') + ttb.export_data(K, data_filename) + + X = ttb.import_data(data_filename) + assert K.isequal(X) + os.unlink(data_filename) + + data_filename = os.path.join(os.path.dirname(__file__),'data','ktensor_int.out') + ttb.export_data(K, data_filename, fmt_data='%d', fmt_weights='%d') + + X = ttb.import_data(data_filename) + assert K.isequal(X) + os.unlink(data_filename) @pytest.mark.indevelopment -def test_export_data_array(): - pass +def test_export_data_array(sample_array): + # truth data + M = sample_array + + # imported data + data_filename = os.path.join(os.path.dirname(__file__),'data','matrix.out') + ttb.export_data(M, data_filename) + X = ttb.import_data(data_filename) + assert (M == X).all() + os.unlink(data_filename) + + data_filename = os.path.join(os.path.dirname(__file__),'data','matrix_int.out') + ttb.export_data(M, data_filename, fmt_data='%d') + + X = ttb.import_data(data_filename) + assert (M == X).all() + os.unlink(data_filename)