├── .dockignore ├── .gitignore ├── .gitmodules ├── .travis.yml ├── Dockerfile ├── LICENSE ├── README.md ├── conda-recipe ├── README.md ├── bld.bat ├── build.sh ├── meta.yaml └── run_test.py ├── examples ├── TT Smoluchowski.ipynb ├── myheisen.py ├── test_amen.py ├── test_common.py ├── test_cross.py ├── test_eigb.py └── test_multifuncrs.py ├── pyproject.toml ├── requirements.txt ├── setup.py └── tt ├── __init__.py ├── amen ├── __init__.py ├── amen_f90.f90 ├── amen_f90.pyf ├── amen_mv.py └── setup.py ├── completion ├── __init__.py ├── als.py └── demo_completion.py ├── core ├── __init__.py ├── core.pyf ├── matrix.py ├── setup.py ├── tools.py ├── tt.py ├── tt_f90.f90 ├── tt_f90.pyf ├── utils.py └── vector.py ├── cross ├── __init__.py ├── oldcross │ ├── cross.f90 │ ├── cross.py │ ├── cross.pyf │ └── tt_cross.py └── setup.py ├── distutils.py ├── doc ├── Makefile ├── conf.py ├── corettutils.rst ├── index.rst ├── tensorintro.rst └── ttalgorithms.rst ├── eigb ├── __init__.py ├── eigb.py ├── int_redefine.h ├── setup.py └── tt_eigb.pyf ├── ksl ├── __init__.py ├── ksl.py ├── setup.py └── tt_ksl.pyf ├── maxvol ├── __init__.py ├── _maxvol.py ├── maxvol.f90 ├── maxvol.pyf └── setup.py ├── multifuncrs.py ├── multifuncrs2.py ├── optimize ├── __init__.py ├── example.py ├── setup.py └── tt_min.py ├── riemannian ├── __init__.py ├── riemannian.py ├── riemannian_test.py └── setup.py ├── setup.py ├── solvers.py ├── tests └── test_vector.py └── utils ├── __init__.py └── setup.py /.dockignore: -------------------------------------------------------------------------------- 1 | /**/* 2 | !/LICENSE 3 | !/README.md 4 | !/pyproject.toml 5 | !/requirements.txt 6 | !/tt/ 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore 157 | # that can be found at 158 | # https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore and 159 | # can be added to the global gitignore or merged into this file. For a more 160 | # nuclear option (not recommended) you can uncomment the following to ignore 161 | # the entire idea folder. 162 | #.idea/ 163 | 164 | # Ignore stubs autogenerated with f2py 165 | *-f2pywrappers.f 166 | *-f2pywrappers2.f90 167 | *module.c 168 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tt/tt-fort"] 2 | path = tt/tt-fort 3 | #url = git@github.com:oseledets/tt-fort 4 | url = https://github.com/oseledets/tt-fort.git 5 | [submodule "tt/cross/rectcross"] 6 | path = tt/cross/rectcross 7 | url = https://bitbucket.org/oseledets/rectcross 8 | [submodule "tt/utils/rect_maxvol"] 9 | path = tt/utils/rect_maxvol 10 | url = https://bitbucket.org/muxas/rect_maxvol 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # After changing this file, check it on: 2 | # http://lint.travis-ci.org/ 3 | language: python 4 | sudo: false 5 | cache: pip 6 | matrix: 7 | fast_finish: true 8 | include: 9 | - python: "2.7" 10 | addons: 11 | apt: 12 | packages: 13 | - libatlas-dev 14 | - libatlas-base-dev 15 | - liblapack-dev 16 | - gfortran 17 | - libgmp-dev 18 | - libmpfr-dev 19 | before_install: 20 | - git submodule update --init --recursive 21 | install: 22 | - pip install -U pip 23 | - pip install -r requirements.txt 24 | - pip install coveralls 25 | script: 26 | - python setup.py install 27 | - cd tt 28 | - python -m unittest discover tests/ 29 | notifications: 30 | email: false 31 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10 2 | 3 | RUN apt update && \ 4 | apt --no-install-recommends install -y \ 5 | gfortran \ 6 | libblas-dev \ 7 | liblapack-dev && \ 8 | pip install --no-cache-dir \ 9 | cython \ 10 | numpy \ 11 | scipy \ 12 | six && \ 13 | rm -rf /var/lib/apt/lists/* 14 | 15 | COPY . /workspace/ttpy 16 | 17 | RUN cd /workspace/ttpy && \ 18 | pip install -v --no-cache-dir . && \ 19 | rm -rf /workspace/ttpy && \ 20 | python -c "import tt" 21 | 22 | WORKDIR /workspace 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013-2017 Ivan Oseledets, Tigran Saluev, Alexander Novikov, Sergey Dolgov, Dmitry Savostyanov, Rafael Ballester 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://api.travis-ci.org/oseledets/ttpy.svg?style=flat-square)](https://travis-ci.org/oseledets/ttpy) 2 | [![Coverage Status](https://img.shields.io/coveralls/oseledets/ttpy/master.svg?style=flat-square)](https://coveralls.io/r/oseledets/ttpy) 3 | [![PyPi](https://img.shields.io/pypi/pyversions/ttpy.svg?style=flat-square)](https://pypi.python.org/pypi/ttpy/) 4 | [![Downloads](https://img.shields.io/pypi/dm/ttpy.svg?style=flat-square)](https://pypi.python.org/pypi/ttpy/) 5 | [![Downloads](https://img.shields.io/docker/pulls/daskol/ttpy)](https://hub.docker.com/r/daskol/ttpy) 6 | 7 | ttpy 8 | ==== 9 | Python implementation of the Tensor Train (TT) -Toolbox. It contains several 10 | important packages for working with the TT-format 11 | in Python. It is able to do TT-interpolation, solve linear systems, eigenproblems, solve dynamical problems. 12 | Several computational routines are done in Fortran (which can be used separatedly), and are wrapped with the f2py tool. 13 | 14 | Installation 15 | ============ 16 | ## Prerequisites 17 | 18 | **It is recommended** that you use [Anaconda Python distribution](https://store.continuum.io/cshop/anaconda/) 19 | which has MKL library built-in. Anaconda Python is used for the development of ttpy. 20 | 21 | ## Pip install 22 | Install dependencies (numpy and cython) 23 | ``` 24 | conda install numpy cython 25 | ``` 26 | Install ttpy 27 | ``` 28 | pip install ttpy 29 | ``` 30 | 31 | ## Installing from source code 32 | 33 | To install the development version, you need to install from the source. 34 | First, clone the repository with all submodules: 35 | ``` 36 | git clone --recursive git://github.com/oseledets/ttpy.git 37 | ``` 38 | ``` 39 | python setup.py install 40 | ``` 41 | To update to the latest version (with all submodules) run 42 | ``` 43 | git pull 44 | git submodule update --init --recursive * 45 | ``` 46 | 47 | ## Pre-built Docker image 48 | 49 | The easiest way to play with TT Toolbox is through using the Docker (see 50 | [Docker Hub page](https://hub.docker.com/r/daskol/ttpy) for details). 51 | In order to use it one needs just pull an image and run a container as in 52 | snippet below. 53 | 54 | ```shell 55 | $ docker pull daskol/ttpy 56 | ... 57 | Status: Image is up to date for daskol/ttpy:latest 58 | docker.io/daskol/ttpy:latest 59 | $ docker run -it --rm -v "$PWD":/workspace daskol/ttpy 60 | Python 3.10.6 (main, Aug 3 2022, 10:13:24) [GCC 10.2.1 20210110] on linux 61 | Type "help", "copyright", "credits" or "license" for more information. 62 | >>> import tt 63 | ``` 64 | 65 | What those packages do 66 | ====================== 67 | 68 | They have the following functionality 69 | 70 | - `tt` : The main package, with tt.vector and tt.matrix classes, basic arithmetic, 71 | norms, scalar products, rounding full -> tt and tt -> full conversion routines, and many others 72 | - `tt.amen` : AMEN solver for linear systems (Python wrapper for Fortran code written by S. V. Dolgov and D. V. Savostyanov) 73 | it can be also used for fast matrix-by-vector products. 74 | - `tt.eigb` : Block eigenvalue solver in the TT-format 75 | - `tt.ksl` : Solution of the linear dynamic problems in the TT-format, using the projector-splitting 76 | KSL scheme. A Python wrapper for a Fortran code (I. V. Oseledets) 77 | - `tt.cross` : Has a working implementation of the black-box cross method. 78 | 79 | 80 | Documentation and examples 81 | ========================== 82 | 83 | The package provides Sphinx-generated documentation. To build HTML version, just do 84 | ``` 85 | cd tt/doc 86 | make html 87 | ``` 88 | 89 | A few examples are available right now under [examples](examples/) directory 90 | 91 | 92 | For any questions, please create an issue on Github. 93 | 94 | 95 | Contributor policy 96 | ==================== 97 | This project is now following the _git flow_ approach. Namely: 98 | 99 | - branch `master` is only for stable versions and releases; 100 | - branch `develop` is main working branch; 101 | - contributor should create new branch for certain feature and then merge with `develop` branch as feature was done; 102 | - each release on `master` branch should correspond to package on PyPI; 103 | - A maintainer checks all the pull request 104 | 105 | A pull request should satisfy the following requirements: 106 | - style and quality description of pull request; 107 | - new changes should be tested and shouldn't break anything; 108 | - pull request for one fix or one feature(could be several commits); 109 | - try to keep the code style of the project; 110 | 111 | Current maintainer is [Ivan Oseledets](oseledets.github.io). 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /conda-recipe/README.md: -------------------------------------------------------------------------------- 1 | To build the ttpy module 2 | 3 | 1. Install the pre requirements 4 | ``` 5 | conda install conda-build 6 | ``` 7 | 2. Add tag to the release on github; 8 | 3. Obtain a tar archive with all the ttpy code (including the submodules); 9 | 4. Load the archive to the web; 10 | 5. Edit the release version, .tar archive url and its md5 check sum in the ```meta.yaml```; 11 | 6. Build the module: 12 | ``` 13 | conda build conda-recipe 14 | ``` 15 | 7. Try to install it locally: 16 | ``` 17 | conda install --use-local conda-recipe 18 | ``` 19 | 8. Upload the module to the binstar: 20 | ``` 21 | binstar login 22 | binstar upload /home/alex/anaconda/conda-bld/linux-64/ttpy-1.0-np19py27_0.tar.bz2 23 | binstar logout 24 | ``` 25 | -------------------------------------------------------------------------------- /conda-recipe/bld.bat: -------------------------------------------------------------------------------- 1 | "%PYTHON%" setup.py install 2 | if errorlevel 1 exit 1 3 | 4 | :: Add more build steps here, if they are necessary. 5 | 6 | :: See 7 | :: http://docs.continuum.io/conda/build.html 8 | :: for a list of environment variables that are set during the build process. 9 | -------------------------------------------------------------------------------- /conda-recipe/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | $PYTHON setup.py install 4 | 5 | # Add more build steps here, if they are necessary. 6 | 7 | # See 8 | # http://docs.continuum.io/conda/build.html 9 | # for a list of environment variables that are set during the build process. 10 | -------------------------------------------------------------------------------- /conda-recipe/meta.yaml: -------------------------------------------------------------------------------- 1 | package: 2 | name: ttpy 3 | version: "1.0" 4 | 5 | source: 6 | # fn: ttpy-1.0.tar 7 | #url: https://dl.dropboxusercontent.com/u/49234889/ttpy-1.0.tar 8 | #md5: ff081595d853c4c563dba96aa9180f1c 9 | source: 10 | git_url: git://github.com/oseledets/ttpy 11 | git_branch: master 12 | # patches: 13 | # List any patch files here 14 | # - fix.patch 15 | 16 | build: 17 | # noarch_python: True 18 | # preserve_egg_dir: True 19 | entry_points: 20 | # Put any entry points (scripts to be generated automatically) here. The 21 | # syntax is module:function. For example 22 | # 23 | # - pyinstrument = pyinstrument:main 24 | # 25 | # Would create an entry point called pyinstrument that calls pyinstrument.main() 26 | 27 | # - pyinstrument = pyinstrument.__main__:main 28 | 29 | # If this is a new build for the same version, increment the build 30 | # number. If you do not include this key, it defaults to 0. 31 | # number: 1 32 | 33 | requirements: 34 | build: 35 | - python 36 | - setuptools 37 | - numpy 38 | - cython 39 | # - accelerate 40 | 41 | run: 42 | - python 43 | - numpy 44 | - cython 45 | # - accelerate 46 | 47 | test: 48 | # Python imports 49 | imports: 50 | - numpy 51 | - tt 52 | 53 | commands: 54 | # You can put test commands to be run here. Use this to test that the 55 | # entry points work. 56 | 57 | # - pyinstrument --help 58 | 59 | # You can also put a file called run_test.py in the recipe that will be run 60 | # at test time. 61 | 62 | # requires: 63 | # Put any additional test requirements here. For example 64 | # - nose 65 | 66 | about: 67 | home: https://github.com/oseledets/ttpy 68 | license: MIT License 69 | summary: u"Python implementation of the TT-Toolbox." 70 | 71 | # See 72 | # http://docs.continuum.io/conda/build.html for 73 | # more information about meta.yaml 74 | -------------------------------------------------------------------------------- /conda-recipe/run_test.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import nose 4 | import tt 5 | verbose = True 6 | nose_argv = sys.argv 7 | nose_argv += ['--detailed-errors', '--exe'] 8 | if verbose: 9 | nose_argv.append('-v') 10 | initial_dir = os.getcwd() 11 | tt_file = os.path.abspath(tt.__file__) 12 | tt_dir = os.path.dirname(tt_file) 13 | os.chdir(tt_dir) 14 | try: 15 | nose.run(argv=nose_argv) 16 | finally: 17 | os.chdir(initial_dir) 18 | -------------------------------------------------------------------------------- /examples/myheisen.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import, division 2 | from six.moves import xrange 3 | import sys 4 | sys.path.append('../') 5 | import tt 6 | import numpy as np 7 | from tt.eigb import eigb 8 | import time 9 | 10 | """ Compute minimal eigenvalues for the Heisenberg model """ 11 | def gen_1d(mat,e,i,d): 12 | w = mat 13 | for j in xrange(i): 14 | w = tt.kron(e,w) 15 | for j in xrange(d-i-1): 16 | w = tt.kron(w,e) 17 | return w 18 | 19 | def gen_heisen(d): 20 | sx = [[0,1],[1,0]] 21 | sx = np.array(sx,dtype=np.float) 22 | sz = [[1,0],[0,-1]] 23 | sz = np.array(sz,dtype=np.float) 24 | sz = 0.5 * sz 25 | sp = [[0,1],[0,0]]; sp = np.array(sp,dtype=np.float) 26 | sm = sp.T 27 | e = np.eye(2) 28 | sx = tt.matrix(sx,1e-12) 29 | sz = tt.matrix(sz,1e-12) 30 | sp = tt.matrix(sp,1e-12) 31 | sm = tt.matrix(sm,1e-12) 32 | e = tt.matrix(e,1e-12) 33 | #Generate ssx, ssz 34 | ssp = [gen_1d(sp,e,i,d) for i in xrange(d)] 35 | ssz = [gen_1d(sz,e,i,d) for i in xrange(d)] 36 | ssm = [gen_1d(sm,e,i,d) for i in xrange(d)] 37 | A = None 38 | for i in xrange(d-1): 39 | A = A + 0.5 * (ssp[i] * ssm[i+1] + ssm[i] * ssp[i+1]) + (ssz[i] * ssz[i+1]) 40 | A = A.round(1e-8) 41 | return A 42 | 43 | if __name__ == '__main__': 44 | 45 | d = 60 #The dimension of the problem (number of spins) 46 | B = 5 # Number of eigenvalues sought 47 | eps = 1e-5 #Accuracy of the computations 48 | 49 | A = gen_heisen(d) 50 | n = A.n 51 | d = A.tt.d 52 | r = [B]*(d+1) 53 | r[0] = 1 54 | r[d] = B 55 | x0 = tt.rand(n,d,r) 56 | t1 = time.time() 57 | print('Matrices are done') 58 | y, lam = eigb(A, x0, eps, max_full_size = 1000) 59 | t2 = time.time() 60 | print('Elapsed time: %3.1f' % (t2 - t1)) 61 | print('Eigenvalues: ', lam) 62 | -------------------------------------------------------------------------------- /examples/test_amen.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import, division 2 | import sys 3 | sys.path.append('../') 4 | import tt 5 | from tt.amen import amen_solve 6 | """ This program test two subroutines: matrix-by-vector multiplication 7 | and linear system solution via AMR scheme""" 8 | 9 | d = 12 10 | A = tt.qlaplace_dd([d]) 11 | x = tt.ones(2,d) 12 | y = amen_solve(A,x,x,1e-6) 13 | 14 | 15 | -------------------------------------------------------------------------------- /examples/test_common.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # test_common.py 3 | # See LICENSE for details 4 | """Test common matrix operation with QTT-toolbox like matmat-operation and matvec-operation. 5 | """ 6 | 7 | from __future__ import print_function, absolute_import, division 8 | import numpy as np 9 | import sys 10 | sys.path.append('../') 11 | import tt 12 | 13 | 14 | x = tt.xfun(2, 3) 15 | e = tt.ones(2, 2) 16 | X = tt.matrix(x, n=[2] * 3, m=[1] * 3) # [0, 1, 2, 3, 4, 5, 6, 7]^T 17 | E = tt.matrix(e, n=[1] * 2, m=[2] * 2) # [1, 1, 1, 1] 18 | #[[ 0. 0. 0. 0.] 19 | # [ 1. 1. 1. 1.] 20 | # [ 2. 2. 2. 2.] 21 | # [ 3. 3. 3. 3.] 22 | # [ 4. 4. 4. 4.] 23 | # [ 5. 5. 5. 5.] 24 | # [ 6. 6. 6. 6.] 25 | # [ 7. 7. 7. 7.]] 26 | print((X * E).full()) 27 | assert np.all((X * E) * np.arange(4) == np.arange(8) * 6.) 28 | 29 | A = tt.matrix(tt.xfun(2, 3), n=[1] * 3, m=[2] * 3) 30 | u = np.arange(8) 31 | assert A * u == 140. 32 | -------------------------------------------------------------------------------- /examples/test_cross.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import, division 2 | import sys 3 | sys.path.append('../') 4 | import numpy as np 5 | import tt 6 | 7 | d = 30 8 | n = 2 ** d 9 | b = 1E3 10 | h = b / (n + 1) 11 | #x = np.arange(n) 12 | #x = np.reshape(x, [2] * d, order = 'F') 13 | #x = tt.tensor(x, 1e-12) 14 | x = tt.xfun(2, d) 15 | e = tt.ones(2, d) 16 | x = x + e 17 | x = x * h 18 | 19 | 20 | sf = lambda x : np.sin(x) / x #Should be rank 2 21 | 22 | y = tt.multifuncrs([x], sf, 1e-6, y0=tt.ones(2, d)) 23 | #y1 = tt.tensor(sf(x.full()), 1e-8) 24 | 25 | print("pi / 2 ~ ", tt.dot(y, tt.ones(2, d)) * h) 26 | #print (y - y1).norm() / y.norm() 27 | -------------------------------------------------------------------------------- /examples/test_eigb.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import, division 2 | import sys 3 | sys.path.append('../') 4 | import numpy as np 5 | import tt 6 | from tt.eigb import * 7 | import time 8 | 9 | """ This code computes many eigenvalus of the Laplacian operator """ 10 | 11 | d = 8 12 | f = 8 13 | A = tt.qlaplace_dd([d]*f) 14 | #A = (-1)*A 15 | #A = tt.eye(2,d) 16 | n = [2] *(d * f) 17 | r = [8] *(d * f + 1) 18 | r[0] = 1 19 | r[d * f] = 8 #Number of eigenvalues sought 20 | x = tt.rand(n, d * f, r) 21 | #x = tt_ones(2,d) 22 | t = time.time() 23 | y, lam = eigb(A, x, 1e-6) 24 | 25 | t1 = time.time() 26 | print('Eigenvalues:', lam) 27 | print('Time is:', t1-t) 28 | -------------------------------------------------------------------------------- /examples/test_multifuncrs.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import, division 2 | import numpy as np 3 | import tt 4 | a = tt.rand([3, 5, 7, 11], 4, [1, 4, 6, 5, 1]) 5 | b = tt.rand([3, 5, 7, 11], 4, [1, 2, 4, 3, 1]) 6 | c = tt.multifuncrs2([a, b], lambda x: np.sum(x, axis=1), eps=1E-6) 7 | 8 | print("Relative error norm:", (c - (a + b)).norm() / (a + b).norm()) 9 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools >= 40.6.0", "wheel", "numpy", "Cython"] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Cython>=0.24 2 | numpy>=1.11.0; python_version<"3.12" 3 | numpy>=1.11.0,<1.23; python_version=="3.12" 4 | scipy>=0.19 5 | six>=1.10.0 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """TTPY: Python implementation of the Tensor Train (TT) - Toolbox. 3 | 4 | Python implementation of the Tensor Train (TT) - Toolbox. It contains several important packages for working with the 5 | TT-format in Python. It is able to do TT-interpolation, solve linear systems, eigenproblems, solve dynamical problems. 6 | Several computational routines are done in Fortran (which can be used separatedly), and are wrapped with the f2py tool. 7 | """ 8 | 9 | import builtins 10 | import os 11 | import sys 12 | 13 | from numpy.distutils.core import setup 14 | from numpy.distutils.misc_util import Configuration 15 | 16 | # This is a hack to build ttpy from source tree. We set the variable in order 17 | # to avoid loading of modules which are not built yet. The same work around is 18 | # used in NumPy. 19 | builtins.__TTPY_SETUP__ = True 20 | 21 | DOCLINES = (__doc__ or '').split('\n') 22 | 23 | PLATFORMS = [ 24 | 'Windows', 25 | 'Linux', 26 | 'Solaris', 27 | 'Mac OS-X', 28 | 'Unix', 29 | ] 30 | 31 | CLASSIFIERS = """\ 32 | Development Status :: 4 - Beta 33 | Intended Audience :: Science/Research 34 | Intended Audience :: Developers 35 | License :: OSI Approved :: MIT License 36 | Programming Language :: Fortran 37 | Programming Language :: Python 38 | Programming Language :: Python :: 2 39 | Programming Language :: Python :: 2.6 40 | Programming Language :: Python :: 2.7 41 | Programming Language :: Python :: Implementation :: CPython 42 | Topic :: Software Development 43 | Topic :: Scientific/Engineering 44 | Operating System :: Microsoft :: Windows 45 | Operating System :: POSIX 46 | Operating System :: Unix 47 | Operating System :: MacOS 48 | """ 49 | 50 | MAJOR = 1 51 | MINOR = 2 52 | MICRO = 0 53 | ISRELEASED = True 54 | VERSION = '%d.%d.%d' % (MAJOR, MINOR, MICRO) 55 | 56 | 57 | def configuration(parent_package='', top_path=None): 58 | config = Configuration(None, parent_package, top_path) 59 | config.set_options( 60 | ignore_setup_xxx_py=True, 61 | assume_default_configuration=True, 62 | delegate_options_to_subpackages=True, 63 | quiet=True, 64 | ) 65 | config.add_subpackage('tt') 66 | return config 67 | 68 | def setup_package(): 69 | metadata = dict( 70 | name='ttpy', 71 | version=VERSION, 72 | description = DOCLINES[0], 73 | long_description = '\n'.join(DOCLINES[2:]), 74 | url='https://github.com/oseledets/ttpy', 75 | download_url='https://github.com/oseledets/ttpy/tarball/v' + VERSION, 76 | author='Ivan Oseledets', 77 | maintainer='Ivan Oseledets', 78 | author_email='ivan.oseledets@gmail.com', 79 | platforms=PLATFORMS, 80 | classifiers=[line for line in CLASSIFIERS.split('\n') if line], 81 | configuration=configuration, 82 | ) 83 | 84 | # Move to source tree root, inject source tree root to python path, and 85 | # reverse changes as soon as setup is done. The issue is that tt.distutils 86 | # module should be in python path. 87 | cur_dir = os.getcwd() 88 | src_dir = os.path.abspath(os.path.dirname(__file__)) 89 | sys.path.insert(0, src_dir) 90 | os.chdir(src_dir) 91 | try: 92 | setup(**metadata) 93 | finally: 94 | os.chdir(cur_dir) 95 | sys.path.remove(src_dir) 96 | 97 | 98 | if __name__ == '__main__': 99 | setup_package() 100 | # Remove flag to avoid potential problems. 101 | del builtins.__TTPY_SETUP__ 102 | -------------------------------------------------------------------------------- /tt/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import, division 2 | from sys import platform, stderr 3 | 4 | # First of all, we should check context in which package is loaded. If there is 5 | # such variable then we should to avoid loading of submodules. 6 | try: 7 | __TTPY_SETUP__ 8 | except NameError: 9 | __TTPY_SETUP__ = False 10 | 11 | if __TTPY_SETUP__: 12 | stderr.write('Building from ttpy source directory.\n') 13 | else: 14 | try: 15 | from .core.tt import * 16 | except: 17 | import ctypes 18 | try: 19 | ctypes.CDLL("libmkl_rt.so", ctypes.RTLD_GLOBAL) 20 | except: 21 | try: 22 | if platform.startswith('linux'): 23 | ctypes.CDLL("liblapack.so", ctypes.RTLD_GLOBAL) 24 | elif platform.startswith('darwin'): 25 | ctypes.CDLL("liblapack.dylib", ctypes.RTLD_GLOBAL) 26 | except: 27 | print("Did not find MKL or LAPACK library") 28 | from .core.tt import * 29 | 30 | from .multifuncrs import multifuncrs 31 | from .multifuncrs2 import multifuncrs2 32 | from .solvers import GMRES 33 | -------------------------------------------------------------------------------- /tt/amen/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import, division 2 | from . import amen_f90 3 | import tt 4 | from .amen_mv import amen_mv 5 | 6 | 7 | def amen_solve(A, f, x0, eps, kickrank=4, nswp=20, local_prec='n', 8 | local_iters=2, local_restart=40, trunc_norm=1, max_full_size=50, verb=1): 9 | """ Approximate linear system solution in the tensor-train (TT) format 10 | using Alternating minimal energy (AMEN approach) 11 | 12 | 13 | :References: Sergey Dolgov, Dmitry. Savostyanov 14 | 15 | Paper 1: http://arxiv.org/abs/1301.6068 16 | Paper 2: http://arxiv.org/abs/1304.1222 17 | 18 | :param A: Matrix in the TT-format 19 | :type A: matrix 20 | :param f: Right-hand side in the TT-format 21 | :type f: tensor 22 | :param x0: TT-tensor of initial guess. 23 | :type x0: tensor 24 | :param eps: Accuracy. 25 | :type eps: float 26 | :param kickrank: compression rank of the residual Z, i.e. enrichment size [4] 27 | :param nswp: maximal number of sweeps [50] 28 | :param local_prec: local preconditioner: '' (no prec.), 'ljacobi', 'cjacobi', 'rjacobi' [''] 29 | :param local_iters: dimension of local gmres [40] 30 | :param local_restart: dimension of local gmres [40] 31 | :param trunc_norm: truncate in either Frob. ('fro'), or residual norm ('residual') ['residual'] 32 | :param max_full_size: maximal size of the local matrix for the full solver [50] 33 | :param verb: 0 -- no info output, 1 -- print info output 34 | 35 | :Example: 36 | 37 | >>> import tt 38 | >>> import tt.amen #Needed, not imported automatically 39 | >>> a = tt.qlaplace_dd([8, 8, 8]) #3D-Laplacian 40 | >>> rhs = tt.ones(2, 3 * 8) #Right-hand side of all ones 41 | >>> x = tt.amen.amen_solve(a, rhs, rhs, 1e-8) 42 | amen_solve: swp=1, max_dx= 9.766E-01, max_res= 3.269E+00, max_rank=5 43 | amen_solve: swp=2, max_dx= 4.293E-01, max_res= 8.335E+00, max_rank=9 44 | amen_solve: swp=3, max_dx= 1.135E-01, max_res= 5.341E+00, max_rank=13 45 | amen_solve: swp=4, max_dx= 9.032E-03, max_res= 5.908E-01, max_rank=17 46 | amen_solve: swp=5, max_dx= 9.500E-04, max_res= 7.636E-02, max_rank=21 47 | amen_solve: swp=6, max_dx= 4.002E-05, max_res= 5.573E-03, max_rank=25 48 | amen_solve: swp=7, max_dx= 4.949E-06, max_res= 8.418E-04, max_rank=29 49 | amen_solve: swp=8, max_dx= 9.618E-07, max_res= 2.599E-04, max_rank=33 50 | amen_solve: swp=9, max_dx= 2.792E-07, max_res= 6.336E-05, max_rank=37 51 | amen_solve: swp=10, max_dx= 4.730E-08, max_res= 1.663E-05, max_rank=41 52 | amen_solve: swp=11, max_dx= 1.508E-08, max_res= 5.463E-06, max_rank=45 53 | amen_solve: swp=12, max_dx= 3.771E-09, max_res= 1.847E-06, max_rank=49 54 | amen_solve: swp=13, max_dx= 7.797E-10, max_res= 6.203E-07, max_rank=53 55 | amen_solve: swp=14, max_dx= 1.747E-10, max_res= 2.058E-07, max_rank=57 56 | amen_solve: swp=15, max_dx= 8.150E-11, max_res= 8.555E-08, max_rank=61 57 | amen_solve: swp=16, max_dx= 2.399E-11, max_res= 4.215E-08, max_rank=65 58 | amen_solve: swp=17, max_dx= 7.871E-12, max_res= 1.341E-08, max_rank=69 59 | amen_solve: swp=18, max_dx= 3.053E-12, max_res= 6.982E-09, max_rank=73 60 | >>> print (tt.matvec(a, x) - rhs).norm() / rhs.norm() 61 | 5.5152374305127345e-09 62 | """ 63 | m = A.m.copy() 64 | rx0 = x0.r.copy() 65 | psx0 = x0.ps.copy() 66 | if A.is_complex or f.is_complex: 67 | amen_f90.amen_f90.ztt_amen_wrapper(f.d, A.n, m, 68 | A.tt.r, A.tt.ps, A.tt.core, 69 | f.r, f.ps, f.core, 70 | rx0, psx0, x0.core, 71 | eps, kickrank, nswp, local_iters, local_restart, trunc_norm, max_full_size, verb, local_prec) 72 | else: 73 | if x0.is_complex: 74 | x0 = x0.real() 75 | rx0 = x0.r.copy() 76 | psx0 = x0.ps.copy() 77 | amen_f90.amen_f90.dtt_amen_wrapper(f.d, A.n, m, 78 | A.tt.r, A.tt.ps, A.tt.core, 79 | f.r, f.ps, f.core, 80 | rx0, psx0, x0.core, 81 | eps, kickrank, nswp, local_iters, local_restart, trunc_norm, max_full_size, verb, local_prec) 82 | x = tt.tensor() 83 | x.d = f.d 84 | x.n = m.copy() 85 | x.r = rx0 86 | if A.is_complex or f.is_complex: 87 | x.core = amen_f90.amen_f90.zcore.copy() 88 | else: 89 | x.core = amen_f90.amen_f90.core.copy() 90 | amen_f90.amen_f90.deallocate_result() 91 | x.get_ps() 92 | return tt.vector.from_list(tt.tensor.to_list(x)) 93 | -------------------------------------------------------------------------------- /tt/amen/amen_f90.f90: -------------------------------------------------------------------------------- 1 | module amen_f90 2 | use ttamen_lib 3 | use python_conv_lib 4 | real(8), allocatable :: core(:) 5 | complex(8), allocatable :: zcore(:) 6 | contains 7 | 8 | subroutine deallocate_result() 9 | if ( allocated(core) ) then 10 | deallocate(core) 11 | end if 12 | if ( allocated(zcore) ) then 13 | deallocate(zcore) 14 | end if 15 | end subroutine deallocate_result 16 | 17 | subroutine dtt_amen_wrapper(d, n, m, rA, psA, crA, ry, psy, cry, rx, psx, crx, tol, kickrank, nswp, & 18 | local_iters, local_restart, trunc_norm, max_full_size, verb, local_prec) 19 | implicit none 20 | integer, intent(in) :: d 21 | integer, intent(in) :: n(d) 22 | integer, intent(inout) :: m(d) 23 | integer, intent(in) :: rA(d+1) 24 | integer, intent(in) :: psA(d+1) 25 | real(8), intent(in) :: crA(:) 26 | integer, intent(in) :: ry(d+1) 27 | integer, intent(in) :: psy(d+1) 28 | real(8), intent(in) :: cry(:) 29 | integer, intent(inout) :: rx(d+1) 30 | integer, intent(inout) :: psx(d+1) 31 | real(8), intent(in) :: crx(:) 32 | 33 | double precision,intent(in) :: tol 34 | integer,intent(in) :: kickrank, local_iters, local_restart, nswp, trunc_norm, verb, max_full_size 35 | character,intent(in) :: local_prec 36 | 37 | type(dtt) :: A, y, x 38 | integer :: nm(d) 39 | nm = n * m 40 | call arrays_to_sdv(nm,rA,d,psA,crA,A) 41 | call arrays_to_sdv(n,ry,d,psy,cry,y) 42 | call arrays_to_sdv(m,rx,d,psx,crx,x) 43 | call dtt_amen_solve(A, y, tol, x, kickrank, nswp, local_prec, local_iters, local_restart, trunc_norm, max_full_size, verb) 44 | call dealloc(A) 45 | call dealloc(y) 46 | call sdv_to_arrays(m,rx,d,psx,core,x) 47 | call dealloc(x) 48 | 49 | end subroutine dtt_amen_wrapper 50 | 51 | subroutine ztt_amen_wrapper(d, n, m, rA, psA, crA, ry, psy, cry, rx, psx, crx, tol, kickrank, nswp, & 52 | local_iters, local_restart, trunc_norm, max_full_size, verb, local_prec) 53 | implicit none 54 | integer, intent(in) :: d 55 | integer, intent(in) :: n(d) 56 | integer, intent(inout) :: m(d) 57 | integer, intent(in) :: rA(d+1) 58 | integer, intent(in) :: psA(d+1) 59 | complex(8), intent(in) :: crA(:) 60 | integer, intent(in) :: ry(d+1) 61 | integer, intent(in) :: psy(d+1) 62 | complex(8), intent(in) :: cry(:) 63 | integer, intent(inout) :: rx(d+1) 64 | integer, intent(inout) :: psx(d+1) 65 | complex(8), intent(in) :: crx(:) 66 | 67 | double precision,intent(in) :: tol 68 | integer,intent(in) :: kickrank, local_iters, local_restart, nswp, trunc_norm, verb, max_full_size 69 | character,intent(in) :: local_prec 70 | 71 | type(ztt) :: A, y, x 72 | integer :: nm(d) 73 | nm = n * m 74 | call arrays_to_sdv(nm,rA,d,psA,crA,A) 75 | call arrays_to_sdv(n,ry,d,psy,cry,y) 76 | call arrays_to_sdv(m,rx,d,psx,crx,x) 77 | call ztt_amen_solve(A, y, tol, x, kickrank, nswp, local_prec, local_iters, local_restart, trunc_norm, max_full_size, verb) 78 | call dealloc(A) 79 | call dealloc(y) 80 | call sdv_to_arrays(m,rx,d,psx,zcore,x) 81 | call dealloc(x) 82 | 83 | end subroutine ztt_amen_wrapper 84 | end module amen_f90 85 | -------------------------------------------------------------------------------- /tt/amen/amen_f90.pyf: -------------------------------------------------------------------------------- 1 | ! -*- f90 -*- 2 | ! Note: the context of this file is case sensitive. 3 | python module amen_f90 ! in 4 | interface ! in :amen_f90 5 | module amen_f90 ! in amen_f90.f90 6 | use ttamen_lib 7 | use python_conv_lib 8 | real(kind=8), allocatable,dimension(:) :: core 9 | complex(kind=8), allocatable,dimension(:) :: zcore 10 | subroutine deallocate_result ! in amen_f90.f90:amen_f90 11 | end subroutine deallocate_result 12 | subroutine dtt_amen_wrapper(d,n,m,ra,psa,cra,ry,psy,cry,rx,psx,crx,tol,kickrank,nswp,local_iters,local_restart,trunc_norm,max_full_size,verb,local_prec) ! in amen_f90.f90:amen_f90 13 | integer intent(in) :: d 14 | integer dimension(d),intent(in),depend(d) :: n 15 | integer dimension(d),intent(inout),depend(d) :: m 16 | integer dimension(d + 1),intent(in),depend(d) :: ra 17 | integer dimension(d + 1),intent(in),depend(d) :: psa 18 | real(kind=8) dimension(:),intent(in) :: cra 19 | integer dimension(d + 1),intent(in),depend(d) :: ry 20 | integer dimension(d + 1),intent(in),depend(d) :: psy 21 | real(kind=8) dimension(:),intent(in) :: cry 22 | integer dimension(d + 1),intent(inout),depend(d) :: rx 23 | integer dimension(d + 1),intent(inout),depend(d) :: psx 24 | real(kind=8) dimension(:),intent(in) :: crx 25 | double precision intent(in) :: tol 26 | integer intent(in) :: kickrank 27 | integer intent(in) :: nswp 28 | integer intent(in) :: local_iters 29 | integer intent(in) :: local_restart 30 | integer intent(in) :: trunc_norm 31 | integer intent(in) :: max_full_size 32 | integer intent(in) :: verb 33 | character intent(in) :: local_prec 34 | end subroutine dtt_amen_wrapper 35 | subroutine ztt_amen_wrapper(d,n,m,ra,psa,cra,ry,psy,cry,rx,psx,crx,tol,kickrank,nswp,local_iters,local_restart,trunc_norm,max_full_size,verb,local_prec) ! in amen_f90.f90:amen_f90 36 | integer intent(in) :: d 37 | integer dimension(d),intent(in),depend(d) :: n 38 | integer dimension(d),intent(inout),depend(d) :: m 39 | integer dimension(d + 1),intent(in),depend(d) :: ra 40 | integer dimension(d + 1),intent(in),depend(d) :: psa 41 | complex(kind=8) dimension(:),intent(in) :: cra 42 | integer dimension(d + 1),intent(in),depend(d) :: ry 43 | integer dimension(d + 1),intent(in),depend(d) :: psy 44 | complex(kind=8) dimension(:),intent(in) :: cry 45 | integer dimension(d + 1),intent(inout),depend(d) :: rx 46 | integer dimension(d + 1),intent(inout),depend(d) :: psx 47 | complex(kind=8) dimension(:),intent(in) :: crx 48 | double precision intent(in) :: tol 49 | integer intent(in) :: kickrank 50 | integer intent(in) :: nswp 51 | integer intent(in) :: local_iters 52 | integer intent(in) :: local_restart 53 | integer intent(in) :: trunc_norm 54 | integer intent(in) :: max_full_size 55 | integer intent(in) :: verb 56 | character intent(in) :: local_prec 57 | end subroutine ztt_amen_wrapper 58 | end module amen_f90 59 | end interface 60 | end python module amen_f90 61 | ! This file was auto-generated with f2py (version:2). 62 | ! See http://cens.ioc.ee/projects/f2py2e/ 63 | -------------------------------------------------------------------------------- /tt/amen/setup.py: -------------------------------------------------------------------------------- 1 | # setup.py 2 | # This script will build the main subpackages 3 | # See LICENSE for details 4 | 5 | from __future__ import print_function, absolute_import 6 | from numpy.distutils.misc_util import Configuration 7 | 8 | 9 | AMEN_SRC = [ 10 | 'amen_f90.f90', 11 | 'amen_f90.pyf', 12 | ] 13 | 14 | 15 | def configuration(parent_package='', top_path=None): 16 | config = Configuration('amen', parent_package, top_path) 17 | config.add_extension( 18 | 'amen_f90', 19 | sources=AMEN_SRC, 20 | depends=['mytt'], 21 | libraries=['mytt'], 22 | ) 23 | 24 | return config 25 | 26 | 27 | if __name__ == '__main__': 28 | print('This is the wrong setup.py to run') 29 | -------------------------------------------------------------------------------- /tt/completion/__init__.py: -------------------------------------------------------------------------------- 1 | # ALS completion 2 | from als import ttSparseALS 3 | del als 4 | 5 | from demo_completion import demo_completion 6 | -------------------------------------------------------------------------------- /tt/completion/als.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | try: 3 | xrange 4 | except NameError: 5 | xrange = range 6 | import numpy as np 7 | import tt 8 | import time 9 | import copy 10 | 11 | def reshape(x, shape): 12 | ''' 13 | Reshape given numpy.array into new shape with Fortran-ordering 14 | 15 | Parameters: 16 | :np.array: x 17 | given numpy array 18 | :list, tuple, np.array: shape 19 | new shape 20 | ''' 21 | return np.reshape(x, shape, order = 'F') 22 | 23 | def getRow(leftU, rightV, jVec): 24 | ''' 25 | Compute X_{\geq \mu}^T \otimes X_{leq \mu} 26 | X_{\geq \mu} = V_{\mu+1}(j_{\mu}) \ldots V_{d} (j_{d}) [left interface matrix] 27 | X_{\leq \mu} = U_{1} (j_{1}) \ldots U_{\mu-1}(j_{\mu-1}) [right interface matrix] 28 | 29 | Parameters: 30 | :list of numpy.arrays: leftU 31 | left-orthogonal cores from 1 to \mu-1 32 | :list of numpy.arrays: rightV 33 | right-orthogonal cores from \mu+1 to d 34 | :list, tuple, np.array: jVec 35 | indices for each dimension n[k] 36 | Returns: 37 | :numpy.array: result 38 | Kronecker product between left and right interface 39 | matrices. Left matrix is transposed. 40 | ''' 41 | jLeft = None 42 | jRight = None 43 | if len(leftU) > 0: 44 | jLeft = jVec[:len(leftU)] 45 | if len(rightV) > 0: 46 | jRight = jVec[-len(rightV):] 47 | 48 | multU = np.ones([1,1]) 49 | for k in xrange(len(leftU)): 50 | multU = np.dot(multU, leftU[k][:, jLeft[k], :]) 51 | multV= np.ones([1,1]) 52 | for k in xrange(len(rightV)-1, -1, -1): 53 | multV = np.dot(rightV[k][:, jRight[k], :], multV) 54 | 55 | result = np.kron(multV.T, multU) 56 | return result 57 | 58 | def orthLRFull(coreList, mu, splitResult = True): 59 | ''' 60 | Orthogonalize list of TT-cores. 61 | 62 | Parameters: 63 | :list: coreList 64 | list of TT-cores (stored as numpy arrays) 65 | :int: mu 66 | separating index for left and right orthogonalization. 67 | Output cores will be left-orthogonal for dimensions from 1 to \mu-1 68 | and right-orthogonal for dimensions from \mu+1 to d 69 | :boolean: splitResult = True 70 | Controls whether outut should be splitted into left-, non-, right-orthogonal 71 | parts or not. 72 | 73 | Returns: 74 | :list: resultU 75 | left-orthogonal cores with indices from 1 to \mu-1 76 | :np.array: W 77 | \mu-th core 78 | :list: reultV 79 | right-orthogonal cores with indices from \mu+1 to d 80 | OR 81 | :list: resultU + [W] + resultV 82 | concatenated list of cores 83 | ''' 84 | d = len(coreList) 85 | assert (mu >= 0) and (mu <= d) 86 | resultU = [] 87 | for k in xrange(mu): 88 | core = coreList[k].copy() 89 | if k > 0: 90 | core = np.einsum('ijk,li->ljk', core, R) 91 | [r1, n, r2] = core.shape 92 | if (k < mu-1): 93 | core = reshape(core, [r1*n, r2]) 94 | Q, R = np.linalg.qr(core) 95 | rnew = Q.shape[1] 96 | core = reshape(Q, [r1, n, rnew]) 97 | resultU = resultU + [core] 98 | if mu > 0: 99 | W = core.copy() 100 | resultV = [] 101 | for k in xrange(d-1, mu, -1): 102 | core = coreList[k].copy() 103 | if (k < d-1): 104 | core = np.einsum('ijk,lk->ijl', core, R) 105 | [r1, n, r2] = core.shape 106 | if (k > mu+1): 107 | core = reshape(core, [r1, n*r2]) 108 | Q, R = np.linalg.qr(core.T) 109 | rnew = Q.shape[1] 110 | core = reshape(Q.T, [rnew, n, r2]) 111 | resultV = [core] + resultV 112 | if mu < d-1: 113 | if mu > 0: 114 | W = np.einsum('ijk,lk->ijl', W, R) 115 | else: 116 | W = np.einsum('ijk,lk->ijl', coreList[0], R) 117 | if splitResult: 118 | return resultU, W, resultV 119 | return resultU + [W] + resultV 120 | 121 | def computeFunctional(x, cooP): 122 | ''' 123 | Compute value of functional J(X) = ||PX - PA||^2_F, 124 | where P is projector into index subspace of known elements, 125 | X is our approximation, 126 | A is original tensor. 127 | 128 | Parameters: 129 | :tt.vector: x 130 | current approximation [X] 131 | :dict: cooP 132 | dictionary with two records 133 | - 'indices': numpy.array of P x d shape, 134 | contains index subspace of P known elements; 135 | each string is an index of one element. 136 | - 'values': numpy array of size P, 137 | contains P known values. 138 | 139 | Returns: 140 | :float: result 141 | value of functional 142 | ''' 143 | indices = cooP['indices'] 144 | values = cooP['values'] 145 | 146 | [P, d] = indices.shape 147 | assert P == len(values) 148 | 149 | result = 0 150 | for p in xrange(P): 151 | index = tuple(indices[p, :]) 152 | result += (x[index] - values[p])**2 153 | result *= 0.5 154 | return result 155 | 156 | 157 | def ttSparseALS(cooP, shape, x0=None, ttRank=1, tol=1e-5, maxnsweeps=20, verbose=True, alpha=1e-2): 158 | ''' 159 | TT completion via Alternating Least Squares algorithm. 160 | 161 | Parameters: 162 | :dict: cooP 163 | dictionary with two records 164 | - 'indices': numpy.array of P x d shape, 165 | contains index subspace of P known elements; 166 | each string is an index of one element. 167 | - 'values': numpy array of size P, 168 | contains P known values. 169 | :list, numpy.array: shape 170 | full-format shape of tensor to be completed [dimensions] 171 | :tt.vector: x0 = None 172 | initial approximation of completed tensor 173 | If it is specified, parameters 'shape' and 'ttRank' will be ignored 174 | :int, numpy.array: ttRank = 1 175 | assumed rank of completed tensor 176 | :float: tol = 1e-5 177 | tolerance for functional value 178 | :int: maxnsweeps = 20 179 | maximal number of sweeps [sequential optimization of all d cores 180 | in right or left direction] 181 | :boolean: verbose = True 182 | switcher of messages from function 183 | :float: alpha: = 1e-2 184 | regularizer of least squares problem for each slice of current TT core. 185 | [rcond parameter for np.linalg.lstsq] 186 | 187 | Returns: 188 | :tt.vector: xNew 189 | completed TT vector 190 | :list: fit 191 | list of functional values at each sweep 192 | ''' 193 | indices = cooP['indices'] 194 | values = cooP['values'] 195 | 196 | [P, d] = indices.shape 197 | assert P == len(values) 198 | 199 | timeVal = time.clock() 200 | if x0 is None: 201 | x = tt.rand(shape, r = ttRank) 202 | x = x.round(0.) 203 | x = (1./x.norm())*x 204 | else: 205 | x = copy.deepcopy(x0) 206 | assert d == x.d 207 | # TODO: also check if cooP indices are aligned with shape 208 | normP = np.linalg.norm(values) 209 | values /= normP 210 | fitList = [] 211 | sweepTimeList = [] 212 | initTime = time.clock() - timeVal 213 | 214 | timeVal = time.clock() 215 | coreList = tt.vector.to_list(x) 216 | #coreList = orthLRFull(coreList, mu = d, splitResult = False) 217 | # orthTime = time.clock() - timeVal 218 | 219 | if verbose: 220 | print("Initialization time: %.3f seconds (proc.time)" % (initTime)) 221 | # print "Orthogonalizing time: %.3f seconds (proc.time)" % (orthTime) 222 | 223 | for sweep in xrange(maxnsweeps): 224 | sweepStart = time.clock() 225 | # list left + right 226 | [kStart, kEnd, kStep] = [0, d, 1] 227 | # select direction of sweep 228 | ''' 229 | if sweep % 2 == 0: # left to rigth 230 | [kStart, kEnd, kStep] = [0, d, 1] 231 | else: # right to left 232 | [kStart, kEnd, kStep] = [d-1, -1, -1] 233 | ''' 234 | # fix k-th core to update 235 | for k in xrange(kStart, kEnd, kStep): 236 | [r1, n, r2] = coreList[k].shape 237 | core = np.zeros([r1, n, r2]) 238 | leftU = [] 239 | rightV = [] 240 | if k > 0: 241 | leftU = coreList[:k] 242 | if k < d-1: 243 | rightV = coreList[k+1:] 244 | for i in xrange(n): 245 | thetaI = np.where(indices[:, k] == i)[0] 246 | if len(thetaI) > 0: 247 | A = np.zeros([len(thetaI), r1*r2]) 248 | for j in xrange(len(thetaI)): 249 | tmp = getRow(leftU, rightV, indices[thetaI[j], :]) 250 | A[j:j+1, :] += tmp # .flatten(order = 'F') 251 | vecCoreSlice, _, _, _ = np.linalg.lstsq(A, values[thetaI])#, rcond = alpha) 252 | # 0.5*np.linalg.norm(np.dot(A, vecCoreSlice) - values[thetaI])**2. 253 | core[:, i, :] += reshape(vecCoreSlice, [r1, r2]) #### 254 | ''' 255 | if k < (d-1): 256 | core = reshape(core, [r1*n, r2]) 257 | Q, R = np.linalg.qr(core) 258 | rnew = Q.shape[1] 259 | core = reshape(Q, [r1, n, rnew]) 260 | coreList[k+1] = np.einsum('ijk,li->ljk', coreList[k+1], R) 261 | ''' 262 | coreList[k] = core.copy() 263 | ''' 264 | else: 265 | if (k > 0): 266 | core = reshape(core, [r1, n*r2]) 267 | Q, R = np.linalg.qr(core.T) 268 | rnew = Q.shape[1] 269 | core = reshape(Q.T, [rnew, n, r2]) 270 | coreList[k-1] = np.einsum('ijk,lk->ijl', coreList[k-1], R) 271 | ''' 272 | 273 | xNew = tt.vector.from_list(coreList) 274 | fit = computeFunctional(xNew, cooP) 275 | fitList.append(fit) 276 | if fit < tol: 277 | break 278 | if sweep > 0: 279 | if abs(fit - fitList[-2]) < tol: 280 | break 281 | sweepTimeList.append(time.clock() - sweepStart) 282 | if verbose: 283 | print("sweep %d/%d\t fit value: %.5e\t time: %.3f seconds (proc.time)" % (sweep+1, maxnsweeps, fit, sweepTimeList[-1])) 284 | if verbose: 285 | print("Total sweep time: %.3f seconds (proc.time)\t Total time: %.3f seconds (proc.time)" % (sum(sweepTimeList), sum(sweepTimeList) + initTime))# + orthTime) 286 | info = {'fit': fitList, 'initTime': initTime, 'sweepTime': sweepTimeList} # 'orthTime': orthTime, 287 | xNew *= normP 288 | values *= normP 289 | 290 | return xNew, info 291 | -------------------------------------------------------------------------------- /tt/completion/demo_completion.py: -------------------------------------------------------------------------------- 1 | # we need numpy, tt 2 | from __future__ import print_function 3 | try: 4 | xrange 5 | except NameError: 6 | xrange = range 7 | 8 | import numpy as np 9 | import tt, tt.cross 10 | from tt.cross.rectcross import cross 11 | # fascilitating reshape 12 | def reshape(x, shape): 13 | return np.reshape(x, shape, order = 'F') 14 | # our TT-als module 15 | from als import ttSparseALS 16 | # import visualization tools 17 | import matplotlib.pyplot as plt 18 | from matplotlib import gridspec 19 | 20 | 21 | def demo_completion(): 22 | d = 3 23 | n=20 24 | crossR=18 25 | shape = np.array([n]*d) 26 | def func(X): 27 | return 1./(1+(X - n/2)**2).sum(axis = 1)**0.5 28 | 29 | # tt-approximation built via cross approximation 30 | x0 = tt.rand(np.array([n]*d), r = crossR) 31 | tta = cross(func, x0) 32 | print("TT-cross ranks: ", tta.r) 33 | 34 | R = 10 35 | gamma = 0.25 36 | P = int(np.floor(gamma*d*n*(R**2))) 37 | Pb = 100 38 | 39 | # random choice 40 | indices = np.random.choice(n, [P, d]) 41 | indicesB = np.random.choice(n, [Pb, d]) 42 | # making list of tupled stings [indices] 43 | indices = [tuple(indices[k, :]) for k in xrange(indices.shape[0])] 44 | indicesB = [tuple(indicesB[k, :]) for k in xrange(indicesB.shape[0])] 45 | 46 | # set naturally filters input to be unique 47 | indices = set(indices) 48 | indicesB = set(indicesB) 49 | # convert it into list 50 | indices = list(indices) 51 | indicesB = list(indicesB) 52 | # return into numpy.array form 53 | indices = np.array(indices) 54 | indicesB = np.array(indicesB) 55 | 56 | print("Unique sample points: %d/%d (%d)" % (indices.shape[0], P, n**d)) 57 | 58 | vals = func(indices) 59 | cooP = {'values': vals, 'indices': indices} 60 | cooPb = {'values': func(indicesB), 'indices': indicesB} 61 | 62 | maxR = 5 63 | x0 = tt.rand(shape, r=1) 64 | x0 = x0 * (1./ x0.norm()) 65 | x0 = x0.round(0.) 66 | 67 | 68 | # verbose 69 | vb = True 70 | 71 | X1, f = ttSparseALS( 72 | cooP, 73 | shape, 74 | x0=None, 75 | ttRank=maxR, 76 | maxnsweeps=50, 77 | verbose=vb, 78 | tol=1e-8, 79 | alpha = 1e-3 80 | ) 81 | 82 | 83 | # Restore original, initial and approximation into full-format (do not try it in higher dimensions!) 84 | xf1 = X1.full() # approximation ALS 85 | a = tta.full() # original 86 | b = np.zeros([n]*d) # initial 87 | for p in xrange(indices.shape[0]): 88 | b[tuple(indices[p,:])] += vals[p] 89 | 90 | # Visualize slices of original, completed and initial tensors. Colormap is standartized. 91 | plt.clf() 92 | M = [a, xf1, b] 93 | titles = ['Original', 'Completed (ALS)', 'Initial'] 94 | nRow = n 95 | nCol = 3 96 | fig = plt.figure(figsize=(5*nCol, nRow*5)) 97 | gs = gridspec.GridSpec(nRow, nCol, wspace=0., hspace=1e-2, right=1-0.5/nCol)#top=1 - 0.5/nRow, 98 | #bottom=0.5/nRow, left=0.5/nCol, right=1 - 0.5/nCol) 99 | 100 | for k in xrange(nRow): 101 | vmin = [x[k, :, :].min() for x in M] 102 | vmax = [x[k, :, :].max() for x in M] 103 | vmin = min( vmin) 104 | vmax = max( vmax) 105 | for l in xrange(nCol): 106 | ax = plt.subplot(gs[k, l]) 107 | im = ax.imshow(M[l][k, :, :].T, vmin=vmin, vmax=vmax, interpolation='none') 108 | ax.set_axis_off() 109 | ax.set_xticklabels([]) 110 | ax.set_yticklabels([]) 111 | if k == 0: 112 | ax.set_title(titles[l], fontsize=20) 113 | if l == (nCol-1): 114 | box = ax.get_position() 115 | ax.set_position([box.x0*1.05, box.y0, box.width, box.height]) 116 | axColor = plt.axes([box.x0*1.05 + box.width * 1.05, box.y0, 0.01, box.height]) 117 | fig.colorbar(im, cax = axColor) 118 | 119 | 120 | #fig.subplots_adjust(right = 0.5) 121 | #cbar_ax = fig.add_axes([0.55, 0.45, 0.005, 0.11]) 122 | #fig.colorbar(im, cax=cbar_ax) 123 | #fig.subplots_adjust(wspace=0., hspace=0.) 124 | plt.savefig('demo_completion_gridplot.pdf', dpi=300) 125 | #fig.show() 126 | # plot functional curves 127 | 128 | plt.clf() 129 | fig = plt.figure() 130 | plt.semilogy(f['fit'], label='ALS') 131 | plt.xlabel('It.num.') 132 | plt.ylabel('ln( Fit )') 133 | plt.grid(True) 134 | plt.legend() 135 | plt.title('Funval ALS') 136 | plt.savefig('demo_completion_fitplot.pdf', dpi=300) 137 | 138 | if __name__ == '__main__': 139 | demo_completion() 140 | -------------------------------------------------------------------------------- /tt/core/__init__.py: -------------------------------------------------------------------------------- 1 | """TT core module 2 | """ 3 | -------------------------------------------------------------------------------- /tt/core/core.pyf: -------------------------------------------------------------------------------- 1 | ! -*- f90 -*- 2 | ! Note: the context of this file is case sensitive. 3 | 4 | python module core_f90 ! in 5 | interface ! in :core_f90 6 | module core ! in :core_f90:core.f90 7 | complex(kind=8), allocatable,dimension(:) :: zresult_core 8 | real(kind=8), allocatable,dimension(:) :: result_core 9 | subroutine dealloc ! in :core_f90:core.f90:core 10 | end subroutine dealloc 11 | subroutine dmat_mat(d,n,m,k,cr1,cr1size,cr2,cr2size,r1,r2,rres) ! in :core_f90:core.f90:core 12 | integer, optional,intent(in),check(len(n)>=d),depend(n) :: d=len(n) 13 | integer dimension(d),intent(in) :: n 14 | integer dimension(d),intent(in),depend(d) :: m 15 | integer dimension(d),intent(in),depend(d) :: k 16 | real(kind=8) dimension(cr1size),intent(in) :: cr1 17 | integer, optional,intent(in),check(len(cr1)>=cr1size),depend(cr1) :: cr1size=len(cr1) 18 | real(kind=8) dimension(cr2size),intent(in) :: cr2 19 | integer, optional,intent(in),check(len(cr2)>=cr2size),depend(cr2) :: cr2size=len(cr2) 20 | integer dimension(d + 1),intent(in),depend(d) :: r1 21 | integer dimension(d + 1),intent(in),depend(d) :: r2 22 | integer dimension(d + 1),intent(out),depend(d) :: rres 23 | end subroutine dmat_mat 24 | subroutine zmat_mat(d,n,m,k,cr1,cr1size,cr2,cr2size,r1,r2,rres) ! in :core_f90:core.f90:core 25 | integer, optional,intent(in),check(len(n)>=d),depend(n) :: d=len(n) 26 | integer dimension(d),intent(in) :: n 27 | integer dimension(d),intent(in),depend(d) :: m 28 | integer dimension(d),intent(in),depend(d) :: k 29 | complex(kind=8) dimension(cr1size),intent(in) :: cr1 30 | integer, optional,intent(in),check(len(cr1)>=cr1size),depend(cr1) :: cr1size=len(cr1) 31 | complex(kind=8) dimension(cr2size),intent(in) :: cr2 32 | integer, optional,intent(in),check(len(cr2)>=cr2size),depend(cr2) :: cr2size=len(cr2) 33 | integer dimension(d + 1),intent(in),depend(d) :: r1 34 | integer dimension(d + 1),intent(in),depend(d) :: r2 35 | integer dimension(d + 1),intent(out),depend(d) :: rres 36 | end subroutine zmat_mat 37 | end module core 38 | end interface 39 | end python module core_f90 40 | 41 | ! This file was auto-generated with f2py (version:2). 42 | ! See http://cens.ioc.ee/projects/f2py2e/ 43 | -------------------------------------------------------------------------------- /tt/core/matrix.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import, division 2 | from six.moves import xrange 3 | import numpy as _np 4 | from numbers import Number as _Number 5 | from . import core_f90 as _core_f90 6 | from . import vector as _vector 7 | 8 | 9 | class matrix(object): 10 | 11 | def __init__(self, a=None, eps=1e-14, n=None, m=None, rmax=100000): 12 | 13 | self.n = 0 14 | self.m = 0 15 | self.tt = _vector.vector() 16 | 17 | if isinstance(a, _vector.vector): # Convert from a tt.vector 18 | if (n is None or m is None): 19 | n1 = _np.sqrt(a.n).astype(_np.int32) 20 | m1 = _np.sqrt(a.n).astype(_np.int32) 21 | else: 22 | n1 = _np.array(n, dtype=_np.int32) 23 | m1 = _np.array(m, dtype=_np.int32) 24 | self.n = n1 25 | self.m = m1 26 | self.tt.core = a.core.copy() 27 | self.tt.ps = a.ps.copy() 28 | self.tt.r = a.r.copy() 29 | self.tt.n = a.n.copy() 30 | self.tt.d = self.tt.n.size 31 | return 32 | 33 | if isinstance(a, _np.ndarray): 34 | d = a.ndim // 2 35 | p = a.shape 36 | self.n = _np.array(p[:d], dtype=_np.int32) 37 | self.m = _np.array(p[d:], dtype=_np.int32) 38 | prm = _np.arange(2 * d) 39 | prm = prm.reshape((d, 2), order='F') 40 | prm = prm.T 41 | prm = prm.flatten('F') 42 | sz = self.n * self.m 43 | b = a.transpose(prm).reshape(sz, order='F') 44 | self.tt = _vector.vector(b, eps, rmax) 45 | return 46 | 47 | if isinstance(a, matrix): 48 | self.n = a.n.copy() 49 | self.m = a.m.copy() 50 | self.tt = a.tt.copy() 51 | return 52 | @property 53 | def r(self): 54 | return self.tt.r 55 | 56 | @property 57 | def d(self): 58 | return self.tt.d 59 | 60 | @staticmethod 61 | def from_list(a): 62 | d = len(a) # Number of cores 63 | res = matrix() 64 | n = _np.zeros(d, dtype=_np.int32) 65 | r = _np.zeros(d + 1, dtype=_np.int32) 66 | m = _np.zeros(d, dtype=_np.int32) 67 | cr = _np.array([]) 68 | for i in xrange(d): 69 | cr = _np.concatenate((cr, a[i].flatten('F'))) 70 | r[i] = a[i].shape[0] 71 | r[i + 1] = a[i].shape[3] 72 | n[i] = a[i].shape[1] 73 | m[i] = a[i].shape[2] 74 | res.n = n 75 | res.m = m 76 | tt = _vector.vector() 77 | tt.n = n * m 78 | tt.core = cr 79 | tt.r = r 80 | tt.d = d 81 | tt.get_ps() 82 | res.tt = tt 83 | return res 84 | 85 | @staticmethod 86 | def to_list(ttmat): 87 | tt = ttmat.tt 88 | d = tt.d 89 | r = tt.r 90 | n = ttmat.n 91 | m = ttmat.m 92 | ps = tt.ps 93 | core = tt.core 94 | res = [] 95 | for i in xrange(d): 96 | cur_core = core[ps[i] - 1:ps[i + 1] - 1] 97 | cur_core = cur_core.reshape( 98 | (r[i], n[i], m[i], r[i + 1]), order='F') 99 | res.append(cur_core) 100 | return res 101 | 102 | def write(self, fname): 103 | self.tt.write(fname) 104 | 105 | def __repr__(self): 106 | res = "This is a %d-dimensional matrix \n" % self.tt.d 107 | r = self.tt.r 108 | d = self.tt.d 109 | n = self.n 110 | m = self.m 111 | for i in range(d): 112 | res = res + ("r(%d)=%d, n(%d)=%d, m(%d)=%d \n" % 113 | (i, r[i], i, n[i], i, m[i])) 114 | res = res + ("r(%d)=%d \n" % (d, r[d])) 115 | return res 116 | 117 | @property 118 | def erank(self): 119 | return self.tt.erank 120 | 121 | @property 122 | def is_complex(self): 123 | return self.tt.is_complex 124 | 125 | @property 126 | def T(self): 127 | """Transposed TT-matrix""" 128 | mycrs = matrix.to_list(self) 129 | trans_crs = [] 130 | for cr in mycrs: 131 | trans_crs.append(_np.transpose(cr, [0, 2, 1, 3])) 132 | return matrix.from_list(trans_crs) 133 | 134 | def real(self): 135 | """Return real part of a matrix.""" 136 | return matrix(self.tt.real(), n=self.n, m=self.m) 137 | 138 | def imag(self): 139 | """Return imaginary part of a matrix.""" 140 | return matrix(self.tt.imag(), n=self.n, m=self.m) 141 | 142 | def c2r(self): 143 | """Get real matrix from complex one suitable for solving complex linear system with real solver. 144 | 145 | For matrix :math:`M(i_1,j_1,\\ldots,i_d,j_d) = \\Re M + i\\Im M` returns (d+1)-dimensional matrix 146 | :math:`\\tilde{M}(i_1,j_1,\\ldots,i_d,j_d,i_{d+1},j_{d+1})` of form 147 | :math:`\\begin{bmatrix}\\Re M & -\\Im M \\\\ \\Im M & \\Re M \\end{bmatrix}`. This function 148 | is useful for solving complex linear system :math:`\\mathcal{A}X = B` with real solver by 149 | transforming it into 150 | 151 | .. math:: 152 | \\begin{bmatrix}\\Re\\mathcal{A} & -\\Im\\mathcal{A} \\\\ 153 | \\Im\\mathcal{A} & \\Re\\mathcal{A} \\end{bmatrix} 154 | \\begin{bmatrix}\\Re X \\\\ \\Im X\\end{bmatrix} = 155 | \\begin{bmatrix}\\Re B \\\\ \\Im B\\end{bmatrix}. 156 | 157 | """ 158 | return matrix(a=self.tt.__complex_op('M'), n=_np.concatenate( 159 | (self.n, [2])), m=_np.concatenate((self.m, [2]))) 160 | 161 | def r2c(self): 162 | """Get complex matrix from real one made by ``matrix.c2r()``. 163 | 164 | For matrix :math:`\\tilde{M}(i_1,j_1,\\ldots,i_d,j_d,i_{d+1},j_{d+1})` returns complex matrix 165 | 166 | .. math:: 167 | M(i_1,j_1,\\ldots,i_d,j_d) = \\tilde{M}(i_1,j_1,\\ldots,i_d,j_d,0,0) + i\\tilde{M}(i_1,j_1,\\ldots,i_d,j_d,1,0). 168 | 169 | """ 170 | tmp = self.tt.copy() 171 | newcore = _np.array(tmp.core, dtype=_np.complex) 172 | cr = newcore[tmp.ps[-2] - 1:tmp.ps[-1] - 1] 173 | cr = cr.reshape((tmp.r[-2], tmp.n[-1], tmp.r[-1]), order='F') 174 | cr[:, 1, :] *= 1j 175 | cr[:, 2:, :] = 0.0 176 | newcore[tmp.ps[-2] - 1:tmp.ps[-1] - 1] = cr.flatten('F') 177 | tmp.core = newcore 178 | return matrix(sum(tmp, axis=tmp.d - 1), n=self.n, m=self.m) 179 | 180 | def __getitem__(self, index): 181 | if len(index) == 2: 182 | if isinstance(index[0], int) and index[1] == slice(None): 183 | # row requested 184 | row = index[0] 185 | mycrs = matrix.to_list(self) 186 | crs = [] 187 | for i in xrange(self.tt.d): 188 | crs.append(mycrs[i][:, row % self.n[i], :, :].copy()) 189 | row //= self.n[i] 190 | return _vector.vector.from_list(crs) 191 | elif isinstance(index[1], int) and index[0] == slice(None): 192 | # col requested 193 | col = index[1] 194 | mycrs = matrix.to_list(self) 195 | crs = [] 196 | for i in xrange(self.tt.d): 197 | crs.append(mycrs[i][:, :, col % self.m[i], :].copy()) 198 | col //= self.m[i] 199 | return _vector.vector.from_list(crs) 200 | elif isinstance(index[0], int) and isinstance(index[1], int): 201 | # element requested 202 | pass 203 | else: 204 | # complicated submatrix requested 205 | pass 206 | 207 | def __add__(self, other): 208 | if other is None: 209 | return self 210 | c = matrix() 211 | c.tt = self.tt + other.tt 212 | c.n = _np.asanyarray(self.n, dtype=_np.int32).copy() 213 | c.m = _np.asanyarray(self.m, dtype=_np.int32).copy() 214 | return c 215 | 216 | def __radd__(self, other): 217 | if other is None: 218 | return self 219 | return other + self 220 | 221 | def __sub__(self, other): 222 | c = matrix() 223 | c.tt = self.tt - other.tt 224 | c.n = _np.asanyarray(self.n, dtype=_np.int32).copy() 225 | c.m = _np.asanyarray(self.m, dtype=_np.int32).copy() 226 | return c 227 | 228 | def __neg__(self): 229 | return (-1) * self 230 | 231 | def __matmul__(self, other): 232 | """ 233 | Multiplication of two TT-matrices 234 | """ 235 | from . import tools as _tools 236 | diff = len(self.n) - len(other.m) 237 | L = self if diff >= 0 else _tools.kron(self, matrix(_tools.ones(1, abs(diff)))) 238 | R = other if diff <= 0 else _tools.kron(other, matrix(_tools.ones(1, abs(diff)))) 239 | c = matrix() 240 | c.n = L.n.copy() 241 | c.m = R.m.copy() 242 | res = _vector.vector() 243 | res.d = L.tt.d 244 | res.n = c.n * c.m 245 | if L.is_complex or R.is_complex: 246 | res.r = _core_f90.core.zmat_mat( 247 | L.n, L.m, R.m, _np.array( 248 | L.tt.core, dtype=_np.complex), _np.array( 249 | R.tt.core, dtype=_np.complex), L.tt.r, R.tt.r) 250 | res.core = _core_f90.core.zresult_core.copy() 251 | else: 252 | res.r = _core_f90.core.dmat_mat( 253 | L.n, L.m, R.m, _np.real( 254 | L.tt.core), _np.real( 255 | R.tt.core), L.tt.r, R.tt.r) 256 | res.core = _core_f90.core.result_core.copy() 257 | _core_f90.core.dealloc() 258 | res.get_ps() 259 | c.tt = res 260 | return c 261 | 262 | def __rmul__(self, other): 263 | if hasattr(other, '__matmul__') and not isinstance(other, _np.ndarray): 264 | return other.__matmul__(self) 265 | else: 266 | c = matrix() 267 | c.tt = other * self.tt 268 | c.n = self.n 269 | c.m = self.m 270 | return c 271 | 272 | def __mul__(self, other): 273 | if hasattr(other, '__matmul__') and not isinstance(other, _np.ndarray): 274 | return self.__matmul__(other) 275 | elif isinstance(other, (_vector.vector, _Number)): 276 | c = matrix() 277 | c.tt = self.tt * other 278 | c.n = self.n 279 | c.m = self.m 280 | return c 281 | else: 282 | x = _np.asanyarray(other).flatten(order='F') 283 | N = _np.prod(self.m) 284 | if N != x.size: 285 | raise ValueError 286 | x = _np.reshape(x, _np.concatenate(([1], self.m)), order='F') 287 | cores = _vector.vector.to_list(self.tt) 288 | curr = x.copy() 289 | for i in range(len(cores)): 290 | core = cores[i] 291 | core = _np.reshape( 292 | core, [ 293 | self.tt.r[i], self.n[i], self.m[i], self.tt.r[ 294 | i + 1]], order='F') 295 | # print(curr.shape, core.shape) 296 | curr = _np.tensordot(curr, core, axes=([0, 1], [0, 2])) 297 | curr = _np.rollaxis(curr, -1) 298 | curr = _np.sum(curr, axis=0) 299 | return curr.flatten(order='F') 300 | 301 | def __kron__(self, other): 302 | """ Kronecker product of two TT-matrices """ 303 | from . import tools as _tools 304 | if other is None: 305 | return self 306 | a = self 307 | b = other 308 | c = matrix() 309 | c.n = _np.concatenate((a.n, b.n)) 310 | c.m = _np.concatenate((a.m, b.m)) 311 | c.tt = _tools.kron(a.tt, b.tt) 312 | return c 313 | 314 | def norm(self): 315 | return self.tt.norm() 316 | 317 | def round(self, eps=1e-14, rmax=100000): 318 | """ Computes an approximation to a 319 | TT-matrix in with accuracy EPS 320 | """ 321 | c = matrix() 322 | c.tt = self.tt.round(eps, rmax) 323 | c.n = self.n.copy() 324 | c.m = self.m.copy() 325 | return c 326 | 327 | def copy(self): 328 | """ Creates a copy of the TT-matrix """ 329 | c = matrix() 330 | c.tt = self.tt.copy() 331 | c.n = self.n.copy() 332 | c.m = self.m.copy() 333 | return c 334 | 335 | def __diag__(self): 336 | """ Computes the diagonal of the TT-matrix""" 337 | c = _vector.vector() 338 | c.n = self.n.copy() 339 | c.r = self.tt.r.copy() 340 | c.d = self.tt.d # Number are NOT referenced 341 | c.get_ps() 342 | c.alloc_core() 343 | # Actually copy the data 344 | for i in xrange(c.d): 345 | cur_core1 = _np.zeros((c.r[i], c.n[i], c.r[i + 1])) 346 | cur_core = self.tt.core[self.tt.ps[i] - 1:self.tt.ps[i + 1] - 1] 347 | cur_core = cur_core.reshape( 348 | c.r[i], self.n[i], self.m[i], c.r[ 349 | i + 1], order='F') 350 | for j in xrange(c.n[i]): 351 | cur_core1[:, j, :] = cur_core[:, j, j, :] 352 | c.core[c.ps[i] - 1:c.ps[i + 1] - 1] = cur_core1.flatten('F') 353 | return c 354 | 355 | def full(self): 356 | """ Transforms a TT-matrix into a full matrix""" 357 | N = self.n.prod() 358 | M = self.m.prod() 359 | a = self.tt.full() 360 | d = self.tt.d 361 | sz = _np.vstack((self.n, self.m)).flatten('F') 362 | a = a.reshape(sz, order='F') 363 | # Design a permutation 364 | prm = _np.arange(2 * d) 365 | prm = prm.reshape((d, 2), order='F') 366 | prm = prm.transpose() 367 | prm = prm.flatten('F') 368 | # Get the inverse permutation 369 | iprm = [0] * (2 * d) 370 | for i in xrange(2 * d): 371 | iprm[prm[i]] = i 372 | a = a.transpose(iprm).reshape(N, M, order='F') 373 | a = a.reshape(N, M) 374 | return a 375 | 376 | def rmean(self): 377 | return self.tt.rmean() 378 | -------------------------------------------------------------------------------- /tt/core/setup.py: -------------------------------------------------------------------------------- 1 | # setup.py 2 | # This script will build the main subpackages 3 | # See LICENSE for details 4 | 5 | from __future__ import print_function, absolute_import 6 | from numpy.distutils.misc_util import Configuration 7 | from os.path import join 8 | from tt.distutils import get_extra_fflags 9 | 10 | 11 | TTFORT_DIR = '../tt-fort' 12 | 13 | TT_SRC = [ 14 | 'tt_f90.f90', 15 | 'tt_f90.pyf', 16 | ] 17 | 18 | TTCORE_SRC = [ 19 | 'matrix_util.f90', 20 | 'core.f90', 21 | ] 22 | 23 | 24 | def configuration(parent_package='', top_path=None): 25 | ttcore_src = [join(TTFORT_DIR, x) for x in TTCORE_SRC] 26 | ttcore_src.append('core.pyf') 27 | 28 | config = Configuration('core', parent_package, top_path) 29 | config.add_extension( 30 | 'tt_f90', 31 | sources=TT_SRC, 32 | depends=[ 33 | 'mytt', 34 | 'print_lib', 35 | ], 36 | libraries=[ 37 | 'mytt', 38 | 'print_lib' 39 | ], 40 | ) 41 | config.add_extension( 42 | 'core_f90', 43 | sources=ttcore_src, 44 | extra_f90_compile_args=get_extra_fflags(), 45 | ) 46 | 47 | return config 48 | 49 | 50 | if __name__ == '__main__': 51 | print('This is the wrong setup.py to run') 52 | -------------------------------------------------------------------------------- /tt/core/tt.py: -------------------------------------------------------------------------------- 1 | # Here we import all necessary staff from external files 2 | from __future__ import print_function, absolute_import, division 3 | 4 | # main classes 5 | from .matrix import matrix 6 | from .vector import vector, tensor 7 | 8 | 9 | # tools 10 | from .tools import matvec, col, kron, dot, mkron, concatenate, sum, reshape, permute 11 | from .tools import eye, diag, Toeplitz, qshift, qlaplace_dd, IpaS 12 | from .tools import ones, rand, linspace, sin, cos, delta, stepfun, unit, xfun 13 | 14 | # utility 15 | from . import utils 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /tt/core/tt_f90.f90: -------------------------------------------------------------------------------- 1 | module tt_f90 2 | use tt_lib 3 | use ttop_lib 4 | use time_lib 5 | use python_conv_lib 6 | real(8), allocatable :: core(:) 7 | complex(8), allocatable :: zcore(:) 8 | contains 9 | 10 | 11 | subroutine dfull_to_tt(a,n,d,eps,rmax,r,ps) 12 | implicit none 13 | integer, intent(in), optional :: rmax 14 | integer, intent(in) :: d 15 | real(8), intent(in) :: eps 16 | integer, intent(in) :: n(:) 17 | integer, intent(out) :: r(d+1) 18 | integer, intent(out) :: ps(d+1) 19 | real(8), intent(in) :: a(:) 20 | type(dtt) :: tt 21 | integer :: n1(d) 22 | if (present(rmax) ) then 23 | call svd(n, a, tt, eps, rmax) 24 | else 25 | call svd(n, a, tt, eps) 26 | end if 27 | call sdv_to_arrays(n1,r,d,ps,core,tt) 28 | call dealloc(tt) 29 | end subroutine dfull_to_tt 30 | 31 | subroutine zfull_to_tt(a,n,d,eps,rmax,r,ps) 32 | implicit none 33 | integer, intent(in), optional :: rmax 34 | integer, intent(in) :: d 35 | real(8), intent(in) :: eps 36 | integer, intent(in) :: n(:) 37 | integer, intent(out) :: r(d+1) 38 | integer, intent(out) :: ps(d+1) 39 | complex(8), intent(in) :: a(:) 40 | type(ztt) :: tt 41 | integer :: n1(d) 42 | call svd(n,a,tt,eps,rmax) 43 | call sdv_to_arrays(n1,r,d,ps,zcore,tt) 44 | call dealloc(tt) 45 | end subroutine zfull_to_tt 46 | 47 | subroutine tt_dealloc() 48 | if ( allocated(core) ) then 49 | deallocate(core) 50 | end if 51 | if ( allocated(zcore) ) then 52 | deallocate(zcore) 53 | end if 54 | end subroutine tt_dealloc 55 | 56 | subroutine dtt_write_wrapper(n,r,d,ps,cr,crsize,fnam) 57 | use ttio_lib 58 | implicit none 59 | integer, intent(in) :: d 60 | integer, intent(in) :: n(d) 61 | integer, intent(in) :: r(d+1) 62 | integer, intent(in) :: ps(d+1) 63 | character(len=*),intent(in) :: fnam 64 | integer, intent(in) :: crsize 65 | double precision, intent(in) :: cr(crsize) 66 | type(dtt) :: tt 67 | call arrays_to_sdv(n,r,d,ps,cr,tt) 68 | call write(tt,fnam) 69 | call dealloc(tt) 70 | end subroutine dtt_write_wrapper 71 | 72 | subroutine ztt_write_wrapper(n,r,d,ps,cr,crsize,fnam) 73 | use ttio_lib 74 | implicit none 75 | integer, intent(in) :: d 76 | integer, intent(in) :: n(d) 77 | integer, intent(in) :: r(d+1) 78 | integer, intent(in) :: ps(d+1) 79 | character(len=*),intent(in) :: fnam 80 | integer, intent(in) :: crsize 81 | complex(8), intent(in) :: cr(crsize) 82 | type(ztt) :: tt 83 | call arrays_to_sdv(n,r,d,ps,cr,tt) 84 | call write(tt,fnam) 85 | call dealloc(tt) 86 | end subroutine ztt_write_wrapper 87 | 88 | subroutine dtt_read_wrapper(n,r,d,d0,ps,fnam) 89 | use ttio_lib 90 | implicit none 91 | integer, intent(in) :: d0 92 | integer, intent(out) :: d 93 | integer, intent(inout) :: n(d0) 94 | integer, intent(inout) :: r(d0+1) 95 | integer, intent(inout) :: ps(d0+1) 96 | character(len=*),intent(in) :: fnam 97 | type(dtt) :: tt 98 | call read(tt,fnam) 99 | call sdv_to_arrays(n,r,d,ps,core,tt) 100 | call dealloc(tt) 101 | end subroutine dtt_read_wrapper 102 | 103 | subroutine ztt_read_wrapper(n,r,d,d0,ps,fnam) 104 | use ttio_lib 105 | implicit none 106 | integer, intent(in) :: d0 107 | integer, intent(out) :: d 108 | integer, intent(inout) :: n(d0) 109 | integer, intent(inout) :: r(d0+1) 110 | integer, intent(inout) :: ps(d0+1) 111 | character(len=*),intent(in) :: fnam 112 | type(ztt) :: tt 113 | call read(tt,fnam) 114 | call sdv_to_arrays(n,r,d,ps,zcore,tt) 115 | call dealloc(tt) 116 | end subroutine ztt_read_wrapper 117 | 118 | 119 | !a should be preallocated, and filled by zeros 120 | subroutine dtt_to_full(n,r,d,ps,cr,crsize,a,asize) 121 | implicit none 122 | integer, intent(in) :: d 123 | integer, intent(in) :: n(d) 124 | integer, intent(in) :: r(d+1) 125 | integer, intent(in) :: ps(d+1) 126 | integer, intent(in) :: asize 127 | integer, intent(in) :: crsize 128 | double precision, intent(in) :: cr(crsize) 129 | double precision, intent(out) :: a(asize) 130 | type(dtt) :: tt 131 | a(1:asize)=0d0 132 | call arrays_to_sdv(n,r,d,ps,cr,tt) 133 | call full(tt,a) 134 | call dealloc(tt) 135 | end subroutine dtt_to_full 136 | 137 | subroutine ztt_to_full(n,r,d,ps,cr,crsize,a,asize) 138 | implicit none 139 | integer, intent(in) :: d 140 | integer, intent(in) :: n(d) 141 | integer, intent(in) :: r(d+1) 142 | integer, intent(in) :: ps(d+1) 143 | integer, intent(in) :: asize 144 | integer, intent(in) :: crsize 145 | complex(8), intent(in) :: cr(crsize) 146 | complex(8), intent(out) :: a(asize) 147 | type(ztt) :: tt 148 | a(1:asize)=(0d0,0d0) 149 | call arrays_to_sdv(n,r,d,ps,cr,tt) 150 | call full(tt,a) 151 | call dealloc(tt) 152 | end subroutine ztt_to_full 153 | 154 | subroutine dtt_add(n,d,r1,r2,ps1,ps2,core1,core2,rres,psres) 155 | implicit none 156 | integer, intent(in) :: d 157 | integer, intent(in) :: n(d) 158 | integer, intent(in) :: r1(d+1) 159 | integer, intent(in) :: r2(d+1) 160 | integer, intent(in) :: ps1(d+1) 161 | integer, intent(in) :: ps2(d+1) 162 | integer, intent(out) :: rres(d+1) 163 | integer, intent(out) :: psres(d+1) 164 | real(8), intent(in) :: core1(:) 165 | real(8), intent(in) :: core2(:) 166 | type(dtt) :: tt1, tt2 167 | integer :: n1(d) 168 | call arrays_to_sdv(n,r1,d,ps1,core1,tt1) 169 | call arrays_to_sdv(n,r2,d,ps2,core2,tt2) 170 | call axpy(1.d0,tt1,1.d0,tt2) 171 | !tt1 is the sum 172 | call sdv_to_arrays(n1,rres,d,psres,core,tt2) 173 | call dealloc(tt1) 174 | call dealloc(tt2) 175 | 176 | 177 | end subroutine dtt_add 178 | 179 | subroutine ztt_add(n,d,r1,r2,ps1,ps2,core1,core2,rres,psres) 180 | implicit none 181 | integer, intent(in) :: d 182 | integer, intent(in) :: n(d) 183 | integer, intent(in) :: r1(d+1) 184 | integer, intent(in) :: r2(d+1) 185 | integer, intent(in) :: ps1(d+1) 186 | integer, intent(in) :: ps2(d+1) 187 | integer, intent(out) :: rres(d+1) 188 | integer, intent(out) :: psres(d+1) 189 | complex(8), intent(in) :: core1(:) 190 | complex(8), intent(in) :: core2(:) 191 | complex(8) :: ONE 192 | type(ztt) :: tt1, tt2 193 | integer :: n1(d) 194 | ONE = (1d0, 0d0) 195 | call arrays_to_sdv(n,r1,d,ps1,core1,tt1) 196 | call arrays_to_sdv(n,r2,d,ps2,core2,tt2) 197 | call axpy(ONE,tt1,ONE,tt2) 198 | !tt1 is the sum 199 | call sdv_to_arrays(n1,rres,d,psres,zcore,tt2) 200 | call dealloc(tt1) 201 | call dealloc(tt2) 202 | end subroutine ztt_add 203 | 204 | 205 | subroutine dtt_compr2(n,d,r,ps,cr,eps,rmax) 206 | implicit none 207 | integer, intent(in) :: d 208 | integer, intent(in) :: n(d) 209 | integer, intent(inout) :: r(d+1) 210 | integer, intent(inout) :: ps(d+1) 211 | real(8), intent(in) :: cr(:) 212 | real(8), intent(in) :: eps 213 | integer, intent(in) :: rmax 214 | type(dtt) :: tt 215 | integer :: n1(d) 216 | call arrays_to_sdv(n,r,d,ps,cr,tt) 217 | call svd(tt,eps,rmax) 218 | call sdv_to_arrays(n1,r,d,ps,core,tt) 219 | call dealloc(tt) 220 | end subroutine dtt_compr2 221 | 222 | subroutine ztt_compr2(n,d,r,ps,cr,eps,rmax) 223 | implicit none 224 | integer, intent(in) :: d 225 | integer, intent(in) :: n(d) 226 | integer, intent(inout) :: r(d+1) 227 | integer, intent(inout) :: ps(d+1) 228 | complex(8), intent(in) :: cr(:) 229 | real(8), intent(in) :: eps 230 | integer, intent(in) :: rmax 231 | type(ztt) :: tt 232 | integer :: n1(d) 233 | call arrays_to_sdv(n,r,d,ps,cr,tt) 234 | call svd(tt,eps,rmax) 235 | call sdv_to_arrays(n1,r,d,ps,zcore,tt) 236 | call dealloc(tt) 237 | end subroutine ztt_compr2 238 | 239 | subroutine dtt_nrm(n,d,r,ps,cr,nrm) 240 | implicit none 241 | integer, intent(in) :: d 242 | integer, intent(in) :: n(d) 243 | integer, intent(in) :: r(d+1) 244 | integer, intent(in) :: ps(d+1) 245 | real(8), intent(in) :: cr(:) 246 | real(8), intent(out) :: nrm 247 | type(dtt) :: tt 248 | call arrays_to_sdv(n,r,d,ps,cr,tt) 249 | nrm=norm(tt) 250 | call dealloc(tt) 251 | end subroutine dtt_nrm 252 | 253 | subroutine ztt_nrm(n,d,r,ps,cr,nrm) 254 | implicit none 255 | integer, intent(in) :: d 256 | integer, intent(in) :: n(d) 257 | integer, intent(in) :: r(d+1) 258 | integer, intent(in) :: ps(d+1) 259 | complex(8), intent(in) :: cr(:) 260 | real(8), intent(out) :: nrm 261 | type(ztt) :: tt 262 | call arrays_to_sdv(n,r,d,ps,cr,tt) 263 | nrm=norm(tt) 264 | call dealloc(tt) 265 | 266 | end subroutine ztt_nrm 267 | 268 | subroutine dtt_dotprod(n,d,r1,r2,ps1,ps2,core1,core2,dt,dtsize) 269 | implicit none 270 | integer, intent(in) :: d 271 | integer, intent(in) :: dtsize 272 | integer, intent(in) :: n(d) 273 | integer, intent(in) :: r1(d+1) 274 | integer, intent(in) :: r2(d+1) 275 | integer, intent(in) :: ps1(d+1) 276 | integer, intent(in) :: ps2(d+1) 277 | real(8), intent(in) :: core1(:) 278 | real(8), intent(in) :: core2(:) 279 | real(8), intent(out) :: dt(dtsize) 280 | type(dtt) :: tt1, tt2 281 | call arrays_to_sdv(n,r1,d,ps1,core1,tt1) 282 | call arrays_to_sdv(n,r2,d,ps2,core2,tt2) 283 | dt = dot(tt1,tt2) 284 | call dealloc(tt1) 285 | call dealloc(tt2) 286 | end subroutine dtt_dotprod 287 | 288 | subroutine ztt_dotprod(n,d,r1,r2,ps1,ps2,core1,core2,dt,dtsize) 289 | implicit none 290 | integer, intent(in) :: d 291 | integer, intent(in) :: dtsize 292 | integer, intent(in) :: n(d) 293 | integer, intent(in) :: r1(d+1) 294 | integer, intent(in) :: r2(d+1) 295 | integer, intent(in) :: ps1(d+1) 296 | integer, intent(in) :: ps2(d+1) 297 | complex(8), intent(in) :: core1(:) 298 | complex(8), intent(in) :: core2(:) 299 | complex(8), intent(out) :: dt(dtsize) 300 | type(ztt) :: tt1, tt2 301 | call arrays_to_sdv(n,r1,d,ps1,core1,tt1) 302 | call arrays_to_sdv(n,r2,d,ps2,core2,tt2) 303 | dt = dot(tt1,tt2) 304 | call dealloc(tt1) 305 | call dealloc(tt2) 306 | end subroutine ztt_dotprod 307 | !Later on we will avoid allocation in + and hdm, where the result have a very specific 308 | !size, i.e., ranks core size can be precomputed 309 | 310 | subroutine dtt_hdm(n,d,r1,r2,ps1,ps2,core1,core2,rres,psres) 311 | integer, intent(in) :: d 312 | integer, intent(in) :: n(d) 313 | integer, intent(in) :: r1(d+1) 314 | integer, intent(in) :: r2(d+1) 315 | integer, intent(in) :: ps1(d+1) 316 | integer, intent(in) :: ps2(d+1) 317 | integer, intent(out) :: rres(d+1) 318 | integer, intent(out) :: psres(d+1) 319 | real(8), intent(in) :: core1(:) 320 | real(8), intent(in) :: core2(:) 321 | type(dtt) :: tt1, tt2, tt 322 | integer :: n1(d) 323 | call arrays_to_sdv(n,r1,d,ps1,core1,tt1) 324 | call arrays_to_sdv(n,r2,d,ps2,core2,tt2) 325 | call dtt_ha1(tt1,tt2,tt) 326 | call dealloc(tt1) 327 | call dealloc(tt2) 328 | call sdv_to_arrays(n1,rres,d,psres,core,tt) 329 | call dealloc(tt) 330 | end subroutine dtt_hdm 331 | 332 | subroutine ztt_hdm(n,d,r1,r2,ps1,ps2,core1,core2,rres,psres) 333 | integer, intent(in) :: d 334 | integer, intent(in) :: n(d) 335 | integer, intent(in) :: r1(d+1) 336 | integer, intent(in) :: r2(d+1) 337 | integer, intent(in) :: ps1(d+1) 338 | integer, intent(in) :: ps2(d+1) 339 | integer, intent(out) :: rres(d+1) 340 | integer, intent(out) :: psres(d+1) 341 | complex(8), intent(in) :: core1(:) 342 | complex(8), intent(in) :: core2(:) 343 | type(ztt) :: tt1, tt2, tt 344 | integer :: n1(d) 345 | call arrays_to_sdv(n,r1,d,ps1,core1,tt1) 346 | call arrays_to_sdv(n,r2,d,ps2,core2,tt2) 347 | call ztt_ha1(tt1,tt2,tt) 348 | call dealloc(tt1) 349 | call dealloc(tt2) 350 | call sdv_to_arrays(n1,rres,d,psres,zcore,tt) 351 | call dealloc(tt) 352 | end subroutine ztt_hdm 353 | 354 | ! Check, if we can call an external python function from Fortran 355 | 356 | 357 | 358 | 359 | end module tt_f90 360 | -------------------------------------------------------------------------------- /tt/core/tt_f90.pyf: -------------------------------------------------------------------------------- 1 | python module tt_f90 ! in 2 | interface ! in :tt_f90 3 | module tt_f90 ! in :tt_f90:tt_f90.f90 4 | use tt_lib 5 | use python_conv_lib 6 | use ttop_lib 7 | real(kind=8), allocatable,dimension(:) :: core 8 | complex(kind=8), allocatable,dimension(:) :: zcore 9 | 10 | subroutine dfull_to_tt(a,n,d,eps,rmax,r,ps) ! in :tt_f90:tt_f90.f90:tt_f90 11 | real(kind=8) dimension(:),intent(in) :: a 12 | integer dimension(:),intent(in) :: n 13 | integer intent(in) :: d 14 | real(kind=8) intent(in) :: eps 15 | integer, optional, intent(in) :: rmax 16 | integer dimension(d + 1),intent(out),depend(d) :: r 17 | integer dimension(d + 1),intent(out),depend(d) :: ps 18 | end subroutine dfull_to_tt 19 | 20 | subroutine zfull_to_tt(a,n,d,eps,rmax,r,ps) ! in :tt_f90:tt_f90.f90:tt_f90 21 | complex(kind=8) dimension(:),intent(in) :: a 22 | integer dimension(:),intent(in) :: n 23 | integer intent(in) :: d 24 | real(kind=8) intent(in) :: eps 25 | integer, optional, intent(in) :: rmax 26 | integer dimension(d + 1),intent(out),depend(d) :: r 27 | integer dimension(d + 1),intent(out),depend(d) :: ps 28 | end subroutine zfull_to_tt 29 | 30 | subroutine tt_dealloc ! in :tt_f90:tt_f90.f90:tt_f90 31 | end subroutine tt_dealloc 32 | 33 | subroutine dtt_write_wrapper(n,r,d,ps,cr,crsize,fnam) 34 | integer dimension(d),intent(in) :: n 35 | integer dimension(d + 1),intent(in),depend(d) :: r 36 | integer, optional,intent(in),check(len(n)>=d),depend(n) :: d=len(n) 37 | integer dimension(d + 1),intent(in),depend(d) :: ps 38 | character(len=*),intent(in) :: fnam 39 | double precision dimension(crsize),intent(in) :: cr 40 | integer, optional,intent(in),check(len(cr)>=crsize),depend(cr) :: crsize=len(cr) 41 | end subroutine dtt_write_wrapper 42 | 43 | subroutine ztt_write_wrapper(n,r,d,ps,cr,crsize,fnam) 44 | integer dimension(d),intent(in) :: n 45 | integer dimension(d + 1),intent(in),depend(d) :: r 46 | integer, optional,intent(in),check(len(n)>=d),depend(n) :: d=len(n) 47 | integer dimension(d + 1),intent(in),depend(d) :: ps 48 | character(len=*),intent(in) :: fnam 49 | complex(8) dimension(crsize),intent(in) :: cr 50 | integer, optional,intent(in),check(len(cr)>=crsize),depend(cr) :: crsize=len(cr) 51 | end subroutine ztt_write_wrapper 52 | 53 | subroutine dtt_to_full(n,r,d,ps,cr,crsize,a,asize) ! in :tt_f90:tt_f90.f90:tt_f90 54 | integer dimension(d),intent(in) :: n 55 | integer dimension(d + 1),intent(in),depend(d) :: r 56 | integer, optional,intent(in),check(len(n)>=d),depend(n) :: d=len(n) 57 | integer dimension(d + 1),intent(in),depend(d) :: ps 58 | double precision dimension(crsize),intent(in) :: cr 59 | integer, optional,intent(in),check(len(cr)>=crsize),depend(cr) :: crsize=len(cr) 60 | double precision dimension(asize),intent(out),depend(asize) :: a 61 | integer intent(in) :: asize 62 | end subroutine dtt_to_full 63 | 64 | subroutine ztt_to_full(n,r,d,ps,cr,crsize,a,asize) ! in :tt_f90:tt_f90.f90:tt_f90 65 | integer dimension(d),intent(in) :: n 66 | integer dimension(d + 1),intent(in),depend(d) :: r 67 | integer, optional,intent(in),check(len(n)>=d),depend(n) :: d=len(n) 68 | integer dimension(d + 1),intent(in),depend(d) :: ps 69 | complex(8) dimension(crsize),intent(in) :: cr 70 | integer, optional,intent(in),check(len(cr)>=crsize),depend(cr) :: crsize=len(cr) 71 | complex(8) dimension(asize),intent(out),depend(asize) :: a 72 | integer intent(in) :: asize 73 | end subroutine ztt_to_full 74 | 75 | subroutine dtt_add(n,d,r1,r2,ps1,ps2,core1,core2,rres,psres) ! in :tt_f90:tt_f90.f90:tt_f90 76 | integer dimension(d),intent(in) :: n 77 | integer, optional,intent(in),check(len(n)>=d),depend(n) :: d=len(n) 78 | integer dimension(d + 1),intent(in),depend(d) :: r1 79 | integer dimension(d + 1),intent(in),depend(d) :: r2 80 | integer dimension(d + 1),intent(in),depend(d) :: ps1 81 | integer dimension(d + 1),intent(in),depend(d) :: ps2 82 | real(kind=8) dimension(:),intent(in) :: core1 83 | real(kind=8) dimension(:),intent(in) :: core2 84 | integer dimension(d + 1),intent(out),depend(d) :: rres 85 | integer dimension(d + 1),intent(out),depend(d) :: psres 86 | end subroutine dtt_add 87 | 88 | subroutine ztt_add(n,d,r1,r2,ps1,ps2,core1,core2,rres,psres) ! in :tt_f90:tt_f90.f90:tt_f90 89 | integer dimension(d),intent(in) :: n 90 | integer, optional,intent(in),check(len(n)>=d),depend(n) :: d=len(n) 91 | integer dimension(d + 1),intent(in),depend(d) :: r1 92 | integer dimension(d + 1),intent(in),depend(d) :: r2 93 | integer dimension(d + 1),intent(in),depend(d) :: ps1 94 | integer dimension(d + 1),intent(in),depend(d) :: ps2 95 | complex(kind=8) dimension(:),intent(in) :: core1 96 | complex(kind=8) dimension(:),intent(in) :: core2 97 | integer dimension(d + 1),intent(out),depend(d) :: rres 98 | integer dimension(d + 1),intent(out),depend(d) :: psres 99 | end subroutine ztt_add 100 | subroutine dtt_compr2(n,d,r,ps,cr,eps,rmax) ! in :tt_f90:tt_f90.f90:tt_f90 101 | integer dimension(d),intent(in) :: n 102 | integer, optional,intent(in),check(len(n)>=d),depend(n) :: d=len(n) 103 | integer dimension(d + 1),intent(inout),depend(d) :: r 104 | integer dimension(d + 1),intent(inout),depend(d) :: ps 105 | real(kind=8) dimension(:),intent(in) :: cr 106 | real(kind=8) intent(in) :: eps 107 | integer intent(in) :: rmax 108 | end subroutine dtt_compr2 109 | subroutine ztt_compr2(n,d,r,ps,cr,eps,rmax) ! in :tt_f90:tt_f90.f90:tt_f90 110 | integer dimension(d),intent(in) :: n 111 | integer, optional,intent(in),check(len(n)>=d),depend(n) :: d=len(n) 112 | integer dimension(d + 1),intent(inout),depend(d) :: r 113 | integer dimension(d + 1),intent(inout),depend(d) :: ps 114 | complex(kind=8) dimension(:),intent(in) :: cr 115 | real(kind=8) intent(in) :: eps 116 | integer intent(in) :: rmax 117 | end subroutine ztt_compr2 118 | subroutine dtt_dotprod(n,d,r1,r2,ps1,ps2,core1,core2,dt,dtsize) 119 | integer dimension(d),intent(in) :: n 120 | integer, intent(in) :: dtsize 121 | real(kind=8) dimension(dtsize), intent(out) :: dt 122 | integer, optional,intent(in),check(len(n)>=d),depend(n) :: d=len(n) 123 | integer dimension(d + 1),intent(in),depend(d) :: r1 124 | integer dimension(d + 1),intent(in),depend(d) :: r2 125 | integer dimension(d + 1),intent(in),depend(d) :: ps1 126 | integer dimension(d + 1),intent(in),depend(d) :: ps2 127 | real(kind=8) dimension(:),intent(in) :: core1 128 | real(kind=8) dimension(:),intent(in) :: core2 129 | end subroutine dtt_dotprod 130 | subroutine ztt_dotprod(n,d,r1,r2,ps1,ps2,core1,core2,dt,dtsize) 131 | integer dimension(d),intent(in) :: n 132 | integer, intent(in) :: dtsize 133 | complex(kind=8) dimension(dtsize), intent(out) :: dt 134 | integer, optional,intent(in),check(len(n)>=d),depend(n) :: d=len(n) 135 | integer dimension(d + 1),intent(in),depend(d) :: r1 136 | integer dimension(d + 1),intent(in),depend(d) :: r2 137 | integer dimension(d + 1),intent(in),depend(d) :: ps1 138 | integer dimension(d + 1),intent(in),depend(d) :: ps2 139 | complex(kind=8) dimension(:),intent(in) :: core1 140 | complex(kind=8) dimension(:),intent(in) :: core2 141 | end subroutine ztt_dotprod 142 | 143 | subroutine dtt_nrm(n,d,r,ps,cr,nrm) ! in :tt_f90:tt_f90.f90:tt_f90 144 | integer dimension(d),intent(in) :: n 145 | integer, optional,intent(in),check(len(n)>=d),depend(n) :: d=len(n) 146 | integer dimension(d + 1),intent(in),depend(d) :: r 147 | integer dimension(d + 1),intent(in),depend(d) :: ps 148 | real(kind=8) dimension(:),intent(in) :: cr 149 | real(kind=8) intent(out) :: nrm 150 | end subroutine dtt_nrm 151 | subroutine ztt_nrm(n,d,r,ps,cr,nrm) ! in :tt_f90:tt_f90.f90:tt_f90 152 | integer dimension(d),intent(in) :: n 153 | integer, optional,intent(in),check(len(n)>=d),depend(n) :: d=len(n) 154 | integer dimension(d + 1),intent(in),depend(d) :: r 155 | integer dimension(d + 1),intent(in),depend(d) :: ps 156 | complex(kind=8) dimension(:),intent(in) :: cr 157 | real(kind=8) intent(out) :: nrm 158 | end subroutine ztt_nrm 159 | subroutine dtt_hdm(n,d,r1,r2,ps1,ps2,core1,core2,rres,psres) ! in :tt_f90:tt_f90.f90:tt_f90 160 | integer dimension(d),intent(in) :: n 161 | integer, optional,intent(in),check(len(n)>=d),depend(n) :: d=len(n) 162 | integer dimension(d + 1),intent(in),depend(d) :: r1 163 | integer dimension(d + 1),intent(in),depend(d) :: r2 164 | integer dimension(d + 1),intent(in),depend(d) :: ps1 165 | integer dimension(d + 1),intent(in),depend(d) :: ps2 166 | real(kind=8) dimension(:),intent(in) :: core1 167 | real(kind=8) dimension(:),intent(in) :: core2 168 | integer dimension(d + 1),intent(out),depend(d) :: rres 169 | integer dimension(d + 1),intent(out),depend(d) :: psres 170 | end subroutine dtt_hdm 171 | subroutine ztt_hdm(n,d,r1,r2,ps1,ps2,core1,core2,rres,psres) ! in :tt_f90:tt_f90.f90:tt_f90 172 | integer dimension(d),intent(in) :: n 173 | integer, optional,intent(in),check(len(n)>=d),depend(n) :: d=len(n) 174 | integer dimension(d + 1),intent(in),depend(d) :: r1 175 | integer dimension(d + 1),intent(in),depend(d) :: r2 176 | integer dimension(d + 1),intent(in),depend(d) :: ps1 177 | integer dimension(d + 1),intent(in),depend(d) :: ps2 178 | complex(kind=8) dimension(:),intent(in) :: core1 179 | complex(kind=8) dimension(:),intent(in) :: core2 180 | integer dimension(d + 1),intent(out),depend(d) :: rres 181 | integer dimension(d + 1),intent(out),depend(d) :: psres 182 | end subroutine ztt_hdm 183 | end module tt_f90 184 | end interface 185 | end python module tt_f90 186 | 187 | ! This file was auto-generated with f2py (version:2). 188 | ! See http://cens.ioc.ee/projects/f2py2e/ 189 | -------------------------------------------------------------------------------- /tt/core/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import, division 2 | from six.moves import xrange 3 | import sys 4 | import numpy as _np 5 | 6 | if sys.version_info < (3, 5): 7 | from fractions import gcd as _gcd 8 | else: 9 | from math import gcd as _gcd 10 | 11 | # Available functions: 12 | # ind2sub, gcd, my_chop2 13 | 14 | 15 | def ind2sub(siz, idx): 16 | ''' 17 | Translates full-format index into tt.vector one's. 18 | ---------- 19 | Parameters: 20 | siz - tt.vector modes 21 | idx - full-vector index 22 | Note: not vectorized. 23 | ''' 24 | n = len(siz) 25 | subs = _np.empty((n)) 26 | k = _np.cumprod(siz[:-1]) 27 | k = _np.concatenate((_np.ones(1), k)) 28 | for i in xrange(n - 1, -1, -1): 29 | subs[i] = _np.floor(idx / k[i]) 30 | idx = idx % k[i] 31 | return subs.astype(_np.int32) 32 | 33 | 34 | def gcd(a, b): 35 | '''Greatest common divider''' 36 | f = _np.frompyfunc(_gcd, 2, 1) 37 | return f(a, b) 38 | 39 | 40 | def my_chop2(sv, eps): # from ttpy/multifuncr.py 41 | if eps <= 0.0: 42 | r = len(sv) 43 | return r 44 | sv0 = _np.cumsum(abs(sv[::-1]) ** 2)[::-1] 45 | ff = [i for i in range(len(sv0)) if sv0[i] < eps ** 2] 46 | if len(ff) == 0: 47 | return len(sv) 48 | else: 49 | return _np.amin(ff) 50 | -------------------------------------------------------------------------------- /tt/cross/__init__.py: -------------------------------------------------------------------------------- 1 | from .rectcross import rect_cross 2 | __all__ = ['rect_cross'] 3 | 4 | -------------------------------------------------------------------------------- /tt/cross/oldcross/cross.f90: -------------------------------------------------------------------------------- 1 | module cross 2 | use tt_lib 3 | use ttop_lib 4 | use time_lib 5 | use dmrgfun_lib 6 | use python_conv_lib 7 | double precision, allocatable :: core(:) 8 | contains 9 | subroutine tt_cross(d,n,r,ps,fun,eps) 10 | integer, intent(in) :: d 11 | integer, intent(in) :: n(d) 12 | integer, intent(out) :: r(d+1) 13 | integer, intent(out) :: ps(d+1) 14 | double precision, intent(in) :: eps 15 | type (dtt) :: tt,tt1 16 | external :: fun 17 | double precision :: fun 18 | integer :: ind(d),i,nit 19 | double precision val,t1,t2 20 | !fun is a function of a multiindex of length d 21 | !print *,'d=',d 22 | !print *,'n=',n 23 | !do i=1,d 24 | ! ind(i) = i 25 | !end do 26 | !val = fun(d,ind) !Hehe 27 | do i=1,d 28 | tt%n(i)=n(i) 29 | end do 30 | tt%l=1 31 | tt%m=d 32 | 33 | !Measure time to call fun 34 | !nit=10000 35 | !t1=timef() 36 | !do i=1,nit 37 | ! val=fun(d,ind) 38 | !end do 39 | !t2=timef() 40 | !print *,'Time to call a python fun from fortran:',(t2-t1)/nit 41 | !t1=timef() 42 | !do i=1,nit 43 | ! val=fun1(d,ind) 44 | !end do 45 | !t2=timef() 46 | !print *,'Time to call fortran from fortran',(t2-t1)/(nit) 47 | !Initialize tt 48 | call dtt_ones(tt) 49 | 50 | !Now we have to send fun to dmrg 51 | !call dtt_dmrgf(tt, eps, maxiter, coresize, kick, dfun) 52 | 53 | !t1=timef() 54 | call dtt_dmrgf(tt,eps,fun) 55 | call sdv_to_arrays(n,r,d,ps,core,tt) 56 | call dealloc(tt) 57 | !t2=timef() 58 | !print *,'Time for Python callback',t2-t1 59 | !tt1%l=1 60 | !tt1%m=d 61 | !do i=1,d 62 | ! tt1%n(i)=n(i) 63 | !end do 64 | 65 | !call dtt_ones(tt1) 66 | !t1=timef() 67 | !call dtt_dmrgf(tt1,eps,fun1) 68 | !t2=timef() 69 | !print *,'Time for Fortran fun:',t2-t1 70 | end subroutine tt_cross 71 | 72 | subroutine cross_dealloc() 73 | deallocate(core) 74 | end subroutine cross_dealloc 75 | ! function fun1(d,x) result(res) 76 | ! integer, intent(in) :: d 77 | ! integer, intent(in) :: x(d) 78 | ! double precision :: res 79 | ! res=sum(x(1:d)) 80 | !end function fun1 81 | 82 | 83 | end module cross 84 | -------------------------------------------------------------------------------- /tt/cross/oldcross/cross.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import tt 3 | """ This module is an implementation of the cross method for tensors """ 4 | """ What we do, we gradually apply 2d methods, and we basically need to recompute 5 | UU^{-1} and VV^{-1} each time; the problem is then to go to next step in a sweep""" 6 | 7 | def cross(n, f): 8 | 9 | pass 10 | -------------------------------------------------------------------------------- /tt/cross/oldcross/cross.pyf: -------------------------------------------------------------------------------- 1 | ! -*- f90 -*- 2 | ! Note: the context of this file is case sensitive. 3 | python module tt_cross__user__routines 4 | interface tt_cross_user_interface 5 | function fun(d,x) result(res) 6 | integer, intent(in) :: x(d) 7 | integer, optional, depend(x), intent(in,hide) :: d = len(x) 8 | double precision :: res 9 | end function fun 10 | end interface tt_cross_user_interface 11 | end python module tt_cross__user__routines 12 | 13 | python module cross ! in 14 | interface ! in :cross 15 | module cross ! in :cross:cross.f90 16 | use tt_lib 17 | use time_lib 18 | use ttop_lib 19 | double precision, allocatable :: core(:) 20 | subroutine tt_cross(d,n,r,ps,fun,eps) ! in :cross:cross.f90:cross 21 | use tt_cross__user__routines 22 | integer, optional,intent(in,hide),check(len(n)>=d),depend(n) :: d=len(n) 23 | integer dimension(d),intent(in) :: n 24 | integer dimension(d+1), intent(out) :: r 25 | integer dimension(d+1), intent(out) :: ps 26 | external fun 27 | double precision :: fun 28 | double precision intent(in) :: eps 29 | end subroutine tt_cross 30 | subroutine cross_dealloc() 31 | end subroutine cross_dealloc 32 | end module cross 33 | end interface 34 | end python module cross 35 | 36 | ! This file was auto-generated with f2py (version:2). 37 | ! See http://cens.ioc.ee/projects/f2py2e/ 38 | -------------------------------------------------------------------------------- /tt/cross/oldcross/tt_cross.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import, division 2 | from six.moves import xrange 3 | import numpy as np 4 | from . import cross 5 | from .tt_tensor2 import * 6 | 7 | class black_box_tensor: 8 | def __init__(self,sz,f=None,eps=1e-6): 9 | self.f = f 10 | self.n = np.array(sz,dtype=np.int32) 11 | self.eps=eps 12 | def dmrg(self): 13 | d = self.n.size 14 | #r = np.ones((d+1,1),dtype=int32) 15 | #ps = np.ones((d+1,1), dtype=int32) 16 | r,ps=cross.cross.tt_cross(self.n,self.f,self.eps) 17 | tt=tt_tensor() 18 | tt.n = self.n 19 | tt.ps = ps 20 | tt.r = r 21 | tt.d = d 22 | tt.core=cross.cross.core.copy() 23 | cross.cross.cross_dealloc() #Clear the allocated core 24 | tt.ps = ps.copy() 25 | self.tt = tt 26 | #This class will receive a 3d function a grid (i.e., f is an f(x,y,z)) 27 | class fun_qtt: #Only QTT is assumed, i.e. d is an array [d1,d2,d3,...,] 28 | def __init__(self,f,d,a,b,order='F'): 29 | self.f = f 30 | self.d = np.array(d,dtype=np.int32) 31 | self.m = self.d.size 32 | self.a = np.array(a) #The sizes of the boxes 33 | self.sz = 2**self.d 34 | self.h = (np.array(b)-self.a)/np.array((self.sz-1),dtype=float) 35 | self.sm = np.zeros((self.m,self.d.sum()),dtype=np.int32) 36 | self.full_sz = np.array([2]*self.d.sum(),dtype=np.int32) 37 | self.order = order 38 | start=0 39 | for i in xrange(self.m): 40 | for j in xrange(self.d[i]): 41 | self.sm[i,j+start]=2**(j) 42 | start = start + self.d[i] 43 | # ind_tt1 = (i1) + (i2-1)*2 + (i3-1)*4 + ... 44 | # ind_tt2 = (i2) + (i2-1)*2 + 45 | # we have self.m 46 | def __call__(self,ind): 47 | #We are given a QTT index ind, have to convert it to a TT index 48 | if self.order is 'F': 49 | ind_tt=np.dot(self.sm,(np.array(ind,dtype=np.int32)-1)) 50 | else: 51 | ind_tt=np.dot(self.sm,(np.array(ind,dtype=np.int32))) 52 | 53 | x = self.a + self.h*ind_tt 54 | return self.f(x) 55 | 56 | class fun_ind: #No QTT is assumed, just convert index -> float 57 | def __init__(self,f,n,a,b,order='F'): 58 | self.f = f 59 | self.n = np.array(n, dtype = np.int32) 60 | self.d = self.n.size 61 | self.a = np.array(a) 62 | self.h = (np.array(b)-self.a)/np.array((self.n-1),dtype=float) 63 | def __call__(self,ind): 64 | if self.order is 'F': 65 | x = self.a + self.h*(np.array(ind,dtype=np.int32)-1) 66 | else: 67 | x = self.a + self.h*np.array(ind,dtype=np.int32) 68 | return self.f(x) 69 | -------------------------------------------------------------------------------- /tt/cross/setup.py: -------------------------------------------------------------------------------- 1 | # setup.py 2 | # This script will build the main subpackages 3 | # See LICENSE for details 4 | 5 | from __future__ import print_function, absolute_import 6 | from numpy.distutils.misc_util import Configuration 7 | 8 | 9 | def configuration(parent_package='',top_path=None): 10 | config = Configuration('cross', parent_package, top_path) 11 | config.add_subpackage('rectcross') 12 | return config 13 | 14 | 15 | if __name__ == '__main__': 16 | print('This is the wrong setup.py to run') 17 | -------------------------------------------------------------------------------- /tt/distutils.py: -------------------------------------------------------------------------------- 1 | from numpy.distutils import customized_fcompiler 2 | 3 | 4 | def get_extra_fflags(): 5 | fflags = [] 6 | fcompiler = customized_fcompiler() 7 | if fcompiler.compiler_type in ('g95', 'gnu', 'gnu95'): 8 | if fcompiler.get_version() >= '10': 9 | fflags.append('-fallow-argument-mismatch') 10 | return fflags 11 | -------------------------------------------------------------------------------- /tt/doc/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/ttpy.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/ttpy.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/ttpy" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/ttpy" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | -------------------------------------------------------------------------------- /tt/doc/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # ttpy documentation build configuration file, created by 4 | # sphinx-quickstart on Fri Apr 12 16:14:47 2013. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | sys.path.insert(0, os.path.abspath('../../')) 20 | 21 | # -- General configuration ----------------------------------------------------- 22 | 23 | # If your documentation needs a minimal Sphinx version, state it here. 24 | #needs_sphinx = '1.0' 25 | 26 | # Add any Sphinx extension module names here, as strings. They can be extensions 27 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 28 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.pngmath', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode'] 29 | 30 | # Add any paths that contain templates here, relative to this directory. 31 | templates_path = ['_templates'] 32 | 33 | # The suffix of source filenames. 34 | source_suffix = '.rst' 35 | 36 | # The encoding of source files. 37 | #source_encoding = 'utf-8-sig' 38 | 39 | # The master toctree document. 40 | master_doc = 'index' 41 | 42 | # General information about the project. 43 | project = u'ttpy' 44 | copyright = u'2013, I. Oseledets' 45 | 46 | # The version info for the project you're documenting, acts as replacement for 47 | # |version| and |release|, also used in various other places throughout the 48 | # built documents. 49 | # 50 | # The short X.Y version. 51 | version = '0.9' 52 | # The full version, including alpha/beta/rc tags. 53 | release = '0.9' 54 | 55 | # The language for content autogenerated by Sphinx. Refer to documentation 56 | # for a list of supported languages. 57 | #language = None 58 | 59 | # There are two options for replacing |today|: either, you set today to some 60 | # non-false value, then it is used: 61 | #today = '' 62 | # Else, today_fmt is used as the format for a strftime call. 63 | #today_fmt = '%B %d, %Y' 64 | 65 | # List of patterns, relative to source directory, that match files and 66 | # directories to ignore when looking for source files. 67 | exclude_patterns = ['_build'] 68 | 69 | # The reST default role (used for this markup: `text`) to use for all documents. 70 | #default_role = None 71 | 72 | # If true, '()' will be appended to :func: etc. cross-reference text. 73 | #add_function_parentheses = True 74 | 75 | # If true, the current module name will be prepended to all description 76 | # unit titles (such as .. function::). 77 | #add_module_names = True 78 | 79 | # If true, sectionauthor and moduleauthor directives will be shown in the 80 | # output. They are ignored by default. 81 | #show_authors = False 82 | 83 | # The name of the Pygments (syntax highlighting) style to use. 84 | pygments_style = 'sphinx' 85 | 86 | # A list of ignored prefixes for module index sorting. 87 | #modindex_common_prefix = [] 88 | 89 | # If true, keep warnings as "system message" paragraphs in the built documents. 90 | #keep_warnings = False 91 | 92 | 93 | # -- Options for HTML output --------------------------------------------------- 94 | 95 | # The theme to use for HTML and HTML Help pages. See the documentation for 96 | # a list of builtin themes. 97 | html_theme = 'agogo' 98 | 99 | # Theme options are theme-specific and customize the look and feel of a theme 100 | # further. For a list of options available for each theme, see the 101 | # documentation. 102 | #html_theme_options = {} 103 | 104 | # Add any paths that contain custom themes here, relative to this directory. 105 | #html_theme_path = [] 106 | 107 | # The name for this set of Sphinx documents. If None, it defaults to 108 | # " v documentation". 109 | #html_title = None 110 | 111 | # A shorter title for the navigation bar. Default is the same as html_title. 112 | #html_short_title = None 113 | 114 | # The name of an image file (relative to this directory) to place at the top 115 | # of the sidebar. 116 | #html_logo = None 117 | 118 | # The name of an image file (within the static path) to use as favicon of the 119 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 120 | # pixels large. 121 | #html_favicon = None 122 | 123 | # Add any paths that contain custom static files (such as style sheets) here, 124 | # relative to this directory. They are copied after the builtin static files, 125 | # so a file named "default.css" will overwrite the builtin "default.css". 126 | html_static_path = ['_static'] 127 | 128 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 129 | # using the given strftime format. 130 | #html_last_updated_fmt = '%b %d, %Y' 131 | 132 | # If true, SmartyPants will be used to convert quotes and dashes to 133 | # typographically correct entities. 134 | #html_use_smartypants = True 135 | 136 | # Custom sidebar templates, maps document names to template names. 137 | #html_sidebars = {} 138 | 139 | # Additional templates that should be rendered to pages, maps page names to 140 | # template names. 141 | #html_additional_pages = {} 142 | 143 | # If false, no module index is generated. 144 | #html_domain_indices = True 145 | 146 | # If false, no index is generated. 147 | #html_use_index = True 148 | 149 | # If true, the index is split into individual pages for each letter. 150 | #html_split_index = False 151 | 152 | # If true, links to the reST sources are added to the pages. 153 | #html_show_sourcelink = True 154 | 155 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 156 | #html_show_sphinx = True 157 | 158 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 159 | #html_show_copyright = True 160 | 161 | # If true, an OpenSearch description file will be output, and all pages will 162 | # contain a tag referring to it. The value of this option must be the 163 | # base URL from which the finished HTML is served. 164 | #html_use_opensearch = '' 165 | 166 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 167 | #html_file_suffix = None 168 | 169 | # Output file base name for HTML help builder. 170 | htmlhelp_basename = 'ttpydoc' 171 | 172 | 173 | # -- Options for LaTeX output -------------------------------------------------- 174 | 175 | latex_elements = { 176 | # The paper size ('letterpaper' or 'a4paper'). 177 | #'papersize': 'letterpaper', 178 | 179 | # The font size ('10pt', '11pt' or '12pt'). 180 | #'pointsize': '10pt', 181 | 182 | # Additional stuff for the LaTeX preamble. 183 | #'preamble': '', 184 | } 185 | 186 | # Grouping the document tree into LaTeX files. List of tuples 187 | # (source start file, target name, title, author, documentclass [howto/manual]). 188 | latex_documents = [ 189 | ('index', 'ttpy.tex', u'ttpy Documentation', 190 | u'I. Oseledets', 'manual'), 191 | ] 192 | 193 | # The name of an image file (relative to this directory) to place at the top of 194 | # the title page. 195 | #latex_logo = None 196 | 197 | # For "manual" documents, if this is true, then toplevel headings are parts, 198 | # not chapters. 199 | #latex_use_parts = False 200 | 201 | # If true, show page references after internal links. 202 | #latex_show_pagerefs = False 203 | 204 | # If true, show URL addresses after external links. 205 | #latex_show_urls = False 206 | 207 | # Documents to append as an appendix to all manuals. 208 | #latex_appendices = [] 209 | 210 | # If false, no module index is generated. 211 | #latex_domain_indices = True 212 | 213 | 214 | # -- Options for manual page output -------------------------------------------- 215 | 216 | # One entry per manual page. List of tuples 217 | # (source start file, name, description, authors, manual section). 218 | man_pages = [ 219 | ('index', 'ttpy', u'ttpy Documentation', 220 | [u'I. Oseledets'], 1) 221 | ] 222 | 223 | # If true, show URL addresses after external links. 224 | #man_show_urls = False 225 | 226 | 227 | # -- Options for Texinfo output ------------------------------------------------ 228 | 229 | # Grouping the document tree into Texinfo files. List of tuples 230 | # (source start file, target name, title, author, 231 | # dir menu entry, description, category) 232 | texinfo_documents = [ 233 | ('index', 'ttpy', u'ttpy Documentation', 234 | u'I. Oseledets', 'ttpy', 'One line description of project.', 235 | 'Miscellaneous'), 236 | ] 237 | 238 | # Documents to append as an appendix to all manuals. 239 | #texinfo_appendices = [] 240 | 241 | # If false, no module index is generated. 242 | #texinfo_domain_indices = True 243 | 244 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 245 | #texinfo_show_urls = 'footnote' 246 | 247 | # If true, do not generate a @detailmenu in the "Top" node's menu. 248 | #texinfo_no_detailmenu = False 249 | -------------------------------------------------------------------------------- /tt/doc/corettutils.rst: -------------------------------------------------------------------------------- 1 | Core TT utils 2 | ************* 3 | 4 | .. .. automodule:: tt.core 5 | 6 | Tensor manipulation routines 7 | ============================ 8 | 9 | ``tt.tensor`` is the main class for manipulating TT-compressed tensors. To compress a tensor given 10 | as full-weight NumPy array ``arr``, simply call 11 | 12 | >>> arr_tt = tt.tensor(arr) 13 | 14 | Then the full-weight array can be recovered from TT-representation using 15 | 16 | >>> arr = arr_tt.full() 17 | 18 | TT-tensor objects support basic element-wise arithmetic: 19 | 20 | >>> a_tt, b_tt = tt.tensor(a), tt_tensor(b) 21 | >>> c_tt = 0.3 * a_tt + b_tt - a_tt * b_tt 22 | 23 | Such operations do usually increase TT-ranks, thus applying TT-rounding in these situations is recommended: 24 | 25 | >>> c_tt = c_tt.round(1E-14) 26 | 27 | Further details are descripted below. 28 | 29 | .. autoclass:: tt.tensor 30 | :members: 31 | 32 | 33 | Basic operations in TT-format 34 | ----------------------------- 35 | 36 | .. automodule:: tt 37 | :members: diag, kron, mkron, concatenate, sum, matvec 38 | 39 | Generation of standard TT-tensors 40 | ----------------------------------- 41 | 42 | .. automodule:: tt 43 | :members: ones, xfun, sin, cos, delta, stepfun, rand 44 | 45 | Matrix manipulation routines 46 | ============================ 47 | 48 | ``tt.matrix`` is a special class for manipulating TT-decomposed matrices, i. e. decompositions of the form 49 | 50 | .. math:: 51 | 52 | A(i_1,\ldots,i_d;j_1,\ldots,j_d) = \sum_{\alpha_1,\ldots,\alpha_{d-1}}A_1(i_1,j_1,\alpha_1)\cdot A_2(\alpha_1,i_2,j_2,\alpha_2) \cdot \ldots \cdot A_d(\alpha_{d-1},i_d, j_d), 53 | 54 | which are just regular TT-decompositions of tensors :math:`A(i_1,j_1;i_2,j_2;\ldots;i_d,j_d)` with merged indices :math:`i_k,j_k`. 55 | 56 | .. autoclass:: tt.matrix 57 | :members: 58 | 59 | Generation of standard TT-matrices 60 | ---------------------------------- 61 | 62 | .. automodule:: tt 63 | :members: eye, qlaplace_dd, Toeplitz 64 | 65 | -------------------------------------------------------------------------------- /tt/doc/index.rst: -------------------------------------------------------------------------------- 1 | .. ttpy documentation master file, created by 2 | sphinx-quickstart on Fri Apr 12 16:14:47 2013. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | TTPY Documentation 7 | ****************** 8 | 9 | .. |ttpy| replace:: **ttpy** 10 | 11 | |ttpy| is a powerful Python package designed for manipulating large highly multidimensional arrays 12 | also called **tensors**. It is developed in the `Institute of Numerical Mathematics of Russian Academy 13 | of Sciences `_ and based on the so-called Tensor Train 14 | decomposition techniques. 15 | 16 | The Tensor Train decomposition of a tensor :math:`A(i_1,\ldots,i_d)` of shape 17 | :math:`n_1\times\ldots\times n_d` is basically any it's representation in form 18 | 19 | .. math:: 20 | 21 | A(i_1,\ldots,i_d) = \sum_{\alpha_1,\ldots,\alpha_{d-1}}A_1(i_1,\alpha_1)\cdot A_2(\alpha_1,i_2,\alpha_2) \cdot \ldots \cdot A_d(\alpha_{d-1},i_d). 22 | 23 | 24 | For more detailed information on tensor algebra basics and the Tensor Train decomposition theory, see :doc:`tensorintro` topic. 25 | 26 | Documentation contents: 27 | 28 | .. toctree:: 29 | :maxdepth: 2 30 | 31 | tensorintro 32 | corettutils 33 | ttalgorithms 34 | 35 | .. only:: html 36 | 37 | ****************** 38 | Indices and Tables 39 | ****************** 40 | 41 | * :ref:`genindex` 42 | * :ref:`search` 43 | 44 | -------------------------------------------------------------------------------- /tt/doc/tensorintro.rst: -------------------------------------------------------------------------------- 1 | Tensors and tensor decompositions 2 | ********************************* 3 | 4 | .. automodule:: tt 5 | 6 | -------------------------------------------------------------------------------- /tt/doc/ttalgorithms.rst: -------------------------------------------------------------------------------- 1 | TT algorithms 2 | ************* 3 | 4 | .. autofunction:: tt.multifuncrs 5 | 6 | .. autofunction:: tt.amen.amen_solve 7 | 8 | .. autofunction:: tt.cross.rect_cross 9 | 10 | .. autofunction:: tt.eigb.eigb 11 | 12 | .. autofunction:: tt.ksl.ksl 13 | -------------------------------------------------------------------------------- /tt/eigb/__init__.py: -------------------------------------------------------------------------------- 1 | from .eigb import * 2 | -------------------------------------------------------------------------------- /tt/eigb/eigb.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import, division 2 | import numpy as np 3 | from . import tt_eigb 4 | from tt import tensor 5 | 6 | 7 | def eigb(A, y0, eps, rmax=150, nswp=20, max_full_size=1000, verb=1): 8 | """ Approximate computation of minimal eigenvalues in tensor train format 9 | This function uses alternating least-squares algorithm for the computation of several 10 | minimal eigenvalues. If you want maximal eigenvalues, just send -A to the function. 11 | 12 | :Reference: 13 | 14 | 15 | S. V. Dolgov, B. N. Khoromskij, I. V. Oseledets, and D. V. Savostyanov. 16 | Computation of extreme eigenvalues in higher dimensions using block tensor train format. Computer Phys. Comm., 17 | 185(4):1207-1216, 2014. http://dx.doi.org/10.1016/j.cpc.2013.12.017 18 | 19 | 20 | :param A: Matrix in the TT-format 21 | :type A: matrix 22 | :param y0: Initial guess in the block TT-format, r(d+1) is the number of eigenvalues sought 23 | :type y0: tensor 24 | :param eps: Accuracy required 25 | :type eps: float 26 | :param rmax: Maximal rank 27 | :type rmax: int 28 | :param kickrank: Addition rank, the larger the more robus the method, 29 | :type kickrank: int 30 | :rtype: A tuple (ev, tensor), where ev is a list of eigenvalues, tensor is an approximation to eigenvectors. 31 | 32 | :Example: 33 | 34 | 35 | >>> import tt 36 | >>> import tt.eigb 37 | >>> d = 8; f = 3 38 | >>> r = [8] * (d * f + 1); r[d * f] = 8; r[0] = 1 39 | >>> x = tt.rand(n, d * f, r) 40 | >>> a = tt.qlaplace_dd([8, 8, 8]) 41 | >>> sol, ev = tt.eigb.eigb(a, x, 1e-6, verb=0) 42 | Solving a block eigenvalue problem 43 | Looking for 8 eigenvalues with accuracy 1E-06 44 | swp: 1 er = 35.93 rmax:19 45 | swp: 2 er = 4.51015E-04 rmax:18 46 | swp: 3 er = 1.87584E-12 rmax:17 47 | Total number of matvecs: 0 48 | >>> print ev 49 | [ 0.00044828 0.00089654 0.00089654 0.00089654 0.0013448 0.0013448 50 | 0.0013448 0.00164356] 51 | 52 | 53 | 54 | """ 55 | ry = y0.r.copy() 56 | lam = tt_eigb.tt_block_eig.tt_eigb(y0.d, A.n, A.m, A.tt.r, A.tt.core, y0.core, ry, eps, 57 | rmax, ry[y0.d], 0, nswp, max_full_size, verb) 58 | y = tensor() 59 | y.d = y0.d 60 | y.n = A.n.copy() 61 | y.r = ry 62 | y.core = tt_eigb.tt_block_eig.result_core.copy() 63 | tt_eigb.tt_block_eig.deallocate_result() 64 | y.get_ps() 65 | return y, lam 66 | -------------------------------------------------------------------------------- /tt/eigb/int_redefine.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oseledets/ttpy/22dff3d2cf52b4b23aae40119f1eec39675db9ef/tt/eigb/int_redefine.h -------------------------------------------------------------------------------- /tt/eigb/setup.py: -------------------------------------------------------------------------------- 1 | # setup.py 2 | # This script will build the main subpackages 3 | # See LICENSE for details 4 | 5 | from __future__ import print_function, absolute_import 6 | from numpy.distutils.misc_util import Configuration 7 | from os.path import join 8 | from tt.distutils import get_extra_fflags 9 | 10 | 11 | TTFORT_DIR = '../tt-fort/' 12 | PRIMME_DIR = '../tt-fort/primme' 13 | 14 | TTEIGB_SRC = [ 15 | 'ttals.f90', 16 | 'tt_eigb.f90', 17 | ] 18 | 19 | 20 | def configuration(parent_package='', top_path=None): 21 | tteigb_src = [join(TTFORT_DIR, x) for x in TTEIGB_SRC] 22 | tteigb_src.append('tt_eigb.pyf') 23 | 24 | config = Configuration('eigb', parent_package, top_path) 25 | config.add_library( 26 | 'primme', 27 | sources=[join(PRIMME_DIR, '*.c')] + [join(PRIMME_DIR, '*.f')], 28 | include_dirs=['.'], 29 | ) 30 | config.add_extension( 31 | 'tt_eigb', 32 | sources=tteigb_src, 33 | depends=[ 34 | 'primme', 35 | 'mytt', 36 | 'print_lib', 37 | ], 38 | libraries=[ 39 | 'primme', 40 | 'mytt', 41 | 'print_lib', 42 | ], 43 | extra_f90_compile_args=get_extra_fflags(), 44 | ) 45 | 46 | return config 47 | 48 | 49 | if __name__ == '__main__': 50 | print('This is the wrong setup.py to run') 51 | -------------------------------------------------------------------------------- /tt/eigb/tt_eigb.pyf: -------------------------------------------------------------------------------- 1 | ! -*- f90 -*- 2 | ! Note: the context of this file is case sensitive. 3 | python module tt_eigb 4 | interface 5 | module tt_block_eig ! in tt_eigb.f90 6 | real(kind=8), allocatable,dimension(:) :: result_core 7 | subroutine deallocate_result ! in tt_eigb.f90:tt_block_eig 8 | end subroutine deallocate_result 9 | subroutine tt_eigb(d,n,m,ra,cra,cry0,ry,eps,rmax,lambda, B, kickrank,nswp,max_full_size,verb) ! in tt_eigb.f90:tt_block_eig 10 | integer intent(in) :: d 11 | integer, intent(in) :: B 12 | integer dimension(*),intent(in) :: n 13 | integer dimension(*),intent(in) :: m 14 | integer dimension(*),intent(in) :: ra 15 | real(kind=8) dimension(*),intent(in) :: cra 16 | real(kind=8) dimension(*),intent(in) :: cry0 17 | real(kind=8) dimension(B), intent(out) :: lambda 18 | integer dimension(*),intent(inout) :: ry 19 | real(kind=8) intent(in) :: eps 20 | integer intent(in) :: rmax 21 | integer, optional,intent(in) :: kickrank 22 | integer, optional,intent(in) :: nswp 23 | integer, optional, intent(in) :: max_full_size 24 | integer, optional,intent(in) :: verb 25 | end subroutine tt_eigb 26 | end module tt_block_eig 27 | end interface 28 | end python module tt_eigb 29 | 30 | ! This file was auto-generated with f2py (version:2). 31 | ! See http://cens.ioc.ee/projects/f2py2e/ 32 | -------------------------------------------------------------------------------- /tt/ksl/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import, division 2 | 3 | from .ksl import * 4 | -------------------------------------------------------------------------------- /tt/ksl/ksl.py: -------------------------------------------------------------------------------- 1 | """ Dynamical TT-approximation """ 2 | from __future__ import print_function, absolute_import, division 3 | import numpy as np 4 | from . import dyn_tt 5 | import tt 6 | 7 | def ksl(A, y0, tau, verb=1, scheme='symm', space=8, rmax=2000, use_normest=1): 8 | """ Dynamical tensor-train approximation based on projector splitting 9 | This function performs one step of dynamical tensor-train approximation 10 | for the equation 11 | 12 | .. math :: 13 | \\frac{dy}{dt} = A y, \\quad y(0) = y_0 14 | 15 | and outputs approximation for :math:`y(\\tau)` 16 | 17 | :References: 18 | 19 | 20 | 1. Christian Lubich, Ivan Oseledets, and Bart Vandereycken. 21 | Time integration of tensor trains. arXiv preprint 1407.2042, 2014. 22 | 23 | http://arxiv.org/abs/1407.2042 24 | 25 | 2. Christian Lubich and Ivan V. Oseledets. A projector-splitting integrator 26 | for dynamical low-rank approximation. BIT, 54(1):171-188, 2014. 27 | 28 | http://dx.doi.org/10.1007/s10543-013-0454-0 29 | 30 | :param A: Matrix in the TT-format 31 | :type A: matrix 32 | :param y0: Initial condition in the TT-format, 33 | :type y0: tensor 34 | :param tau: Timestep 35 | :type tau: float 36 | :param scheme: The integration scheme, possible values: 'symm' -- second order, 'first' -- first order 37 | :type scheme: str 38 | :param space: Maximal dimension of the Krylov space for the local EXPOKIT solver. 39 | :type space: int 40 | :param use_normest: Use matrix norm estimation instead of the true 1-norm in KSL procedure. 0 -use true norm, 1 - Higham norm estimator, 2 - fixed norm=1.0 (for testing purposes only) 41 | :type use_normest: int, default: 1 42 | :rtype: tensor 43 | 44 | :Example: 45 | 46 | 47 | >>> import tt 48 | >>> import tt.ksl 49 | >>> import numpy as np 50 | >>> d = 8 51 | >>> a = tt.qlaplace_dd([d, d, d]) 52 | >>> y0, ev = tt.eigb.eigb(a, tt.rand(2 , 24, 2), 1e-6, verb=0) 53 | Solving a block eigenvalue problem 54 | Looking for 1 eigenvalues with accuracy 1E-06 55 | swp: 1 er = 1.1408 rmax:2 56 | swp: 2 er = 190.01 rmax:2 57 | swp: 3 er = 2.72582E-08 rmax:2 58 | Total number of matvecs: 0 59 | >>> y1 = tt.ksl.ksl(a, y0, 1e-2) 60 | Solving a real-valued dynamical problem with tau=1E-02 61 | >>> print tt.dot(y1, y0) / (y1.norm() * y0.norm()) - 1 #Eigenvectors should not change 62 | 0.0 63 | """ 64 | 65 | ry = y0.r.copy() 66 | if scheme is 'symm': 67 | tp = 2 68 | else: 69 | tp = 1 70 | 71 | usenrm = int(use_normest) 72 | 73 | # Check for dtype 74 | y = tt.vector() 75 | if np.iscomplex(A.tt.core).any() or np.iscomplex(y0.core).any(): 76 | dyn_tt.dyn_tt.ztt_ksl( 77 | y0.d, 78 | A.n, 79 | A.m, 80 | A.tt.r, 81 | A.tt.core + 0j, 82 | y0.core + 0j, 83 | ry, 84 | tau, 85 | rmax, 86 | 0, 87 | 10, 88 | verb, 89 | tp, 90 | space, 91 | usenrm 92 | ) 93 | y.core = dyn_tt.dyn_tt.zresult_core.copy() 94 | else: 95 | A.tt.core = np.real(A.tt.core) 96 | y0.core = np.real(y0.core) 97 | dyn_tt.dyn_tt.tt_ksl( 98 | y0.d, 99 | A.n, 100 | A.m, 101 | A.tt.r, 102 | A.tt.core, 103 | y0.core, 104 | ry, 105 | tau, 106 | rmax, 107 | 0, 108 | 10, 109 | verb, 110 | tp, 111 | space, 112 | usenrm 113 | ) 114 | y.core = dyn_tt.dyn_tt.dresult_core.copy() 115 | dyn_tt.dyn_tt.deallocate_result() 116 | y.d = y0.d 117 | y.n = A.n.copy() 118 | y.r = ry 119 | y.get_ps() 120 | return y 121 | 122 | 123 | def diag_ksl(A, y0, tau, verb=1, scheme='symm', space=8, rmax=2000): 124 | """ Dynamical tensor-train approximation based on projector splitting 125 | This function performs one step of dynamical tensor-train approximation with diagonal matrix, i.e. it solves the equation 126 | for the equation 127 | 128 | .. math :: 129 | \\frac{dy}{dt} = V y, \\quad y(0) = y_0 130 | 131 | and outputs approximation for :math:`y(\\tau)` 132 | 133 | :References: 134 | 135 | 136 | 1. Christian Lubich, Ivan Oseledets, and Bart Vandereycken. 137 | Time integration of tensor trains. arXiv preprint 1407.2042, 2014. 138 | 139 | http://arxiv.org/abs/1407.2042 140 | 141 | 2. Christian Lubich and Ivan V. Oseledets. A projector-splitting integrator 142 | for dynamical low-rank approximation. BIT, 54(1):171-188, 2014. 143 | 144 | http://dx.doi.org/10.1007/s10543-013-0454-0 145 | 146 | :param A: Matrix in the TT-format 147 | :type A: matrix 148 | :param y0: Initial condition in the TT-format, 149 | :type y0: tensor 150 | :param tau: Timestep 151 | :type tau: float 152 | :param scheme: The integration scheme, possible values: 'symm' -- second order, 'first' -- first order 153 | :type scheme: str 154 | :param space: Maximal dimension of the Krylov space for the local EXPOKIT solver. 155 | :type space: int 156 | :rtype: tensor 157 | 158 | :Example: 159 | 160 | 161 | >>> import tt 162 | >>> import tt.ksl 163 | >>> import numpy as np 164 | >>> d = 8 165 | >>> a = tt.qlaplace_dd([d, d, d]) 166 | >>> y0, ev = tt.eigb.eigb(a, tt.rand(2 , 24, 2), 1e-6, verb=0) 167 | Solving a block eigenvalue problem 168 | Looking for 1 eigenvalues with accuracy 1E-06 169 | swp: 1 er = 1.1408 rmax:2 170 | swp: 2 er = 190.01 rmax:2 171 | swp: 3 er = 2.72582E-08 rmax:2 172 | Total number of matvecs: 0 173 | >>> y1 = tt.ksl.ksl(a, y0, 1e-2) 174 | Solving a real-valued dynamical problem with tau=1E-02 175 | >>> print tt.dot(y1, y0) / (y1.norm() * y0.norm()) - 1 #Eigenvectors should not change 176 | 0.0 177 | """ 178 | ry = y0.r.copy() 179 | if scheme is 'symm': 180 | tp = 2 181 | else: 182 | tp = 1 183 | 184 | # Check for dtype 185 | y = tt.vector() 186 | if np.iscomplex(A.core).any() or np.iscomplex(y0.core).any(): 187 | dyn_tt.dyn_diag_tt.ztt_diag_ksl( 188 | y0.d, 189 | A.n, 190 | A.r, 191 | A.core + 0j, 192 | y0.core + 0j, 193 | ry, 194 | tau, 195 | rmax, 196 | 0, 197 | 10, 198 | verb, 199 | tp, 200 | space) 201 | y.core = dyn_tt.dyn_diag_tt.zresult_core.copy() 202 | else: 203 | A.core = np.real(A.core) 204 | y0.core = np.real(y0.core) 205 | dyn_tt.dyn_diag_tt.dtt_diag_ksl( 206 | y0.d, 207 | A.n, 208 | A.r, 209 | A.core, 210 | y0.core, 211 | ry, 212 | tau, 213 | rmax, 214 | 0, 215 | 10, 216 | verb, 217 | tp, 218 | space) 219 | y.core = dyn_tt.dyn_diag_tt.dresult_core.copy() 220 | dyn_tt.dyn_diag_tt.deallocate_result() 221 | y.d = y0.d 222 | y.n = A.n.copy() 223 | y.r = ry 224 | y.get_ps() 225 | return y 226 | -------------------------------------------------------------------------------- /tt/ksl/setup.py: -------------------------------------------------------------------------------- 1 | # setup.py 2 | # This script will build the main subpackages 3 | # See LICENSE for details 4 | 5 | from __future__ import print_function, absolute_import 6 | from numpy.distutils.misc_util import Configuration 7 | from os.path import join 8 | from tt.distutils import get_extra_fflags 9 | 10 | 11 | TTFORT_DIR = '../tt-fort' 12 | EXPM_DIR = '../tt-fort/expm' 13 | 14 | EXPOKIT_SRC = [ 15 | 'explib.f90', 16 | 'normest.f90', 17 | 'expokit.f', 18 | 'dlacn1.f', 19 | 'dlapst.f', 20 | 'dlarpc.f', 21 | 'zlacn1.f', 22 | ] 23 | 24 | TTKSL_SRC = [ 25 | 'ttals.f90', 26 | 'tt_ksl.f90', 27 | 'tt_diag_ksl.f90' 28 | ] 29 | 30 | 31 | def configuration(parent_package='', top_path=None): 32 | expokit_src = [join(EXPM_DIR, x) for x in EXPOKIT_SRC] 33 | 34 | ttksl_src = [join(TTFORT_DIR, x) for x in TTKSL_SRC] 35 | ttksl_src.append('tt_ksl.pyf') 36 | 37 | fflags = get_extra_fflags() 38 | 39 | config = Configuration('ksl', parent_package, top_path) 40 | config.add_library( 41 | 'expokit', 42 | sources=expokit_src, 43 | extra_f77_compile_args=fflags, 44 | extra_f90_compile_args=fflags, 45 | ) 46 | config.add_extension( 47 | 'dyn_tt', 48 | sources=ttksl_src, 49 | depends=[ 50 | 'print_lib', 51 | 'expokit', 52 | 'mytt', 53 | ], 54 | libraries=[ 55 | 'print_lib', 56 | 'expokit', 57 | 'mytt', 58 | ], 59 | extra_f90_compile_args=fflags, 60 | ) 61 | 62 | return config 63 | 64 | 65 | if __name__ == '__main__': 66 | print('This is the wrong setup.py to run') 67 | -------------------------------------------------------------------------------- /tt/ksl/tt_ksl.pyf: -------------------------------------------------------------------------------- 1 | ! -*- f90 -*- 2 | ! Note: the context of this file is case sensitive. 3 | python module dyn_tt 4 | interface 5 | module dyn_tt ! 6 | real(kind=8), allocatable,dimension(:) :: dresult_core 7 | complex(kind=8), allocatable, dimension(:) :: zresult_core 8 | 9 | subroutine deallocate_result ! in tt_eigb.f90:tt_block_eig 10 | end subroutine deallocate_result 11 | subroutine tt_ksl(d,n,m,ra,cra,cry0,ry,tau,rmax,kickrank,nswp,verb, typ0, order0, usenrm0) 12 | integer intent(in) :: d 13 | integer dimension(*),intent(in) :: n 14 | integer dimension(*),intent(in) :: m 15 | integer dimension(*),intent(in) :: ra 16 | real(kind=8) dimension(*),intent(in) :: cra 17 | real(kind=8) dimension(*),intent(in) :: cry0 18 | integer dimension(*),intent(inout) :: ry 19 | real(kind=8) intent(in) :: tau 20 | integer intent(in) :: rmax 21 | integer, optional,intent(in) :: kickrank 22 | integer, optional,intent(in) :: nswp 23 | integer, optional,intent(in) :: verb 24 | integer, optional, intent(in) :: typ0 25 | integer, optional, intent(in) :: order0 26 | integer, optional, intent(in) :: usenrm0 27 | end subroutine tt_ksl 28 | 29 | 30 | subroutine ztt_ksl(d,n,m,ra,cra,cry0,ry,tau,rmax,kickrank,nswp,verb, typ0, order0, usenrm0) 31 | integer intent(in) :: d 32 | integer dimension(*),intent(in) :: n 33 | integer dimension(*),intent(in) :: m 34 | integer dimension(*),intent(in) :: ra 35 | complex(kind=8) dimension(*),intent(in) :: cra 36 | complex(kind=8) dimension(*),intent(in) :: cry0 37 | integer dimension(*),intent(inout) :: ry 38 | real(kind=8) intent(in) :: tau 39 | integer intent(in) :: rmax 40 | integer, optional, intent(in) :: kickrank 41 | integer, optional, intent(in) :: nswp 42 | integer, optional, intent(in) :: verb 43 | integer, optional, intent(in) :: typ0 44 | integer, optional, intent(in) :: order0 45 | integer, optional, intent(in) :: usenrm0 46 | end subroutine ztt_ksl 47 | end module dyn_tt 48 | module dyn_diag_tt ! 49 | real(kind=8), allocatable,dimension(:) :: dresult_core 50 | complex(kind=8), allocatable, dimension(:) :: zresult_core 51 | subroutine deallocate_result ! in tt_eigb.f90:tt_block_eig 52 | end subroutine deallocate_result 53 | subroutine dtt_diag_ksl(d,n,ra,cra,cry0,ry,tau,rmax,kickrank,nswp,verb, typ0, order0) 54 | integer intent(in) :: d 55 | integer dimension(*),intent(in) :: n 56 | integer dimension(*),intent(in) :: ra 57 | real(kind=8) dimension(*),intent(in) :: cra 58 | real(kind=8) dimension(*),intent(in) :: cry0 59 | integer dimension(*),intent(inout) :: ry 60 | real(kind=8) intent(in) :: tau 61 | integer intent(in) :: rmax 62 | integer, optional,intent(in) :: kickrank 63 | integer, optional,intent(in) :: nswp 64 | integer, optional,intent(in) :: verb 65 | integer, optional, intent(in) :: typ0 66 | integer, optional, intent(in) :: order0 67 | end subroutine dtt_diag_ksl 68 | 69 | 70 | subroutine ztt_diag_ksl(d,n,ra,cra,cry0,ry,tau,rmax,kickrank,nswp,verb, typ0, order0) 71 | integer intent(in) :: d 72 | integer dimension(*),intent(in) :: n 73 | integer dimension(*),intent(in) :: ra 74 | complex(kind=8) dimension(*),intent(in) :: cra 75 | complex(kind=8) dimension(*),intent(in) :: cry0 76 | integer dimension(*),intent(inout) :: ry 77 | real(kind=8) intent(in) :: tau 78 | integer intent(in) :: rmax 79 | integer, optional, intent(in) :: kickrank 80 | integer, optional, intent(in) :: nswp 81 | integer, optional, intent(in) :: verb 82 | integer, optional, intent(in) :: typ0 83 | integer, optional, intent(in) :: order0 84 | end subroutine ztt_diag_ksl 85 | end module dyn_diag_tt 86 | end interface 87 | end python module dyn_tt 88 | 89 | ! This file was auto-generated with f2py (version:2). 90 | ! See http://cens.ioc.ee/projects/f2py2e/ 91 | -------------------------------------------------------------------------------- /tt/maxvol/__init__.py: -------------------------------------------------------------------------------- 1 | from ._maxvol import * 2 | -------------------------------------------------------------------------------- /tt/maxvol/_maxvol.py: -------------------------------------------------------------------------------- 1 | from .maxvol import dmaxvol, zmaxvol 2 | from numpy import arange, asanyarray, iscomplexobj 3 | 4 | 5 | def maxvol(a, nswp=20, tol=5e-2): 6 | a = asanyarray(a) 7 | if a.shape[0] <= a.shape[1]: 8 | return arange(a.shape[0]) 9 | if iscomplexobj(a): 10 | return zmaxvol(a, nswp, tol) - 1 11 | else: 12 | return dmaxvol(a, nswp, tol) - 1 13 | -------------------------------------------------------------------------------- /tt/maxvol/maxvol.f90: -------------------------------------------------------------------------------- 1 | subroutine dmaxvol(a, n, r, ind, nswp, tol) 2 | implicit none 3 | integer, intent(in) :: r 4 | integer, intent(in) :: n 5 | real(8), intent(in) :: a(n,r) 6 | integer, intent(out) :: ind(r) 7 | integer tmp_ind(r),p(n),ipiv(r) 8 | real(8) :: ba(r,n),c(n,r), u(n), v(r) 9 | real(8), intent(in) :: tol 10 | logical not_converged 11 | integer, intent(in) :: nswp 12 | integer info,i,j,swp, big_ind,i0,j0,tmp 13 | integer idamax 14 | external idamax 15 | !tol=5e-2 16 | !nswp=20 17 | !Generate initial approximation 18 | ba = transpose(a) 19 | call dcopy(n*r,a,1,c,1) 20 | do i = 1,n 21 | p(i) = i 22 | end do 23 | call dgetrf(n,r,c,n,ipiv,info) 24 | do i = 1,r 25 | j = ipiv(i) 26 | if ( j .ne. i ) then 27 | tmp = p(i) 28 | p(i) = p(j) 29 | p(j) = tmp 30 | end if 31 | end do 32 | ind(1:r) = p(1:r) 33 | if (info .ne. 0) then 34 | print *, 'Maxvol failed at dgetrf' 35 | end if 36 | do i = 1,r 37 | tmp_ind(i) = i 38 | end do 39 | call dgetrs('t',r,n,c,n,tmp_ind,ba,r,info) 40 | 41 | if (info .ne. 0) then 42 | print *,'Maxvol failed at dgetrs' 43 | end if 44 | not_converged = .true. 45 | swp = 1 46 | !Now start the main iteration 47 | do while (not_converged .and. swp <= nswp) 48 | big_ind = idamax(r*n,ba,1) 49 | j0 = mod(big_ind - 1,r) + 1 ! This value is from 1 to r 50 | i0 = (big_ind - j0)/ r + 1 ! This value seems OK: If it is smaller, it goes 51 | if ( i0 > n ) then 52 | print *,'aarrgh' 53 | print *,'big_ind=',big_ind, 'i0=',i0,'j0=',j0,'n=',n,'r=',r 54 | end if 55 | 56 | if ( dabs(ba(j0,i0)) <= 1 + tol ) then 57 | not_converged = .false. 58 | else 59 | 60 | u(1:n) = ba(j0,:) 61 | v(1:r) = ba(:,ind(j0)) - ba(:,i0) 62 | u(1:n) = u(1:n) / ba(j0,i0) 63 | call dger(r,n,1.d0,v,1,u,1,ba,r) 64 | swp = swp + 1 65 | ind(j0) = i0 66 | 67 | end if 68 | 69 | end do 70 | 71 | end subroutine dmaxvol 72 | 73 | subroutine zmaxvol(a, n, r, ind, nswp, tol) 74 | implicit none 75 | integer, intent(in) :: r 76 | integer, intent(in) :: n 77 | complex(8), intent(in) :: a(n,r) 78 | integer, intent(out) :: ind(r) 79 | integer tmp_ind(r),p(n),ipiv(r) 80 | complex(8) :: ba(r,n),c(n,r), u(n), v(r) 81 | real(8), intent(in) :: tol 82 | logical not_converged 83 | integer, intent(in) :: nswp 84 | integer info,i,j,swp, big_ind,i0,j0,tmp 85 | complex(8) :: ONE 86 | integer izamax 87 | external izamax 88 | !tol=5e-2 89 | !nswp=20 90 | !Generate initial approximation 91 | ONE = (1d0, 0d0) 92 | ba = transpose(a) 93 | call zcopy(n*r,a,1,c,1) 94 | do i = 1,n 95 | p(i) = i 96 | end do 97 | call zgetrf(n,r,c,n,ipiv,info) 98 | do i = 1,r 99 | j = ipiv(i) 100 | if ( j .ne. i ) then 101 | tmp = p(i) 102 | p(i) = p(j) 103 | p(j) = tmp 104 | end if 105 | end do 106 | ind(1:r) = p(1:r) 107 | if (info .ne. 0) then 108 | print *, 'Maxvol failed at zgetrf' 109 | end if 110 | do i = 1,r 111 | tmp_ind(i) = i 112 | end do 113 | call zgetrs('T',r,n,c,n,tmp_ind,ba,r,info) 114 | 115 | if (info .ne. 0) then 116 | print *,'Maxvol failed at zgetrs' 117 | end if 118 | not_converged = .true. 119 | swp = 1 120 | !Now start the main iteration 121 | do while (not_converged .and. swp <= nswp) 122 | big_ind = izamax(r*n,ba,1) 123 | j0 = mod(big_ind - 1,r) + 1 ! This value is from 1 to r 124 | i0 = (big_ind - j0)/ r + 1 ! This value seems OK: If it is smaller, it goes 125 | if ( i0 > n ) then 126 | print *,'aarrgh' 127 | print *,'big_ind=',big_ind, 'i0=',i0,'j0=',j0,'n=',n,'r=',r 128 | end if 129 | 130 | if ( zabs(ba(j0,i0)) <= 1 + tol ) then 131 | not_converged = .false. 132 | else 133 | 134 | u(1:n) = ba(j0,:) 135 | v(1:r) = ba(:,ind(j0)) - ba(:,i0) 136 | u(1:n) = u(1:n) / ba(j0,i0) 137 | call zgeru(r,n,ONE,v,1,u,1,ba,r) 138 | swp = swp + 1 139 | ind(j0) = i0 140 | 141 | end if 142 | 143 | end do 144 | 145 | end subroutine zmaxvol 146 | -------------------------------------------------------------------------------- /tt/maxvol/maxvol.pyf: -------------------------------------------------------------------------------- 1 | ! -*- f90 -*- 2 | ! Note: the context of this file is case sensitive. 3 | 4 | python module maxvol ! in 5 | interface ! in :maxvol 6 | subroutine dmaxvol(a,n,r,ind,nswp,tol) ! in :maxvol:maxvol.f90 7 | real(kind=8) dimension(n,r),intent(in) :: a 8 | integer, optional,intent(in),check(shape(a,0)==n),depend(a) :: n=shape(a,0) 9 | integer, optional,intent(in),check(shape(a,1)==r),depend(a) :: r=shape(a,1) 10 | integer dimension(r),intent(out),depend(r) :: ind 11 | integer intent(in) :: nswp 12 | real(kind=8) intent(in) :: tol 13 | end subroutine dmaxvol 14 | 15 | subroutine zmaxvol(a,n,r,ind,nswp,tol) ! in :maxvol:maxvol.f90 16 | complex(kind=8) dimension(n,r),intent(in) :: a 17 | integer, optional,intent(in),check(shape(a,0)==n),depend(a) :: n=shape(a,0) 18 | integer, optional,intent(in),check(shape(a,1)==r),depend(a) :: r=shape(a,1) 19 | integer dimension(r),intent(out),depend(r) :: ind 20 | integer intent(in) :: nswp 21 | real(kind=8) intent(in) :: tol 22 | end subroutine zmaxvol 23 | end interface 24 | end python module maxvol 25 | 26 | ! This file was auto-generated with f2py (version:2). 27 | ! See http://cens.ioc.ee/projects/f2py2e/ 28 | -------------------------------------------------------------------------------- /tt/maxvol/setup.py: -------------------------------------------------------------------------------- 1 | # setup.py 2 | # This script will build the main subpackages 3 | # See LICENSE for details 4 | 5 | from __future__ import print_function, absolute_import 6 | from numpy.distutils.misc_util import Configuration 7 | 8 | 9 | MAXVOL_SRC = [ 10 | 'maxvol.f90', 11 | 'maxvol.pyf', 12 | ] 13 | 14 | 15 | def configuration(parent_package='', top_path=None): 16 | config = Configuration('maxvol', parent_package, top_path) 17 | config.add_extension('maxvol', sources=MAXVOL_SRC) 18 | return config 19 | 20 | 21 | if __name__ == '__main__': 22 | print('This is the wrong setup.py to run') 23 | -------------------------------------------------------------------------------- /tt/multifuncrs.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import, division 2 | import numpy as np 3 | from numpy import prod, nonzero, size 4 | import math 5 | import tt 6 | from tt.maxvol import maxvol 7 | import copy 8 | 9 | 10 | def reshape(a, size): 11 | return np.reshape(a, size, order='F') 12 | 13 | 14 | def my_chop2(sv, eps): 15 | if eps <= 0.0: 16 | r = len(sv) 17 | return r 18 | sv0 = np.cumsum(abs(sv[::-1]) ** 2)[::-1] 19 | ff = [i for i in range(len(sv0)) if sv0[i] < eps ** 2] 20 | if len(ff) == 0: 21 | return len(sv) 22 | else: 23 | return np.amin(ff) 24 | 25 | # Cross approximation of a (vector-)function of several TT-tensors. 26 | # [Y]=MULTIFUNCRS(X,FUNS,EPS,VARARGIN) 27 | # Computes approximation to the functions FUNS(X{1},...,X{N}) with accuracy EPS 28 | # X should be a cell array of nx TT-tensors of equal sizes. 29 | # The function FUNS should receive a 2d array V of sizes I x N, where the 30 | # first dimension stays for the reduced set of spatial indices, and the 31 | # second is the enumerator of X. 32 | # The returned sizes should be I x D2, where D2 is the number of 33 | # components in FUNS. D2 should be either provided as the last (d+1)-th 34 | # TT-rank of the initial guess, or given explicitly as an option (see 35 | # below). 36 | # For example, a linear combination reads FUNS=@(x)(x*W), W is a N x D2 37 | # matrix. 38 | # 39 | # Options are provided in form 40 | # 'PropertyName1',PropertyValue1,'PropertyName2',PropertyValue2 and so 41 | # on. The parameters are set to default (in brackets in the following) 42 | # The list of option names and default values are: 43 | # o y0 - initial approximation [random rank-2 tensor] 44 | # o nswp - maximal number of DMRG sweeps [10] 45 | # o rmax - maximal TT rank [Inf] 46 | # o verb - verbosity level, 0-silent, 1-sweep info, 2-block info [1] 47 | # o kickrank - the rank-increasing parameter [5] 48 | # o d2 - the last rank of y, that is dim(FUNS) [1] 49 | # o qr - do (or not) qr before maxvol [false] 50 | # o pcatype - How to compute the enrichment of the basis, 'uchol' - 51 | # Incomplete Cholesky, 'svd' - SVD [svd] 52 | 53 | 54 | def multifuncrs(X, funs, eps=1E-6, 55 | nswp=10, 56 | kickrank=5, 57 | y0=None, 58 | rmax=999999, # TODO:infinity \ 59 | kicktype='amr-two', \ 60 | pcatype='svd', \ 61 | trunctype='fro', \ 62 | d2=1, \ 63 | do_qr=False, \ 64 | verb=1): 65 | """Cross approximation of a (vector-)function of several TT-tensors. 66 | 67 | :param X: tuple of TT-tensors 68 | :param funs: multivariate function 69 | :param eps: accuracy 70 | """ 71 | 72 | dtype = np.float64 73 | if len([x for x in X if x.is_complex]) > 0: 74 | dtype = np.complex128 75 | 76 | y = y0 77 | wasrand = False 78 | 79 | nx = len(X) 80 | d = X[0].d 81 | n = X[0].n 82 | rx = np.transpose(np.array([ttx.r for ttx in X])) 83 | #crx = [tt.tensor.to_list(ttx) for x in X] 84 | #crx = zip(*crx) 85 | crx = np.transpose(np.array([tt.tensor.to_list(ttx) 86 | for ttx in X], dtype=np.object)) 87 | crx = np.empty((nx, d), dtype=np.object) 88 | i = 0 89 | for ttx in X: 90 | v = tt.tensor.to_list(ttx) 91 | j = 0 92 | for w in v: 93 | crx[i, j] = w 94 | j = j + 1 95 | i = i + 1 96 | crx = crx.T 97 | if y is None: 98 | ry = d2 * np.ones((d + 1,), dtype=np.int32) 99 | ry[0] = 1 100 | y = tt.rand(n, d, ry) 101 | wasrand = True 102 | 103 | ry = y.r 104 | cry = tt.tensor.to_list(y) 105 | 106 | Ry = np.zeros((d + 1, ), dtype=np.object) 107 | Ry[0] = np.array([[1.0]], dtype=dtype) 108 | Ry[d] = np.array([[1.0]], dtype=dtype) 109 | Rx = np.zeros((d + 1, nx), dtype=np.object) 110 | Rx[0, :] = np.ones(nx, dtype=dtype) 111 | Rx[d, :] = np.ones(nx, dtype=dtype) 112 | 113 | block_order = [+d, -d] 114 | 115 | # orth 116 | for i in range(0, d - 1): 117 | cr = cry[i] 118 | cr = reshape(cr, (ry[i] * n[i], ry[i + 1])) 119 | cr, rv = np.linalg.qr(cr) 120 | cr2 = cry[i + 1] 121 | cr2 = reshape(cr2, (ry[i + 1], n[i + 1] * ry[i + 2])) 122 | cr2 = np.dot(rv, cr2) # matrix multiplication 123 | ry[i + 1] = cr.shape[1] 124 | cr = reshape(cr, (ry[i], n[i], ry[i + 1])) 125 | cry[i + 1] = reshape(cr2, (ry[i + 1], n[i + 1], ry[i + 2])) 126 | cry[i] = cr 127 | 128 | Ry[i + 1] = np.dot(Ry[i], reshape(cr, (ry[i], n[i] * ry[i + 1]))) 129 | Ry[i + 1] = reshape(Ry[i + 1], (ry[i] * n[i], ry[i + 1])) 130 | curind = [] 131 | if wasrand: 132 | # EVERY DAY I'M SHUFFLIN' 133 | curind = np.random.permutation(n[i] * ry[i])[:ry[i + 1]] 134 | else: 135 | curind = maxvol(Ry[i + 1]) 136 | Ry[i + 1] = Ry[i + 1][curind, :] 137 | for j in range(0, nx): 138 | try: 139 | Rx[i + 1, j] = reshape(crx[i, j], 140 | (rx[i, j], n[i] * rx[i + 1, j])) 141 | except: 142 | pass 143 | Rx[i + 1, j] = np.dot(Rx[i, j], Rx[i + 1, j]) 144 | Rx[i + 1, j] = reshape(Rx[i + 1, j], (ry[i] * n[i], rx[i + 1, j])) 145 | Rx[i + 1, j] = Rx[i + 1, j][curind, :] 146 | 147 | d2 = ry[d] 148 | ry[d] = 1 149 | cry[d - 1] = np.transpose(cry[d - 1], [2, 0, 1]) # permute 150 | 151 | last_sweep = False 152 | swp = 1 153 | 154 | dy = np.zeros((d, )) 155 | max_dy = 0 156 | 157 | cur_order = copy.copy(block_order) 158 | order_index = 1 159 | i = d - 1 160 | # can't use 'dir' identifier in python 161 | dirn = int(math.copysign(1, cur_order[order_index])) 162 | 163 | # DMRG sweeps 164 | while swp <= nswp or dirn > 0: 165 | 166 | oldy = reshape(cry[i], (d2 * ry[i] * n[i] * ry[i + 1],)) 167 | 168 | if not last_sweep: 169 | # compute the X superblocks 170 | curbl = np.zeros((ry[i] * n[i] * ry[i + 1], nx), dtype=dtype) 171 | for j in range(0, nx): 172 | cr = reshape(crx[i, j], (rx[i, j], n[i] * rx[i + 1, j])) 173 | cr = np.dot(Rx[i, j], cr) 174 | cr = reshape(cr, (ry[i] * n[i], rx[i + 1, j])) 175 | cr = np.dot(cr, Rx[i + 1, j]) 176 | curbl[:, j] = cr.flatten('F') 177 | # call the function 178 | newy = funs(curbl) 179 | # multiply with inverted Ry 180 | newy = reshape(newy, (ry[i], n[i] * ry[i + 1] * d2)) 181 | newy = np.linalg.solve(Ry[i], newy) # y = R \ y 182 | newy = reshape(newy, (ry[i] * n[i] * ry[i + 1], d2)) 183 | newy = reshape(np.transpose(newy), (d2 * ry[i] * n[i], ry[i + 1])) 184 | newy = np.transpose(np.linalg.solve( 185 | np.transpose(Ry[i + 1]), np.transpose(newy))) # y=y/R 186 | newy = reshape(newy, (d2 * ry[i] * n[i] * ry[i + 1],)) 187 | else: 188 | newy = oldy 189 | 190 | dy[i] = np.linalg.norm(newy - oldy) / np.linalg.norm(newy) 191 | max_dy = max(max_dy, dy[i]) 192 | 193 | # truncation 194 | if dirn > 0: # left-to-right 195 | newy = reshape(newy, (d2, ry[i] * n[i] * ry[i + 1])) 196 | newy = reshape(np.transpose(newy), (ry[i] * n[i], ry[i + 1] * d2)) 197 | else: 198 | newy = reshape(newy, (d2 * ry[i], n[i] * ry[i + 1])) 199 | 200 | r = 0 # defines a variable in global scope 201 | 202 | if kickrank >= 0: 203 | u, s, v = np.linalg.svd(newy, full_matrices=False) 204 | v = np.conj(np.transpose(v)) 205 | if trunctype == "fro" or last_sweep: 206 | r = my_chop2(s, eps / math.sqrt(d) * np.linalg.norm(s)) 207 | else: 208 | # truncate taking into account the (r+1) overhead in the cross 209 | # (T.S.: what?) 210 | cums = abs(s * np.arange(2, len(s) + 2)) ** 2 211 | cums = np.cumsum(cums[::-1])[::-1] 212 | cums = cums / cums[0] 213 | ff = [i for i in range(len(cums)) if cums[i] < eps ** 2 / d] 214 | if len(ff) == 0: 215 | r = len(s) 216 | else: 217 | r = np.amin(ff) 218 | r = min(r, rmax, len(s)) 219 | else: 220 | if dirn > 0: 221 | u, v = np.linalg.qr(newy) 222 | v = np.conj(np.transpose(v)) 223 | r = u.shape[1] 224 | s = np.ones((r, )) 225 | else: 226 | v, u = np.linalg.qr(np.transpose(newy)) 227 | v = np.conj(v) 228 | u = np.transpose(u) 229 | r = u.shape[1] 230 | s = np.ones((r, )) 231 | 232 | if verb > 1: 233 | print('=multifuncrs= block %d{%d}, dy: %3.3e, r: %d' % (i, dirn, dy[i], r)) 234 | 235 | # kicks and interfaces 236 | if dirn > 0 and i < d - 1: 237 | u = u[:, :r] 238 | v = np.dot(v[:, :r], np.diag(s[:r])) 239 | 240 | # kick 241 | radd = 0 242 | rv = 1 243 | if not last_sweep and kickrank > 0: 244 | uk = None 245 | if kicktype == 'amr-two': 246 | # AMR(two)-like kick. 247 | 248 | # compute the X superblocks 249 | ind2 = np.unique(np.random.randint( 250 | 0, ry[i + 2] * n[i + 1], ry[i + 1])) 251 | #ind2 = np.unique(np.floor(np.random.rand(ry[i + 1]) * (ry[i + 2] * n[i + 1]))) 252 | rkick = len(ind2) 253 | curbl = np.zeros((ry[i] * n[i] * rkick, nx), dtype=dtype) 254 | for j in range(nx): 255 | cr1 = reshape( 256 | crx[i, j], (rx[i, j], n[i] * rx[i + 1, j])) 257 | cr1 = np.dot(Rx[i, j], cr1) 258 | cr1 = reshape(cr1, (ry[i] * n[i], rx[i + 1, j])) 259 | cr2 = reshape( 260 | crx[i + 1, j], (rx[i + 1, j] * n[i + 1], rx[i + 2, j])) 261 | cr2 = np.dot(cr2, Rx[i + 2, j]) 262 | cr2 = reshape( 263 | cr2, (rx[i + 1, j], n[i + 1] * ry[i + 2])) 264 | cr2 = cr2[:, ind2] 265 | curbl[:, j] = reshape( 266 | np.dot(cr1, cr2), (ry[i] * n[i] * rkick,)) 267 | # call the function 268 | uk = funs(curbl) 269 | uk = reshape(uk, (ry[i], n[i] * rkick * d2)) 270 | uk = np.linalg.solve(Ry[i], uk) 271 | uk = reshape(uk, (ry[i] * n[i], rkick * d2)) 272 | if pcatype == 'svd': 273 | uk, sk, vk = np.linalg.svd(uk, full_matrices=False) 274 | vk = np.conj(np.transpose(vk)) 275 | uk = uk[:, :min(kickrank, uk.shape[1])] 276 | else: 277 | # uk = uchol(np.transpose(uk), kickrank + 1) # TODO 278 | uk = uk[:, :max(uk.shape[1] - kickrank + 1, 1):-1] 279 | else: 280 | uk = np.random.rand(ry[i] * n[i], kickrank) 281 | u, rv = np.linalg.qr(np.concatenate((u, uk), axis=1)) 282 | radd = uk.shape[1] 283 | v = np.concatenate( 284 | (v, np.zeros((ry[i + 1] * d2, radd), dtype=dtype)), axis=1) 285 | v = np.dot(rv, np.conj(np.transpose(v))) 286 | r = u.shape[1] 287 | 288 | cr2 = cry[i + 1] 289 | cr2 = reshape(cr2, (ry[i + 1], n[i + 1] * ry[i + 2])) 290 | v = reshape(v, (r * ry[i + 1], d2)) 291 | v = reshape(np.transpose(v), (d2 * r, ry[i + 1])) 292 | v = np.dot(v, cr2) 293 | 294 | ry[i + 1] = r 295 | 296 | u = reshape(u, (ry[i], n[i], r)) 297 | v = reshape(v, (d2, r, n[i + 1], ry[i + 2])) 298 | 299 | cry[i] = u 300 | cry[i + 1] = v 301 | 302 | Ry[i + 1] = np.dot(Ry[i], reshape(u, (ry[i], n[i] * ry[i + 1]))) 303 | Ry[i + 1] = reshape(Ry[i + 1], (ry[i] * n[i], ry[i + 1])) 304 | curind = maxvol(Ry[i + 1]) 305 | Ry[i + 1] = Ry[i + 1][curind, :] 306 | for j in range(nx): 307 | Rx[i + 1, j] = reshape(crx[i, j], 308 | (rx[i, j], n[i] * rx[i + 1, j])) 309 | Rx[i + 1, j] = np.dot(Rx[i, j], Rx[i + 1, j]) 310 | Rx[i + 1, j] = reshape(Rx[i + 1, j], 311 | (ry[i] * n[i], rx[i + 1, j])) 312 | Rx[i + 1, j] = Rx[i + 1, j][curind, :] 313 | elif dirn < 0 and i > 0: 314 | u = np.dot(u[:, :r], np.diag(s[:r])) 315 | v = np.conj(v[:, :r]) 316 | 317 | radd = 0 318 | rv = 1 319 | if not last_sweep and kickrank > 0: 320 | if kicktype == 'amr-two': 321 | # compute the X superblocks 322 | ind2 = np.unique(np.random.randint( 323 | 0, ry[i - 1] * n[i - 1], ry[i])) 324 | rkick = len(ind2) 325 | curbl = np.zeros( 326 | (rkick * n[i] * ry[i + 1], nx), dtype=dtype) 327 | for j in range(nx): 328 | cr1 = reshape( 329 | crx[i, j], (rx[i, j] * n[i], rx[i + 1, j])) 330 | cr1 = np.dot(cr1, Rx[i + 1, j]) 331 | cr1 = reshape(cr1, (rx[i, j], n[i] * ry[i + 1])) 332 | cr2 = reshape( 333 | crx[i - 1, j], (rx[i - 1, j], n[i - 1] * rx[i, j])) 334 | cr2 = np.dot(Rx[i - 1, j], cr2) 335 | cr2 = reshape(cr2, (ry[i - 1] * n[i - 1], rx[i, j])) 336 | cr2 = cr2[ind2, :] 337 | curbl[:, j] = reshape( 338 | np.dot(cr2, cr1), (rkick * n[i] * ry[i + 1],)) 339 | # calling the function 340 | uk = funs(curbl) 341 | uk = reshape(uk, (rkick * n[i] * ry[i + 1], d2)) 342 | uk = reshape(np.transpose( 343 | uk), (d2 * rkick * n[i], ry[i + 1])) 344 | uk = np.transpose(np.linalg.solve( 345 | np.transpose(Ry[i + 1]), np.transpose(uk))) 346 | uk = reshape(uk, (d2 * rkick, n[i] * ry[i + 1])) 347 | if pcatype == 'svd': 348 | vk, sk, uk = np.linalg.svd(uk, full_matrices=False) 349 | uk = np.conj(np.transpose(uk)) 350 | # TODO: refactor 351 | uk = uk[:, :min(kickrank, uk.shape[1])] 352 | else: 353 | # uk = uchol(uk, kickrank + 1) # TODO 354 | uk = uk[:, :max(uk.shape[1] - kickrank + 1, 1):-1] 355 | else: 356 | uk = np.random.rand(n[i] * ry[i + 1], kickrank) 357 | v, rv = np.linalg.qr(np.concatenate((v, uk), axis=1)) 358 | radd = uk.shape[1] 359 | u = np.concatenate( 360 | (u, np.zeros((d2 * ry[i], radd), dtype=dtype)), axis=1) 361 | u = np.dot(u, np.transpose(rv)) 362 | r = v.shape[1] 363 | cr2 = cry[i - 1] 364 | cr2 = reshape(cr2, (ry[i - 1] * n[i - 1], ry[i])) 365 | u = reshape(u, (d2, ry[i] * r)) 366 | u = reshape(np.transpose(u), (ry[i], r * d2)) 367 | u = np.dot(cr2, u) 368 | 369 | u = reshape(u, (ry[i - 1] * n[i - 1] * r, d2)) 370 | u = reshape(np.transpose(u), (d2, ry[i - 1], n[i - 1], r)) 371 | v = reshape(np.transpose(v), (r, n[i], ry[i + 1])) 372 | 373 | ry[i] = r 374 | cry[i - 1] = u 375 | cry[i] = v 376 | 377 | Ry[i] = np.dot(reshape(v, (ry[i] * n[i], ry[i + 1])), Ry[i + 1]) 378 | Ry[i] = reshape(Ry[i], (ry[i], n[i] * ry[i + 1])) 379 | curind = maxvol(np.transpose(Ry[i])) 380 | Ry[i] = Ry[i][:, curind] 381 | for j in range(nx): 382 | Rx[i, j] = reshape(crx[i, j], (rx[i, j] * n[i], rx[i + 1, j])) 383 | Rx[i, j] = np.dot(Rx[i, j], Rx[i + 1, j]) 384 | Rx[i, j] = reshape(Rx[i, j], (rx[i, j], n[i] * ry[i + 1])) 385 | Rx[i, j] = Rx[i, j][:, curind] 386 | elif dirn > 0 and i == d - 1: 387 | newy = np.dot(np.dot(u[:, :r], np.diag(s[:r])), 388 | np.conj(np.transpose(v[:, :r]))) 389 | newy = reshape(newy, (ry[i] * n[i] * ry[i + 1], d2)) 390 | cry[i] = reshape(np.transpose(newy), (d2, ry[i], n[i], ry[i + 1])) 391 | elif dirn < 0 and i == 0: 392 | newy = np.dot(np.dot(u[:, :r], np.diag(s[:r])), 393 | np.conj(np.transpose(v[:, :r]))) 394 | newy = reshape(newy, (d2, ry[i], n[i], ry[i + 1])) 395 | cry[i] = newy 396 | 397 | i = i + dirn 398 | cur_order[order_index] = cur_order[order_index] - dirn 399 | if cur_order[order_index] == 0: 400 | order_index = order_index + 1 401 | if verb > 0: 402 | print('=multifuncrs= sweep %d{%d}, max_dy: %3.3e, erank: %g' % (swp, order_index, max_dy, 403 | math.sqrt(np.dot(ry[:d], n * ry[1:]) / np.sum(n)))) 404 | 405 | if last_sweep: 406 | break 407 | if max_dy < eps and dirn < 0: 408 | last_sweep = True 409 | kickrank = 0 410 | 411 | if order_index >= len(cur_order): 412 | cur_order = copy.copy(block_order) 413 | order_index = 0 414 | if last_sweep: 415 | cur_order = [d - 1] 416 | 417 | max_dy = 0 418 | swp = swp + 1 419 | 420 | dirn = int(math.copysign(1, cur_order[order_index])) 421 | i = i + dirn 422 | 423 | cry[d - 1] = np.transpose(cry[d - 1][:, :, :, 0], [1, 2, 0]) 424 | y = tt.tensor.from_list(cry) 425 | return y 426 | -------------------------------------------------------------------------------- /tt/optimize/__init__.py: -------------------------------------------------------------------------------- 1 | """TT module for optimization.""" 2 | -------------------------------------------------------------------------------- /tt/optimize/example.py: -------------------------------------------------------------------------------- 1 | """Example of using tt.optimize module.""" 2 | 3 | from __future__ import print_function, absolute_import, division 4 | import tt 5 | from tt.optimize import tt_min 6 | from scipy.optimize import rosen 7 | 8 | 9 | def my_rosen(x): 10 | return rosen(x.T) 11 | 12 | print("Minimize 4-d Rosenbrock function on a 4-dimensional grid (512 points " + 13 | "along each dimension). The global minimum is 0 in the (1, 1, 1, 1) point.") 14 | val, x_full = tt_min.min_func(my_rosen, -2, 2, d=4, n0=512, rmax=10, nswp=30) 15 | 16 | tens = tt.rand([3, 4, 5, 4, 3], 5, 3) 17 | min_element = min(tens.full().flatten()) 18 | print("Minimize random 5-dimensional TT tensor with ranks equal to 3. " + 19 | "The minimal element is %f" % min_element) 20 | val, point = tt_min.min_tens(tens, rmax=10, nswp=30) 21 | -------------------------------------------------------------------------------- /tt/optimize/setup.py: -------------------------------------------------------------------------------- 1 | # setup.py 2 | # This script will build the main subpackages 3 | # See LICENSE for details 4 | 5 | from __future__ import print_function, absolute_import, division 6 | from numpy.distutils.misc_util import Configuration 7 | 8 | 9 | def configuration(parent_package='', top_path=None): 10 | config = Configuration('optimize', parent_package, top_path) 11 | return config 12 | 13 | 14 | if __name__ == '__main__': 15 | print('This is the wrong setup.py to run') 16 | -------------------------------------------------------------------------------- /tt/optimize/tt_min.py: -------------------------------------------------------------------------------- 1 | """This module contains a prototype implementation of the 2 | TT-cross-based minimization procedure 3 | """ 4 | from __future__ import print_function, absolute_import, division 5 | from six.moves import xrange 6 | import numpy as np 7 | import math 8 | import tt 9 | from ..maxvol import maxvol 10 | from ..utils.rect_maxvol import rect_maxvol 11 | 12 | 13 | def reshape(a, sz): 14 | return np.reshape(a, sz, order='F') 15 | 16 | 17 | def mkron(a, b): 18 | return np.kron(a, b) 19 | 20 | 21 | def mysvd(a, full_matrices=False): 22 | try: 23 | return np.linalg.svd(a, full_matrices) 24 | except: 25 | return np.linalg.svd(a + np.max(np.abs(a).flatten()) * 1e-14 * 26 | np.random.randn(a.shape[0], a.shape[1]), full_matrices) 27 | 28 | 29 | def min_func(fun, bounds_min, bounds_max, d=None, rmax=10, 30 | n0=64, nswp=10, verb=True, smooth_fun=None): 31 | """Find (approximate) minimal value of the function on a d-dimensional grid.""" 32 | if d is None: 33 | d = len(bounds_min) 34 | a = np.asanyarray(bounds_min).copy() 35 | b = np.asanyarray(bounds_max).copy() 36 | else: 37 | a = np.ones(d) * bounds_min 38 | b = np.ones(d) * bounds_max 39 | 40 | if smooth_fun is None: 41 | smooth_fun = lambda p, lam: (math.pi / 2 - np.arctan(p - lam)) 42 | #smooth_fun = lambda p, lam: np.exp(-10*(p - lam)) 43 | 44 | # We do not need to store the cores, only the interfaces! 45 | Rx = [[]] * (d + 1) # Python list for the interfaces 46 | Rx[0] = np.ones((1, 1)) 47 | Rx[d] = np.ones((1, 1)) 48 | Jy = [np.empty(0, dtype=np.int)] * (d + 1) 49 | ry = rmax * np.ones(d + 1, dtype=np.int) 50 | ry[0] = 1 51 | ry[d] = 1 52 | n = n0 * np.ones(d, dtype=np.int) 53 | fun_evals = 0 54 | 55 | grid = [np.reshape(np.linspace(a[i], b[i], n[i]), (n[i], 1)) 56 | for i in xrange(d)] 57 | for i in xrange(d - 1): 58 | #cr1 = y[i] 59 | ry[i + 1] = min(ry[i + 1], n[i] * ry[i]) 60 | cr1 = np.random.randn(ry[i], n[i], ry[i + 1]) 61 | cr1 = reshape(cr1, (ry[i] * n[i], ry[i + 1])) 62 | q, r = np.linalg.qr(cr1) 63 | ind = maxvol(q) 64 | w1 = mkron(np.ones((n[i], 1), dtype=np.int), Jy[i]) 65 | w2 = mkron(grid[i], np.ones((ry[i], 1), dtype=np.int)) 66 | Jy[i + 1] = np.hstack((w1, w2)) 67 | Jy[i + 1] = reshape(Jy[i + 1], (ry[i] * n[i], -1)) 68 | Jy[i + 1] = Jy[i + 1][ind, :] 69 | 70 | # Jy{i+1} = [kron(ones(n(i),1), Jy{i}), kron((1:n(i))', ones(ry(i),1))]; 71 | # Jy{i+1} = Jy{i+1}(ind,:); 72 | swp = 0 73 | dirn = -1 74 | i = d - 1 75 | lm = float('Inf') 76 | while swp < nswp: 77 | # Right-to-left sweep 78 | # The idea: compute the current core; compute the function of it; 79 | # Shift locally or globally? Local shift would be the first try 80 | # Compute the current core 81 | 82 | if np.size(Jy[i]) == 0: 83 | w1 = np.zeros((ry[i] * n[i] * ry[i + 1], 0), dtype=np.int) 84 | else: 85 | w1 = mkron(np.ones((n[i] * ry[i + 1], 1), dtype=np.int), Jy[i]) 86 | w2 = mkron(mkron(np.ones((ry[i + 1], 1), dtype=np.int), 87 | grid[i]), np.ones((ry[i], 1), dtype=np.int)) 88 | if np.size(Jy[i + 1]) == 0: 89 | w3 = np.zeros((ry[i] * n[i] * ry[i + 1], 0), dtype=np.int) 90 | else: 91 | w3 = mkron(Jy[i + 1], np.ones((ry[i] * n[i], 1), dtype=np.int)) 92 | 93 | J = np.hstack((w1, w2, w3)) 94 | # Just add some random indices to J, which is rnr x d, need to make rn (r + r0) x add, 95 | # i.e., just generate random r, random n and random multiindex 96 | 97 | cry = fun(J) 98 | fun_evals += cry.size 99 | cry = reshape(cry, (ry[i], n[i], ry[i + 1])) 100 | min_cur = np.min(cry.flatten("F")) 101 | ind_cur = np.argmin(cry.flatten("F")) 102 | if lm > min_cur: 103 | lm = min_cur 104 | x_full = J[ind_cur, :] 105 | val = fun(x_full) 106 | if verb: 107 | print('New record:', val, 'Point:', x_full, 'fevals:', fun_evals) 108 | cry = smooth_fun(cry, lm) 109 | if (dirn < 0 and i > 0): 110 | cry = reshape(cry, (ry[i], n[i] * ry[i + 1])) 111 | cry = cry.T 112 | #q, r = np.linalg.qr(cry) 113 | u, s, v = mysvd(cry, full_matrices=False) 114 | ry[i] = min(ry[i], rmax) 115 | q = u[:, :ry[i]] 116 | ind = rect_maxvol(q)[0] # maxvol(q) 117 | ry[i] = ind.size 118 | w1 = mkron(np.ones((ry[i + 1], 1), dtype=np.int), grid[i]) 119 | if np.size(Jy[i + 1]) == 0: 120 | w2 = np.zeros((n[i] * ry[i + 1], 0), dtype=np.int) 121 | else: 122 | w2 = mkron(Jy[i + 1], np.ones((n[i], 1), dtype=np.int)) 123 | Jy[i] = np.hstack((w1, w2)) 124 | Jy[i] = reshape(Jy[i], (n[i] * ry[i + 1], -1)) 125 | Jy[i] = Jy[i][ind, :] 126 | 127 | if (dirn > 0 and i < d - 1): 128 | cry = reshape(cry, (ry[i] * n[i], ry[i + 1])) 129 | q, r = np.linalg.qr(cry) 130 | #ind = maxvol(q) 131 | ind = rect_maxvol(q)[0] 132 | ry[i + 1] = ind.size 133 | w1 = mkron(np.ones((n[i], 1), dtype=np.int), Jy[i]) 134 | w2 = mkron(grid[i], np.ones((ry[i], 1), dtype=np.int)) 135 | Jy[i + 1] = np.hstack((w1, w2)) 136 | Jy[i + 1] = reshape(Jy[i + 1], (ry[i] * n[i], -1)) 137 | Jy[i + 1] = Jy[i + 1][ind, :] 138 | 139 | i += dirn 140 | if i == d or i == -1: 141 | dirn = -dirn 142 | i += dirn 143 | swp = swp + 1 144 | return val, x_full 145 | 146 | 147 | def min_tens(tens, rmax=10, nswp=10, verb=True, smooth_fun=None): 148 | """Find (approximate) minimal element in a TT-tensor.""" 149 | if smooth_fun is None: 150 | smooth_fun = lambda p, lam: (math.pi / 2 - np.arctan(p - lam)) 151 | d = tens.d 152 | Rx = [[]] * (d + 1) # Python list for the interfaces 153 | Rx[0] = np.ones((1, 1)) 154 | Rx[d] = np.ones((1, 1)) 155 | Jy = [np.empty(0, dtype=np.int)] * (d + 1) 156 | ry = rmax * np.ones(d + 1, dtype=np.int) 157 | ry[0] = 1 158 | ry[d] = 1 159 | n = tens.n 160 | elements_seen = 0 161 | phi_left = [np.empty(0)] * (d + 1) 162 | phi_left[0] = np.array([1]) 163 | phi_right = [np.empty(0)] * (d + 1) 164 | phi_right[d] = np.array([1]) 165 | cores = tt.tensor.to_list(tens) 166 | 167 | # Fill initial multiindex J randomly. 168 | grid = [np.reshape(range(n[i]), (n[i], 1)) for i in xrange(d)] 169 | for i in xrange(d - 1): 170 | ry[i + 1] = min(ry[i + 1], n[i] * ry[i]) 171 | ind = sorted(np.random.permutation(ry[i] * n[i])[0:ry[i + 1]]) 172 | w1 = mkron(np.ones((n[i], 1), dtype=np.int), Jy[i]) 173 | w2 = mkron(grid[i], np.ones((ry[i], 1), dtype=np.int)) 174 | Jy[i + 1] = np.hstack((w1, w2)) 175 | Jy[i + 1] = reshape(Jy[i + 1], (ry[i] * n[i], -1)) 176 | Jy[i + 1] = Jy[i + 1][ind, :] 177 | phi_left[i + 1] = np.tensordot(phi_left[i], cores[i], 1) 178 | phi_left[i + 1] = reshape(phi_left[i + 1], (ry[i] * n[i], -1)) 179 | phi_left[i + 1] = phi_left[i + 1][ind, :] 180 | swp = 0 181 | dirn = -1 182 | i = d - 1 183 | lm = float('Inf') 184 | while swp < nswp: 185 | # Right-to-left sweep 186 | # The idea: compute the current core; compute the function of it; 187 | # Shift locally or globally? Local shift would be the first try 188 | # Compute the current core 189 | 190 | if np.size(Jy[i]) == 0: 191 | w1 = np.zeros((ry[i] * n[i] * ry[i + 1], 0), dtype=np.int) 192 | else: 193 | w1 = mkron(np.ones((n[i] * ry[i + 1], 1), dtype=np.int), Jy[i]) 194 | w2 = mkron(mkron(np.ones((ry[i + 1], 1), dtype=np.int), 195 | grid[i]), np.ones((ry[i], 1), dtype=np.int)) 196 | if np.size(Jy[i + 1]) == 0: 197 | w3 = np.zeros((ry[i] * n[i] * ry[i + 1], 0), dtype=np.int) 198 | else: 199 | w3 = mkron(Jy[i + 1], np.ones((ry[i] * n[i], 1), dtype=np.int)) 200 | J = np.hstack((w1, w2, w3)) 201 | 202 | phi_right[i] = np.tensordot(cores[i], phi_right[i + 1], 1) 203 | phi_right[i] = reshape(phi_right[i], (-1, n[i] * ry[i + 1])) 204 | 205 | cry = np.tensordot( 206 | phi_left[i], np.tensordot( 207 | cores[i], phi_right[ 208 | i + 1], 1), 1) 209 | elements_seen += cry.size 210 | cry = reshape(cry, (ry[i], n[i], ry[i + 1])) 211 | min_cur = np.min(cry.flatten("F")) 212 | ind_cur = np.argmin(cry.flatten("F")) 213 | if lm > min_cur: 214 | lm = min_cur 215 | x_full = J[ind_cur, :] 216 | val = tens[x_full] 217 | if verb: 218 | print('New record:', val, 'Point:', x_full, 'elements seen:', elements_seen) 219 | cry = smooth_fun(cry, lm) 220 | if dirn < 0 and i > 0: 221 | cry = reshape(cry, (ry[i], n[i] * ry[i + 1])) 222 | cry = cry.T 223 | #q, r = np.linalg.qr(cry) 224 | u, s, v = mysvd(cry, full_matrices=False) 225 | ry[i] = min(ry[i], rmax) 226 | q = u[:, :ry[i]] 227 | ind = rect_maxvol(q)[0] # maxvol(q) 228 | ry[i] = ind.size 229 | w1 = mkron(np.ones((ry[i + 1], 1), dtype=np.int), grid[i]) 230 | if np.size(Jy[i + 1]) == 0: 231 | w2 = np.zeros((n[i] * ry[i + 1], 0), dtype=np.int) 232 | else: 233 | w2 = mkron(Jy[i + 1], np.ones((n[i], 1), dtype=np.int)) 234 | Jy[i] = np.hstack((w1, w2)) 235 | Jy[i] = reshape(Jy[i], (n[i] * ry[i + 1], -1)) 236 | Jy[i] = Jy[i][ind, :] 237 | phi_right[i] = np.tensordot(cores[i], phi_right[i + 1], 1) 238 | phi_right[i] = reshape(phi_right[i], (-1, n[i] * ry[i + 1])) 239 | phi_right[i] = phi_right[i][:, ind] 240 | 241 | if dirn > 0 and i < d - 1: 242 | cry = reshape(cry, (ry[i] * n[i], ry[i + 1])) 243 | q, r = np.linalg.qr(cry) 244 | #ind = maxvol(q) 245 | ind = rect_maxvol(q)[0] 246 | ry[i + 1] = ind.size 247 | phi_left[i + 1] = np.tensordot(phi_left[i], cores[i], 1) 248 | phi_left[i + 1] = reshape(phi_left[i + 1], (ry[i] * n[i], -1)) 249 | phi_left[i + 1] = phi_left[i + 1][ind, :] 250 | w1 = mkron(np.ones((n[i], 1), dtype=np.int), Jy[i]) 251 | w2 = mkron(grid[i], np.ones((ry[i], 1), dtype=np.int)) 252 | Jy[i + 1] = np.hstack((w1, w2)) 253 | Jy[i + 1] = reshape(Jy[i + 1], (ry[i] * n[i], -1)) 254 | Jy[i + 1] = Jy[i + 1][ind, :] 255 | 256 | i += dirn 257 | if i == d or i == -1: 258 | dirn = -dirn 259 | i += dirn 260 | swp = swp + 1 261 | return val, x_full 262 | -------------------------------------------------------------------------------- /tt/riemannian/__init__.py: -------------------------------------------------------------------------------- 1 | """TT module for Riemannian optimization on fixed TT-rank tensor manifolds.""" 2 | -------------------------------------------------------------------------------- /tt/riemannian/riemannian.py: -------------------------------------------------------------------------------- 1 | # References: 2 | # [1] C. Lubich, I. Oseledets and B. Vandereycken, Time integration of 3 | # tensor trains. 4 | 5 | from __future__ import print_function, absolute_import, division 6 | from six.moves import xrange 7 | import tt 8 | import numpy as np 9 | from math import ceil 10 | from numba import jit 11 | 12 | 13 | def reshape(a, sz): 14 | return np.reshape(a, sz, order="F") 15 | 16 | def cores_orthogonalization_step(coresX, dim, left_to_right=True): 17 | """TT-Tensor X orthogonalization step. 18 | 19 | The function can change the shape of some cores. 20 | """ 21 | cc = coresX[dim] 22 | r1, n, r2 = cc.shape 23 | if left_to_right: 24 | # Left to right orthogonalization step. 25 | assert(0 <= dim < len(coresX) - 1) 26 | cc, rr = np.linalg.qr(reshape(cc, (-1, r2))) 27 | r2 = cc.shape[1] 28 | coresX[dim] = reshape(cc, (r1, n, r2)) 29 | coresX[dim+1] = np.tensordot(rr, coresX[dim+1], 1) 30 | else: 31 | # Right to left orthogonalization step. 32 | assert(0 < dim < len(coresX)) 33 | cc, rr = np.linalg.qr(reshape(cc, (r1, -1)).T) 34 | r1 = cc.shape[1] 35 | coresX[dim] = reshape(cc.T, (r1, n, r2)) 36 | coresX[dim-1] = np.tensordot(coresX[dim-1], rr.T, 1) 37 | return coresX 38 | 39 | 40 | # The three functions below are here for debugging. They help to implement 41 | # the algorithms in a straightforward and inefficient manner to compare with. 42 | def left(X, i): 43 | """Compute the orthogonal matrix Q_{\leq i} as defined in [1].""" 44 | if i < 0: 45 | return np.ones([1, 1]) 46 | answ = np.ones([1, 1]) 47 | cores = tt.tensor.to_list(X) 48 | for dim in xrange(i+1): 49 | answ = np.tensordot(answ, cores[dim], 1) 50 | answ = reshape(answ, (-1, X.r[i+1])) 51 | return answ 52 | 53 | 54 | def right(X, i): 55 | """Compute the orthogonal matrix Q_{\geq i} as defined in [1].""" 56 | if i > X.d-1: 57 | return np.ones([1, 1]) 58 | answ = np.ones([1, 1]) 59 | cores = tt.tensor.to_list(X) 60 | for dim in xrange(X.d-1, i-1, -1): 61 | answ = np.tensordot(cores[dim], answ, 1) 62 | answ = reshape(answ, (X.r[i], -1)) 63 | return answ.T 64 | 65 | 66 | def unfolding(tens, i): 67 | """Compute the i-th unfolding of a tensor.""" 68 | return reshape(tens.full(), (np.prod(tens.n[0:(i+1)]), -1)) 69 | 70 | 71 | # The two functions below are for updating the rhs and lhs matrices fast in 72 | # the projection process. Do not put them inside the project function because 73 | # if you do, jit will compile them on each call making everything slow. 74 | @jit(nopython=True) 75 | def _update_lhs(lhs, xCore, zCore, new_lhs): 76 | """ Function to be called from the project()""" 77 | # TODO: Use intermediate variable to use 5 nested loops instead of 6. 78 | r_old_x, n, r_x = xCore.shape 79 | num_obj, r_old_z, n, r_z = zCore.shape 80 | for idx in range(num_obj): 81 | for val in range(n): 82 | for alpha_old_z in range(r_old_z): 83 | for alpha_z in range(r_z): 84 | for alpha_old_x in range(r_old_x): 85 | for alpha_x in range(r_x): 86 | curr_value = lhs[idx, alpha_old_x, alpha_old_z] 87 | curr_value *= xCore[alpha_old_x, val, alpha_x] 88 | curr_value *= zCore[idx, alpha_old_z, val, alpha_z] 89 | new_lhs[idx, alpha_x, alpha_z] += curr_value 90 | 91 | 92 | @jit(nopython=True) 93 | def _update_rhs(curr_rhs, xCore, zCore, new_rhs): 94 | """ Function to be called from the project()""" 95 | # TODO: Use intermediate variable to use 5 nested loops instead of 6. 96 | r_x, n, r_old_x = xCore.shape 97 | num_obj, r_z, n, r_old_z = zCore.shape 98 | for idx in range(num_obj): 99 | for val in range(n): 100 | for alpha_old_z in range(r_old_z): 101 | for alpha_z in range(r_z): 102 | for alpha_old_x in range(r_old_x): 103 | for alpha_x in range(r_x): 104 | curr_value = curr_rhs[idx, alpha_old_z, alpha_old_x] 105 | curr_value *= xCore[alpha_x, val, alpha_old_x] 106 | curr_value *= zCore[idx, alpha_z, val, alpha_old_z] 107 | new_rhs[idx, alpha_z, alpha_x] += curr_value 108 | 109 | 110 | def project(X, Z, use_jit=False, debug=False): 111 | """ Project tensor Z on the tangent space of tensor X. 112 | 113 | X is a tensor in the TT format. 114 | Z can be a tensor in the TT format or a list of tensors (in this case 115 | the function computes projection of the sum off all tensors in the list: 116 | project(X, Z) = P_X(\sum_i Z_i) 117 | ). 118 | This function implements an algorithm from the paper [1], theorem 3.1. 119 | The jit version of the code is much faster when projecting a lot of tensors 120 | simultaneously (in other words Z is a list with many tensors). 121 | 122 | Returns a tensor in the TT format with the TT-ranks equal 2 * rank(Z). 123 | """ 124 | zArr = None 125 | if isinstance(Z, tt.vector): 126 | zArr = [Z] 127 | else: 128 | zArr = Z 129 | 130 | # Get rid of redundant ranks (they cause technical difficulties). 131 | X = X.round(eps=0) 132 | 133 | numDims, modeSize = X.d, X.n 134 | coresX = tt.tensor.to_list(X) 135 | coresZ = [None] * len(zArr) 136 | for idx in xrange(len(zArr)): 137 | assert(modeSize == zArr[idx].n).all() 138 | coresZ[idx] = tt.tensor.to_list(zArr[idx]) 139 | 140 | if not use_jit and len(zArr) > 10: 141 | print('Consider using use_jit=True option to speed up the projection ' 142 | 'process.') 143 | if use_jit: 144 | for dim in xrange(numDims): 145 | r1, n, r2 = coresZ[0][dim].shape 146 | for idx in xrange(len(zArr)): 147 | if (r1, n, r2) != coresZ[idx][dim].shape: 148 | print('Warning: cannot use the jit version when not all ' 149 | 'the ranks in the Z array are equal each other. ' 150 | 'Switching to the non-jit version.') 151 | use_jit = False 152 | 153 | if use_jit: 154 | zCoresDim = [None] * numDims 155 | for dim in xrange(numDims): 156 | r1, n, r2 = coresZ[0][dim].shape 157 | zCoresDim[dim] = np.zeros([len(zArr), r1, n, r2]) 158 | for idx in xrange(len(zArr)): 159 | if (r1, n, r2) != coresZ[idx][dim].shape: 160 | print('Warning: cannot use the jit version when not all ' 161 | 'the ranks in the Z array are equal each other. ' 162 | 'Switching to the non-jit version.') 163 | use_jit = False 164 | zCoresDim[dim][idx, :, :, :] = coresZ[idx][dim] 165 | # Initialize the cores of the projection_X(sum z[i]). 166 | coresP = [] 167 | for dim in xrange(numDims): 168 | r1 = 2 * X.r[dim] 169 | r2 = 2 * X.r[dim+1] 170 | if dim == 0: 171 | r1 = 1 172 | if dim == numDims - 1: 173 | r2 = 1 174 | coresP.append(np.zeros((r1, modeSize[dim], r2))) 175 | # rhs[dim] is a len(zArr) x zArr[idx] x X.rank_dim.rank_dim ndarray. 176 | # Right to left orthogonalization of X and preparation of the rhs vectors. 177 | for dim in xrange(numDims-1, 0, -1): 178 | # Right to left orthogonalization of the X cores. 179 | coresX = cores_orthogonalization_step(coresX, dim, left_to_right=False) 180 | r1, n, r2 = coresX[dim].shape 181 | 182 | # Fill the right orthogonal part of the projection. 183 | for value in xrange(modeSize[dim]): 184 | coresP[dim][0:r1, value, 0:r2] = coresX[dim][:, value, :] 185 | 186 | rhs = [None] * (numDims+1) 187 | for dim in xrange(numDims): 188 | rhs[dim] = np.zeros([len(zArr), zArr[idx].r[dim], coresX[dim].shape[0]]) 189 | rhs[numDims] = np.ones([len(zArr), 1, 1]) 190 | 191 | for dim in xrange(numDims-1, 0, -1): 192 | _update_rhs(rhs[dim+1], coresX[dim], zCoresDim[dim], rhs[dim]) 193 | 194 | if debug: 195 | assert(np.allclose(X.full(), tt.tensor.from_list(coresX).full())) 196 | 197 | # lsh is a len(zArr) x X.rank_dim x zArr[idx].rank_dim ndarray. 198 | lhs = np.ones([len(zArr), 1, 1]) 199 | # Left to right sweep. 200 | for dim in xrange(numDims): 201 | cc = coresX[dim].copy() 202 | r1, n, r2 = cc.shape 203 | if dim < numDims-1: 204 | # Left to right orthogonalization. 205 | cc = reshape(cc, (-1, r2)) 206 | cc, rr = np.linalg.qr(cc) 207 | r2 = cc.shape[1] 208 | # Warning: since ranks can change here, do not use X.r! 209 | # Use coresX[dim].shape instead. 210 | if debug: 211 | # Need to do it before the move non orthogonal part rr to 212 | # the coresX[dim+1]. 213 | rightQ = right(tt.tensor.from_list(coresX), dim+1) 214 | coresX[dim] = reshape(cc, (r1, n, r2)).copy() 215 | coresX[dim+1] = np.tensordot(rr, coresX[dim+1], 1) 216 | 217 | new_lhs = np.zeros([len(zArr), r2, zArr[idx].r[dim+1]]) 218 | _update_lhs(lhs, coresX[dim], zCoresDim[dim], new_lhs) 219 | 220 | # See the correspondic section in the non-jit version of this 221 | # code for a less confusing implementation of 222 | # the transformation below. 223 | currPCore = np.einsum('ijk,iklm->ijlm', lhs, zCoresDim[dim]) 224 | currPCore = reshape(currPCore, (len(zArr), r1*n, -1)) 225 | currPCore -= np.einsum('ij,kjl->kil', cc, new_lhs) 226 | currPCore = np.einsum('ijk,ikl', currPCore, rhs[dim+1]) 227 | currPCore = reshape(currPCore, (r1, modeSize[dim], r2)) 228 | if dim == 0: 229 | coresP[dim][0:r1, :, 0:r2] += currPCore 230 | else: 231 | coresP[dim][r1:, :, 0:r2] += currPCore 232 | if debug: 233 | explicit_sum = np.zeros((r1, modeSize[dim], r2)) 234 | for idx in xrange(len(zArr)): 235 | leftQm1 = left(tt.tensor.from_list(coresX), dim-1) 236 | leftQ = left(tt.tensor.from_list(coresX), dim) 237 | 238 | first = np.tensordot(leftQm1.T, unfolding(zArr[idx], dim-1), 1) 239 | second = reshape(first, (-1, np.prod(modeSize[dim+1:]))) 240 | if dim < numDims-1: 241 | explicit = second.dot(rightQ) 242 | orth_cc = reshape(coresX[dim], (-1, coresX[dim].shape[2])) 243 | explicit -= orth_cc.dot(leftQ.T.dot(unfolding(zArr[idx], dim)).dot(rightQ)) 244 | else: 245 | explicit = second 246 | explicit_sum += reshape(explicit, currPCore.shape) 247 | assert(np.allclose(explicit_sum, currPCore)) 248 | lhs = new_lhs 249 | 250 | if dim == 0: 251 | coresP[dim][0:r1, :, r2:] = coresX[dim] 252 | else: 253 | coresP[dim][r1:, :, r2:] = coresX[dim] 254 | 255 | if dim == numDims-1: 256 | coresP[dim][r1:, :, 0:r2] += np.einsum('ijk,iklm->jlm', lhs, zCoresDim[dim]) 257 | 258 | if debug: 259 | assert(np.allclose(X.full(), tt.tensor.from_list(coresX).full())) 260 | return tt.tensor.from_list(coresP) 261 | else: 262 | # Non-jit version of the code. 263 | # Initialize the cores of the projection_X(sum z[i]). 264 | coresP = [] 265 | for dim in xrange(numDims): 266 | r1 = 2 * X.r[dim] 267 | r2 = 2 * X.r[dim+1] 268 | if dim == 0: 269 | r1 = 1 270 | if dim == numDims - 1: 271 | r2 = 1 272 | coresP.append(np.zeros((r1, modeSize[dim], r2))) 273 | # rhs[idx][dim] is an (Z.rank_dim * X.rank_dim) x 1 vector 274 | rhs = [[0] * (numDims+1) for _ in xrange(len(zArr))] 275 | for idx in xrange(len(zArr)): 276 | rhs[idx][numDims] = np.ones([1, 1]) 277 | # Right to left sweep to orthogonalize the cores and prepare rhs. 278 | for dim in xrange(numDims-1, 0, -1): 279 | # Right to left orthogonalization of the X cores. 280 | coresX = cores_orthogonalization_step(coresX, dim, left_to_right=False) 281 | r1, n, r2 = coresX[dim].shape 282 | 283 | # Fill the right orthogonal part of the projection. 284 | coresP[dim][0:r1, :, 0:r2] = coresX[dim] 285 | # Compute rhs. 286 | for idx in xrange(len(zArr)): 287 | coreProd = np.tensordot(coresZ[idx][dim], coresX[dim], axes=(1, 1)) 288 | coreProd = np.transpose(coreProd, (0, 2, 1, 3)) 289 | coreProd = reshape(coreProd, (zArr[idx].r[dim]*r1, zArr[idx].r[dim+1]*r2)) 290 | rhs[idx][dim] = np.dot(coreProd, rhs[idx][dim+1]) 291 | if debug: 292 | assert(np.allclose(X.full(), tt.tensor.from_list(coresX).full())) 293 | 294 | # lsh[idx] is an X.rank_dim x zArr[idx].rank_dim matrix. 295 | lhs = [np.ones([1, 1]) for _ in xrange(len(zArr))] 296 | # Left to right sweep. 297 | for dim in xrange(numDims - 1): 298 | if debug: 299 | rightQ = right(tt.tensor.from_list(coresX), dim+1) 300 | # Left to right orthogonalization of the X cores. 301 | coresX = cores_orthogonalization_step(coresX, dim, left_to_right=True) 302 | r1, n, r2 = coresX[dim].shape 303 | cc = reshape(coresX[dim], (-1, r2)) 304 | 305 | for idx in xrange(len(zArr)): 306 | currZCore = reshape(coresZ[idx][dim], (zArr[idx].r[dim], -1)) 307 | currPCore = np.dot(lhs[idx], currZCore) 308 | 309 | # TODO: consider using np.einsum. 310 | coreProd = np.tensordot(coresX[dim], coresZ[idx][dim], axes=(1, 1)) 311 | coreProd = np.transpose(coreProd, (0, 2, 1, 3)) 312 | coreProd = reshape(coreProd, (r1*zArr[idx].r[dim], r2*zArr[idx].r[dim+1])) 313 | lhs[idx] = reshape(lhs[idx], (1, -1)) 314 | lhs[idx] = np.dot(lhs[idx], coreProd) 315 | lhs[idx] = reshape(lhs[idx], (r2, zArr[idx].r[dim+1])) 316 | 317 | currPCore = reshape(currPCore, (-1, zArr[idx].r[dim+1])) 318 | currPCore -= np.dot(cc, lhs[idx]) 319 | rhs[idx][dim+1] = reshape(rhs[idx][dim+1], (zArr[idx].r[dim+1], r2)) 320 | currPCore = np.dot(currPCore, rhs[idx][dim+1]) 321 | currPCore = reshape(currPCore, (r1, modeSize[dim], r2)) 322 | if dim == 0: 323 | coresP[dim][0:r1, :, 0:r2] += currPCore 324 | else: 325 | coresP[dim][r1:, :, 0:r2] += currPCore 326 | 327 | if debug: 328 | leftQm1 = left(tt.tensor.from_list(coresX), dim-1) 329 | leftQ = left(tt.tensor.from_list(coresX), dim) 330 | 331 | first = np.tensordot(leftQm1.T, unfolding(zArr[idx], dim-1), 1) 332 | second = reshape(first, (-1, np.prod(modeSize[dim+1:]))) 333 | if dim < numDims-1: 334 | explicit = second.dot(rightQ) 335 | orth_cc = reshape(coresX[dim], (-1, coresX[dim].shape[2])) 336 | explicit -= orth_cc.dot(leftQ.T.dot(unfolding(zArr[idx], dim)).dot(rightQ)) 337 | else: 338 | explicit = second 339 | explicit = reshape(explicit, currPCore.shape) 340 | assert(np.allclose(explicit, currPCore)) 341 | 342 | if dim == 0: 343 | coresP[dim][0:r1, :, r2:] = coresX[dim] 344 | else: 345 | coresP[dim][r1:, :, r2:] = coresX[dim] 346 | 347 | for idx in xrange(len(zArr)): 348 | r1, n, r2 = coresX[numDims-1].shape 349 | currZCore = reshape(coresZ[idx][numDims-1], (zArr[idx].r[numDims-1], -1)) 350 | currPCore = np.dot(lhs[idx], currZCore) 351 | currPCore = reshape(currPCore, (r1, n, r2)) 352 | coresP[numDims-1][r1:, :, 0:r2] += currPCore 353 | 354 | if debug: 355 | assert(np.allclose(X.full(), tt.tensor.from_list(coresX).full())) 356 | return tt.tensor.from_list(coresP) 357 | 358 | 359 | def projector_splitting_add(Y, delta, debug=False): 360 | """Compute Y + delta via the projector splitting scheme. 361 | 362 | This function implements the projector splitting scheme (section 4.2 of [1]). 363 | 364 | The result is a TT-tensor with the TT-ranks equal to the TT-ranks of Y.""" 365 | # Get rid of redundant ranks (they cause technical difficulties). 366 | delta = delta.round(eps=0) 367 | numDims = delta.d 368 | assert(numDims == Y.d) 369 | modeSize = delta.n 370 | assert(modeSize == Y.n).all() 371 | coresDelta = tt.tensor.to_list(delta) 372 | coresY = tt.tensor.to_list(Y) 373 | # rhs[dim] is an (delta.rank_dim * Y.rank_dim) x 1 vector 374 | rhs = [None] * (numDims+1) 375 | rhs[numDims] = np.ones([1, 1]) 376 | # Right to left sweep to orthogonalize the cores and prepare the rhs. 377 | for dim in xrange(numDims-1, 0, -1): 378 | # Right to left orthogonalization of the Y cores. 379 | coresY = cores_orthogonalization_step(coresY, dim, left_to_right=False) 380 | r1, n, r2 = coresY[dim].shape 381 | 382 | # rhs computation. 383 | coreProd = np.tensordot(coresDelta[dim], coresY[dim], axes=(1, 1)) 384 | coreProd = np.transpose(coreProd, (0, 2, 1, 3)) 385 | coreProd = reshape(coreProd, (delta.r[dim]*r1, delta.r[dim+1]*r2)) 386 | rhs[dim] = np.dot(coreProd, rhs[dim+1]) 387 | if debug: 388 | assert(np.allclose(Y.full(), tt.tensor.from_list(coresY).full())) 389 | 390 | # lsh is an Y.rank_dim x delta.rank_dim matrix. 391 | lhs = np.ones([1, 1]) 392 | # s is an Y.rank_dim x Y.rank_dim matrix. 393 | s = np.ones([1, 1]) 394 | # Left to right projector splitting sweep. 395 | for dim in xrange(numDims): 396 | # Y^+ (formula 4.10) 397 | cc = coresDelta[dim].copy() 398 | r1, n, r2 = coresY[dim].shape 399 | cc = np.tensordot(lhs, cc, 1) 400 | rhs[dim+1] = reshape(rhs[dim+1], (delta.r[dim+1], r2)) 401 | cc = reshape(cc, (-1, delta.r[dim+1])) 402 | cc = np.dot(cc, rhs[dim+1]) 403 | if debug: 404 | first = np.kron(np.eye(modeSize[dim]), left(tt.tensor.from_list(coresY), dim-1).T) 405 | second = np.dot(first, unfolding(delta, dim)) 406 | explicit = np.dot(second, right(tt.tensor.from_list(coresY), dim+1)) 407 | assert(np.allclose(explicit, cc)) 408 | cc += reshape(np.tensordot(s, coresY[dim], 1), (-1, Y.r[dim+1])) 409 | if dim < numDims-1: 410 | cc, rr = np.linalg.qr(cc) 411 | # TODO: do we need to use r1 = cc.shape[1] here???? 412 | cc = reshape(cc, coresY[dim].shape) 413 | coresY[dim] = cc.copy() 414 | 415 | if dim < numDims-1: 416 | coreProd = np.tensordot(coresY[dim], coresDelta[dim], axes=(1, 1)) 417 | coreProd = np.transpose(coreProd, (0, 2, 1, 3)) 418 | coreProd = reshape(coreProd, (r1*delta.r[dim], r2*delta.r[dim+1])) 419 | lhs = reshape(lhs, (1, -1)) 420 | lhs = np.dot(lhs, coreProd) 421 | lhs = reshape(lhs, (r2, delta.r[dim+1])) 422 | 423 | if dim < numDims-1: 424 | # Y^- (formula 4.7) 425 | s = rr - np.dot(lhs, rhs[dim+1]) 426 | if debug: 427 | first = left(tt.tensor.from_list(coresY), dim).T 428 | second = np.dot(first, unfolding(delta, dim)) 429 | explicit = np.dot(second, right(tt.tensor.from_list(coresY), dim+1)) 430 | assert(np.allclose(explicit, np.dot(lhs, rhs[dim+1]))) 431 | 432 | return tt.tensor.from_list(coresY) 433 | 434 | 435 | def tt_qr(X, left_to_right=True): 436 | """ 437 | Orthogonalizes a TT tensor from left to right or 438 | from right to left. 439 | :param: X - thensor to orthogonalise 440 | :param: direction - direction. May be 'lr/LR' or 'rl/RL' 441 | for left/right orthogonalization 442 | :return: X_orth, R - orthogonal tensor and right (left) 443 | upper (lower) triangular matrix 444 | 445 | >>> import tt, numpy as np 446 | >>> x = tt.rand(np.array([2, 3, 4, 5]), d=4) 447 | >>> x_q, r = tt_qr(x, left_to_right=True) 448 | >>> np.allclose((rm[0][0]*x_q).norm(), x.norm()) 449 | True 450 | >>> x_u, l = tt_qr(x, left_to_right=False) 451 | >>> np.allclose((l[0][0]*x_u).norm(), x.norm()) 452 | True 453 | """ 454 | # Get rid of redundant ranks (they cause technical difficulties). 455 | X = X.round(eps=0) 456 | numDims = X.d 457 | coresX = tt.tensor.to_list(X) 458 | 459 | if left_to_right: 460 | # Left to right orthogonalization of the X cores. 461 | for dim in xrange(0, numDims-1): 462 | coresX = cores_orthogonalization_step( 463 | coresX, dim, left_to_right=left_to_right) 464 | last_core = coresX[numDims-1] 465 | r1, n, r2 = last_core.shape 466 | last_core, rr = np.linalg.qr(reshape(last_core, (-1, r2))) 467 | coresX[numDims-1] = reshape(last_core, (r1, n, -1)) 468 | else: 469 | # Right to left orthogonalization of X cores 470 | for dim in xrange(numDims-1, 0, -1): 471 | coresX = cores_orthogonalization_step( 472 | coresX, dim, left_to_right=left_to_right) 473 | last_core = coresX[0] 474 | r1, n, r2 = last_core.shape 475 | last_core, rr = np.linalg.qr( 476 | np.transpose(reshape(last_core, (r1, -1))) 477 | ) 478 | coresX[0] = reshape( 479 | np.transpose(last_core), 480 | (-1, n, r2)) 481 | rr = np.transpose(rr) 482 | 483 | return tt.tensor.from_list(coresX), rr 484 | -------------------------------------------------------------------------------- /tt/riemannian/riemannian_test.py: -------------------------------------------------------------------------------- 1 | import random 2 | import unittest 3 | import copy 4 | import numpy as np 5 | import tt 6 | from copy import deepcopy 7 | import riemannian 8 | 9 | 10 | class TestTTLearning(unittest.TestCase): 11 | 12 | def setUp(self): 13 | # Reproducibility. 14 | random.seed(0) 15 | np.random.seed(2) 16 | 17 | def test_projector_splitting_add(self): 18 | for debug_mode in [False, True]: 19 | Y = tt.rand([5, 2, 3], 3, [1, 2, 3, 1]) 20 | my_res = riemannian.projector_splitting_add(Y.copy(), Y.copy(), 21 | debug=debug_mode) 22 | np.testing.assert_array_almost_equal(2 * Y.full(), my_res.full()) 23 | 24 | def test_project(self): 25 | def random_tanget_space_point(X): 26 | coresX = tt.tensor.to_list(X) 27 | point = 0 * tt.ones(X.n) 28 | for dim in range(X.d): 29 | curr = deepcopy(coresX) 30 | curr[dim] = np.random.rand(curr[dim].shape[0], 31 | curr[dim].shape[1], 32 | curr[dim].shape[2]) 33 | point += tt.tensor.from_list(curr) 34 | return point 35 | 36 | for debug_mode in [False, True]: 37 | for use_jit in [False, True]: 38 | X = tt.rand([4, 4, 4], 3, [1, 4, 4, 1]) 39 | Z = random_tanget_space_point(X) 40 | PZ = riemannian.project(X, Z, use_jit=use_jit, 41 | debug=debug_mode) 42 | np.testing.assert_array_almost_equal(Z.full(), PZ.full()) 43 | 44 | X = tt.rand([2, 3, 4], 3, [1, 5, 4, 1]) 45 | Z = random_tanget_space_point(X) 46 | PZ = riemannian.project(X, Z, use_jit=use_jit, 47 | debug=debug_mode) 48 | np.testing.assert_array_almost_equal(Z.full(), PZ.full()) 49 | 50 | def test_project_sum_equal_ranks(self): 51 | for debug_mode in [False, True]: 52 | for use_jit in [False, True]: 53 | X = tt.rand([4, 4, 4], 3, [1, 4, 4, 1]) 54 | Z = [0] * 7 55 | for idx in range(7): 56 | Z[idx] = tt.rand([4, 4, 4], 3, [1, 2, 3, 1]) 57 | project_sum = riemannian.project(X, Z, use_jit=use_jit, 58 | debug=debug_mode) 59 | 60 | sum_project = X * 0 61 | for idx in range(len(Z)): 62 | sum_project += riemannian.project(X, Z[idx], 63 | use_jit=use_jit, 64 | debug=debug_mode) 65 | np.testing.assert_array_almost_equal(sum_project.full(), 66 | project_sum.full()) 67 | 68 | def test_project_sum_different_ranks(self): 69 | for debug_mode in [False, True]: 70 | for use_jit in [False, True]: 71 | X = tt.rand([4, 4, 4], 3, [1, 4, 4, 1]) 72 | Z = [0] * 7 73 | Z[0] = tt.rand([4, 4, 4], 3, [1, 4, 4, 1]) 74 | Z[1] = tt.rand([4, 4, 4], 3, [1, 4, 3, 1]) 75 | Z[2] = tt.rand([4, 4, 4], 3, [1, 2, 3, 1]) 76 | for idx in range(3, 7): 77 | Z[idx] = tt.rand([4, 4, 4], 3, [1, 2, 2, 1]) 78 | project_sum = riemannian.project(X, Z, use_jit=use_jit, 79 | debug=debug_mode) 80 | 81 | sum_project = X * 0 82 | for idx in range(len(Z)): 83 | sum_project += riemannian.project(X, Z[idx], 84 | use_jit=use_jit, 85 | debug=debug_mode) 86 | np.testing.assert_array_almost_equal(sum_project.full(), 87 | project_sum.full()) 88 | 89 | if __name__ == '__main__': 90 | unittest.main() 91 | -------------------------------------------------------------------------------- /tt/riemannian/setup.py: -------------------------------------------------------------------------------- 1 | # setup.py 2 | # This script will build the main subpackages 3 | # See LICENSE for details 4 | 5 | from __future__ import print_function, absolute_import 6 | from numpy.distutils.misc_util import Configuration 7 | 8 | 9 | def configuration(parent_package='', top_path=None): 10 | config = Configuration('riemannian', parent_package, top_path) 11 | return config 12 | 13 | 14 | if __name__ == '__main__': 15 | print('This is the wrong setup.py to run') 16 | -------------------------------------------------------------------------------- /tt/setup.py: -------------------------------------------------------------------------------- 1 | # setup.py 2 | # This script will build the main subpackages 3 | # See LICENSE for details 4 | 5 | from __future__ import print_function, absolute_import 6 | 7 | import sys 8 | 9 | from os.path import join 10 | from sys import version_info 11 | 12 | from distutils.util import get_platform 13 | from numpy.distutils.misc_util import Configuration 14 | 15 | from tt.distutils import get_extra_fflags 16 | 17 | 18 | TTFORT_DIR = 'tt-fort' 19 | TTFORT_SRC = [ 20 | 'nan.f90', 21 | 'default.f90', 22 | 'timef.f90', 23 | 'say.f90', 24 | 'rnd.f90', 25 | 'ptype.f90', 26 | 'sort.f90', 27 | 'trans.f90', 28 | 'ort.f90', 29 | 'mat.f90', 30 | 'check.f90', 31 | 'lr.f90', 32 | 'maxvol.f90', 33 | 'svd.f90', 34 | 'matrix_util.f90', 35 | 'tt.f90', 36 | 'ttaux.f90', 37 | 'ttop.f90', 38 | 'ttio.f90', 39 | 'tts.f90', 40 | 'python_conv.f90', 41 | 'tt_linalg.f90', 42 | 'ttlocsolve.f90', 43 | 'ttnodeop.f90', 44 | 'ttamen.f90', 45 | ] 46 | 47 | PRINT_DIR = 'tt-fort/print' 48 | PRINT_SRC = [ 49 | 'putstrmodule.F90', 50 | 'dispmodule.f90', 51 | ] 52 | 53 | 54 | def configuration(parent_package='', top_path=None): 55 | try: 56 | cache_tag = sys.implementation.cache_tag 57 | except AttributeError: 58 | cache_tag = None 59 | platform_tag = get_platform() 60 | version_tag = '%s.%s' % version_info[:2] 61 | 62 | # In Python 3.10.6 the way how platform specifier is defined had been 63 | # changed. First, Python version were used in platform spec but then it was 64 | # replaced with Python implementation name and version (e.g. cpython-310). 65 | include_dirs = [] 66 | for tag in filter(None, [cache_tag, version_tag]): 67 | plat_specifier = '.%s-%s' % (platform_tag, tag) 68 | include_dirs.append('build/temp' + plat_specifier) 69 | 70 | config = Configuration('tt', parent_package, top_path) 71 | config.add_include_dirs(include_dirs) 72 | config.set_options( 73 | ignore_setup_xxx_py=True, 74 | assume_default_configuration=True, 75 | delegate_options_to_subpackages=True, 76 | quiet=False, 77 | ) 78 | 79 | config.add_library('print_lib', sources=[join(PRINT_DIR, x) for x in PRINT_SRC]) 80 | config.add_library(name='mytt', 81 | sources=[join(TTFORT_DIR, x) for x in TTFORT_SRC], 82 | extra_f90_compile_args=get_extra_fflags()) 83 | 84 | config.add_subpackage('core') 85 | config.add_subpackage('amen') 86 | config.add_subpackage('ksl') 87 | config.add_subpackage('eigb') 88 | config.add_subpackage('maxvol') 89 | config.add_subpackage('cross') 90 | config.add_subpackage('optimize') 91 | config.add_subpackage('utils') 92 | config.add_subpackage('riemannian') 93 | 94 | return config 95 | 96 | 97 | if __name__ == '__main__': 98 | print('This is the wrong setup.py to run') 99 | -------------------------------------------------------------------------------- /tt/solvers.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import, division 2 | import numpy as np 3 | import tt 4 | 5 | import scipy.linalg as la 6 | 7 | # TT-GMRES 8 | 9 | 10 | def GMRES(A, u_0, b, eps=1e-6, maxit=100, m=20, _iteration=0, callback=None, verbose=0): 11 | """ 12 | Flexible TT GMRES 13 | :param A: matvec(x[, eps]) 14 | :param u_0: initial vector 15 | :param b: answer 16 | :param maxit: max number of iterations 17 | :param eps: required accuracy 18 | :param m: number of iteration without restart 19 | :param _iteration: iteration counter 20 | :param callback: 21 | :param verbose: to print debug info or not 22 | :return: answer, residual 23 | 24 | >>> from tt import GMRES 25 | >>> def matvec(x, eps): 26 | >>> return tt.matvec(S, x).round(eps) 27 | >>> answer, res = GMRES(matvec, u_0, b, eps=1e-8) 28 | """ 29 | maxitexceeded = False 30 | converged = False 31 | 32 | if verbose: 33 | print('GMRES(m=%d, _iteration=%d, maxit=%d)' % (m, _iteration, maxit)) 34 | v = np.ones((m + 1), dtype=object) * np.nan 35 | R = np.ones((m, m)) * np.nan 36 | g = np.zeros(m) 37 | s = np.ones(m) * np.nan 38 | c = np.ones(m) * np.nan 39 | v[0] = b - A(u_0, eps=eps) 40 | v[0] = v[0].round(eps) 41 | resnorm = v[0].norm() 42 | curr_beta = resnorm 43 | bnorm = b.norm() 44 | wlen = resnorm 45 | q = m 46 | for j in range(m): 47 | _iteration += 1 48 | 49 | delta = eps / (curr_beta / resnorm) 50 | 51 | if verbose: 52 | print("it = %d delta = " % _iteration, delta) 53 | 54 | v[j] *= 1.0 / wlen 55 | v[j + 1] = A(v[j], eps=delta) 56 | for i in range(j + 1): 57 | R[i, j] = tt.dot(v[j + 1], v[i]) 58 | v[j + 1] = v[j + 1] - R[i, j] * v[i] 59 | v[j + 1] = v[j + 1].round(delta) 60 | 61 | wlen = v[j + 1].norm() 62 | for i in range(j): 63 | r1 = R[i, j] 64 | r2 = R[i + 1, j] 65 | R[i, j] = c[i] * r1 - s[i] * r2 66 | R[i + 1, j] = c[i] * r2 + s[i] * r1 67 | denom = np.hypot(wlen, R[j, j]) 68 | s[j] = wlen / denom 69 | c[j] = -R[j, j] / denom 70 | R[j, j] = -denom 71 | 72 | g[j] = c[j] * curr_beta 73 | curr_beta *= s[j] 74 | 75 | if verbose: 76 | print("it = {}, ||r|| = {}".format(_iteration, curr_beta / bnorm)) 77 | 78 | converged = (curr_beta / bnorm) < eps or (curr_beta / resnorm) < eps 79 | maxitexceeded = _iteration >= maxit 80 | if converged or maxitexceeded: 81 | q = j + 1 82 | break 83 | 84 | y = la.solve_triangular(R[:q, :q], g[:q], check_finite=False) 85 | for idx in range(q): 86 | u_0 += v[idx] * y[idx] 87 | 88 | u_0 = u_0.round(eps) 89 | 90 | if callback is not None: 91 | callback(u_0) 92 | 93 | if converged or maxitexceeded: 94 | return u_0, resnorm / bnorm 95 | return GMRES(A, u_0, b, eps, maxit, m, _iteration, callback=callback, verbose=verbose) 96 | -------------------------------------------------------------------------------- /tt/tests/test_vector.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import, division 2 | import unittest 3 | import tt 4 | import numpy as np 5 | 6 | class TestVector(unittest.TestCase): 7 | def test_qtt_vector(self): 8 | d = 10 9 | eps = 1e-14 10 | a = np.arange(2**d) 11 | v = tt.reshape(tt.vector(a), [2]*d, eps=eps) 12 | r = v.r 13 | self.assertEqual(r.dtype, np.int32, 'Vector ranks are not integers, expected int32') 14 | self.assertTrue((r[1:-1] == 2).all(), 'This vector ranks should be exactly 2') 15 | b = v.full().reshape(-1, order='F') 16 | self.assertTrue(np.linalg.norm(a - b) < 10 * 2**d * eps, 'The approximation error is too large') 17 | 18 | def test_assembly(self): 19 | # Test direct construction of tt.vector from specified kernels 20 | d = 10 21 | h = [None] * d 22 | h[0] = np.zeros((1, 2, 2)) 23 | h[-1] = np.zeros((2, 2, 1)) 24 | 25 | h[0][:, 0, :] = [[0, 1]] 26 | h[0][:, 1, :] = [[1, 1]] 27 | 28 | for i in range(1, d-1): 29 | h[i] = np.zeros((2, 2, 2)) 30 | h[i][:, 0, :] = np.eye(2) 31 | h[i][:, 1, :] = [[1, 0], [2**i, 1]] 32 | 33 | h[-1][:, 0, :] = [[1], [0]] 34 | h[-1][:, 1, :] = [[1], [2**(d-1)]] 35 | 36 | v = tt.vector.from_list(h) 37 | a = v.full().reshape(-1, order='F') 38 | b = np.arange(2**d) 39 | self.assertTrue(np.linalg.norm(a - b) < 1e-15, 'The approximation error is too large') 40 | -------------------------------------------------------------------------------- /tt/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oseledets/ttpy/22dff3d2cf52b4b23aae40119f1eec39675db9ef/tt/utils/__init__.py -------------------------------------------------------------------------------- /tt/utils/setup.py: -------------------------------------------------------------------------------- 1 | # setup.py 2 | # This script will build the main subpackages 3 | # See LICENSE for details 4 | 5 | from __future__ import print_function, absolute_import 6 | from numpy.distutils.misc_util import Configuration 7 | 8 | 9 | def configuration(parent_package='', top_path=None): 10 | config = Configuration('utils', parent_package, top_path) 11 | config.add_subpackage('rect_maxvol') 12 | return config 13 | 14 | 15 | if __name__ == '__main__': 16 | print('This is the wrong setup.py to run') 17 | --------------------------------------------------------------------------------