From de6a2503bc1d3d2fe583df2d2950d74a51f6d991 Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 29 Nov 2022 08:11:47 -0500 Subject: [PATCH 01/33] test(inheritance) check parent branch Placeholder commit to identify the parent branch correctly --- src/TTICE/template_TT_ICE_documentation.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/TTICE/template_TT_ICE_documentation.py b/src/TTICE/template_TT_ICE_documentation.py index ca59285..8be7a28 100644 --- a/src/TTICE/template_TT_ICE_documentation.py +++ b/src/TTICE/template_TT_ICE_documentation.py @@ -21,7 +21,11 @@ def templateFunction(arg1, arg2): def main(): - """This is the main function (TT-ICE)""" + """ + This is the main function (TT-ICE). + Provided that there is a multidimensional numpy array, this function converts the + multidimensional array into TT-format. + """ print(templateFunction(1, 2)) From 253fe6579b205fa9679e9bc0311de9e334b19c2c Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 29 Nov 2022 15:16:43 -0500 Subject: [PATCH 02/33] (fix) ignore error E203 to fix #36 --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e290288..808f07e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,7 +25,7 @@ repos: rev: 5.0.4 hooks: - id: flake8 - args: ["--max-line-length=88"] + args: ["--max-line-length=88", "--extend-ignore=E203"] - repo: https://github.com/pre-commit/mirrors-clang-format rev: v14.0.6 From 83c23dd6f43f5b8ed6dfd2b44d7fc083b4f942a9 Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 29 Nov 2022 15:22:28 -0500 Subject: [PATCH 03/33] (fix) update version of black To avoid any bugs caused by old version --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 808f07e..7e64a91 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,7 +14,7 @@ repos: - id: check-merge-conflict - repo: https://github.com/ambv/black - rev: 22.3.0 + rev: 22.10.0 hooks: - id: black language: python From 071d036454d9c3bb9a88c60d5663128208bccdc1 Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 29 Nov 2022 15:23:22 -0500 Subject: [PATCH 04/33] (fix) add doctests flag to flake8 --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7e64a91..92baf0e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,7 +25,7 @@ repos: rev: 5.0.4 hooks: - id: flake8 - args: ["--max-line-length=88", "--extend-ignore=E203"] + args: ["--max-line-length=88", "--extend-ignore=E203", "--doctests"] - repo: https://github.com/pre-commit/mirrors-clang-format rev: v14.0.6 From e441852ffe1e7c86abf2b77ee0635a8f8092f3ea Mon Sep 17 00:00:00 2001 From: Doruk Aksoy <52582668+dorukaks@users.noreply.github.com> Date: Sun, 4 Dec 2022 03:27:08 -0500 Subject: [PATCH 05/33] Automated documentation creation&publishing to GH-pages (#35) * (test) create initial .yml file to test function There will be A LOT of commits while testing this feature be ready... * (test) create sphinx .yml file to test function * (fix) add installation of sphinx-rtd-theme * (fix) change sphinx-action repository version * (fix) change repo versions for actions * (test) create environment for auto docs * (fix) change theme installation sequence * (test) change rtd_theme import style * (fix) change requirement declaration * (fix) correct id in deploy step * (test) try publishing documentation * (test) tweak action workflow to publish pages * (test) moved autoDocs to avoid build confusion * (fix) change artifact path * (fix) change artifact path * (test) create an empty file to create html folder * (test) change workflow options to make this work * (fix) change build command according to sphinx issues * (fix) indicate config file location in workflow * (fix) indicate config file location in workflow * (fix) change build command * (fix) change build command * (fix) change build command * (fix) change artifact build path * (fix) correct the hyperlink on introduction.rst * (fix) change insrted path from gim to src * (fix) create requirements.txt for whole pipeline * (docs) create links for packages * (test) write a test function into TTICE for docs * (test) create TTICE.rst to test autodocs * (test) change directory path in index.rst * (test) change directory path in index.rst * (test): Change sphinx (GH action) build command Changed the sphinx build command to `make html` * (test): Add sphinx api build to GH actions Added sphinx api build command for building * (test): Change build-command to run for Sphinx Changed the build command to run for sphinx gh action * (refactor) Delete redundant files Delete the following files: requirements.txt environment.yml * (reformat): Remane the sphinx workflow sphinx workflow was renamed to createDocumentation * (feat): Add sphinx build check workflow Added sphinx build check workflow * (refactor) remove redundant module calls in index Removed redundant module calls in index.rst * (wip) Check pre-build command for sphinx-action * (fix) Fix typo for pre-build-command * (feat) Add souce code read in pre-build sphinx * (WIP) Add pre-build comamnd for sphinx-api * (WIP) Test run command on git actions * (feat) Add pre-build source code * (fix) Fix doc to docs * (fix) Fix pre-build rm command * (fix) Remove firefox pop-up and latex build * (feat) Add install dependancies for sphinx * (feat) Upload artifacts for the html documentation * (feat) Add pages deploy * (feat) upload artifacts * (fix) Correct artifact path * (fix) Correct publish_dir * (feat) Add Manual push to gh-pages * (refactor) Remove autoDocs.yml * (refactor) Remove comment from createDocumentation * Squashed commit of the following: commit f35355a19feccb44c60d09ff66164b7ba541a9a6 Merge: fbb7d23 339ce03 Author: sahilbhola14 Date: Tue Nov 29 19:51:59 2022 -0500 Merge branch 'main' of github.com:PinguDevTeam/ICF-MI-framework into main commit fbb7d2325d36994fe080c39ae1737f83ba825538 Merge: 90ec97b 8b8c071 Author: sahilbhola14 Date: Mon Nov 28 14:43:07 2022 -0500 Merge branch 'main' of github.com:PinguDevTeam/ICF-MI-framework into main * (fix) Add source code path to conf Co-authored-by: sahilbhola14 --- .github/workflows/createDocumentation.yml | 33 +++++++++++++++++++++++ docs/_build/html/emptyFile.txt | 0 docs/conf.py | 9 +++++-- docs/introduction.rst | 2 +- docs/requirements.txt | 2 ++ scripts/create_documentation.sh | 6 ++--- src/TTICE/TTICE.rst | 21 +++++++++++++++ src/TTICE/__init__.py | 11 ++++++++ 8 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/createDocumentation.yml create mode 100644 docs/_build/html/emptyFile.txt create mode 100644 docs/requirements.txt create mode 100644 src/TTICE/TTICE.rst diff --git a/.github/workflows/createDocumentation.yml b/.github/workflows/createDocumentation.yml new file mode 100644 index 0000000..85064e6 --- /dev/null +++ b/.github/workflows/createDocumentation.yml @@ -0,0 +1,33 @@ +name: Code Documentation + +on: + pull_request: + push: + branches: [main, dev] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-python@v3 + - name: Install dependancies + run : ./scripts/configure_sphinx.sh + - name: Build source code + run: ./scripts/create_documentation.sh + - name: Commit changes + run: | + cd docs/_build/html + git init + touch .nojekyll + git add -A + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git commit -m 'deploy' + - name: Force push + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + branch: gh-pages + force: true + directory: ./docs/_build/html diff --git a/docs/_build/html/emptyFile.txt b/docs/_build/html/emptyFile.txt new file mode 100644 index 0000000..e69de29 diff --git a/docs/conf.py b/docs/conf.py index 0d1030e..03ddbfb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -7,7 +7,7 @@ import sys sys.path.insert(0, os.path.abspath("../")) -sys.path.insert(0, os.path.abspath("../GIM/")) +sys.path.insert(0, os.path.abspath("../src")) # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html\ @@ -22,7 +22,12 @@ # https://www.sphinx-doc.org/en/master/usage/configuration.html#general- # configuration -extensions = ["sphinx.ext.autodoc", "sphinx.ext.napoleon", "sphinx.ext.viewcode"] +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", + "sphinx.ext.viewcode", + "sphinx_rtd_theme", +] templates_path = ["_templates"] exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] diff --git a/docs/introduction.rst b/docs/introduction.rst index 6bddbb7..7d23e20 100644 --- a/docs/introduction.rst +++ b/docs/introduction.rst @@ -20,7 +20,7 @@ Within the scope of our pipeline, we will treat **TT-ICE** as a multilinear dime REFERENCES: =============== * `Google `_ , search engine has been used throughout the project. -* Aksoy et al., An Incremental Tensor-Train Decomposition Algorithm, 'arXiv preprint '_ +* Aksoy et al., An Incremental Tensor-Train Decomposition Algorithm, `arXiv preprint `_ * Other kind of text ``Bold reference``. * Bold **letters**. diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..145ba72 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,2 @@ +furo==2021.11.16 +sphinx_rtd_theme diff --git a/scripts/create_documentation.sh b/scripts/create_documentation.sh index 48d7599..f980421 100755 --- a/scripts/create_documentation.sh +++ b/scripts/create_documentation.sh @@ -3,10 +3,10 @@ # Run this script from the root of the project. cd docs -echo $PWD +# echo $PWD rm `find . -name "*rst" ! -name "index.rst" ! -name "introduction.rst"` sphinx-apidoc -o . ../src make html -make latexpdf -firefox ./_build/html/index.html +# make latexpdf +# firefox ./_build/html/index.html diff --git a/src/TTICE/TTICE.rst b/src/TTICE/TTICE.rst new file mode 100644 index 0000000..3999272 --- /dev/null +++ b/src/TTICE/TTICE.rst @@ -0,0 +1,21 @@ +TTICE +^^^^^^^^^^^^^ +**TT-ICE** is a novel incremental tensor-train (TT) decomposition algorithm which is developed to decompose high dimensional data streams into tensor-train format. +**TT-ICE** uses orthogonal projections and sequential application of SVD to compute the missing orthogonal directions in the TT-cores. +For a stream of d-dimensional tensors, **TT-ICE** accumulates the stream along an additional (d+1)-th dimension and trains d 3 dimensional TT-cores that span the first d dimensions of the stream. +The (d+1)-th TT-core resulting from the decomposition process contains coefficient vectors unique to each datum. +Within the scope of our pipeline, we will treat **TT-ICE** as a multilinear dimensionality reduction tool and use the coefficient vectors contained in the last TT-core to train the surrogate model. + +.. HEADING: +.. =============== +.. * If necessart mention some points here. + +REFERENCES: +=============== +* Aksoy et al., An Incremental Tensor-Train Decomposition Algorithm, `arXiv preprint `_ +* Other kind of text ``Bold reference``. +* Bold **letters**. + +Developers: +======== +| Doruk Aksoy (University of Michgan, Ann Arbor) diff --git a/src/TTICE/__init__.py b/src/TTICE/__init__.py index e69de29..276df40 100644 --- a/src/TTICE/__init__.py +++ b/src/TTICE/__init__.py @@ -0,0 +1,11 @@ +def testFcn(a): + """This is a test function to check if docstrings are working + + Args: + a (float): random value + + Returns: + (float): input plus 2 + """ + a += 2 + return a From c3c986d06feb3d3a0049d1df0b5d37f63ab44d5f Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 09:57:47 -0500 Subject: [PATCH 06/33] (feat): create init file to create DaMAT pack --- src/TTICE/DaMAT/__init__.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/TTICE/DaMAT/__init__.py diff --git a/src/TTICE/DaMAT/__init__.py b/src/TTICE/DaMAT/__init__.py new file mode 100644 index 0000000..c237504 --- /dev/null +++ b/src/TTICE/DaMAT/__init__.py @@ -0,0 +1,13 @@ +""" +Welcome to DaMAT package documentation! + + +This python package currently offers support for multidimensional tensors +in Tensor-Train format.We use the TT-SVD algorithm proposed by Ivan Oseledets +and TT-ICE algorithm proposed by Doruk Aksoy. + +In future releases, the coverage may be extended to other tensor decomposition formats +such as CP and/or Tucker. +""" + +from .ttObject import ttObject # noqa: F401 From 95bef29d8d21e0467fe1549dfcb3ff21b3776d0a Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 09:59:23 -0500 Subject: [PATCH 07/33] (feat) create ttObject for Tensor Train objects --- src/TTICE/DaMAT/ttObject.py | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/TTICE/DaMAT/ttObject.py diff --git a/src/TTICE/DaMAT/ttObject.py b/src/TTICE/DaMAT/ttObject.py new file mode 100644 index 0000000..227ece9 --- /dev/null +++ b/src/TTICE/DaMAT/ttObject.py @@ -0,0 +1,9 @@ +""" +This is a python class for tensors in TT-format. +Through this object you can compute TT-decomposition of multidimensional arrays in +one shot using [TTSVD algorithm](https://epubs.siam.org/doi/epdf/10.1137/090752286) +or incrementally using [TT-ICE algorithm](https://arxiv.org/abs/2211.12487). + +Furthermore, this object allows exporting and importing TT-cores using native +format (`.ttc`) and intermediary formats (`.txt`). +""" From a1fade61cd502eb0603f91fb41a4df49a5150fbf Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 09:59:55 -0500 Subject: [PATCH 08/33] (feat) create utils file for helper functions --- src/TTICE/DaMAT/utils.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/TTICE/DaMAT/utils.py diff --git a/src/TTICE/DaMAT/utils.py b/src/TTICE/DaMAT/utils.py new file mode 100644 index 0000000..e2ce8ac --- /dev/null +++ b/src/TTICE/DaMAT/utils.py @@ -0,0 +1 @@ +""" This file contains the utility functions for running DaMAT pack""" From 637504f3fb7d722514a17fc3c8180729d092cd2c Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 11:45:54 -0500 Subject: [PATCH 09/33] (feat) create ttObject and initialization function --- src/TTICE/DaMAT/ttObject.py | 117 ++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/src/TTICE/DaMAT/ttObject.py b/src/TTICE/DaMAT/ttObject.py index 227ece9..d636af8 100644 --- a/src/TTICE/DaMAT/ttObject.py +++ b/src/TTICE/DaMAT/ttObject.py @@ -7,3 +7,120 @@ Furthermore, this object allows exporting and importing TT-cores using native format (`.ttc`) and intermediary formats (`.txt`). """ + +import numpy as np + + +class ttObject: + """ + Python object for tensors in Tensor-Train format. + + This object computes the TT-decomposition of multidimensional arrays using + `TTSVD`_, `TT-ICE`_, and `TT-ICE*`_. + It furthermore contains an inferior method, ITTD, for benchmarking purposes. + + This object handles various operations in TT-format such as dot product and norm. + Also provides supporting operations for uncompressed multidimensional arrays such + as reshaping and transpose. + Once the tensor train approximation for a multidimensional array is computed, you + can compute projection of appropriate arrays onto the TT-cores, reconstruct + projected or compressed tensors and compute projection/compression accuracy. + + You can also save/load tensors as `.ttc` or `.txt` files. + Attributes + ---------- + inputType: type + Type of the input data. This determines how the object is initialized. + originalData: + Original multidimensional input array. Generally will not be stored + after computing + an initial set of TT-cores. + method: :obj:`str` + Method of computing the initial set of TT-cores. Currently only accepts + `'ttsvd'` as input + ttEpsilon: :obj:`float` + Desired relative error upper bound. + ttCores: :obj:`list` of :obj:`numpy.array` + Cores of the TT-decomposition. Stored as a list of numpy arrays. + ttRanks: :obj:`list` of :obj:`int` + TT-ranks of the decomposition. Stored as a list of integers. + nCores: :obj:`int` + Number of TT-cores in the decomposition. + nElements: :obj:`int` + Number of entries present in the current `ttObject`. + originalShape: :obj:`tuple` or :obj:`list` + Original shape of the multidimensional array. + reshapedShape: :obj:`tuple` or :obj:`list` + Shape of the multidimensional array after reshaping. Note that + this attribute is only meaningful before computing a TT-decomposition + indexOrder: :obj:`list` of :obj:`int` + Keeps track of original indices in case of transposing the input array. + .. _TTSVD: + https://epubs.siam.org/doi/epdf/10.1137/090752286 + _TT-ICE: + https://arxiv.org/abs/2211.12487 + _TT-ICE*: + https://arxiv.org/abs/2211.12487 + + """ + + def __init__( + self, + data, + epsilon: float = None, + keepData: bool = False, + samplesAlongLastDimension: bool = True, + method: str = "ttsvd", + ) -> None: + """ + Initializes the ttObject. + + Parameters + ---------- + data: :obj:`numpy.array` or :obj:`list` + Main input to the ttObject. It can either be a multidimensional + `numpy array` or `list of numpy arrays`. + If list of numpy arrays are presented as input, the object will interpret it + as the TT-cores of an existing decomposition. + epsilon: :obj:`float`, optional + The relative error upper bound desired for approximation. Optional for cases + when `data` has type `list`. + keepData: :obj:`bool`, optional + Optional boolean variable to determine if the original array will be kept + after compression. Set to `False` by default. + samplesAlongLastDimension: :obj:`bool`, optional + Boolean variable to ensure if the samples are stacked along the last + dimension. Assumed to be `True` since it is one of the assumptions in + TT-ICE. + method: :obj:`str`, optional + Determines the computation method for tensor train decomposition of + the multidimensional array presented as `data`. Set to `'ttsvd'` by default. + + Currently the package only has support for ttsvd, additional support such as + `ttcross` might be included in the future. + + """ + # self.ttRanks=ranks + self.ttCores = None + self.nCores = None + self.nElements = None + self.inputType = type(data) + self.method = method + self.keepOriginal = keepData + self.originalData = data + self.samplesAlongLastDimension = samplesAlongLastDimension + if self.inputType is np.memmap: + self.inputType = np.ndarray + + if self.inputType == np.ndarray: + self.ttEpsilon = epsilon + self.originalShape = list(data.shape) + self.reshapedShape = self.originalShape + self.indexOrder = [idx for idx in range(len(self.originalShape))] + elif self.inputType == list: + self.nCores = len(data) + self.ttCores = data + self.reshapedShape = [core.shape[1] for core in self.ttCores] + self.updateRanks() + else: + raise TypeError("Unknown input type!") From bf2bbed0ec069577578e5d53d05dad3db5a3a26f Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 11:47:18 -0500 Subject: [PATCH 10/33] (feat) create coreOccupancy as property --- src/TTICE/DaMAT/ttObject.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/TTICE/DaMAT/ttObject.py b/src/TTICE/DaMAT/ttObject.py index d636af8..dc48b27 100644 --- a/src/TTICE/DaMAT/ttObject.py +++ b/src/TTICE/DaMAT/ttObject.py @@ -7,6 +7,7 @@ Furthermore, this object allows exporting and importing TT-cores using native format (`.ttc`) and intermediary formats (`.txt`). """ +import warnings import numpy as np @@ -124,3 +125,20 @@ def __init__( self.updateRanks() else: raise TypeError("Unknown input type!") + + @property + def coreOccupancy(self) -> None: + """ + :obj:`list` of :obj:`float`: A metric showing the *relative rank* of each + TT-core. This metric is used for a heuristic enhancement tool in `TT-ICE*` + algorithm + """ + try: + return [ + core.shape[-1] / np.prod(core.shape[:-1]) for core in self.ttCores[:-1] + ] + except ValueError: + warnings.warn( + "No TT cores exist, maybe forgot calling object.ttDecomp?", Warning + ) + return None From 3537e760e0fcbd399f1301a0bc4791698c45014f Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 11:48:09 -0500 Subject: [PATCH 11/33] (feat) create compressionRatio as property --- src/TTICE/DaMAT/ttObject.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/TTICE/DaMAT/ttObject.py b/src/TTICE/DaMAT/ttObject.py index dc48b27..f982225 100644 --- a/src/TTICE/DaMAT/ttObject.py +++ b/src/TTICE/DaMAT/ttObject.py @@ -142,3 +142,16 @@ def coreOccupancy(self) -> None: "No TT cores exist, maybe forgot calling object.ttDecomp?", Warning ) return None + + @property + def compressionRatio(self) -> float: + """ + :obj:`float`: A metric showing how much compression with respect to the + original multidimensional array is achieved. + """ + originalNumEl = 1 + compressedNumEl = 0 + for core in self.ttCores: + originalNumEl *= core.shape[1] + compressedNumEl += np.prod(core.shape) + return originalNumEl / compressedNumEl From a82c518bb38de38eab98404a2ce0f7000120207b Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 11:53:30 -0500 Subject: [PATCH 12/33] (feat) create changeShape function --- src/TTICE/DaMAT/ttObject.py | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/TTICE/DaMAT/ttObject.py b/src/TTICE/DaMAT/ttObject.py index f982225..a881e6a 100644 --- a/src/TTICE/DaMAT/ttObject.py +++ b/src/TTICE/DaMAT/ttObject.py @@ -155,3 +155,43 @@ def compressionRatio(self) -> float: originalNumEl *= core.shape[1] compressedNumEl += np.prod(core.shape) return originalNumEl / compressedNumEl + + def changeShape(self, newShape: tuple or list) -> None: + """ + Function to change shape of input tensors and keeping track of the reshaping. + Reshapes `originalData` and saves the final shape in `reshapedShape` + + Note + ---- + A simple `numpy.reshape` would be sufficient for this purpose but in order to + keep track of the shape changes the `reshapedShape` attribute also needs to be + updated accordingly. + + Parameters + ---------- + newShape:obj:`tuple` or `list` + New shape of the tensor + + Raises + ------ + warning + If an attempt is done to modify the shape after computing a + TT-decomposition. + This is important since it will cause incompatibilities in other functions + regarding the shape of the uncompressed tensor. + + """ + if self.ttCores is not None: + warnings.warning( + "Warning! You are reshaping the original data after computing a\ + TT-decomposition! We will proceed without reshaping self.originalData!!" + ) + return None + self.reshapedShape = newShape + self.originalData = np.reshape(self.originalData, self.reshapedShape) + self.reshapedShape = list(self.originalData.shape) + # Changed reshapedShape to a list for the trick in ttICEstar + if self.samplesAlongLastDimension: + self.singleDataShape = self.reshapedShape[:-1] + # This line assumes we keep the last index as the samples index and don't + # interfere with its shape From efba213515fb9843d2fa58f7c6494a7fcf7937d6 Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 11:54:02 -0500 Subject: [PATCH 13/33] (feat) create computeTranspose function --- src/TTICE/DaMAT/ttObject.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/TTICE/DaMAT/ttObject.py b/src/TTICE/DaMAT/ttObject.py index a881e6a..d0a6adc 100644 --- a/src/TTICE/DaMAT/ttObject.py +++ b/src/TTICE/DaMAT/ttObject.py @@ -195,3 +195,33 @@ def changeShape(self, newShape: tuple or list) -> None: self.singleDataShape = self.reshapedShape[:-1] # This line assumes we keep the last index as the samples index and don't # interfere with its shape + + def computeTranspose(self, newOrder: list) -> None: + """ + Transposes the axes of `originalData`. + + Similar to `changeShape`, a simple + `numpy.transpose` would be sufficient for this purpose but in order to + keep track of the transposition order `indexOrder` attribute also needs + to be updated accordingly. + + Parameters + ---------- + newOrder:obj:`list` + New order of the axes. + + Raises + ------ + ValueError + When the number of transposition axes are not equal to the number of + dimensions of `originalData`. + """ + assert self.inputType == np.ndarray and self.ttCores is None + if len(newOrder) != len(self.indexOrder): + raise ValueError( + "size of newOrder and self.indexOrder does not match. \ + Maybe you forgot reshaping?" + ) + self.indexOrder = [self.indexOrder[idx] for idx in newOrder] + self.originalData = self.originalData.transpose(newOrder) + self.reshapedShape = list(self.originalData.shape) From 8f81c121691e40cd21ecc3a33f8199f90cb4cefb Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 11:55:09 -0500 Subject: [PATCH 14/33] (feat) create save function for trained TT-cores --- src/TTICE/DaMAT/ttObject.py | 56 +++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/TTICE/DaMAT/ttObject.py b/src/TTICE/DaMAT/ttObject.py index d0a6adc..4103a35 100644 --- a/src/TTICE/DaMAT/ttObject.py +++ b/src/TTICE/DaMAT/ttObject.py @@ -11,6 +11,8 @@ import numpy as np +from pickle import dump + class ttObject: """ @@ -225,3 +227,57 @@ def computeTranspose(self, newOrder: list) -> None: self.indexOrder = [self.indexOrder[idx] for idx in newOrder] self.originalData = self.originalData.transpose(newOrder) self.reshapedShape = list(self.originalData.shape) + + def saveData( + self, fileName: str, directory="./", justCores=True, outputType="ttc" + ) -> None: + """ + Writes the computed TT-cores to an external file. + + Parameters + ---------- + fileName:obj:`str` + + directory:obj:`str` + Location to save files with respect to the present working directory. + justCores:obj:`bool` + Boolean variable to determine if `originalData` will be discarded + or not while saving. + outputType:obj:`str` + Type of the output file. `ttc` for pickled `ttObject`, `txt` for + individual text files for each TT-core. + """ + if justCores: + if outputType == "ttc": + with open(directory + fileName + ".ttc", "wb") as saveFile: + temp = ttObject(self.ttCores) + for attribute in vars(self): + if attribute != "originalData": + setattr(temp, attribute, eval(f"self.{attribute}")) + dump(temp, saveFile) + elif outputType == "txt": + for coreIdx, core in enumerate(self.ttCores): + np.savetxt( + directory + f"{fileName}_{coreIdx}.txt", + core.reshape(-1, core.shape[-1]), + header=f"{core.shape[0]} {core.shape[1]} {core.shape[2]}", + delimiter=" ", + ) + elif outputType == "npy": + for coreIdx, core in enumerate(self.ttCores): + np.save( + directory + f"{fileName}_{coreIdx}.npy", + core, + ) + else: + raise ValueError(f"Output type {outputType} is not supported!") + else: + if outputType == "txt": + raise ValueError( + ".txt type outputs are only supported for justCores=True!!" + ) + if self.method == "ttsvd": + with open(directory + fileName + ".ttc", "wb") as saveFile: + dump(self, saveFile) + else: + raise ValueError("Unknown Method!") From ee0311358477ddcda5a619d6c10a10d4e22a773d Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 11:55:59 -0500 Subject: [PATCH 15/33] (feat) create load function for trained TT-cores --- src/TTICE/DaMAT/ttObject.py | 51 ++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/TTICE/DaMAT/ttObject.py b/src/TTICE/DaMAT/ttObject.py index 4103a35..9bdcb46 100644 --- a/src/TTICE/DaMAT/ttObject.py +++ b/src/TTICE/DaMAT/ttObject.py @@ -11,7 +11,7 @@ import numpy as np -from pickle import dump +from pickle import dump, load class ttObject: @@ -281,3 +281,52 @@ def saveData( dump(self, saveFile) else: raise ValueError("Unknown Method!") + + @staticmethod + def loadData(fileName: str, numCores=None) -> "ttObject": + """ + Loads data from a `.ttc` or `.txt` file + + + Static method to load TT-cores into a ttObject object. + Note + ---- + If data is stored in {coreFile}_{coreIdx}.txt format, + the input fileName should just be coreFile.txt + + Parameters + ---------- + fileName:obj:`str` + Name of the file that will be loaded. + numCores:obj:`int` + Number of cores that the resulting `ttObject` will have + (Only required when input data format is `.txt`) + """ + fileExt = fileName.split(".")[-1] + if fileExt == "ttc": + with open(fileName, "rb") as f: + dataSetObject = load(f) + return dataSetObject + elif fileExt == "txt": + if numCores is None: + raise ValueError("Number of cores are not defined!!") + fileBody = fileName.split(".")[0] + coreList = [] + for coreIdx in range(numCores): + with open(f"{fileBody}_{coreIdx}.{fileExt}"): + coreShape = f.readline()[2:-1] + coreShape = [int(item) for item in coreShape.split(" ")] + coreList.append( + np.loadtxt(f"{fileBody}_{coreIdx}.{fileExt}").reshape[coreShape] + ) + return ttObject(coreList) + elif fileExt == "npy": + if numCores is None: + raise ValueError("Number of cores are not defined!!") + fileBody = fileName.split(".")[0] + coreList = [] + for coreIdx in range(numCores): + coreList.append(np.load(f"{fileBody}_{coreIdx}.{fileExt}")) + return ttObject(coreList) + else: + raise ValueError(f"{fileExt} files are not supported!") From ae959510855653c0d19f6873ef343a42e9c9fd14 Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 11:58:53 -0500 Subject: [PATCH 16/33] (feat) create projectTensor function This function will be used for new data assimilation --- src/TTICE/DaMAT/ttObject.py | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/TTICE/DaMAT/ttObject.py b/src/TTICE/DaMAT/ttObject.py index 9bdcb46..e04ce60 100644 --- a/src/TTICE/DaMAT/ttObject.py +++ b/src/TTICE/DaMAT/ttObject.py @@ -330,3 +330,43 @@ def loadData(fileName: str, numCores=None) -> "ttObject": return ttObject(coreList) else: raise ValueError(f"{fileExt} files are not supported!") + + def projectTensor(self, newData: np.array, upTo=None) -> np.array: + """ + Projects tensors onto basis spanned by TT-cores. + + Given a tensor with appropriate dimensions, this function leverages + the fact that TT-cores obtained through `TTSVD`_ and `TT-ICE`_ are + column-orthonormal in the mode-2 unfolding. + + Note + ---- + This function will not yield correct results if the TT-cores are + not comprised of orthogonal vectors. + + Parameters + ---------- + newData:obj:`np.aray` + Tensor to be projected + upTo:obj:`int`, optional + Index that the projection will be terminated. If an integer is + passed as this parameter, `newTensor` will be projected up to + (not including) the core that has index `upTo`. Assumes 1-based + indexing. + + .. _TTSVD: + https://epubs.siam.org/doi/epdf/10.1137/090752286 + _TT-ICE: + https://arxiv.org/abs/2211.12487 + _TT-ICE*: + https://arxiv.org/abs/2211.12487 + """ + for coreIdx, core in enumerate(self.ttCores): + if (coreIdx == len(self.ttCores) - 1) or coreIdx == upTo: + break + newData = ( + core.reshape(np.prod(core.shape[:2]), -1).transpose() + ) @ newData.reshape( + self.ttRanks[coreIdx] * self.ttCores[coreIdx].shape[1], -1 + ) + return newData From fbcfe787103335188632e3d7b11d7cd8e2146fab Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 12:04:13 -0500 Subject: [PATCH 17/33] (feat) create reconstruct function This function will be useful to retrieve full tensors --- src/TTICE/DaMAT/ttObject.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/TTICE/DaMAT/ttObject.py b/src/TTICE/DaMAT/ttObject.py index e04ce60..40a0205 100644 --- a/src/TTICE/DaMAT/ttObject.py +++ b/src/TTICE/DaMAT/ttObject.py @@ -370,3 +370,40 @@ def projectTensor(self, newData: np.array, upTo=None) -> np.array: self.ttRanks[coreIdx] * self.ttCores[coreIdx].shape[1], -1 ) return newData + + def reconstruct(self, projectedData, upTo=None): + """ + Reconstructs tensors using TT-cores. + + Assumes that `projectedData` is a slice from the last + TT-core. + While reconstructing any projected tensor from `projectTensor`, + this function leverages the fact that TT-cores obtained through + `TTSVD`_ and `TT-ICE`_ are column-orthonormal in the mode-2 + unfolding. + + Note + ---- + This function might not yield correct results for projected tensors + if the TT-cores are not comprised of orthogonal vectors. + + Parameters + ---------- + projectedData:obj:`np.aray` + A tensor slice (or alternatively an array) + upTo:obj:`int`, optional + Index that the reconstruction will be terminated. If an integer is + passed as this parameter, `projectedData` will be projected up to + (not including) the core that has index `upTo`. Assumes 1-based + indexing. + .. _TTSVD: + https://epubs.siam.org/doi/epdf/10.1137/090752286 + _TT-ICE: + https://arxiv.org/abs/2211.12487 + + """ + if upTo is None: + upTo = len(self.ttCores) - 1 # Write the core index in 1-indexed form!! + for core in self.ttCores[:upTo][::-1]: + projectedData = np.tensordot(core, projectedData, axes=(-1, 0)) + return projectedData From a228dabdba32ce61e95b9281ef65ce5ad5bf2c9e Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 12:05:01 -0500 Subject: [PATCH 18/33] (feat) create updateRanks function This function updates TT-ranks after incremental updates --- src/TTICE/DaMAT/ttObject.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/TTICE/DaMAT/ttObject.py b/src/TTICE/DaMAT/ttObject.py index 40a0205..72c9e41 100644 --- a/src/TTICE/DaMAT/ttObject.py +++ b/src/TTICE/DaMAT/ttObject.py @@ -407,3 +407,12 @@ def reconstruct(self, projectedData, upTo=None): for core in self.ttCores[:upTo][::-1]: projectedData = np.tensordot(core, projectedData, axes=(-1, 0)) return projectedData + + def updateRanks(self) -> None: + """ + Updates the ranks of the `ttObject` after incremental updates. + """ + self.ttRanks = [1] + for core in self.ttCores: + self.ttRanks.append(core.shape[-1]) + return None From 85d52e92a5b445da761b49bc4ffa33390869dcb3 Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 12:05:55 -0500 Subject: [PATCH 19/33] (feat) create computeRelError function Given a new tensor, this function computes the relative projection error --- src/TTICE/DaMAT/ttObject.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/TTICE/DaMAT/ttObject.py b/src/TTICE/DaMAT/ttObject.py index 72c9e41..33bb6b3 100644 --- a/src/TTICE/DaMAT/ttObject.py +++ b/src/TTICE/DaMAT/ttObject.py @@ -416,3 +416,40 @@ def updateRanks(self) -> None: for core in self.ttCores: self.ttRanks.append(core.shape[-1]) return None + + def computeRelError(self, newTensor: np.array) -> np.array: + """ + Computes relative error by projecting data onto TT-cores. + + This function computes the error induced by projecting `data` onto the TT-cores + of `ttObject`. The last index of `newTensor` is assumed to be the number of + individual observations. + + Note + ---- + - In order to compute the projection onto the TT-cores, the dimensions of `data` + should match that of `ttObject`. + - If a single observation will be passed as `newTensor`, an additional + index/dimension should be introduced either through reshaping or [:,None] + + Parameters + ---------- + newTensor:obj:`np.array` + Tensor for which the projection error is computed + + Returns + ------- + relError:obj:`np.array` + Array of relative errors + """ + elementwiseNorm = np.linalg.norm(newTensor, axis=0) + for _ in range(len(newTensor.shape) - 2): + elementwiseNorm = np.linalg.norm(elementwiseNorm, axis=0) + projectedData = self.projectTensor(newTensor) + reconstructedData = self.reconstruct(projectedData).reshape(newTensor.shape) + difference = newTensor - reconstructedData + differenceNorm = np.linalg.norm(difference, axis=0) + for _ in range(len(difference.shape) - 2): + differenceNorm = np.linalg.norm(differenceNorm, axis=0) + relError = differenceNorm / elementwiseNorm + return relError From 056ab67f7072336f6e04cff3c5c6cae777154b30 Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 12:09:08 -0500 Subject: [PATCH 20/33] (feat) create computeRecError function Given an original tensor, this function reconstructs the corresponding tensor from TT-cores and computes the relative reconstruction error --- src/TTICE/DaMAT/ttObject.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/TTICE/DaMAT/ttObject.py b/src/TTICE/DaMAT/ttObject.py index 33bb6b3..a6ad940 100644 --- a/src/TTICE/DaMAT/ttObject.py +++ b/src/TTICE/DaMAT/ttObject.py @@ -453,3 +453,25 @@ def computeRelError(self, newTensor: np.array) -> np.array: differenceNorm = np.linalg.norm(differenceNorm, axis=0) relError = differenceNorm / elementwiseNorm return relError + + def computeRecError(self, data: np.array, start=None, finish=None) -> None: + """ + Function to compute relative error by reconstructing data from slices + of TT-cores. + Currently not implemented. + """ + if finish is None: + finish = start + 1 + rec = self.reconstruct(self.ttCores[-1][:, start:finish, :]).reshape( + self.reshapedShape[:-1] + [-1] + ) + data = data.reshape(self.reshapedShape[:-1] + [-1]) + diff = data - rec + elementwiseNorm = np.linalg.norm(data, axis=0) + for _ in range(len(data.shape) - 2): + elementwiseNorm = np.linalg.norm(elementwiseNorm, axis=0) + diffNorm = np.linalg.norm(diff, axis=0) + for _ in range(len(diff.shape) - 2): + diffNorm = np.linalg.norm(diffNorm, axis=0) + recError = diffNorm / elementwiseNorm + return recError From 742e821e44555fef675e416a73096d5b868ad7c4 Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 12:12:00 -0500 Subject: [PATCH 21/33] (feat) create ttsvd helper function This function computes the TT-decomposition using the TTSVD algorithm --- src/TTICE/DaMAT/utils.py | 58 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/TTICE/DaMAT/utils.py b/src/TTICE/DaMAT/utils.py index e2ce8ac..080a356 100644 --- a/src/TTICE/DaMAT/utils.py +++ b/src/TTICE/DaMAT/utils.py @@ -1 +1,59 @@ """ This file contains the utility functions for running DaMAT pack""" + +import numpy as np + + +def ttsvd(data, dataNorm, eps=0.1, dtype=np.float32): + """ + Computes Tensor-Train decomposition/approximation of tensors using `TTSVD`_ + algorithm. + + Parameters + ---------- + data:obj:`numpy.array` + Tensor to be decomposed/approximated. + dataNorm:obj:`float` + Norm of the tensor. This parameter is used to determine the truncation bound. + eps:obj:`float`, optional + Relative error upper bound for TT-decomposition. Set to 0.1 by default. + dtype:obj:`type`, optional + Data type to be used during computations. Set to `np.float32` by default . + + Returns + ------- + ranks:obj:`list` + List of TT-ranks. + cores:obj:`numpy.ndarray` + Cores of the TT-approximation. + + .. _TTSVD: + https://epubs.siam.org/doi/epdf/10.1137/090752286 + """ + inputShape = data.shape + dimensions = len(data.shape) + delta = (eps / ((dimensions - 1) ** (0.5))) * dataNorm + ranks = [1] + cores = [] + for k in range(dimensions - 1): + nk = inputShape[k] + data = data.reshape( + ranks[k] * nk, int(np.prod(data.shape) / (ranks[k] * nk)), order="F" + ).astype(dtype) + u, s, v = np.linalg.svd(data, False, True) + slist = list(s * s) + slist.reverse() + truncpost = [ + idx for idx, element in enumerate(np.cumsum(slist)) if element <= delta**2 + ] + ranks.append(len(s) - len(truncpost)) + + u = u[:, : ranks[-1]] + + cores.append(u.reshape(ranks[k], nk, ranks[k + 1], order="F")) + data = np.zeros_like(v[: ranks[-1], :]) + for idx, sigma in enumerate(s[: ranks[-1]]): + data[idx, :] = sigma * v[idx, :] + + ranks.append(1) + cores.append(data.reshape(ranks[-2], inputShape[-1], ranks[-1], order="F")) + return ranks, cores From 84538733e0a50583cb4128bb5ed81b60c4b83f88 Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 12:12:34 -0500 Subject: [PATCH 22/33] (feat) create ttdecomp function This function computes the TT-decomposition using utils.ttsvd --- src/TTICE/DaMAT/ttObject.py | 50 +++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/TTICE/DaMAT/ttObject.py b/src/TTICE/DaMAT/ttObject.py index a6ad940..5ec5f08 100644 --- a/src/TTICE/DaMAT/ttObject.py +++ b/src/TTICE/DaMAT/ttObject.py @@ -8,9 +8,11 @@ format (`.ttc`) and intermediary formats (`.txt`). """ import warnings +import time import numpy as np +from .utils import ttsvd from pickle import dump, load @@ -475,3 +477,51 @@ def computeRecError(self, data: np.array, start=None, finish=None) -> None: diffNorm = np.linalg.norm(diffNorm, axis=0) recError = diffNorm / elementwiseNorm return recError + + def ttDecomp(self, norm=None, dtype=np.float32) -> "ttObject.ttCores": + """ + Computes TT-decomposition of a multidimensional array using `TTSVD`_ algorithm. + + Currently only supports `ttsvd` as method. In the future additional formats may + be covered. + + Parameters + ---------- + norm:obj:`float`, optional + Norm of the tensor to be compressed + dtype:obj:`type`, optional + Desired data type for the compression. Intended to allow lower precision + if needed. + + Raises + ------ + ValueError + When `method` is not one of the admissible methods. + + + The following attributes are modified as a result of this function: + ------- + - `ttObject.ttCores` + - `ttObject.ttRanks` + - `ttObject.compressionRatio` + + .. _TTSVD: + https://epubs.siam.org/doi/epdf/10.1137/090752286 + """ + if norm is None: + norm = np.linalg.norm(self.originalData) + if self.method == "ttsvd": + startTime = time.time() + self.ttRanks, self.ttCores = ttsvd( + self.originalData, norm, self.ttEpsilon, dtype=dtype + ) + self.compressionTime = time.time() - startTime + self.nCores = len(self.ttCores) + self.nElements = 0 + for cores in self.ttCores: + self.nElements += np.prod(cores.shape) + if not self.keepOriginal: + self.originalData = None + return None + else: + raise ValueError("Method unknown. Please select a valid method!") From b7475a2a035f55e858730e9573330cc033ecfbe6 Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 12:14:19 -0500 Subject: [PATCH 23/33] (feat) create deltasvd helper function This function computes the error truncated SVD for TT-ICE and TT-ICE* --- src/TTICE/DaMAT/utils.py | 53 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/TTICE/DaMAT/utils.py b/src/TTICE/DaMAT/utils.py index 080a356..389ee85 100644 --- a/src/TTICE/DaMAT/utils.py +++ b/src/TTICE/DaMAT/utils.py @@ -57,3 +57,56 @@ def ttsvd(data, dataNorm, eps=0.1, dtype=np.float32): ranks.append(1) cores.append(data.reshape(ranks[-2], inputShape[-1], ranks[-1], order="F")) return ranks, cores + + +def deltaSVD(data, dataNorm, dimensions, eps=0.1): + """ + Performs delta-truncated SVD similar to that of the `TTSVD`_ algorithm. + + Given an unfolding matrix of a tensor, its norm and the number of dimensions + of the original tensor, this function computes the truncated SVD of `data`. + The truncation error is determined using the dimension dependant _delta_ formula + from `TTSVD`_ paper. + + Parameters + ---------- + data:obj:`numpy.array` + Matrix for which the truncated SVD will be performed. + dataNorm:obj:`float` + Norm of the matrix. This parameter is used to determine the truncation bound. + dimensions:obj:`int` + Number of dimensions of the original tensor. This parameter is used to determine + the truncation bound. + eps:obj:`float`, optional + Relative error upper bound for TT-decomposition. + + Returns + ------- + u:obj:`numpy.ndarray` + Column-wise orthonormal matrix of left-singular vectors. _Truncated_ + s:obj:`numpy.array` + Array of singular values. _Truncated_ + v:obj:`numpy.ndarray` + Row-wise orthonormal matrix of right-singular vectors. _Truncated_ + + .. _TTSVD: + https://epubs.siam.org/doi/epdf/10.1137/090752286 + """ + + # TODO: input checking + + delta = (eps / ((dimensions - 1) ** (0.5))) * dataNorm + try: + u, s, v = np.linalg.svd(data, False, True) + except np.linalg.LinAlgError: + print("Numpy svd did not converge, using qr+svd") + q, r = np.linalg.qr(data) + u, s, v = np.linalg.svd(r) + u = q @ u + slist = list(s * s) + slist.reverse() + truncpost = [ + idx for idx, element in enumerate(np.cumsum(slist)) if element <= delta**2 + ] + truncationRank = len(s) - len(truncpost) + return u[:, :truncationRank], s[:truncationRank], v[:truncationRank, :] From e39bcd437ed46a61a4a540eab70e658fc5528854 Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 12:14:54 -0500 Subject: [PATCH 24/33] (feat) create ttICE function This function computes the TT-decomposition incrementally using TT-ICE algorithm --- src/TTICE/DaMAT/ttObject.py | 100 +++++++++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/src/TTICE/DaMAT/ttObject.py b/src/TTICE/DaMAT/ttObject.py index 5ec5f08..9eb8d1d 100644 --- a/src/TTICE/DaMAT/ttObject.py +++ b/src/TTICE/DaMAT/ttObject.py @@ -12,7 +12,7 @@ import numpy as np -from .utils import ttsvd +from .utils import ttsvd, deltaSVD from pickle import dump, load @@ -525,3 +525,101 @@ def ttDecomp(self, norm=None, dtype=np.float32) -> "ttObject.ttCores": return None else: raise ValueError("Method unknown. Please select a valid method!") + + def ttICE(self, newTensor, epsilon=None, tenNorm=None) -> None: + """ + `TT-ICE`_ algorithmn without any heuristic upgrades. + + Given a set of TT-cores, this function provides incremental updates + to the TT-cores to approximate `newTensor` within a relative error + defined in `epsilon` + + Note + ---- + This algorithm/function relies on the fact that TT-cores are columnwise + orthonormal in the mode-2 unfolding. + + Parameters + ---------- + newTensor:obj:`np.array` + New/streamed tensor that will be used to expand the orthonormal bases + defined in TT-cores + epsilon:obj:`float`, optional + Relative error upper bound for approximating `newTensor` after incremental + updates. If not defined, `ttObject.ttEpsilon` is used. + tenNorm:obj:`float`, optional + Norm of `newTensor` + + Notes + ------- + **The following attributes are modified as a result of this function:** + - `ttObject.ttCores` + - `ttObject.ttRanks` + - `ttObject.compressionRatio` + .. _TT-ICE: + https://arxiv.org/abs/2211.12487 + """ + if tenNorm is None: + tenNorm = np.linalg.norm(newTensor) + if epsilon is None: + epsilon = self.ttEpsilon + newTensorSize = len(newTensor.shape) - 1 + newTensor = newTensor.reshape(list(self.reshapedShape[:-1]) + [-1])[None, :] + newTensor = newTensor.reshape(self.reshapedShape[0], -1) + Ui = self.ttCores[0].reshape(self.reshapedShape[0], -1) + Ri = newTensor - Ui @ (Ui.T @ newTensor) + for coreIdx in range(0, len(self.ttCores) - 2): + URi, _, _ = deltaSVD(Ri, tenNorm, newTensorSize, epsilon) + self.ttCores[coreIdx] = np.hstack((Ui, URi)).reshape( + self.ttCores[coreIdx].shape[0], self.reshapedShape[coreIdx], -1 + ) + self.ttCores[coreIdx + 1] = np.concatenate( + ( + self.ttCores[coreIdx + 1], + np.zeros( + ( + URi.shape[-1], + self.reshapedShape[coreIdx + 1], + self.ttRanks[coreIdx + 2], + ) + ), + ), + axis=0, + ) + Ui = self.ttCores[coreIdx].reshape( + self.ttCores[coreIdx].shape[0] * self.reshapedShape[coreIdx], -1 + ) + newTensor = (Ui.T @ newTensor).reshape( + np.prod(self.ttCores[coreIdx + 1].shape[:-1]), -1 + ) + Ui = self.ttCores[coreIdx + 1].reshape( + self.ttCores[coreIdx].shape[-1] * self.reshapedShape[coreIdx + 1], -1 + ) + Ri = newTensor - Ui @ (Ui.T @ newTensor) + coreIdx = len(self.ttCores) - 2 + URi, _, _ = deltaSVD(Ri, tenNorm, newTensorSize, epsilon) + self.ttCores[coreIdx] = np.hstack((Ui, URi)) + self.ttCores[coreIdx + 1] = np.concatenate( + ( + self.ttCores[coreIdx + 1], + np.zeros( + ( + URi.shape[-1], + self.ttCores[coreIdx + 1].shape[1], + self.ttRanks[coreIdx + 2], + ) + ), + ), + axis=0, + ) + newTensor = self.ttCores[coreIdx].T @ newTensor + self.ttCores[coreIdx] = self.ttCores[coreIdx].reshape( + self.ttCores[coreIdx - 1].shape[-1], self.reshapedShape[coreIdx], -1 + ) + coreIdx += 1 + Ui = self.ttCores[coreIdx].reshape(self.ttCores[coreIdx].shape[0], -1) + self.ttCores[coreIdx] = np.hstack((Ui, newTensor)).reshape( + self.ttCores[coreIdx].shape[0], -1, 1 + ) + self.updateRanks() + return None From c1f13d141459bd1c38dc08c778f1adba305f201a Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 12:16:02 -0500 Subject: [PATCH 25/33] (feat) create ttICEstar function This function computes the TT-decomposition incrementally using TT-ICE* algorithm --- src/TTICE/DaMAT/ttObject.py | 177 ++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) diff --git a/src/TTICE/DaMAT/ttObject.py b/src/TTICE/DaMAT/ttObject.py index 9eb8d1d..eb25937 100644 --- a/src/TTICE/DaMAT/ttObject.py +++ b/src/TTICE/DaMAT/ttObject.py @@ -623,3 +623,180 @@ def ttICE(self, newTensor, epsilon=None, tenNorm=None) -> None: ) self.updateRanks() return None + + def ttICEstar( + self, + newTensor: np.array, + epsilon: float = None, + tenNorm: float = None, + elementwiseNorm: np.array = None, + elementwiseEpsilon: np.array = None, + heuristicsToUse: list = ["skip", "subselect", "occupancy"], + occupancyThreshold: float = 0.8, + simpleEpsilonUpdate: bool = False, + ) -> None: + """ + `TT-ICE*`_ algorithmn with heuristic performance upgrades. + + Given a set of TT-cores, this function provides incremental updates + to the TT-cores to approximate `newTensor` within a relative error + defined in `epsilon`. + + Note + ---- + This algorithm/function relies on the fact that TT-cores are columnwise + orthonormal in the mode-2 unfolding. + + Parameters + ---------- + newTensor:obj:`np.array` + New/streamed tensor that will be used to expand the orthonormal bases + defined in TT-cores + epsilon:obj:`float`, optional + Relative error upper bound for approximating `newTensor` after incremental + updates. If not defined, `ttObject.ttEpsilon` is used. + tenNorm:obj:`float`, optional + Norm of `newTensor`. + elementwiseNorm:obj:`np.array`, optional + Individual norms of the observations in `newTensor`. + elementwiseEpsilon:obj:`np.array`, optional + Individual relative projection errors of the observations in `newTensor`. + heuristicsToUse:obj:`list`, optional + List of heuristics to use while updating TT-cores. Currently only accepts + `'skip'`, `'subselect'`, and `'occupancy'`. + occupancyThreshold:obj:`float`, optional + Threshold determining whether to skip updating a single core or not. Not + used if `'occupancy'` is not in `heuristicsToUse` + simpleEpsilonUpdate:obj:`bool`, optional + Uses the simple epsilon update equation. *Warning*: this relies on the + assumption that all observations in `newTensor` have similar norms. + + Notes + ------- + **The following attributes are modified as a result of this function:** + - `ttObject.ttCores` + - `ttObject.ttRanks` + - `ttObject.compressionRatio` + .. _TT-ICE*: + https://arxiv.org/abs/2211.12487 + """ + if epsilon is None: + epsilon = self.ttEpsilon + if ("subselect" in heuristicsToUse) and (newTensor.shape[-1] == 1): + warnings.warning( + "The streamed tensor has only 1 observation in it. \ + Subselect heuristic will not be useful!!" + ) + newTensor = newTensor.reshape(list(self.reshapedShape[:-1]) + [-1])[None, :] + updEpsilon = epsilon + newTensorSize = len(newTensor.shape) - 1 + + if elementwiseEpsilon is None: + elementwiseEpsilon = self.computeRelError(newTensor) + if "skip" in heuristicsToUse: + if np.mean(elementwiseEpsilon) <= epsilon: + newTensor = self.projectTensor(newTensor) + self.ttCores[-1] = np.hstack( + (self.ttCores[-1].reshape(self.ttRanks[-2], -1), newTensor) + ).reshape(self.ttRanks[-2], -1, 1) + return None + if tenNorm is None and elementwiseNorm is None: + tenNorm = np.linalg.norm(newTensor) + elif tenNorm is None: + tenNorm = np.linalg.norm(elementwiseNorm) + + select = [True] * newTensor.shape[-1] + discard = [False] * newTensor.shape[-1] + if "subselect" in heuristicsToUse: + select = elementwiseEpsilon > epsilon + discard = elementwiseEpsilon <= epsilon + if simpleEpsilonUpdate: + updEpsilon = ( + epsilon * newTensor.shape[-1] + - np.mean(elementwiseEpsilon[discard]) * discard.sum() + ) / (select.sum()) + else: + if elementwiseNorm is None: + elementwiseNorm = np.linalg.norm(newTensor, axis=0) + for _ in range(len(self.ttCores) - 1): + elementwiseNorm = np.linalg.norm(elementwiseNorm, axis=0) + allowedError = (self.ttEpsilon * np.linalg.norm(elementwiseNorm)) ** 2 + discardedError = np.sum( + (elementwiseEpsilon[discard] * elementwiseNorm[discard]) ** 2 + ) + updEpsilon = np.sqrt( + (allowedError - discardedError) + / (np.linalg.norm(elementwiseNorm[select]) ** 2) + ) + self.reshapedShape[-1] = np.array( + select + ).sum() # a little trick for ease of coding + + indexString = "[" + for _ in range(len(self.reshapedShape)): + # this heuristic assumes that the last dimension is for observations + indexString += ":," + selectString = indexString + "select]" + selected = eval("newTensor" + selectString) + + selected = selected.reshape(list(self.reshapedShape[:-1]) + [-1])[None, :] + for coreIdx in range(0, len(self.ttCores) - 1): + selected = selected.reshape(np.prod(self.ttCores[coreIdx].shape[:-1]), -1) + if ("occupancy" in heuristicsToUse) and ( + self.coreOccupancy[coreIdx] >= occupancyThreshold + ): + pass + else: + Ui = self.ttCores[coreIdx].reshape( + self.ttCores[coreIdx].shape[0] * self.reshapedShape[coreIdx], -1 + ) + Ri = selected - Ui @ (Ui.T @ selected) + if (elementwiseNorm is None) or ("subselect" not in heuristicsToUse): + URi, _, _ = deltaSVD( + Ri, np.linalg.norm(selected), newTensorSize, updEpsilon + ) + else: + URi, _, _ = deltaSVD( + Ri, + np.linalg.norm(elementwiseNorm[select]), + newTensorSize, + updEpsilon, + ) + self.ttCores[coreIdx] = np.hstack((Ui, URi)) + self.ttCores[coreIdx + 1] = np.concatenate( + ( + self.ttCores[coreIdx + 1], + np.zeros( + ( + URi.shape[-1], + self.ttCores[coreIdx + 1].shape[1], + self.ttRanks[coreIdx + 2], + ) + ), + ), + axis=0, + ) + + self.ttCores[coreIdx] = self.ttCores[coreIdx].reshape( + np.prod(self.ttCores[coreIdx].shape[:-1]), -1 + ) + # #project onto existing core and reshape for next core + selected = (self.ttCores[coreIdx].T @ selected).reshape( + self.ttCores[coreIdx + 1].shape[0] * self.reshapedShape[coreIdx + 1], -1 + ) + # fold back the previous core + self.ttCores[coreIdx] = self.ttCores[coreIdx].reshape( + -1, self.reshapedShape[coreIdx], self.ttCores[coreIdx].shape[-1] + ) + # self.ttCores[coreIdx]=self.ttCores[coreIdx].reshape(self.ttCores[coreIdx].shape[0],self.reshapedShape[coreIdx],-1) + # #fold back the previous core + self.updateRanks() + coreIdx += 1 + # coreIdx=len(self.ttCores), i.e working on the last core + self.ttCores[coreIdx] = self.ttCores[coreIdx].reshape( + self.ttCores[coreIdx].shape[0], -1 + ) + self.ttCores[coreIdx] = np.hstack( + (self.ttCores[coreIdx], self.projectTensor(newTensor)) + ).reshape(self.ttCores[coreIdx].shape[0], -1, 1) + return None From 6c0be5b5fc2e25c8b50949b892f5b789774a1c51 Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 12:27:11 -0500 Subject: [PATCH 26/33] (feat) create compression pipeline This python script compresses the ICF simulation outputs and saves the resulting TT-cores --- src/TTICE/compressionPipeline.py | 103 +++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/TTICE/compressionPipeline.py diff --git a/src/TTICE/compressionPipeline.py b/src/TTICE/compressionPipeline.py new file mode 100644 index 0000000..78ebb4e --- /dev/null +++ b/src/TTICE/compressionPipeline.py @@ -0,0 +1,103 @@ +import os +import time + +import numpy as np +import DaMAT as dmt + +method = "ttsvd" +heuristics = ["skip", "occupancy"] +occThreshold = 1 +compMetrFile = "compressionMetrics.txt" # This file is for me +lines2Print = [] +""" +Don't modify those 3 lines above they are settings to the compression algorithm. +""" + +cwd = os.getcwd() +epsilon = 0.05 # This is the relative approximation error threshold +nTrainingRuns = 640 # Modify this number accordingly +stpIdx = ( + 300 # If you downsample before saving data, change this to an appropriate number +) +step = 10 # If you downsample before saving data, change this to 1 +iterStep = 1 + +dataDir = "./" # Folder that the 2048x1528x3x300 numpy arrays are stored +# I suggest you to put all the training runs in one folder +saveDir = "./" # Folder that you will save the TT-cores +saveName = "trainedCore" # Name of the saved TT-core files +dataName = "run_" # I assumed that you name all the run files "run_" + +""" +Pick one of the two loops below and proceed +""" +# OPTION 1 +# If you use the following loop, have all the train runs at one place +for runIdx in os.listdir(): + data = np.load(dataDir + runIdx, mmap_mode="r") +# OPTION 2 +# If you use the following loop, name the training runs with consecutive +# numbers starting from 0 +for runIdx in range(nTrainingRuns): + print(f"Run: {runIdx}") + data = np.load(dataDir + dataName + f"{runIdx}", mmap_mode="r") + + # After you pick one of the loops above, comment the other. + # The rest should be in the same loop since we are compressing. + + stIdx = 0 + if runIdx == 0: + # I'm checking here if we are at the first run. Please modify this if + # statement accordingly + dataSet = dmt.ttObject( + data[:, :, :, stIdx][:, :, :, None], + epsilon=epsilon, + keepData=False, + samplesAlongLastDimension=True, + method=method, + ) + dataSet.changeShape([16, 32, 32, 191, 3, 1]) + dataSet.ttDecomp() + lines2Print.append(f"{0}") + lines2Print.append(f"{dataSet.compressionTime}") + lines2Print.append(f"{dataSet.compressionRatio}") + lines2Print.append(" ".join(map(str, dataSet.ttRanks))) + lines2Print.append("\n") + + # If you end up downsampling the timesteps before saving the data, change + # this to 1 + stIdx = 9 + else: + stIdx = 0 + + for iterIdx in range(stIdx, stpIdx, step): + stTime = time.time() + streamedTensor = data[:, :, :, iterIdx][:, :, :, None].reshape( + dataSet.reshapedShape[:-1] + [-1] + ) + dataSet.ttICEstar( + streamedTensor, + epsilon=epsilon, + heuristicsToUse=heuristics, + occupancyThreshold=occThreshold, + ) + stepTime = time.time() - stTime + lines2Print.append(f"{iterStep}") + lines2Print.append(f"{stepTime}") + lines2Print.append(f"{dataSet.compressionRatio}") + lines2Print.append(" ".join(map(str, dataSet.ttRanks))) + lines2Print.append("\n") + print( + f"Run {runIdx} timestep {iterIdx} (overall: {iterStep})\ + done in {round(stepTime,4)}s" + ) + with open(compMetrFile, "a") as txt: + txt.writelines(" ".join(lines2Print)) + lines2Print = [] + iterStep += 1 + """ + I'm saving after each simulation here, it will slow down compression time a little + bit but it will save us a lot of valuable time if compression fails prematurely for + some reason + """ + dataSet.saveData(saveName, directory=saveDir, justCores=True, outputType="npy") From cb1993f66d10ac2b7cb64022c01ab6e42c0d0c36 Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 12:28:02 -0500 Subject: [PATCH 27/33] (fix) remove unused files --- src/TTICE/__init__.py | 11 -------- src/TTICE/template_TT_ICE_documentation.py | 33 ---------------------- 2 files changed, 44 deletions(-) delete mode 100644 src/TTICE/__init__.py delete mode 100644 src/TTICE/template_TT_ICE_documentation.py diff --git a/src/TTICE/__init__.py b/src/TTICE/__init__.py deleted file mode 100644 index 276df40..0000000 --- a/src/TTICE/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -def testFcn(a): - """This is a test function to check if docstrings are working - - Args: - a (float): random value - - Returns: - (float): input plus 2 - """ - a += 2 - return a diff --git a/src/TTICE/template_TT_ICE_documentation.py b/src/TTICE/template_TT_ICE_documentation.py deleted file mode 100644 index 8be7a28..0000000 --- a/src/TTICE/template_TT_ICE_documentation.py +++ /dev/null @@ -1,33 +0,0 @@ -def templateFunction(arg1, arg2): - """Summary line. - - Extended description of function. - - Note: - Do not include the `self` parameter in the ``Args`` section. - - Args: - arg1 (float): Description of arg1 - arg2 (float): Description of arg2 - - Returns: - (float): Sum of arg1 and arg2 - - """ - - m = arg1 + arg2 - - return m - - -def main(): - """ - This is the main function (TT-ICE). - Provided that there is a multidimensional numpy array, this function converts the - multidimensional array into TT-format. - """ - print(templateFunction(1, 2)) - - -if __name__ == "__main__": - main() From ca789b31a4af93501782393a5138aae3b818540e Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 12:30:45 -0500 Subject: [PATCH 28/33] (refactor) relocate TTICE pack --- src/TTICE/compressionPipeline.py => compressionPipeline.py | 4 ++-- src/TTICE/{DaMAT => }/__init__.py | 2 +- src/TTICE/{DaMAT => }/ttObject.py | 0 src/TTICE/{DaMAT => }/utils.py | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename src/TTICE/compressionPipeline.py => compressionPipeline.py (98%) rename src/TTICE/{DaMAT => }/__init__.py (90%) rename src/TTICE/{DaMAT => }/ttObject.py (100%) rename src/TTICE/{DaMAT => }/utils.py (100%) diff --git a/src/TTICE/compressionPipeline.py b/compressionPipeline.py similarity index 98% rename from src/TTICE/compressionPipeline.py rename to compressionPipeline.py index 78ebb4e..4607ba0 100644 --- a/src/TTICE/compressionPipeline.py +++ b/compressionPipeline.py @@ -2,7 +2,7 @@ import time import numpy as np -import DaMAT as dmt +import TTICE as ttice method = "ttsvd" heuristics = ["skip", "occupancy"] @@ -49,7 +49,7 @@ if runIdx == 0: # I'm checking here if we are at the first run. Please modify this if # statement accordingly - dataSet = dmt.ttObject( + dataSet = ttice.ttObject( data[:, :, :, stIdx][:, :, :, None], epsilon=epsilon, keepData=False, diff --git a/src/TTICE/DaMAT/__init__.py b/src/TTICE/__init__.py similarity index 90% rename from src/TTICE/DaMAT/__init__.py rename to src/TTICE/__init__.py index c237504..4b10c94 100644 --- a/src/TTICE/DaMAT/__init__.py +++ b/src/TTICE/__init__.py @@ -1,5 +1,5 @@ """ -Welcome to DaMAT package documentation! +Welcome to TTICE documentation! This python package currently offers support for multidimensional tensors diff --git a/src/TTICE/DaMAT/ttObject.py b/src/TTICE/ttObject.py similarity index 100% rename from src/TTICE/DaMAT/ttObject.py rename to src/TTICE/ttObject.py diff --git a/src/TTICE/DaMAT/utils.py b/src/TTICE/utils.py similarity index 100% rename from src/TTICE/DaMAT/utils.py rename to src/TTICE/utils.py From 005b1585cce6ac3f5b41bcd20aa9b901829503fd Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 12:42:51 -0500 Subject: [PATCH 29/33] (fix) check docstrings --- src/TTICE/ttObject.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/TTICE/ttObject.py b/src/TTICE/ttObject.py index eb25937..7d61240 100644 --- a/src/TTICE/ttObject.py +++ b/src/TTICE/ttObject.py @@ -1,5 +1,4 @@ -""" -This is a python class for tensors in TT-format. +"""This is a python class for tensors in TT-format. Through this object you can compute TT-decomposition of multidimensional arrays in one shot using [TTSVD algorithm](https://epubs.siam.org/doi/epdf/10.1137/090752286) or incrementally using [TT-ICE algorithm](https://arxiv.org/abs/2211.12487). From 77ed77e93507ae971d498433fe1ea4c346c58d6b Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 12:45:00 -0500 Subject: [PATCH 30/33] (fix) test docstring creation --- src/TTICE/ttObject.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/TTICE/ttObject.py b/src/TTICE/ttObject.py index 7d61240..be2cc76 100644 --- a/src/TTICE/ttObject.py +++ b/src/TTICE/ttObject.py @@ -16,8 +16,7 @@ class ttObject: - """ - Python object for tensors in Tensor-Train format. + """Python object for tensors in Tensor-Train format. This object computes the TT-decomposition of multidimensional arrays using `TTSVD`_, `TT-ICE`_, and `TT-ICE*`_. From 350b9039330707dda8c9093792c46b65303b3aa8 Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 12:56:06 -0500 Subject: [PATCH 31/33] (fix) experiment to create docstrings as intended --- src/TTICE/ttObject.py | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/TTICE/ttObject.py b/src/TTICE/ttObject.py index be2cc76..6b3ab7f 100644 --- a/src/TTICE/ttObject.py +++ b/src/TTICE/ttObject.py @@ -75,16 +75,13 @@ def __init__( samplesAlongLastDimension: bool = True, method: str = "ttsvd", ) -> None: - """ - Initializes the ttObject. + """Initializes the ttObject. - Parameters - ---------- - data: :obj:`numpy.array` or :obj:`list` - Main input to the ttObject. It can either be a multidimensional - `numpy array` or `list of numpy arrays`. - If list of numpy arrays are presented as input, the object will interpret it - as the TT-cores of an existing decomposition. + Params: + data (numpy.array or list): Main input to the ttObject. It can either be a + multidimensional `numpy array` or `list of numpy arrays`. If list of + numpy arrays are presented as input, the object will interpret it as + the TT-cores of an existing decomposition. epsilon: :obj:`float`, optional The relative error upper bound desired for approximation. Optional for cases when `data` has type `list`. @@ -163,16 +160,13 @@ def changeShape(self, newShape: tuple or list) -> None: Function to change shape of input tensors and keeping track of the reshaping. Reshapes `originalData` and saves the final shape in `reshapedShape` - Note - ---- - A simple `numpy.reshape` would be sufficient for this purpose but in order to - keep track of the shape changes the `reshapedShape` attribute also needs to be - updated accordingly. + Note: + A simple `numpy.reshape` would be sufficient for this purpose but in order + to keep track of the shape changes the `reshapedShape` attribute also needs + to be updated accordingly. - Parameters - ---------- - newShape:obj:`tuple` or `list` - New shape of the tensor + Params: + newShape (tuple or list): New shape of the tensor Raises ------ From b933c7479e3201ddf94fdb03d05bcc9566040535 Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 16:34:22 -0500 Subject: [PATCH 32/33] (fix) experiment to create docstrings as intended --- src/TTICE/test_TTICE_documentation.py | 33 +++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/TTICE/test_TTICE_documentation.py diff --git a/src/TTICE/test_TTICE_documentation.py b/src/TTICE/test_TTICE_documentation.py new file mode 100644 index 0000000..ec83210 --- /dev/null +++ b/src/TTICE/test_TTICE_documentation.py @@ -0,0 +1,33 @@ +def templateFunction(arg1, arg2): + """Summary line. + + Extended description of function. + + Note: + Do not include the `self` parameter in the ``Args`` section. + + Args: + arg1 (float): Description of arg1 + arg2 (float): Description of arg2 + + Returns: + (float): Sum of arg1 and arg2 + + """ + + m = arg1 + arg2 + + return m + + +class main: + def __init__( + self, + data, + epsilon: float = None, + keepData: bool = False, + samplesAlongLastDimension: bool = True, + method: str = "ttsvd", + ): + """This is the main function (LSTM)""" + print(templateFunction(1, 2)) From 54b98a9d18f689c48a9fac27ae8676adcccebee8 Mon Sep 17 00:00:00 2001 From: dorukaks Date: Tue, 13 Dec 2022 17:52:25 -0500 Subject: [PATCH 33/33] (fix) attempt hopelessly to create documentation --- src/TTICE/test_TTICE_documentation.py | 2 +- src/TTICE/{TTICE.rst => trial123.rst} | 0 src/TTICE/ttObject.py | 6 ++++++ src/TTICE/utils.py | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) rename src/TTICE/{TTICE.rst => trial123.rst} (100%) diff --git a/src/TTICE/test_TTICE_documentation.py b/src/TTICE/test_TTICE_documentation.py index ec83210..54eb6d1 100644 --- a/src/TTICE/test_TTICE_documentation.py +++ b/src/TTICE/test_TTICE_documentation.py @@ -29,5 +29,5 @@ def __init__( samplesAlongLastDimension: bool = True, method: str = "ttsvd", ): - """This is the main function (LSTM)""" + """This is the main function (TTICE)""" print(templateFunction(1, 2)) diff --git a/src/TTICE/TTICE.rst b/src/TTICE/trial123.rst similarity index 100% rename from src/TTICE/TTICE.rst rename to src/TTICE/trial123.rst diff --git a/src/TTICE/ttObject.py b/src/TTICE/ttObject.py index 6b3ab7f..4fe9071 100644 --- a/src/TTICE/ttObject.py +++ b/src/TTICE/ttObject.py @@ -503,6 +503,12 @@ def ttDecomp(self, norm=None, dtype=np.float32) -> "ttObject.ttCores": if norm is None: norm = np.linalg.norm(self.originalData) if self.method == "ttsvd": + if (dtype is not np.float364) and (self.ttEpsilon < 0.1): + warnings.warn( + f"dtype {dtype} is not recommended for epsilon\ + values less than 0.1, switching to np.float64 instead." + ) + dtype = np.float64 startTime = time.time() self.ttRanks, self.ttCores = ttsvd( self.originalData, norm, self.ttEpsilon, dtype=dtype diff --git a/src/TTICE/utils.py b/src/TTICE/utils.py index 389ee85..22ae757 100644 --- a/src/TTICE/utils.py +++ b/src/TTICE/utils.py @@ -1,4 +1,4 @@ -""" This file contains the utility functions for running DaMAT pack""" +""" This file contains the utility functions for running TTICE pack""" import numpy as np