├── docs ├── vectfit.rst ├── index.rst ├── make.bat ├── Makefile └── conf.py ├── .gitignore ├── .travis.yml ├── src ├── vectfit.h └── vectfit.cpp ├── LICENSE.md ├── tools └── ci │ └── travis-install.sh ├── setup.py ├── README.md ├── tests └── test_vectfit.py └── test_run.ipynb /docs/vectfit.rst: -------------------------------------------------------------------------------- 1 | vectfit 2 | ========================= 3 | 4 | .. automodule:: vectfit 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.egg-info/ 2 | .ipynb_checkpoints/ 3 | dist/ 4 | build/ 5 | *.py[cod] 6 | .pytest_cache 7 | _build 8 | _generate 9 | tmp 10 | 11 | # OS X 12 | .DS_Store 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | 2 | sudo: required 3 | dist: xenial 4 | language: python 5 | addons: 6 | apt: 7 | packages: 8 | - libblas-dev 9 | - liblapack-dev 10 | config: 11 | retries: true 12 | matrix: 13 | include: 14 | - python: "3.7" 15 | install: 16 | - ./tools/ci/travis-install.sh 17 | script: 18 | - py.test . 19 | -------------------------------------------------------------------------------- /src/vectfit.h: -------------------------------------------------------------------------------- 1 | #ifndef VECTFIT_H 2 | #define VECTFIT_H 3 | 4 | #include 5 | #include "xtensor/xarray.hpp" 6 | #include "xtensor-python/pyarray.hpp" // Numpy bindings 7 | 8 | //! Fast Relaxed Vector Fitting function 9 | std::tuple>, 10 | xt::pyarray>, 11 | xt::pyarray, 12 | xt::pyarray, 13 | double> 14 | vectfit(xt::pyarray &f, 15 | xt::pyarray &s, 16 | xt::pyarray> &poles, 17 | xt::pyarray &weight, 18 | int n_polys = 0, 19 | bool skip_pole = false, 20 | bool skip_res = false); 21 | 22 | //! Multipole formalism evaluation function 23 | xt::pyarray 24 | evaluate(xt::pyarray s, 25 | xt::pyarray> poles, 26 | xt::pyarray> residues, 27 | xt::pyarray polys = (xt::pyarray) {}); 28 | 29 | #endif // VECTFIT_H 30 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jingang Liang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tools/ci/travis-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | PYBIND_BRANCH='master' 5 | PYBIND_REPO='https://github.com/pybind/pybind11' 6 | 7 | XTL_BRANCH='0.6.9' 8 | XTL_REPO='https://github.com/xtensor-stack/xtl' 9 | 10 | XTENSOR_BRANCH='0.21.2' 11 | XTENSOR_REPO='https://github.com/xtensor-stack/xtensor' 12 | 13 | XTENSOR_PYTHON_BRANCH='0.24.1' 14 | XTENSOR_PYTHON_REPO='https://github.com/xtensor-stack/xtensor-python' 15 | 16 | XTENSOR_BLAS_BRANCH='0.17.1' 17 | XTENSOR_BLAS_REPO='https://github.com/xtensor-stack/xtensor-blas' 18 | 19 | 20 | cd $HOME 21 | git clone -b $PYBIND_BRANCH $PYBIND_REPO 22 | cd pybind11 && mkdir build && cd build && cmake .. && sudo make install 23 | pip install $HOME/pybind11 24 | 25 | cd $HOME 26 | git clone -b $XTL_BRANCH $XTL_REPO 27 | cd xtl && mkdir build && cd build && cmake .. && sudo make install 28 | 29 | cd $HOME 30 | git clone -b $XTENSOR_BRANCH $XTENSOR_REPO 31 | cd xtensor && mkdir build && cd build && cmake .. && sudo make install 32 | 33 | cd $HOME 34 | git clone -b $XTENSOR_PYTHON_BRANCH $XTENSOR_PYTHON_REPO 35 | cd xtensor-python && mkdir build && cd build && cmake .. && sudo make install 36 | 37 | cd $HOME 38 | git clone -b $XTENSOR_BLAS_BRANCH $XTENSOR_BLAS_REPO 39 | cd xtensor-blas && mkdir build && cd build && cmake .. && sudo make install 40 | 41 | # Install vectfit 42 | cd $HOME 43 | git clone https://github.com/liangjg/vectfit.git 44 | pip install ./vectfit 45 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | vectfit 2 | ========= 3 | 4 | This implements the Fast Relaxed Vector Fitting algorithm in C++. 5 | 6 | The `vectfit` function approximates a response :math:`\mathbf{f}(s)` (generally 7 | a vector) with a rational function: 8 | 9 | .. math:: 10 | :label: vf 11 | 12 | \mathbf{f}(s) \approx \sum_{j=1}^{N} \frac{\mathbf{r}_j}{s-p_j} + \sum_{n=0}^{Nc} \mathbf{c}_n s^{n} 13 | 14 | where :math:`p_j` and :math:`r_j` are poles and residues in the complex plane 15 | and :math:`r_j` are the polynomial coefficients. 16 | 17 | The identification is done using the pole relocating method known as Vector 18 | Fitting [1]_ with relaxed non-triviality constraint for faster convergence 19 | and smaller fitting errors [2]_, and utilization of matrix structure for fast 20 | solution of the pole identifion step [3]_. 21 | 22 | More details about the algorithm can be found in this website_. 23 | 24 | 25 | Contents: 26 | 27 | .. toctree:: 28 | :maxdepth: 1 29 | 30 | vectfit 31 | 32 | 33 | .. [1] B. Gustavsen and A. Semlyen, "Rational approximation of frequency 34 | domain responses by Vector Fitting", IEEE Trans. Power Delivery, 35 | vol. 14, no. 3, pp. 1052-1061, July 1999. 36 | 37 | .. [2] B. Gustavsen, "Improving the pole relocating properties of vector 38 | fitting", IEEE Trans. Power Delivery, vol. 21, no. 3, pp. 1587-1592, 39 | July 2006. 40 | 41 | .. [3] D. Deschrijver, M. Mrozowski, T. Dhaene, and D. De Zutter, 42 | "Macromodeling of Multiport Systems Using a Fast Implementation of 43 | the Vector Fitting Method", IEEE Microwave and Wireless Components 44 | Letters, vol. 18, no. 6, pp. 383-385, June 2008. 45 | 46 | .. _website: http://www.sintef.no/Projectweb/VECTFIT 47 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, Extension 2 | from setuptools.command.build_ext import build_ext 3 | import sys 4 | import os 5 | import setuptools 6 | 7 | __version__ = '0.1' 8 | 9 | 10 | class get_pybind_include(object): 11 | """Helper class to determine the pybind11 include path 12 | 13 | The purpose of this class is to postpone importing pybind11 14 | until it is actually installed, so that the ``get_include()`` 15 | method can be invoked. """ 16 | 17 | def __init__(self, user=False): 18 | self.user = user 19 | 20 | def __str__(self): 21 | import pybind11 22 | return pybind11.get_include(self.user) 23 | 24 | 25 | class get_numpy_include(object): 26 | """Helper class to determine the numpy include path 27 | 28 | The purpose of this class is to postpone importing numpy 29 | until it is actually installed, so that the ``get_include()`` 30 | method can be invoked. """ 31 | 32 | def __init__(self): 33 | pass 34 | 35 | def __str__(self): 36 | import numpy as np 37 | return np.get_include() 38 | 39 | 40 | ext_modules = [ 41 | Extension( 42 | 'vectfit', 43 | ['src/vectfit.cpp'], 44 | include_dirs=[ 45 | # Path to pybind11 headers 46 | get_pybind_include(), 47 | get_pybind_include(user=True), 48 | get_numpy_include(), 49 | os.path.join(sys.prefix, 'include'), 50 | os.path.join(sys.prefix, 'Library', 'include') 51 | ], 52 | libraries = ['lapack', 'blas'], 53 | language='c++' 54 | ), 55 | ] 56 | 57 | 58 | def has_flag(compiler, flagname): 59 | """Return a boolean indicating whether a flag name is supported on 60 | the specified compiler. 61 | """ 62 | import tempfile 63 | with tempfile.NamedTemporaryFile('w', suffix='.cpp') as f: 64 | f.write('int main (int argc, char **argv) { return 0; }') 65 | try: 66 | compiler.compile([f.name], extra_postargs=[flagname]) 67 | except setuptools.distutils.errors.CompileError: 68 | return False 69 | return True 70 | 71 | 72 | def cpp_flag(compiler): 73 | """Return the -std=c++14 compiler flag and errors when the flag is 74 | no available. 75 | """ 76 | if has_flag(compiler, '-std=c++14'): 77 | return '-std=c++14' 78 | else: 79 | raise RuntimeError('C++14 support is required by xtensor!') 80 | 81 | 82 | class BuildExt(build_ext): 83 | """A custom build extension for adding compiler-specific options.""" 84 | c_opts = { 85 | 'msvc': ['/EHsc'], 86 | 'unix': [], 87 | } 88 | 89 | if sys.platform == 'darwin': 90 | c_opts['unix'] += ['-stdlib=libc++', '-mmacosx-version-min=10.7'] 91 | 92 | def build_extensions(self): 93 | ct = self.compiler.compiler_type 94 | opts = self.c_opts.get(ct, []) 95 | if ct == 'unix': 96 | opts.append('-DVERSION_INFO="%s"' % self.distribution.get_version()) 97 | opts.append(cpp_flag(self.compiler)) 98 | if has_flag(self.compiler, '-fvisibility=hidden'): 99 | opts.append('-fvisibility=hidden') 100 | elif ct == 'msvc': 101 | opts.append('/DVERSION_INFO=\\"%s\\"' % self.distribution.get_version()) 102 | opts.append('-O2') 103 | for ext in self.extensions: 104 | ext.extra_compile_args = opts 105 | build_ext.build_extensions(self) 106 | 107 | with open('README.md') as f: 108 | long_description = f.read() 109 | 110 | setup( 111 | name='vectfit', 112 | version=__version__, 113 | author='Jingang Liang', 114 | author_email='liangjg2008@gmail.com', 115 | url='https://github.com/mit-crpg/vectfit', 116 | description= 'Fast Relaxed Vector Fitting Implementation in C++', 117 | long_description=long_description, 118 | ext_modules=ext_modules, 119 | install_requires=['pybind11>=2.0.1', 'numpy'], 120 | cmdclass={'build_ext': BuildExt}, 121 | zip_safe=False, 122 | ) 123 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fast Relaxed Vector Fitting Implementation in C++ 2 | 3 | [![Travis CI build status](https://travis-ci.org/liangjg/vectfit)](https://travis-ci.org/liangjg/vectfit) 4 | 5 | The `vectfit` function approximates a response ![fs](https://latex.codecogs.com/gif.latex?\mathbf{f}(s)) 6 | (generally a vector) with a rational function: 7 | 8 | ![vf](https://latex.codecogs.com/gif.latex?\mathbf{f}(s)&space;\approx&space;\sum_{j=1}^{N}&space;\frac{\mathbf{r}_j}{s-p_j}&space;+&space;\sum_{n=0}^{Nc}&space;\mathbf{c}_n&space;s^{n}) 9 | 10 | where ![pj](https://latex.codecogs.com/gif.latex?p_j) and 11 | ![rj](https://latex.codecogs.com/gif.latex?r_j) are poles and residues in 12 | the complex plane and ![cn](https://latex.codecogs.com/gif.latex?c_n) are 13 | the polynomial coefficients. 14 | When ![fs](https://latex.codecogs.com/gif.latex?\mathbf{f}(s)) is a vector, all 15 | elements become fitted with a common pole set. 16 | 17 | The identification is done using the pole relocating method known as Vector 18 | Fitting [1] with relaxed non-triviality constraint for faster convergence 19 | and smaller fitting errors [2], and utilization of matrix structure for fast 20 | solution of the pole identifion step [3]. 21 | 22 | - [1] B. Gustavsen and A. Semlyen, "Rational approximation of frequency 23 | domain responses by Vector Fitting", IEEE Trans. Power Delivery, 24 | vol. 14, no. 3, pp. 1052-1061, July 1999. 25 | - [2] B. Gustavsen, "Improving the pole relocating properties of vector 26 | fitting", IEEE Trans. Power Delivery, vol. 21, no. 3, pp. 1587-1592, 27 | July 2006. 28 | - [3] D. Deschrijver, M. Mrozowski, T. Dhaene, and D. De Zutter, 29 | "Macromodeling of Multiport Systems Using a Fast Implementation of 30 | the Vector Fitting Method", IEEE Microwave and Wireless Components 31 | Letters, vol. 18, no. 6, pp. 383-385, June 2008. 32 | 33 | All credit goes to Bjorn Gustavsen for his MATLAB implementation. 34 | (http://www.sintef.no/Projectweb/VECTFIT/) 35 | 36 | 37 | ## Implementation 38 | 39 | The `vectfit` functions are implemented in C++, using [`xtensor`](https://github.com/QuantStack/xtensor) 40 | for multi-dimensional arrays and [`xtensor-blas`](https://github.com/QuantStack/xtensor-blas) 41 | for linear-algebra operations. 42 | The C++ functions are wrapped into a Python extension module through 43 | [`xtensor-python`](https://github.com/QuantStack/xtensor-python) and 44 | [`pybind11`](https://github.com/pybind/pybind11). 45 | 46 | ``` python 47 | import vectfit as m 48 | import numpy as np 49 | 50 | s = np.array([3., 3.5, 4., 4.5, 5., 5.5, 6.]) 51 | f = np.array([[4.98753117e-02, 3.09734513e-01, 1.18811881e+00, 52 | 6.53846154e+00, 2.20000000e+02, 1.03846154e+01, 53 | 3.16831683e+00], 54 | [-4.98753117e-01,-2.21238938e-01, 9.90099010e-01, 55 | 9.61538462e+00, 4.00000000e+02, 2.11538462e+01, 56 | 6.93069307e+00]]) 57 | weight = 1.0/f 58 | init_poles = [4.5 + 0.045j, 4.5 - 0.045j] 59 | poles, residues, cf, fit, rms = m.vectfit(f, s, init_poles, weight) 60 | ``` 61 | 62 | ## Installation 63 | 64 | ### Prerequisites 65 | 66 | - C++ compiler such as g++ 67 | 68 | `sudo apt install g++` 69 | 70 | - Necessary libraries including [`xtensor`](https://github.com/QuantStack/xtensor), 71 | [`xtensor-blas`](https://github.com/QuantStack/xtensor-blas)(>=0.15), 72 | [`xtensor-python`](https://github.com/QuantStack/xtensor-python), and 73 | [`pybind11`](https://github.com/pybind/pybind11) 74 | 75 | Refer to '[installation steps on Ubuntu](./tools/ci/travis-install.sh)' for building 76 | necessary libraries from source, or, it is convenient to install all the libraries through 77 | conda package manager: 78 | 79 | `conda install -c conda-forge xtensor-blas xtensor-python` 80 | 81 | ### Build and install 82 | 83 | **On Unix (Linux, OS X)** 84 | 85 | - clone this repository 86 | - `git clone https://github.com/liangjg/vectfit.git` 87 | - `pip install ./vectfit` 88 | 89 | **On Windows (Requires Visual Studio 2015)** 90 | 91 | - For Python 3.5: 92 | - clone this repository 93 | - `pip install ./vectfit` 94 | - For earlier versions of Python, including Python 2.7: 95 | 96 | xtensor requires a C++14 compliant compiler (i.e. Visual Studio 2015 on 97 | Windows). Running a regular `pip install` command will detect the version 98 | of the compiler used to build Python and attempt to build the extension 99 | with it. We must force the use of Visual Studio 2015. 100 | 101 | - clone this repository 102 | - `"%VS140COMNTOOLS%\..\..\VC\vcvarsall.bat" x64` 103 | - `set DISTUTILS_USE_SDK=1` 104 | - `set MSSdk=1` 105 | - `pip install ./vectfit` 106 | 107 | Note that this requires the user building `vectfit` to have registry edition 108 | rights on the machine, to be able to run the `vcvarsall.bat` script. 109 | 110 | 111 | ## Windows runtime requirements 112 | 113 | On Windows, the Visual C++ 2015 redistributable packages are a runtime 114 | requirement for this project. It can be found [here](https://www.microsoft.com/en-us/download/details.aspx?id=48145). 115 | 116 | If you use the Anaconda python distribution, you may require the Visual Studio 117 | runtime as a platform-dependent runtime requirement for you package: 118 | 119 | ```yaml 120 | requirements: 121 | build: 122 | - python 123 | - setuptools 124 | - pybind11 125 | 126 | run: 127 | - python 128 | - vs2015_runtime # [win] 129 | ``` 130 | 131 | ## Building the documentation 132 | 133 | Documentation for the example project is generated using Sphinx. Sphinx has the 134 | ability to automatically inspect the signatures and documentation strings in 135 | the extension module to generate beautiful documentation in a variety formats. 136 | The following command generates HTML-based reference documentation; for other 137 | formats please refer to the Sphinx manual: 138 | 139 | - `cd vectfit/docs` 140 | - `make html` 141 | 142 | ## Running the tests 143 | 144 | Running the tests requires `pytest`. 145 | 146 | ```bash 147 | py.test . 148 | ``` 149 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | echo. coverage to run coverage check of the documentation if enabled 41 | goto end 42 | ) 43 | 44 | if "%1" == "clean" ( 45 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 46 | del /q /s %BUILDDIR%\* 47 | goto end 48 | ) 49 | 50 | 51 | REM Check if sphinx-build is available and fallback to Python version if any 52 | %SPHINXBUILD% 1>NUL 2>NUL 53 | if errorlevel 9009 goto sphinx_python 54 | goto sphinx_ok 55 | 56 | :sphinx_python 57 | 58 | set SPHINXBUILD=python -m sphinx.__init__ 59 | %SPHINXBUILD% 2> nul 60 | if errorlevel 9009 ( 61 | echo. 62 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 63 | echo.installed, then set the SPHINXBUILD environment variable to point 64 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 65 | echo.may add the Sphinx directory to PATH. 66 | echo. 67 | echo.If you don't have Sphinx installed, grab it from 68 | echo.http://sphinx-doc.org/ 69 | exit /b 1 70 | ) 71 | 72 | :sphinx_ok 73 | 74 | 75 | if "%1" == "html" ( 76 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 77 | if errorlevel 1 exit /b 1 78 | echo. 79 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 80 | goto end 81 | ) 82 | 83 | if "%1" == "dirhtml" ( 84 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 85 | if errorlevel 1 exit /b 1 86 | echo. 87 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 88 | goto end 89 | ) 90 | 91 | if "%1" == "singlehtml" ( 92 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 93 | if errorlevel 1 exit /b 1 94 | echo. 95 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 96 | goto end 97 | ) 98 | 99 | if "%1" == "pickle" ( 100 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 101 | if errorlevel 1 exit /b 1 102 | echo. 103 | echo.Build finished; now you can process the pickle files. 104 | goto end 105 | ) 106 | 107 | if "%1" == "json" ( 108 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 109 | if errorlevel 1 exit /b 1 110 | echo. 111 | echo.Build finished; now you can process the JSON files. 112 | goto end 113 | ) 114 | 115 | if "%1" == "htmlhelp" ( 116 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 117 | if errorlevel 1 exit /b 1 118 | echo. 119 | echo.Build finished; now you can run HTML Help Workshop with the ^ 120 | .hhp project file in %BUILDDIR%/htmlhelp. 121 | goto end 122 | ) 123 | 124 | if "%1" == "qthelp" ( 125 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 129 | .qhcp project file in %BUILDDIR%/qthelp, like this: 130 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\python_example.qhcp 131 | echo.To view the help file: 132 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\python_example.ghc 133 | goto end 134 | ) 135 | 136 | if "%1" == "devhelp" ( 137 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 138 | if errorlevel 1 exit /b 1 139 | echo. 140 | echo.Build finished. 141 | goto end 142 | ) 143 | 144 | if "%1" == "epub" ( 145 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 146 | if errorlevel 1 exit /b 1 147 | echo. 148 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 149 | goto end 150 | ) 151 | 152 | if "%1" == "latex" ( 153 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 154 | if errorlevel 1 exit /b 1 155 | echo. 156 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 157 | goto end 158 | ) 159 | 160 | if "%1" == "latexpdf" ( 161 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 162 | cd %BUILDDIR%/latex 163 | make all-pdf 164 | cd %~dp0 165 | echo. 166 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 167 | goto end 168 | ) 169 | 170 | if "%1" == "latexpdfja" ( 171 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 172 | cd %BUILDDIR%/latex 173 | make all-pdf-ja 174 | cd %~dp0 175 | echo. 176 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 177 | goto end 178 | ) 179 | 180 | if "%1" == "text" ( 181 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 182 | if errorlevel 1 exit /b 1 183 | echo. 184 | echo.Build finished. The text files are in %BUILDDIR%/text. 185 | goto end 186 | ) 187 | 188 | if "%1" == "man" ( 189 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 190 | if errorlevel 1 exit /b 1 191 | echo. 192 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 193 | goto end 194 | ) 195 | 196 | if "%1" == "texinfo" ( 197 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 198 | if errorlevel 1 exit /b 1 199 | echo. 200 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 201 | goto end 202 | ) 203 | 204 | if "%1" == "gettext" ( 205 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 206 | if errorlevel 1 exit /b 1 207 | echo. 208 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 209 | goto end 210 | ) 211 | 212 | if "%1" == "changes" ( 213 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 214 | if errorlevel 1 exit /b 1 215 | echo. 216 | echo.The overview file is in %BUILDDIR%/changes. 217 | goto end 218 | ) 219 | 220 | if "%1" == "linkcheck" ( 221 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 222 | if errorlevel 1 exit /b 1 223 | echo. 224 | echo.Link check complete; look for any errors in the above output ^ 225 | or in %BUILDDIR%/linkcheck/output.txt. 226 | goto end 227 | ) 228 | 229 | if "%1" == "doctest" ( 230 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 231 | if errorlevel 1 exit /b 1 232 | echo. 233 | echo.Testing of doctests in the sources finished, look at the ^ 234 | results in %BUILDDIR%/doctest/output.txt. 235 | goto end 236 | ) 237 | 238 | if "%1" == "coverage" ( 239 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage 240 | if errorlevel 1 exit /b 1 241 | echo. 242 | echo.Testing of coverage in the sources finished, look at the ^ 243 | results in %BUILDDIR%/coverage/python.txt. 244 | goto end 245 | ) 246 | 247 | if "%1" == "xml" ( 248 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 249 | if errorlevel 1 exit /b 1 250 | echo. 251 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 252 | goto end 253 | ) 254 | 255 | if "%1" == "pseudoxml" ( 256 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 257 | if errorlevel 1 exit /b 1 258 | echo. 259 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 260 | goto end 261 | ) 262 | 263 | :end 264 | -------------------------------------------------------------------------------- /tests/test_vectfit.py: -------------------------------------------------------------------------------- 1 | import vectfit as m 2 | from unittest import TestCase 3 | import numpy as np 4 | 5 | 6 | class VectfitTest(TestCase): 7 | 8 | def test_vector(self): 9 | """Test vectfit with vector samples, and simple poles. 10 | It is expected to get exact results with one iteration. 11 | """ 12 | Ns = 101 13 | test_s = np.linspace(3., 7., Ns) 14 | test_poles = [5.0+0.1j, 5.0-0.1j] 15 | test_residues = [[0.5-11.0j, 0.5+11.0j], 16 | [1.5-20.0j, 1.5+20.0j]] 17 | f = np.zeros([2, Ns]) 18 | f[0, :] = np.real(test_residues[0][0]/(test_s - test_poles[0]) + \ 19 | test_residues[0][1]/(test_s - test_poles[1])) 20 | f[1, :] = np.real(test_residues[1][0]/(test_s - test_poles[0]) + \ 21 | test_residues[1][1]/(test_s - test_poles[1])) 22 | weight = 1.0/f 23 | init_poles = [3.5 + 0.035j, 3.5 - 0.035j] 24 | poles, residues, cf, fit, rms = m.vectfit(f, test_s, init_poles, weight) 25 | np.testing.assert_allclose(test_poles, poles, rtol=1e-7) 26 | np.testing.assert_allclose(test_residues, residues, rtol=1e-7) 27 | np.testing.assert_allclose(f, fit, rtol=1e-5) 28 | 29 | def test_poly(self): 30 | """Test vectfit with polynomials.""" 31 | # contructing signal 32 | Ns = 201 33 | test_s = np.linspace(0., 5., Ns) 34 | test_poles = [-20.0+30.0j, -20.0-30.0j] 35 | test_residues = [[5.0+10.0j, 5.0-10.0j]] 36 | test_polys = [[1.0, 2.0, 0.3]] 37 | f = m.evaluate(test_s, test_poles, test_residues, test_polys) 38 | weight = 1.0/f 39 | # intial poles 40 | init_poles = [2.5 + 0.025j, 2.5 - 0.025j] 41 | # iteration 1 42 | poles, residues, cf, fit, rms = m.vectfit(f, test_s, init_poles, 43 | weight, n_polys=3) 44 | # iteration 2 45 | poles, residues, cf, fit, rms = m.vectfit(f, test_s, poles, 46 | weight, n_polys=3) 47 | np.testing.assert_allclose(test_poles, poles, rtol=1e-5) 48 | np.testing.assert_allclose(test_residues, residues, rtol=1e-5) 49 | np.testing.assert_allclose(test_polys, cf, rtol=1e-5) 50 | np.testing.assert_allclose(f, fit, rtol=1e-4) 51 | 52 | def test_real_poles(self): 53 | """Test vectfit with more poles including real poles""" 54 | # contructing signal 55 | Ns = 5000 56 | test_s = np.linspace(1.0e-2, 5.e3, Ns) 57 | test_poles = np.array([ 58 | 9.709261771920490e+02 + 0.0j, 59 | -1.120960794075339e+03 + 0.0j, 60 | 1.923889557426567e+00 + 7.543700246109742e+01j, 61 | 1.923889557426567e+00 - 7.543700246109742e+01j, 62 | 1.159741300380281e+02 + 3.595650922556496e-02j, 63 | 1.159741300380281e+02 - 3.595650922556496e-02j, 64 | 1.546932165729394e+02 + 8.728391144940301e-02j, 65 | 1.546932165729394e+02 - 8.728391144940301e-02j, 66 | 2.280349190818197e+02 + 2.814037559718684e-01j, 67 | 2.280349190818197e+02 - 2.814037559718684e-01j, 68 | 2.313004772627853e+02 + 3.004628477692201e-01j, 69 | 2.313004772627853e+02 - 3.004628477692201e-01j, 70 | 2.787470098364861e+02 + 3.414179169920170e-01j, 71 | 2.787470098364861e+02 - 3.414179169920170e-01j, 72 | 3.570711338764254e+02 + 4.485587371149193e-01j, 73 | 3.570711338764254e+02 - 4.485587371149193e-01j, 74 | 4.701059001346060e+02 + 6.598089307174224e-01j, 75 | 4.701059001346060e+02 - 6.598089307174224e-01j, 76 | 7.275819506342254e+02 + 1.189678974845038e+03j, 77 | 7.275819506342254e+02 - 1.189678974845038e+03j 78 | ]) 79 | test_residues = np.array([[ 80 | -3.269879776751686e+07 + 0.0j, 81 | 1.131087935798761e+09 + 0.0j, 82 | 1.634151281869857e+04 + 2.251103589277891e+05j, 83 | 1.634151281869857e+04 - 2.251103589277891e+05j, 84 | 3.281792303833561e+03 - 1.756079516325274e+04j, 85 | 3.281792303833561e+03 + 1.756079516325274e+04j, 86 | 1.110800880243503e+04 - 4.324813594540043e+04j, 87 | 1.110800880243503e+04 + 4.324813594540043e+04j, 88 | 8.812700704117636e+04 - 2.256520243571103e+05j, 89 | 8.812700704117636e+04 + 2.256520243571103e+05j, 90 | 5.842090495551535e+04 - 1.442159380741478e+05j, 91 | 5.842090495551535e+04 + 1.442159380741478e+05j, 92 | 1.339410514130921e+05 - 2.640767909713812e+05j, 93 | 1.339410514130921e+05 + 2.640767909713812e+05j, 94 | 2.211245633333130e+05 - 3.222447758311512e+05j, 95 | 2.211245633333130e+05 + 3.222447758311512e+05j, 96 | 4.124430059785149e+05 - 4.076023108323907e+05j, 97 | 4.124430059785149e+05 + 4.076023108323907e+05j, 98 | 1.607378314999252e+09 - 1.401163320110452e+08j, 99 | 1.607378314999252e+09 + 1.401163320110452e+08j 100 | ]]) 101 | f = np.zeros((1, Ns)) 102 | for p, r in zip(test_poles, test_residues[0]): 103 | f[0] += (r/(test_s - p)).real 104 | weight = 1.0/f 105 | # intial poles 106 | poles = np.linspace(1.1e-2, 4.8e+3, 10); 107 | poles = poles + poles*0.01j 108 | poles = np.sort(np.append(poles, np.conj(poles))) 109 | # VF iterations 110 | for i in range(10): 111 | poles, residues, cf, fit, rms = m.vectfit(f, test_s, poles, weight) 112 | np.testing.assert_allclose(np.sort(test_poles), np.sort(poles)) 113 | np.testing.assert_allclose(np.sort(test_residues), np.sort(residues)) 114 | np.testing.assert_allclose(f, fit, 1e-3) 115 | 116 | def test_large(self): 117 | """Test vectfit with a large set of poles and samples""" 118 | Ns = 20000 119 | N = 1000 120 | s = np.linspace(1.0e-2, 5.e3, Ns) 121 | poles = np.linspace(1.1e-2, 4.8e+3, N//2); 122 | poles = poles + poles*0.01j 123 | poles = np.sort(np.append(poles, np.conj(poles))) 124 | residues = np.linspace(1e+2, 1e+6, N//2); 125 | residues = residues + residues*0.5j 126 | residues = np.sort(np.append(residues, np.conj(residues))) 127 | residues = residues.reshape((1, N)) 128 | f = np.zeros((1, Ns)) 129 | for p, r in zip(poles, residues[0]): 130 | f[0] += (r/(s - p)).real 131 | weight = 1.0/f 132 | poles_init = np.linspace(1.2e-2, 4.7e+3, N//2); 133 | poles_init = poles_init + poles_init*0.01j 134 | poles_init = np.sort(np.append(poles_init, np.conj(poles_init))) 135 | 136 | poles_fit, residues_fit, cf, f_fit, rms = m.vectfit(f, s, poles_init, weight) 137 | 138 | np.testing.assert_allclose(f, f_fit, 1e-3) 139 | 140 | def test_evaluate(self): 141 | """Test evaluate function""" 142 | Ns = 101 143 | s = np.linspace(-5., 5., Ns) 144 | poles = [-2.0+30.0j, -2.0-30.0j] 145 | residues = [5.0+10.0j, 5.0-10.0j] 146 | f_ref = np.zeros([1, Ns]) 147 | f_ref[0, :] = np.real(residues[0]/(s - poles[0]) + \ 148 | residues[1]/(s - poles[1])) 149 | f = m.evaluate(s, poles, residues) 150 | np.testing.assert_allclose(f_ref, f) 151 | 152 | polys = [1.0, 2.0, 0.3] 153 | for n, c in enumerate(polys): 154 | f_ref[0, :] += c*np.power(s, n) 155 | f = m.evaluate(s, poles, residues, polys) 156 | np.testing.assert_allclose(f_ref, f) 157 | 158 | poles = [5.0+0.1j, 5.0-0.1j] 159 | residues = [[0.5-11.0j, 0.5+11.0j], 160 | [1.5-20.0j, 1.5+20.0j]] 161 | polys = [[1.0, 2.0, 0.3], [4.0, -2.0, -10.0]] 162 | f_ref = np.zeros([2, Ns]) 163 | f_ref[0, :] = np.real(residues[0][0]/(s - poles[0]) + \ 164 | residues[0][1]/(s - poles[1])) 165 | f_ref[1, :] = np.real(residues[1][0]/(s - poles[0]) + \ 166 | residues[1][1]/(s - poles[1])) 167 | for n, c in enumerate(polys[0]): 168 | f_ref[0, :] += c*np.power(s, n) 169 | for n, c in enumerate(polys[1]): 170 | f_ref[1, :] += c*np.power(s, n) 171 | f = m.evaluate(s, poles, residues, polys) 172 | np.testing.assert_allclose(f_ref, f) 173 | -------------------------------------------------------------------------------- /docs/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 23 | help: 24 | @echo "Please use \`make ' where is one of" 25 | @echo " html to make standalone HTML files" 26 | @echo " dirhtml to make HTML files named index.html in directories" 27 | @echo " singlehtml to make a single large HTML file" 28 | @echo " pickle to make pickle files" 29 | @echo " json to make JSON files" 30 | @echo " htmlhelp to make HTML files and a HTML help project" 31 | @echo " qthelp to make HTML files and a qthelp project" 32 | @echo " applehelp to make an Apple Help Book" 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 | @echo " coverage to run coverage check of the documentation (if enabled)" 49 | 50 | .PHONY: clean 51 | clean: 52 | rm -rf $(BUILDDIR)/* 53 | 54 | .PHONY: html 55 | html: 56 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 57 | @echo 58 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 59 | 60 | .PHONY: dirhtml 61 | dirhtml: 62 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 63 | @echo 64 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 65 | 66 | .PHONY: singlehtml 67 | singlehtml: 68 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 69 | @echo 70 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 71 | 72 | .PHONY: pickle 73 | pickle: 74 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 75 | @echo 76 | @echo "Build finished; now you can process the pickle files." 77 | 78 | .PHONY: json 79 | json: 80 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 81 | @echo 82 | @echo "Build finished; now you can process the JSON files." 83 | 84 | .PHONY: htmlhelp 85 | htmlhelp: 86 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 87 | @echo 88 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 89 | ".hhp project file in $(BUILDDIR)/htmlhelp." 90 | 91 | .PHONY: qthelp 92 | qthelp: 93 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 94 | @echo 95 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 96 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 97 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/python_example.qhcp" 98 | @echo "To view the help file:" 99 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/python_example.qhc" 100 | 101 | .PHONY: applehelp 102 | applehelp: 103 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 104 | @echo 105 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 106 | @echo "N.B. You won't be able to view it unless you put it in" \ 107 | "~/Library/Documentation/Help or install it in your application" \ 108 | "bundle." 109 | 110 | .PHONY: devhelp 111 | devhelp: 112 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 113 | @echo 114 | @echo "Build finished." 115 | @echo "To view the help file:" 116 | @echo "# mkdir -p $$HOME/.local/share/devhelp/python_example" 117 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/python_example" 118 | @echo "# devhelp" 119 | 120 | .PHONY: epub 121 | epub: 122 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 123 | @echo 124 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 125 | 126 | .PHONY: latex 127 | latex: 128 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 129 | @echo 130 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 131 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 132 | "(use \`make latexpdf' here to do that automatically)." 133 | 134 | .PHONY: latexpdf 135 | latexpdf: 136 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 137 | @echo "Running LaTeX files through pdflatex..." 138 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 139 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 140 | 141 | .PHONY: latexpdfja 142 | latexpdfja: 143 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 144 | @echo "Running LaTeX files through platex and dvipdfmx..." 145 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 146 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 147 | 148 | .PHONY: text 149 | text: 150 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 151 | @echo 152 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 153 | 154 | .PHONY: man 155 | man: 156 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 157 | @echo 158 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 159 | 160 | .PHONY: texinfo 161 | texinfo: 162 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 163 | @echo 164 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 165 | @echo "Run \`make' in that directory to run these through makeinfo" \ 166 | "(use \`make info' here to do that automatically)." 167 | 168 | .PHONY: info 169 | info: 170 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 171 | @echo "Running Texinfo files through makeinfo..." 172 | make -C $(BUILDDIR)/texinfo info 173 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 174 | 175 | .PHONY: gettext 176 | gettext: 177 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 178 | @echo 179 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 180 | 181 | .PHONY: changes 182 | changes: 183 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 184 | @echo 185 | @echo "The overview file is in $(BUILDDIR)/changes." 186 | 187 | .PHONY: linkcheck 188 | linkcheck: 189 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 190 | @echo 191 | @echo "Link check complete; look for any errors in the above output " \ 192 | "or in $(BUILDDIR)/linkcheck/output.txt." 193 | 194 | .PHONY: doctest 195 | doctest: 196 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 197 | @echo "Testing of doctests in the sources finished, look at the " \ 198 | "results in $(BUILDDIR)/doctest/output.txt." 199 | 200 | .PHONY: coverage 201 | coverage: 202 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 203 | @echo "Testing of coverage in the sources finished, look at the " \ 204 | "results in $(BUILDDIR)/coverage/python.txt." 205 | 206 | .PHONY: xml 207 | xml: 208 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 209 | @echo 210 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 211 | 212 | .PHONY: pseudoxml 213 | pseudoxml: 214 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 215 | @echo 216 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 217 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # vectfit documentation build configuration file, created by 4 | # sphinx-quickstart on Fri Feb 26 00:29:33 2016. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | import numpy 18 | 19 | # If extensions (or modules to document with autodoc) are in another directory, 20 | # add these directories to sys.path here. If the directory is relative to the 21 | # documentation root, use os.path.abspath to make it absolute, like shown here. 22 | #sys.path.insert(0, os.path.abspath('.')) 23 | 24 | # -- General configuration ------------------------------------------------ 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | #needs_sphinx = '1.0' 28 | 29 | # Add any Sphinx extension module names here, as strings. They can be 30 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 31 | # ones. 32 | extensions = [ 33 | 'sphinx.ext.autodoc', 34 | 'sphinx.ext.mathjax', 35 | 'sphinx.ext.intersphinx', 36 | 'sphinx.ext.autosummary', 37 | 'sphinx.ext.napoleon', 38 | ] 39 | 40 | autosummary_generate = True 41 | 42 | # Add any paths that contain templates here, relative to this directory. 43 | templates_path = ['_templates'] 44 | 45 | # The suffix(es) of source filenames. 46 | # You can specify multiple suffix as a list of string: 47 | # source_suffix = ['.rst', '.md'] 48 | source_suffix = '.rst' 49 | 50 | # The encoding of source files. 51 | #source_encoding = 'utf-8-sig' 52 | 53 | # The master toctree document. 54 | master_doc = 'index' 55 | 56 | # General information about the project. 57 | project = u'vectfit' 58 | copyright = u'2018, Jingang Liang' 59 | author = u'Jingang Liang' 60 | 61 | # The version info for the project you're documenting, acts as replacement for 62 | # |version| and |release|, also used in various other places throughout the 63 | # built documents. 64 | # 65 | # The short X.Y version. 66 | version = u'0.1' 67 | # The full version, including alpha/beta/rc tags. 68 | release = u'0.1' 69 | 70 | # The language for content autogenerated by Sphinx. Refer to documentation 71 | # for a list of supported languages. 72 | # 73 | # This is also used if you do content translation via gettext catalogs. 74 | # Usually you set "language" from the command line for these cases. 75 | language = None 76 | 77 | # There are two options for replacing |today|: either, you set today to some 78 | # non-false value, then it is used: 79 | #today = '' 80 | # Else, today_fmt is used as the format for a strftime call. 81 | #today_fmt = '%B %d, %Y' 82 | 83 | # List of patterns, relative to source directory, that match files and 84 | # directories to ignore when looking for source files. 85 | exclude_patterns = ['_build'] 86 | 87 | # The reST default role (used for this markup: `text`) to use for all 88 | # documents. 89 | #default_role = None 90 | 91 | # If true, '()' will be appended to :func: etc. cross-reference text. 92 | #add_function_parentheses = True 93 | 94 | # If true, the current module name will be prepended to all description 95 | # unit titles (such as .. function::). 96 | #add_module_names = True 97 | 98 | # If true, sectionauthor and moduleauthor directives will be shown in the 99 | # output. They are ignored by default. 100 | #show_authors = False 101 | 102 | # The name of the Pygments (syntax highlighting) style to use. 103 | pygments_style = 'sphinx' 104 | 105 | # A list of ignored prefixes for module index sorting. 106 | #modindex_common_prefix = [] 107 | 108 | # If true, keep warnings as "system message" paragraphs in the built documents. 109 | #keep_warnings = False 110 | 111 | # If true, `todo` and `todoList` produce output, else they produce nothing. 112 | todo_include_todos = False 113 | 114 | 115 | # -- Options for HTML output ---------------------------------------------- 116 | 117 | # The theme to use for HTML and HTML Help pages. See the documentation for 118 | # a list of builtin themes. 119 | html_theme = 'alabaster' 120 | 121 | # Theme options are theme-specific and customize the look and feel of a theme 122 | # further. For a list of options available for each theme, see the 123 | # documentation. 124 | #html_theme_options = {} 125 | 126 | # Add any paths that contain custom themes here, relative to this directory. 127 | #html_theme_path = [] 128 | 129 | # The name for this set of Sphinx documents. If None, it defaults to 130 | # " v documentation". 131 | #html_title = None 132 | 133 | # A shorter title for the navigation bar. Default is the same as html_title. 134 | #html_short_title = None 135 | 136 | # The name of an image file (relative to this directory) to place at the top 137 | # of the sidebar. 138 | #html_logo = None 139 | 140 | # The name of an image file (within the static path) to use as favicon of the 141 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 142 | # pixels large. 143 | #html_favicon = None 144 | 145 | # Add any paths that contain custom static files (such as style sheets) here, 146 | # relative to this directory. They are copied after the builtin static files, 147 | # so a file named "default.css" will overwrite the builtin "default.css". 148 | #html_static_path = ['_static'] 149 | 150 | # Add any extra paths that contain custom files (such as robots.txt or 151 | # .htaccess) here, relative to this directory. These files are copied 152 | # directly to the root of the documentation. 153 | #html_extra_path = [] 154 | 155 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 156 | # using the given strftime format. 157 | #html_last_updated_fmt = '%b %d, %Y' 158 | 159 | # If true, SmartyPants will be used to convert quotes and dashes to 160 | # typographically correct entities. 161 | #html_use_smartypants = True 162 | 163 | # Custom sidebar templates, maps document names to template names. 164 | #html_sidebars = {} 165 | 166 | # Additional templates that should be rendered to pages, maps page names to 167 | # template names. 168 | #html_additional_pages = {} 169 | 170 | # If false, no module index is generated. 171 | #html_domain_indices = True 172 | 173 | # If false, no index is generated. 174 | #html_use_index = True 175 | 176 | # If true, the index is split into individual pages for each letter. 177 | #html_split_index = False 178 | 179 | # If true, links to the reST sources are added to the pages. 180 | #html_show_sourcelink = True 181 | 182 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 183 | #html_show_sphinx = True 184 | 185 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 186 | #html_show_copyright = True 187 | 188 | # If true, an OpenSearch description file will be output, and all pages will 189 | # contain a tag referring to it. The value of this option must be the 190 | # base URL from which the finished HTML is served. 191 | #html_use_opensearch = '' 192 | 193 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 194 | #html_file_suffix = None 195 | 196 | # Language to be used for generating the HTML full-text search index. 197 | # Sphinx supports the following languages: 198 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' 199 | # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' 200 | #html_search_language = 'en' 201 | 202 | # A dictionary with options for the search language support, empty by default. 203 | # Now only 'ja' uses this config value 204 | #html_search_options = {'type': 'default'} 205 | 206 | # The name of a javascript file (relative to the configuration directory) that 207 | # implements a search results scorer. If empty, the default will be used. 208 | #html_search_scorer = 'scorer.js' 209 | 210 | # Output file base name for HTML help builder. 211 | htmlhelp_basename = 'vectfitdoc' 212 | 213 | # -- Options for LaTeX output --------------------------------------------- 214 | 215 | latex_elements = { 216 | # The paper size ('letterpaper' or 'a4paper'). 217 | #'papersize': 'letterpaper', 218 | 219 | # The font size ('10pt', '11pt' or '12pt'). 220 | #'pointsize': '10pt', 221 | 222 | # Additional stuff for the LaTeX preamble. 223 | #'preamble': '', 224 | 225 | # Latex figure (float) alignment 226 | #'figure_align': 'htbp', 227 | } 228 | 229 | # Grouping the document tree into LaTeX files. List of tuples 230 | # (source start file, target name, title, 231 | # author, documentclass [howto, manual, or own class]). 232 | latex_documents = [ 233 | (master_doc, 'vectfit.tex', u'vectfit Documentation', 234 | u'Jingang Liang', 'manual'), 235 | ] 236 | 237 | # The name of an image file (relative to this directory) to place at the top of 238 | # the title page. 239 | #latex_logo = None 240 | 241 | # For "manual" documents, if this is true, then toplevel headings are parts, 242 | # not chapters. 243 | #latex_use_parts = False 244 | 245 | # If true, show page references after internal links. 246 | #latex_show_pagerefs = False 247 | 248 | # If true, show URL addresses after external links. 249 | #latex_show_urls = False 250 | 251 | # Documents to append as an appendix to all manuals. 252 | #latex_appendices = [] 253 | 254 | # If false, no module index is generated. 255 | #latex_domain_indices = True 256 | 257 | 258 | # -- Options for manual page output --------------------------------------- 259 | 260 | # One entry per manual page. List of tuples 261 | # (source start file, name, description, authors, manual section). 262 | man_pages = [ 263 | (master_doc, 'vectfit', u'vectfit Documentation', 264 | [author], 1) 265 | ] 266 | 267 | # If true, show URL addresses after external links. 268 | #man_show_urls = False 269 | 270 | 271 | # -- Options for Texinfo output ------------------------------------------- 272 | 273 | # Grouping the document tree into Texinfo files. List of tuples 274 | # (source start file, target name, title, author, 275 | # dir menu entry, description, category) 276 | texinfo_documents = [ 277 | (master_doc, 'vectfit', u'vectfit Documentation', 278 | author, 'vectfit', 'One line description of project.', 279 | 'Miscellaneous'), 280 | ] 281 | 282 | # Documents to append as an appendix to all manuals. 283 | #texinfo_appendices = [] 284 | 285 | # If false, no module index is generated. 286 | #texinfo_domain_indices = True 287 | 288 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 289 | #texinfo_show_urls = 'footnote' 290 | 291 | # If true, do not generate a @detailmenu in the "Top" node's menu. 292 | #texinfo_no_detailmenu = False 293 | 294 | 295 | # Example configuration for intersphinx: refer to the Python standard library. 296 | intersphinx_mapping = {'https://docs.python.org/': None} 297 | -------------------------------------------------------------------------------- /test_run.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "%matplotlib inline\n", 12 | "import numpy as np\n", 13 | "import matplotlib.pyplot as plt\n", 14 | "\n", 15 | "import vectfit as m" 16 | ] 17 | }, 18 | { 19 | "cell_type": "markdown", 20 | "metadata": {}, 21 | "source": [ 22 | "### Function vectfit" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": 2, 28 | "metadata": {}, 29 | "outputs": [ 30 | { 31 | "data": { 32 | "text/plain": [ 33 | "9.619933845662216e-11" 34 | ] 35 | }, 36 | "execution_count": 2, 37 | "metadata": {}, 38 | "output_type": "execute_result" 39 | } 40 | ], 41 | "source": [ 42 | "# construting signal\n", 43 | "Ns = 101\n", 44 | "s = np.linspace(3., 7., Ns)\n", 45 | "poles = [5.0+0.1j, 5.0-0.1j]\n", 46 | "residues = [[0.5-11.0j, 0.5+11.0j],\n", 47 | " [1.5-20.0j, 1.5+20.0j]]\n", 48 | "f = m.evaluate(s, poles, residues)\n", 49 | "weight = 1.0/f\n", 50 | "poles_init = [3.5 + 0.035j, 3.5 - 0.035j]\n", 51 | "\n", 52 | "# run vector fitting\n", 53 | "# single iteration\n", 54 | "poles_fit, residues_fit, cf, f_fit, rms = m.vectfit(f, s, poles_init, weight)\n", 55 | "\n", 56 | "# rms error\n", 57 | "rms" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": 3, 63 | "metadata": { 64 | "collapsed": true 65 | }, 66 | "outputs": [], 67 | "source": [ 68 | "### evaluate at finer points\n", 69 | "s_test = np.linspace(3., 7., 5*Ns)\n", 70 | "f_test = m.evaluate(s_test, poles, residues, cf)" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": 4, 76 | "metadata": {}, 77 | "outputs": [ 78 | { 79 | "data": { 80 | "image/png": "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\n", 81 | "text/plain": [ 82 | "
" 83 | ] 84 | }, 85 | "metadata": {}, 86 | "output_type": "display_data" 87 | } 88 | ], 89 | "source": [ 90 | "# plot\n", 91 | "plt.plot(s, f[0], 'r.', label='f[0]')\n", 92 | "plt.plot(s, f_fit[0], 'b*', label='f_fit[0]')\n", 93 | "plt.plot(s_test, f_test[0], 'g', label='f_test[0]')\n", 94 | "plt.xlabel('s')\n", 95 | "plt.ylabel('f')\n", 96 | "plt.legend()\n", 97 | "plt.show()" 98 | ] 99 | }, 100 | { 101 | "cell_type": "markdown", 102 | "metadata": {}, 103 | "source": [ 104 | "### Large problem" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 5, 110 | "metadata": {}, 111 | "outputs": [], 112 | "source": [ 113 | "Ns = 50000\n", 114 | "N = 1000\n", 115 | "s = np.linspace(1.e-2, 5.e3, Ns)\n", 116 | "poles = np.linspace(1.1e-2, 4.8e+3, N/2);\n", 117 | "poles = poles + poles*0.01j\n", 118 | "poles = np.sort(np.append(poles, np.conj(poles)))\n", 119 | "residues = np.linspace(1e+2, 1e+6, N/2);\n", 120 | "residues = residues + residues*0.5j\n", 121 | "residues = np.sort(np.append(residues, np.conj(residues)))\n", 122 | "residues = residues.reshape((1, N))\n", 123 | "f = np.zeros((1, Ns))\n", 124 | "for p, r in zip(poles, residues[0]):\n", 125 | " f[0] += (r/(s - p)).real\n", 126 | "weight = 1.0/f\n", 127 | "\n", 128 | "poles_init = np.linspace(1.2e-2, 4.7e+3, N/2);\n", 129 | "poles_init = poles_init + poles_init*0.01j\n", 130 | "poles_init = np.sort(np.append(poles_init, np.conj(poles_init)))" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": 6, 136 | "metadata": {}, 137 | "outputs": [ 138 | { 139 | "name": "stdout", 140 | "output_type": "stream", 141 | "text": [ 142 | "CPU times: user 4min 14s, sys: 6min 42s, total: 10min 57s\n", 143 | "Wall time: 2min 51s\n" 144 | ] 145 | }, 146 | { 147 | "data": { 148 | "text/plain": [ 149 | "0.005450713259715198" 150 | ] 151 | }, 152 | "execution_count": 6, 153 | "metadata": {}, 154 | "output_type": "execute_result" 155 | } 156 | ], 157 | "source": [ 158 | "%time poles_fit, residues_fit, cf, f_fit, rms = m.vectfit(f, s, poles_init, weight)\n", 159 | "rms" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": 7, 165 | "metadata": { 166 | "collapsed": true 167 | }, 168 | "outputs": [], 169 | "source": [ 170 | "np.testing.assert_allclose(f, f_fit, 1e-2)" 171 | ] 172 | } 173 | ], 174 | "metadata": { 175 | "kernelspec": { 176 | "display_name": "Python 3", 177 | "language": "python", 178 | "name": "python3" 179 | }, 180 | "language_info": { 181 | "codemirror_mode": { 182 | "name": "ipython", 183 | "version": 3 184 | }, 185 | "file_extension": ".py", 186 | "mimetype": "text/x-python", 187 | "name": "python", 188 | "nbconvert_exporter": "python", 189 | "pygments_lexer": "ipython3", 190 | "version": "3.5.5" 191 | } 192 | }, 193 | "nbformat": 4, 194 | "nbformat_minor": 2 195 | } 196 | -------------------------------------------------------------------------------- /src/vectfit.cpp: -------------------------------------------------------------------------------- 1 | #include "xtensor/xarray.hpp" 2 | #include "xtensor/xview.hpp" 3 | #include "xtensor/xindex_view.hpp" 4 | #include "xtensor/xmath.hpp" 5 | #include "xtensor/xnorm.hpp" 6 | #include "xtensor/xcomplex.hpp" 7 | #include "xtensor-blas/xlinalg.hpp" 8 | #define FORCE_IMPORT_ARRAY 9 | #include "xtensor-python/pyarray.hpp" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "pybind11/pybind11.h" 19 | 20 | using namespace std::complex_literals; 21 | 22 | // Complex number zero 23 | constexpr std::complex C_ZERO {0.0, 0.0}; 24 | 25 | // Tolerances used by relaxed vector fitting 26 | constexpr double TOLlow = 1e-18; 27 | constexpr double TOLhigh = 1e+18; 28 | 29 | //! Multipole formalism evaluation function 30 | //! 31 | //! f(s) = REAL[residues/(s - poles)] + Polynomials(s) 32 | //! Note the input variable s is real and only the real part of the 33 | //! result f is returned. 34 | //! 35 | //! @param s array of variables to be evaluated. dimension: (Ns) 36 | //! @param poles poles. dimension: (N) 37 | //! @param residues residues. dimension: (Nv, N) 38 | //! @param polys curvefit (Polynomial) coefficients. dimension: (Nv, Nc) 39 | //! @return f. dimension: (Nv, Ns) 40 | 41 | xt::pyarray 42 | evaluate(xt::pyarray s, 43 | xt::pyarray> poles, 44 | xt::pyarray> residues, 45 | xt::pyarray polys = (xt::pyarray) {}) 46 | { 47 | // Check input arguments 48 | // s 49 | if (s.dimension() != 1) 50 | { 51 | throw std::invalid_argument("Error: input s is not 1-dimensional."); 52 | } 53 | auto Ns = s.size(); 54 | 55 | // poles 56 | if (poles.dimension() != 1) 57 | { 58 | throw std::invalid_argument("Error: input poles is not 1-dimensional."); 59 | } 60 | auto N = poles.size(); 61 | 62 | // residues 63 | // convert the residues to be two dimensional if it is one dimensional 64 | if (residues.dimension() == 1 && residues.size() == N) 65 | { 66 | residues.reshape({1, N}); 67 | } 68 | else if (residues.dimension() != 2) 69 | { 70 | throw std::invalid_argument("Error: input residues is not 2-dimensional."); 71 | } 72 | else if (residues.shape()[1] != N) 73 | { 74 | throw std::invalid_argument("Error: 2nd dimension of residues does not " 75 | "match the length of poles."); 76 | } 77 | auto Nv = residues.shape()[0]; 78 | 79 | // polynomial coefficients 80 | // none polynomials by default 81 | if (polys.dimension() == 0 || xt::all(xt::equal(polys, 0.))) 82 | { 83 | polys = xt::zeros({Nv, (size_t)0}); 84 | } 85 | else if (polys.dimension() == 1 && Nv == 1) 86 | { 87 | polys.reshape({1, polys.size()}); 88 | } 89 | else if (polys.dimension() !=2) 90 | { 91 | throw std::invalid_argument("Error: input polys is not 2-dimensional."); 92 | } 93 | else if (polys.shape()[0] != Nv) 94 | { 95 | throw std::invalid_argument("Error: 1st dimension of polys does not " 96 | "match the 1st dimension of residues."); 97 | } 98 | auto Nc = polys.shape()[1]; 99 | 100 | // Initialize results array 101 | xt::pyarray f({Nv, Ns}, 0.0); 102 | 103 | // Evaluate the multipole form 104 | size_t m, n; 105 | xt::xtensor, 2> Dk2({Ns, N}, C_ZERO); 106 | for (m = 0; m < N; m++) 107 | { 108 | xt::view(Dk2, xt::all(), m) = 1.0 / (s - poles(m)); 109 | } 110 | for (n = 0; n < Nv; n++) 111 | { 112 | xt::view(f, n) = xt::real(xt::linalg::dot(Dk2, 113 | xt::xarray>(xt::view(residues, n)))); 114 | for (m = 0; m < Nc; m++) 115 | { 116 | xt::view(f, n) += xt::pow(s, m) * polys(n, m); 117 | } 118 | } 119 | 120 | // Return 121 | return f; 122 | } 123 | 124 | 125 | //! Fast Relaxed Vector Fitting function 126 | //! 127 | //! Approximate f(s) with a rational function: 128 | //! f(s)=R*(s*I-A)^(-1) + Polynomials*s 129 | //! where f(s) is a vector of elements. 130 | //! When f(s) is a vector, all elements become fitted with a common pole set. 131 | //! The identification is done using the pole relocating method known as Vector 132 | //! Fitting [1] with relaxed non-triviality constraint for faster convergence 133 | //! and smaller fitting errors [2], and utilization of matrix structure for fast 134 | //! solution of the pole identifion step [3]. 135 | //! [1] B. Gustavsen and A. Semlyen, "Rational approximation of frequency 136 | //! domain responses by Vector Fitting", IEEE Trans. Power Delivery, 137 | //! vol. 14, no. 3, pp. 1052-1061, July 1999. 138 | //! [2] B. Gustavsen, "Improving the pole relocating properties of vector 139 | //! fitting", IEEE Trans. Power Delivery, vol. 21, no. 3, pp. 1587-1592, 140 | //! July 2006. 141 | //! [3] D. Deschrijver, M. Mrozowski, T. Dhaene, and D. De Zutter, 142 | //! "Macromodeling of Multiport Systems Using a Fast Implementation of 143 | //! the Vector Fitting Method", IEEE Microwave and Wireless Components 144 | //! Letters, vol. 18, no. 6, pp. 383-385, June 2008. 145 | //! All credit goes to Bjorn Gustavsen for his MATLAB implementation. 146 | //! (http://www.sintef.no/Projectweb/VECTFIT/) 147 | //! 148 | //! @param f function (vector) to be fitted. dimension: (Nv, Ns) 149 | //! @param s vector of sample points. dimension: (Ns) 150 | //! @param poles vector of initial poles. dimension: (N) 151 | //! @param weight the system matrix is weighted using this array 152 | //! @param n_polys Nc: Number of curvefit (Polynomial) coefficients, [0, 11] 153 | //! @param skip_pole if the pole identification part is skipped 154 | //! @param skip_res if the residue identification part is skipped 155 | //! @return Tuple(poles, residues, polys, fit, rmserr) 156 | 157 | std::tuple>, 158 | xt::pyarray>, 159 | xt::pyarray, 160 | xt::pyarray, 161 | double> 162 | vectfit(xt::pyarray &f, 163 | xt::pyarray &s, 164 | xt::pyarray> &poles, 165 | xt::pyarray &weight, 166 | int n_polys = 0, 167 | bool skip_pole = false, 168 | bool skip_res = false) 169 | { 170 | // Check input arguments 171 | if (f.dimension() != 2) 172 | { 173 | throw std::invalid_argument("Error: input f is not 2-dimensional."); 174 | } 175 | auto shape = f.shape(); 176 | auto Nv = shape[0]; 177 | auto Ns = shape[1]; 178 | if (s.dimension() != 1) 179 | { 180 | throw std::invalid_argument("Error: input s is not 1-dimensional."); 181 | } 182 | if (Ns != s.size()) 183 | { 184 | throw std::invalid_argument("Error: 2nd dimension of f does not match the " 185 | "length of s."); 186 | } 187 | auto N = poles.size(); 188 | if (f.shape() != weight.shape()) 189 | { 190 | throw std::invalid_argument("Error: shape of weight does not match shape of" 191 | " f."); 192 | } 193 | size_t Nc = (size_t)n_polys; 194 | if (Nc < 0 || Nc > 11) 195 | { 196 | throw std::invalid_argument("Error: input n_polys is not in range [0, 11]."); 197 | } 198 | 199 | // Initialize arrays 200 | xt::pyarray> residues({Nv, N}, C_ZERO); // residues (R) 201 | xt::pyarray polys({Nv, Nc}, 0.0); // polynomial coefficients (P) 202 | xt::pyarray fit({Nv, Ns}, 0.0); // fitted signals on s 203 | double rmserr = 0.0; // RMS error between f and fit 204 | 205 | // If 0 poles and 0 cf order, return 206 | if (N == 0 && Nc == 0) 207 | { 208 | rmserr = xt::linalg::norm(f) / std::sqrt(Nv * Ns); 209 | return std::make_tuple(poles, residues, polys, fit, rmserr); 210 | } 211 | 212 | // Iteration variables 213 | size_t m, n; 214 | 215 | //============================================================================ 216 | // POLE IDENTIFICATION 217 | //============================================================================ 218 | if (!skip_pole && N > 0) 219 | { 220 | // Finding out which starting poles are complex 221 | xt::xtensor cindex({N}, 0); // 0-real, 1-complex, 2-conjugate 222 | for (m = 0; m < N; m++) 223 | { 224 | if (std::imag(poles(m)) != 0.0) 225 | { 226 | if ((m == 0) || ((m > 0) && (cindex(m - 1) == 0 || cindex(m - 1) == 2))) 227 | { 228 | if (m >= N - 1 || std::conj(poles(m)) != poles(m + 1)) 229 | { 230 | throw std::invalid_argument("Error: complex poles are not conjugate" 231 | " pairs."); 232 | } 233 | cindex(m) = 1; 234 | cindex(m + 1) = 2; 235 | m++; 236 | } 237 | } 238 | } 239 | 240 | // Building system - matrixes 241 | xt::xtensor, 2> Dk({Ns, N + std::max(Nc, (size_t)1)}, 242 | C_ZERO); 243 | for (m = 0; m < N; m++) 244 | { 245 | auto p = poles(m); 246 | if (cindex(m) == 0) // real pole 247 | xt::view(Dk, xt::all(), m) = 1. / (s - p); 248 | else if (cindex(m) == 1) // complex pole, 1st 249 | xt::view(Dk, xt::all(), m) = 1. / (s - p) + 1. / (s - std::conj(p)); 250 | else if (cindex(m) == 2) // complex pole, 2nd 251 | xt::view(Dk, xt::all(), m) = 1i / (s - std::conj(p)) - 1i / (s - p); 252 | else 253 | throw std::runtime_error("Error: unknown cindex value."); 254 | } 255 | 256 | // Check infinite values 257 | xt::filter(Dk, xt::isinf(Dk)) = TOLhigh + 0.0i; 258 | 259 | // Polynomial terms 260 | xt::view(Dk, xt::all(), N) = std::complex(1.0, 0.0); 261 | for (m = 1; m < Nc; m++) 262 | { 263 | xt::view(Dk, xt::all(), N + m) = xt::pow(s, m) + 0.0i; 264 | } 265 | 266 | // Scaling for last row of LS-problem (pole identification) 267 | double scale = 0.0; 268 | for (m = 0; m < Nv; m++) 269 | { 270 | scale += std::pow(xt::linalg::norm(xt::view(weight, m) * xt::view(f, m)), 271 | 2); 272 | } 273 | scale = std::sqrt(scale) / Ns; 274 | 275 | // A matrix 276 | xt::xtensor AA({Nv * (N + 1), N + 1}, 0.0); 277 | xt::xtensor bb({Nv * (N + 1)}, 0.0); 278 | for (n = 0; n < Nv; n++) 279 | { 280 | xt::xtensor, 2> A1({Ns, N + Nc + N + 1}, C_ZERO); 281 | // left block 282 | for (m = 0; m < N + Nc; m++) 283 | { 284 | xt::view(A1, xt::all(), m) = xt::view(weight, n) * 285 | xt::view(Dk, xt::all(), m); 286 | } 287 | // right block 288 | for (m = 0; m < N + 1; m++) 289 | { 290 | xt::view(A1, xt::all(), N + Nc + m) = -xt::view(weight, n) * 291 | xt::view(Dk, xt::all(), m) * xt::view(f, n); 292 | } 293 | 294 | xt::xtensor A({2 * Ns + 1, N + Nc + N + 1}, 0.0); 295 | xt::view(A, xt::range(0, Ns)) = xt::real(A1); 296 | xt::view(A, xt::range(Ns, 2 * Ns)) = xt::imag(A1); 297 | 298 | // Integral criterion for sigma 299 | if (n == Nv - 1) 300 | { 301 | for (m = 0; m < N + 1; m++) 302 | { 303 | auto d = xt::sum(xt::view(Dk, xt::all(), m))(); 304 | A(2 * Ns, N + Nc + m) = std::real(scale * d); 305 | } 306 | } 307 | 308 | // QR decomposition 309 | // Hotspots of the algorithm 310 | auto QR_tuple = xt::linalg::qr(A); 311 | auto R = std::get<1>(QR_tuple); 312 | xt::view(AA, xt::range(n*(N+1), (n+1)*(N+1))) = 313 | xt::view(R, xt::range(N+Nc, N+Nc+N+1), xt::range(N+Nc, N+Nc+N+1)); 314 | if (n == Nv - 1) 315 | { 316 | auto Q = std::get<0>(QR_tuple); 317 | xt::view(bb, xt::range(n*(N+1), (n+1)*(N+1))) = Ns * scale * 318 | xt::view(Q, Q.shape()[0] - 1, xt::range(N+Nc, Q.shape()[1])); 319 | } 320 | } 321 | 322 | xt::xtensor Escale({N + 1}, 0.0); 323 | for (m = 0; m < N + 1; m++) 324 | { 325 | Escale(m) = 1.0 / xt::linalg::norm(xt::view(AA, xt::all(), m)); 326 | xt::view(AA, xt::all(), m) *= Escale(m); 327 | } 328 | 329 | auto results = xt::linalg::lstsq(AA, bb); 330 | auto x = std::get<0>(results); 331 | x *= Escale; 332 | auto C = xt::xarray(xt::view(x, xt::range(0, x.size() - 1))); 333 | auto D = x(x.size() - 1); 334 | 335 | // Situation: produced D of sigma extremely is small or large 336 | // Solve again, without relaxation 337 | if (std::abs(D) < TOLlow || std::abs(D) > TOLhigh) 338 | { 339 | xt::xtensor AA({Nv * N, N}, 0.0); 340 | xt::xtensor bb({Nv * N}, 0.0); 341 | if (x(x.size() - 1) == 0.0) 342 | { 343 | D = 1.0; 344 | } 345 | else if (std::abs(x(x.size() - 1)) < TOLlow) 346 | { 347 | D = x(x.size() - 1) > 0 ? TOLlow : -TOLlow; 348 | } 349 | else if (std::abs(x(x.size() - 1)) > TOLhigh) 350 | { 351 | D = x(x.size() - 1) > 0 ? TOLhigh : -TOLhigh; 352 | } 353 | 354 | for (n = 0; n < Nv; n++) 355 | { 356 | xt::xtensor, 2> A1({Ns, N + Nc + N}, C_ZERO); 357 | for (m = 0; m < N + Nc; m++) 358 | { 359 | xt::view(A1, xt::all(), m) = xt::view(weight, n) * 360 | xt::view(Dk, xt::all(), m); 361 | } 362 | for (m = 0; m < N; m++) 363 | { 364 | xt::view(A1, xt::all(), N + Nc + m) = -xt::view(weight, n) * 365 | xt::view(Dk, xt::all(), m) * xt::view(f, n); 366 | } 367 | auto A = xt::xarray(xt::concatenate(xt::xtuple(xt::real(A1), 368 | xt::imag(A1)))); 369 | auto b1 = D * xt::view(weight, n) * xt::view(f, n); 370 | auto b = xt::xarray(xt::concatenate(xt::xtuple(xt::real(b1), 371 | xt::imag(b1)))); 372 | 373 | // QR decomposition 374 | auto QR_tuple = xt::linalg::qr(A); 375 | auto Q = std::get<0>(QR_tuple); 376 | auto R = std::get<1>(QR_tuple); 377 | xt::view(AA, xt::range(n*N, (n+1)*N)) = 378 | xt::view(R, xt::range(N+Nc, N+Nc+N), xt::range(N+Nc, N+Nc+N)); 379 | xt::view(bb, xt::range(n*N, (n+1)*N)) = xt::linalg::dot( 380 | xt::transpose(xt::view(Q, xt::all(), xt::range(N+Nc, N+Nc+N))), b); 381 | } 382 | 383 | xt::xtensor Escale ({N}, 0.0); 384 | for (m = 0; m < N; m++) 385 | { 386 | xt::view(Escale, m) = 1.0 / xt::linalg::norm(xt::view(AA, xt::all(), m)); 387 | xt::view(AA, xt::all(), m) *= Escale(m); 388 | } 389 | 390 | auto results = xt::linalg::lstsq(AA, bb); 391 | auto C = std::get<0>(results); 392 | C *= Escale; 393 | } 394 | 395 | // We now calculate the zeros for sigma 396 | xt::xtensor LAMBD({N, N}, 0.0); 397 | xt::xtensor SERB({N, (size_t)1}, 1.0); 398 | for (m = 0; m < N; m++) 399 | { 400 | if (cindex(m) == 0) // real pole 401 | { 402 | LAMBD(m, m) = std::real(poles(m)); 403 | } 404 | else if (cindex(m) == 1) 405 | { 406 | auto x = std::real(poles(m)); 407 | auto y = std::imag(poles(m)); 408 | LAMBD(m, m) = x; 409 | LAMBD(m + 1, m + 1) = x; 410 | LAMBD(m + 1, m) = -y; 411 | LAMBD(m, m + 1) = y; 412 | SERB(m, 1) = 2.0; 413 | SERB(m + 1, 1) = 0.0; 414 | } 415 | } 416 | 417 | // Update poles 418 | C.reshape({1, N}); 419 | auto ZER = LAMBD - xt::linalg::dot(SERB, C) / D; 420 | poles = xt::linalg::eigvals(ZER); 421 | } 422 | 423 | //============================================================================ 424 | // RESIDUE IDENTIFICATION 425 | //============================================================================ 426 | if (!skip_res) 427 | { 428 | // Finding out which poles are complex: 429 | xt::xtensor cindex({N}, 0); // 0-real, 1-complex, 2-conjugate 430 | for (m = 0; m < N; m++) 431 | { 432 | if (std::imag(poles(m)) != 0.0) 433 | { 434 | if ((m == 0) || ((m > 0) && (cindex(m - 1) == 0 || cindex(m - 1) == 2))) 435 | { 436 | if (m >= N - 1 || std::conj(poles(m)) != poles(m + 1)) 437 | { 438 | throw std::invalid_argument("Error: complex poles are not conjugate" 439 | " pairs."); 440 | } 441 | cindex(m) = 1; 442 | cindex(m + 1) = 2; 443 | m++; 444 | } 445 | } 446 | } 447 | 448 | // Calculate the SER for f (new fitting), using the above calculated 449 | // zeros as known poles 450 | xt::xtensor, 2> Dk({Ns, N + Nc}, C_ZERO); 451 | for (m = 0; m < N; m++) 452 | { 453 | auto p = poles(m); 454 | if (cindex(m) == 0) // real pole 455 | xt::view(Dk, xt::all(), m) = 1. / (s - p); 456 | else if (cindex(m) == 1) // complex pole, 1st 457 | xt::view(Dk, xt::all(), m) = 1. / (s - p) + 1. / (s - std::conj(p)); 458 | else if (cindex(m) == 2) // complex pole, 2nd 459 | xt::view(Dk, xt::all(), m) = 1i / (s - std::conj(p)) - 1i / (s - p); 460 | else 461 | throw std::runtime_error("Error: unknown cindex value."); 462 | } 463 | for (m = 0; m < Nc; m++) 464 | { 465 | xt::view(Dk, xt::all(), N + m) = xt::pow(s, m) + 0.0i; 466 | } 467 | 468 | xt::xtensor Cr({Nv, N}, 0.0); 469 | for (n = 0; n < Nv; n++) 470 | { 471 | xt::xtensor, 2> A1({Ns, N + Nc}, C_ZERO); 472 | for (m = 0; m < N + Nc; m++) 473 | { 474 | xt::view(A1, xt::all(), m) = xt::view(weight, n) * 475 | xt::view(Dk, xt::all(), m); 476 | } 477 | auto A = xt::xtensor(xt::concatenate(xt::xtuple(xt::real(A1), 478 | xt::imag(A1)))); 479 | auto b1 = xt::view(weight, n) * xt::view(f, n); 480 | auto b = xt::xtensor(xt::concatenate(xt::xtuple(xt::real(b1), 481 | xt::imag(b1)))); 482 | 483 | xt::xtensor Escale({N + Nc}, 0.0); 484 | for (m = 0; m < N + Nc; m++) 485 | { 486 | xt::view(Escale, m) = 1.0 / xt::linalg::norm(xt::view(A, xt::all(), m)); 487 | xt::view(A, xt::all(), m) *= Escale(m); 488 | } 489 | 490 | auto results = xt::linalg::lstsq(A, b); 491 | auto x = std::get<0>(results); 492 | x *= Escale; 493 | 494 | xt::view(Cr, n) = xt::view(x, xt::range(0, N)); 495 | 496 | if (Nc > 0) 497 | { 498 | xt::view(polys, n) = xt::view(x, xt::range(N, N + Nc)); 499 | } 500 | } 501 | 502 | // Get complex residues 503 | for (m = 0; m < N; m++) 504 | { 505 | if (cindex(m) == 0) 506 | { 507 | for (n = 0; n < Nv; n++) 508 | { 509 | residues(n, m) = std::complex(Cr(n, m)); 510 | } 511 | } 512 | else if (cindex(m) == 1) 513 | { 514 | for (n = 0; n < Nv; n++) 515 | { 516 | auto r1 = Cr(n, m); 517 | auto r2 = Cr(n, m + 1); 518 | residues(n, m) = r1 + 1i * r2; 519 | residues(n, m + 1) = r1 - 1i * r2; 520 | } 521 | } 522 | } 523 | 524 | // Calculate fit on s 525 | fit = evaluate(s, poles, residues, polys); 526 | 527 | // RMS error 528 | rmserr = xt::linalg::norm(fit - f) / std::sqrt(Nv * Ns); 529 | } 530 | 531 | // Return a tuple including the updated results 532 | return std::make_tuple(poles, residues, polys, fit, rmserr); 533 | } 534 | 535 | 536 | // 537 | // Python Module and Docstrings 538 | // 539 | namespace py = pybind11; 540 | 541 | PYBIND11_MODULE(vectfit, m) 542 | { 543 | xt::import_numpy(); 544 | 545 | m.doc() = R"pbdoc( 546 | Vector Fitting Algorithm 547 | ------------------------- 548 | .. currentmodule:: vectfit 549 | .. autosummary:: 550 | :toctree: _generate 551 | 552 | vectfit 553 | evaluate 554 | )pbdoc"; 555 | 556 | m.def("vectfit", &vectfit, R"pbdoc( 557 | Fast Relaxed Vector Fitting function 558 | 559 | A robust numerical method for rational approximation. It updates the 560 | poles and calculates residues based on guessed poles. 561 | 562 | Parameters 563 | ---------- 564 | f : numpy.ndarray 565 | A 2D array of the sample signals to be fitted, (Nv, Ns) 566 | s : numpy.ndarray 567 | A 1D array of the sample points, (Ns) 568 | poles : numpy.ndarray [complex] 569 | Initial poles, real or complex conjugate pairs, (N) 570 | weight : numpy.ndarray 571 | 2D array for weighting f, to control the accuracy of the 572 | approximation, (Nv, Ns) 573 | n_polys : int 574 | Number of polynomial coefficients to be fitted, [0, 11] 575 | skip_pole : bool 576 | Whether or not to skip the calculation of poles 577 | skip_res : bool 578 | Whether or not to skip the calculation of residues (including the 579 | polynomials) 580 | 581 | Returns 582 | ------- 583 | Tuple : (numpy.ndarray [complex], numpy.ndarray [complex], numpy.ndarray, numpy.ndarray, float) 584 | The updated poles, residues, polynomial coefficients, 585 | fitted signals on the sample points, root mean square error 586 | 587 | )pbdoc", py::arg("f"), py::arg("s"), py::arg("poles"), py::arg("weight"), 588 | py::arg("n_polys") = 0, py::arg("skip_pole") = false, 589 | py::arg("skip_res") = false); 590 | 591 | m.def("evaluate", &evaluate, R"pbdoc( 592 | Multipole formalism evaluation function 593 | 594 | Evaluate the multipole formalism at given points with known poles, 595 | residues and polynomial coefficients. 596 | 597 | Parameters 598 | ---------- 599 | s : numpy.ndarray 600 | A 1D array of the points, (Ns) 601 | poles : numpy.ndarray [complex] 602 | A 1D array of the poles, (N) 603 | residues : numpy.ndarray [complex] 604 | 2D array of residues, (Nv, N) 605 | polys : numpy.ndarray 606 | Polynomial coefficients (0-th to Nc-th order), (Nv, Nc) 607 | 608 | Returns 609 | ------- 610 | f : numpy.ndarray 611 | the result array of multipole formalism (real part) 612 | 613 | )pbdoc", py::arg("s"), py::arg("poles"), py::arg("residues"), 614 | py::arg("polys") = (xt::pyarray) {}); 615 | 616 | #ifdef VERSION_INFO 617 | m.attr("__version__") = VERSION_INFO; 618 | #else 619 | m.attr("__version__") = "0.1"; 620 | #endif 621 | } 622 | --------------------------------------------------------------------------------