diff --git a/.github/workflows/basic.yml b/.github/workflows/basic.yml index e6ab9bc..a7ca16b 100644 --- a/.github/workflows/basic.yml +++ b/.github/workflows/basic.yml @@ -12,11 +12,11 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ ubuntu-20.04, ubuntu-22.04 ] + os: [ ubuntu-22.04, ubuntu-latest ] compiler: [ gcc, clang ] BUILD_TYPE: [ Debug, Release ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: dependencies run: sudo apt install libeigen3-dev - name: configure @@ -27,3 +27,4 @@ jobs: run: cmake --build build -- -j4 - name: test run: cd build && ctest + diff --git a/.github/workflows/checkformat.yml b/.github/workflows/checkformat.yml index a2846ca..9a16079 100644 --- a/.github/workflows/checkformat.yml +++ b/.github/workflows/checkformat.yml @@ -13,10 +13,11 @@ jobs: matrix: compiler: [gcc] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: dependencies run: sudo apt install libeigen3-dev clang-format - name: cmake run: cmake -B builddir - name: Format run: cmake --build builddir --target format && git diff --exit-code + diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 71769d4..b2274b0 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -8,13 +8,13 @@ on: jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: compiler: [ gcc ] BUILD_TYPE : [ Debug ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: dependencies run: sudo apt install libeigen3-dev lcov - name: configure @@ -28,4 +28,5 @@ jobs: - name: Generate coverage data run: lcov --directory . --capture --output-file coverage.info - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v5 + diff --git a/CHANGELOG.md b/CHANGELOG.md index e770cf1..ba4224f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,17 @@ -## [Unreleased] +## [1.1.0] - 2025-01-01 ### Added + +- Added the eigen solver `HermEigsSolver` for complex-valued Hermitian matrices +- Added the support for complex Hermitian matrices to various linear algebra classes, such as `Arnoldi`, `Lanczos`, and `BKLDLT` +- `SimpleRandom` can now generate random complex values +- Added testing code for Arnoldi factorization on both real matrices and complex Hermitian matrices +- Added `BKLDLT` tests for complex Hermitian matrices +- Added testing code for the `HermEigsSolver` solver - Added test examples from previous bug reports ### Changed + - Better heuristics for basis restarting in the Lanczos factorization, which fixes [#159](https://github.com/yixuan/spectra/issues/159) - Fixed the support for non-literal data types @@ -19,33 +27,22 @@ [@alecjacobson](https://github.com/alecjacobson), and [@jdumas](https://github.com/jdumas) - Miscellaneous GitHub Actions updates +- Various internal implementation changes to accommodate operations on complex-valued matrices +- Improved the numerical stability of `BKLDLT` linear system solver +- Change `SymEigsBase` to `HermEigsBase` as the base class for both real symmetric and complex Hermitian eigen solvers ## [1.0.1] - 2022-04-06 ### Added -- Added SIMD support for `UpperHessenbergSchur`. This should accelerate - general eigen solvers such as `GenEigsSolver` -- Added test code for `UpperHessenbergSchur` - -### Changed -- Fixed several bugs in the examples caused by the `const` keyword, - reported by [@alexpghayes](https://github.com/alexpghayes) - ([#135](https://github.com/yixuan/spectra/issues/135), - [#137](https://github.com/yixuan/spectra/pull/137)) -- Updated the included [Catch2](https://github.com/catchorg/Catch2) to v2.13.8 - - - -## [1.0.1] - 2022-04-06 -### Added - Added SIMD support for `UpperHessenbergSchur`. This should accelerate general eigen solvers such as `GenEigsSolver` - Added test code for `UpperHessenbergSchur` ### Changed + - Fixed several bugs in the examples caused by the `const` keyword, reported by [@alexpghayes](https://github.com/alexpghayes) ([#135](https://github.com/yixuan/spectra/issues/135), @@ -57,6 +54,7 @@ ## [1.0.0] - 2021-07-01 ### Added + - Added version macros `SPECTRA_MAJOR_VERSION`, `SPECTRA_MINOR_VERSION`, `SPECTRA_PATCH_VERSION`, and `SPECTRA_VERSION` that are included by all eigen solvers - Added the wrapper class `SparseGenComplexShiftSolve` for eigen solver with complex shifts @@ -78,6 +76,7 @@ `SparseSymMatProd` to handle matrix-matrix products and coefficient-wise accessors ### Changed + - **API change**: Spectra now requires C++11 - **API change**: All enumerations have been converted to enum classes (e.g. `LARGEST_MAGN` is now `SortRule::LargestMagn`) @@ -116,6 +115,7 @@ ## [0.9.0] - 2020-05-19 ### Added + - Added support for CMake build, contributed by [Guillaume Acke](https://github.com/guacke) and [Jens Wehner](https://github.com/JensWehner) ([#70](https://github.com/yixuan/spectra/pull/70), [#88](https://github.com/yixuan/spectra/pull/88)) @@ -127,6 +127,7 @@ [Jens Wehner](https://github.com/JensWehner) ### Changed + - Fixed a compiler warning caused by unused parameter, contributed by [Julien Schueller](https://github.com/jschueller) ([#80](https://github.com/yixuan/spectra/pull/80)) - Changed the implementation of `BKLDLT` solver to improve precision in some tests @@ -136,6 +137,7 @@ ## [0.8.1] - 2019-06-05 ### Changed + - Fixed a bug in `BKLDLT` in which a wrong type was used, thanks to [@jdbancal](https://github.com/jdbancal) for the issue [#64](https://github.com/yixuan/spectra/pull/64) @@ -152,6 +154,7 @@ ## [0.8.0] - 2019-04-03 ### Added + - Added a `BKLDLT` class that implements the Bunch-Kaufman LDLT decomposition for symmetric indefinite matrices. According to the Eigen documentation, currently `Eigen::LDLT` cannot handle some special indefinite matrices such @@ -161,6 +164,7 @@ - Added a unit test for `BKLDLT` ### Changed + - `DenseSymShiftSolve` now uses the newly added `BKLDLT` class to do the decomposition. This change broadens the class of matrices that `DenseSymShiftSolve` can handle, reduces memory use, and should also improve @@ -181,6 +185,7 @@ ## [0.7.0] - 2019-01-10 ### Added + - Added a directory `contrib` to include code contributed by users. It is not formally a part of the Spectra library, but it may contain useful solvers and applications based on Spectra. Code in `contrib` may not be fully tested, @@ -194,6 +199,7 @@ - Added a few other internal classes to refactor the eigen solver classes (see below) ### Changed + - **API change**: Spectra now requires Eigen >= 3.3 - **API change**: The library header files are moved into a directory named `Spectra`. Hence the recommended include directive would look like @@ -215,6 +221,7 @@ ## [0.6.2] - 2018-05-22 ### Changed + - Fixed regressions in v0.6.0 on some edge cases - Improved the accuracy of restarting processes in `SymEigsSolver` and `GenEigsSolver` - Updated the included [Catch2](https://github.com/catchorg/Catch2) to v2.2.2 @@ -225,6 +232,7 @@ ## [0.6.1] - 2018-03-03 ### Changed + - Fixed a bug of uninitialized memory - Updated the included [Catch2](https://github.com/catchorg/Catch2) to v2.1.2 @@ -233,6 +241,7 @@ ## [0.6.0] - 2018-03-03 ### Added + - Added virtual destructors to the `SymEigsSolver` and `UpperHessenbergQR` classes to fix compiler warnings, by [Julian Kent](https://github.com/jkflying) - Added a `NUMERICAL_ISSUE` entry to the `COMPUTATION_INFO` enumeration to indicate @@ -245,6 +254,7 @@ types ### Changed + - Documentation updates - Updated the project URL to [https://spectralib.org](https://spectralib.org) - Some internal improvements, such as pre-allocating vectors in loops, and changing @@ -261,12 +271,14 @@ ## [0.5.0] - 2017-02-05 ### Added + - Added the generalized eigen solver `SymGEigsSolver` in the regular inverse mode - Added the wrapper class `SparseRegularInverse` that can be used with `SymGEigsSolver` in the regular inverse mode - Added test code for generalized eigen solver in the regular inverse mode ### Changed + - Improved the numerical precision and stability of some internal linear algebra classes, including `TridiagEigen`, `UpperHessenbergEigen`, and `DoubleShiftQR` @@ -283,6 +295,7 @@ ## [0.4.0] - 2016-11-14 ### Added + - Added an `Uplo` template parameter to the `DenseSymShiftSolve` class - Added the generalized eigen solver `SymGEigsSolver` in the Cholesky mode - Added the wrapper classes `DenseCholesky` and `SparseCholesky` that can be @@ -290,6 +303,7 @@ - Added test code for generalized eigen solver in the Cholesky mode ### Changed + - Updated included [Catch](https://github.com/philsquared/Catch) to v1.5.7 - Improved documentation - Updated Travis CI script @@ -311,6 +325,7 @@ ## [0.3.0] - 2016-07-03 ### Added + - Added the wrapper classes `SparseSymMatProd` and `SparseSymShiftSolve` for sparse symmetric matrices - Added the wrapper class `SparseGenRealShiftSolve` for general sparse matrices @@ -318,6 +333,7 @@ - Using Travis CI for automatic unit test ### Changed + - Updated included [Catch](https://github.com/philsquared/Catch) to v1.5.6 - **API change**: Each eigen solver was moved to its own header file. For example to use `SymEigsShiftSolver` one needs to include @@ -329,6 +345,7 @@ ## [0.2.0] - 2016-02-28 ### Added + - Benchmark script now outputs number of matrix operations - Added this change log - Added a simple built-in random number generator, so that the algorithm @@ -336,6 +353,7 @@ - Added the wrapper class `DenseSymMatProd` for symmetric matrices ### Changed + - Improved Arnoldi factorization - Iteratively corrects orthogonality - Creates new residual vector when invariant subspace is found @@ -352,4 +370,5 @@ ## [0.1.0] - 2015-12-19 ### Added + - Initial release of Spectra diff --git a/CMakeLists.txt b/CMakeLists.txt index ac4bde0..810972c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required (VERSION 3.5 FATAL_ERROR) -project (Spectra VERSION 1.0.1 LANGUAGES CXX) +project (Spectra VERSION 1.1.0 LANGUAGES CXX) # Make CMake look into the ./cmake/ folder for configuration files list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) diff --git a/README.md b/README.md index 491a882..7067339 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![Basic CI](https://github.com/yixuan/spectra/workflows/Basic%20CI/badge.svg) [![codecov](https://codecov.io/gh/yixuan/spectra/branch/master/graph/badge.svg)](https://codecov.io/gh/yixuan/spectra) -> **NOTE**: Spectra 1.0.0 is released, with a lot of +> **NOTE**: Spectra 1.0.0 was released in 2021-07-01, with a lot of > API-breaking changes. Please see the [migration guide](MIGRATION.md) > for a smooth transition to the new version. @@ -86,6 +86,8 @@ For generalized eigen solver with real symmetric matrices For generalized eigen solver with real symmetric matrices, using the shift-and-invert mode - [DavidsonSymEigsSolver](https://spectralib.org/doc/classSpectra_1_1DavidsonSymEigsSolver.html): Jacobi-Davidson eigen solver for real symmetric matrices, with the DPR correction method +- [HermEigsSolver](https://spectralib.org/doc/classSpectra_1_1HermEigsSolver.html): +For complex Hermitian matrices ## Examples diff --git a/doxygen/Doxyfile b/doxygen/Doxyfile index 808065b..53f29dc 100644 --- a/doxygen/Doxyfile +++ b/doxygen/Doxyfile @@ -1,7 +1,7 @@ -# Doxyfile 1.9.0 +# Doxyfile 1.13.0 # This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project. +# Doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. @@ -12,6 +12,16 @@ # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use Doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use Doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options @@ -38,10 +48,10 @@ PROJECT_NAME = "Spectra" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 1.0.1 +PROJECT_NUMBER = 1.1.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer a +# for a project that appears at the top of each page and should give viewers a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = Header-only C++ Library for Large Scale Eigenvalue Problems @@ -53,24 +63,42 @@ PROJECT_BRIEF = Header-only C++ Library for Large Scale Eigenvalue Prob PROJECT_LOGO = +# With the PROJECT_ICON tag one can specify an icon that is included in the tabs +# when the HTML document is shown. Doxygen will copy the logo to the output +# directory. + +PROJECT_ICON = + # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is -# entered, it will be relative to the location where doxygen was started. If +# entered, it will be relative to the location where Doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = ../doc -# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this -# option can be useful when feeding doxygen a huge amount of source files, where -# putting all generated files in the same directory would otherwise causes -# performance problems for the file system. +# If the CREATE_SUBDIRS tag is set to YES then Doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this +# option can be useful when feeding Doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise cause +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. # The default value is: NO. CREATE_SUBDIRS = NO -# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + +# If the ALLOW_UNICODE_NAMES tag is set to YES, Doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. @@ -79,36 +107,28 @@ CREATE_SUBDIRS = NO ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this +# documentation generated by Doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English -# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all generated output in the proper direction. -# Possible values are: None, LTR, RTL and Context. -# The default value is: None. - -OUTPUT_TEXT_DIRECTION = None - -# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# If the BRIEF_MEMBER_DESC tag is set to YES, Doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES -# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# If the REPEAT_BRIEF tag is set to YES, Doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the @@ -139,13 +159,13 @@ ABBREVIATE_BRIEF = "The $name class" \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# doxygen will generate a detailed section even if there is only a brief +# Doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# If the INLINE_INHERITED_MEMB tag is set to YES, Doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. @@ -153,7 +173,7 @@ ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO -# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# If the FULL_PATH_NAMES tag is set to YES, Doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. @@ -163,11 +183,11 @@ FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the path to +# If left blank the directory from which Doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which -# will be relative from the directory where doxygen is started. +# will be relative from the directory where Doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = ../include @@ -181,41 +201,42 @@ STRIP_FROM_PATH = ../include STRIP_FROM_INC_PATH = ../include -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but -# less readable) file names. This can be useful is your file systems doesn't +# If the SHORT_NAMES tag is set to YES, Doxygen will generate much shorter (but +# less readable) file names. This can be useful if your file system doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO -# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the -# first line (until the first dot) of a Javadoc-style comment as the brief -# description. If set to NO, the Javadoc-style will behave just like regular Qt- -# style comments (thus requiring an explicit @brief command for a brief -# description.) +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen will interpret the +# first line (until the first dot, question mark or exclamation mark) of a +# Javadoc-style comment as the brief description. If set to NO, the Javadoc- +# style will behave just like regular Qt-style comments (thus requiring an +# explicit @brief command for a brief description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO -# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# If the JAVADOC_BANNER tag is set to YES then Doxygen will interpret a line # such as # /*************** # as being the beginning of a Javadoc-style comment "banner". If set to NO, the # Javadoc-style will behave just like regular comments and it will not be -# interpreted by doxygen. +# interpreted by Doxygen. # The default value is: NO. JAVADOC_BANNER = NO -# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first -# line (until the first dot) of a Qt-style comment as the brief description. If -# set to NO, the Qt-style will behave just like regular Qt-style comments (thus -# requiring an explicit \brief command for a brief description.) +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will interpret the first +# line (until the first dot, question mark or exclamation mark) of a Qt-style +# comment as the brief description. If set to NO, the Qt-style will behave just +# like regular Qt-style comments (thus requiring an explicit \brief command for +# a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this @@ -227,10 +248,10 @@ QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO -# By default Python docstrings are displayed as preformatted text and doxygen's +# By default Python docstrings are displayed as preformatted text and Doxygen's # special commands cannot be used. By setting PYTHON_DOCSTRING to NO the -# doxygen's special commands can be used and the contents of the docstring -# documentation blocks is shown as doxygen documentation. +# Doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as Doxygen documentation. # The default value is: YES. PYTHON_DOCSTRING = YES @@ -241,7 +262,7 @@ PYTHON_DOCSTRING = YES INHERIT_DOCS = YES -# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# If the SEPARATE_MEMBER_PAGES tag is set to YES then Doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. @@ -258,16 +279,16 @@ TAB_SIZE = 4 # the documentation. An alias has the form: # name=value # For example adding -# "sideeffect=@par Side Effects:\n" +# "sideeffect=@par Side Effects:^^" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines (in the resulting output). You can put ^^ in the value part of an -# alias to insert a newline as if a physical newline was in the original file. -# When you need a literal { or } or , in the value part of an alias you have to -# escape them by means of a backslash (\), this can lead to conflicts with the -# commands \{ and \} for these it is advised to use the version @{ and @} or use -# a double escape (\\{ and \\}) +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) ALIASES = @@ -311,30 +332,30 @@ OPTIMIZE_OUTPUT_SLICE = NO # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, -# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, -# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# language is one of the parsers supported by Doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the -# default for Fortran type files). For instance to make doxygen treat .inc files +# default for Fortran type files). For instance to make Doxygen treat .inc files # as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. When specifying no_extension you should add +# the files are not read by Doxygen. When specifying no_extension you should add # * to the FILE_PATTERNS. # # Note see also the list of default file extension mappings. EXTENSION_MAPPING = -# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# If the MARKDOWN_SUPPORT tag is enabled then Doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See https://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you can -# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# The output of markdown processing is further processed by Doxygen, so you can +# mix Doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. @@ -344,25 +365,45 @@ MARKDOWN_SUPPORT = YES # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. -# Minimum value: 0, maximum value: 99, default value: 5. +# Minimum value: 0, maximum value: 99, default value: 6. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 6 + +# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to +# generate identifiers for the Markdown headings. Note: Every identifier is +# unique. +# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a +# sequence number starting at 0 and GITHUB use the lower case version of title +# with any whitespace replaced by '-' and punctuation characters removed. +# The default value is: DOXYGEN. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. -TOC_INCLUDE_HEADINGS = 5 +MARKDOWN_ID_STYLE = DOXYGEN -# When enabled doxygen tries to link words that correspond to documented +# When enabled Doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or -# globally by setting AUTOLINK_SUPPORT to NO. +# globally by setting AUTOLINK_SUPPORT to NO. Words listed in the +# AUTOLINK_IGNORE_WORDS tag are excluded from automatic linking. # The default value is: YES. AUTOLINK_SUPPORT = YES +# This tag specifies a list of words that, when matching the start of a word in +# the documentation, will suppress auto links generation, if it is enabled via +# AUTOLINK_SUPPORT. This list does not affect affect links explicitly created +# using \# or the \link or commands. +# This tag requires that the tag AUTOLINK_SUPPORT is set to YES. + +AUTOLINK_IGNORE_WORDS = + # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this -# tag to YES in order to let doxygen match functions declarations and +# tag to YES in order to let Doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); -# versus func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. +# versus func(std::string) {}). This also makes the inheritance and +# collaboration diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO @@ -374,16 +415,16 @@ BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen -# will parse them like normal C++ but will assume all classes use public instead -# of private inheritance when no explicit protection keyword is present. +# https://www.riverbankcomputing.com/software) sources only. Doxygen will parse +# them like normal C++ but will assume all classes use public instead of private +# inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make -# doxygen to replace the get and set methods by a property in the documentation. +# Doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. @@ -392,7 +433,7 @@ SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES then doxygen will reuse the documentation of the first +# tag is set to YES then Doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. @@ -450,34 +491,42 @@ TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the -# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small -# doxygen will become slower. If the cache is too large, memory is wasted. The +# code, Doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# Doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 -# symbols. At the end of a run doxygen will report the cache usage and suggest +# symbols. At the end of a run Doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 -# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use -# during processing. When set to 0 doxygen will based this on the number of +# The NUM_PROC_THREADS specifies the number of threads Doxygen is allowed to use +# during processing. When set to 0 Doxygen will based this on the number of # cores available in the system. You can set it explicitly to a value larger # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple # threads. Since this is still an experimental feature the default is set to 1, -# which efficively disables parallel processing. Please report any issues you +# which effectively disables parallel processing. Please report any issues you # encounter. Generating dot graphs in parallel is controlled by the # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. NUM_PROC_THREADS = 1 +# If the TIMESTAMP tag is set different from NO then each generated page will +# contain the date or date and time when the page was generated. Setting this to +# NO can help when comparing the output of multiple runs. +# Possible values are: YES, NO, DATETIME and DATE. +# The default value is: NO. + +TIMESTAMP = NO + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- -# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# If the EXTRACT_ALL tag is set to YES, Doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. @@ -543,7 +592,7 @@ EXTRACT_ANON_NSPACES = NO RESOLVE_UNNAMED_PARAMS = YES -# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. @@ -551,22 +600,31 @@ RESOLVE_UNNAMED_PARAMS = YES HIDE_UNDOC_MEMBERS = NO -# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option -# has no effect if EXTRACT_ALL is enabled. +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# If the HIDE_UNDOC_NAMESPACES tag is set to YES, Doxygen will hide all +# undocumented namespaces that are normally visible in the namespace hierarchy. +# If set to NO, these namespaces will be included in the various overviews. This +# option has no effect if EXTRACT_ALL is enabled. +# The default value is: YES. + +HIDE_UNDOC_NAMESPACES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all friend # declarations. If set to NO, these declarations will be included in the # documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO -# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. @@ -580,37 +638,44 @@ HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO -# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# With the correct setting of option CASE_SENSE_NAMES Doxygen will better be # able to match the capabilities of the underlying filesystem. In case the # filesystem is case sensitive (i.e. it supports files in the same directory # whose names only differ in casing), the option must be set to YES to properly # deal with such files in case they appear in the input. For filesystems that -# are not case sensitive the option should be be set to NO to properly deal with +# are not case sensitive the option should be set to NO to properly deal with # output files written for symbols that only differ in casing, such as for two # classes, one named CLASS and the other named Class, and to also support # references to files without having to specify the exact matching casing. On -# Windows (including Cygwin) and MacOS, users should typically set this option +# Windows (including Cygwin) and macOS, users should typically set this option # to NO, whereas on Linux or other Unix flavors it should typically be set to # YES. -# The default value is: system dependent. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. CASE_SENSE_NAMES = YES -# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# If the HIDE_SCOPE_NAMES tag is set to NO then Doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO -# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then Doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO -# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES then Doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. @@ -623,7 +688,7 @@ SHOW_INCLUDE_FILES = YES SHOW_GROUPED_MEMB_INC = NO -# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. @@ -635,14 +700,14 @@ FORCE_LOCAL_INCLUDES = NO INLINE_INFO = YES -# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# If the SORT_MEMBER_DOCS tag is set to YES then Doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = NO -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# If the SORT_BRIEF_DOCS tag is set to YES then Doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. @@ -650,7 +715,7 @@ SORT_MEMBER_DOCS = NO SORT_BRIEF_DOCS = NO -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then Doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. @@ -662,7 +727,7 @@ SORT_BRIEF_DOCS = NO SORT_MEMBERS_CTORS_1ST = NO -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# If the SORT_GROUP_NAMES tag is set to YES then Doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. @@ -679,11 +744,11 @@ SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# If the STRICT_PROTO_MATCHING option is enabled and Doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a -# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# simple string match. By disabling STRICT_PROTO_MATCHING Doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. @@ -753,24 +818,25 @@ SHOW_FILES = YES SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from +# Doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided -# by doxygen. Whatever the program writes to standard output is used as the file +# by Doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated +# by Doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. You can +# that represents Doxygen's defaults, run Doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. # -# Note that if you run doxygen from a directory containing a file called -# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# Note that if you run Doxygen from a directory containing a file called +# DoxygenLayout.xml, Doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = @@ -785,19 +851,35 @@ LAYOUT_FILE = CITE_BIB_FILES = +# The EXTERNAL_TOOL_PATH tag can be used to extend the search path (PATH +# environment variable) so that external tools such as latex and gs can be +# found. +# Note: Directories specified with EXTERNAL_TOOL_PATH are added in front of the +# path already specified by the PATH variable, and are added in the order +# specified. +# Note: This option is particularly useful for macOS version 14 (Sonoma) and +# higher, when running Doxygen from Doxywizard, because in this case any user- +# defined changes to the PATH are ignored. A typical example on macOS is to set +# EXTERNAL_TOOL_PATH = /Library/TeX/texbin /usr/local/bin +# together with the standard path, the full search path used by doxygen when +# launching external tools will then become +# PATH=/Library/TeX/texbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin + +EXTERNAL_TOOL_PATH = + #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to -# standard output by doxygen. If QUIET is set to YES this implies that the +# standard output by Doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# generated to standard error (stderr) by Doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. @@ -805,52 +887,89 @@ QUIET = NO WARNINGS = YES -# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# If the WARN_IF_UNDOCUMENTED tag is set to YES then Doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES -# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. +# If the WARN_IF_DOC_ERROR tag is set to YES, Doxygen will generate warnings for +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES +# If WARN_IF_INCOMPLETE_DOC is set to YES, Doxygen will warn about incomplete +# function parameter documentation. If set to NO, Doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return -# value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. If -# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# value. If set to NO, Doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC # The default value is: NO. WARN_NO_PARAMDOC = NO -# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, Doxygen will warn about +# undocumented enumeration values. If set to NO, Doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + +# If the WARN_AS_ERROR tag is set to YES then Doxygen will immediately stop when # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS -# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but -# at the end of the doxygen process doxygen will return with a non-zero status. -# Possible values are: NO, YES and FAIL_ON_WARNINGS. +# then Doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the Doxygen process Doxygen will return with a non-zero status. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then Doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined Doxygen will not +# write the warning messages in between other messages but write them at the end +# of a run, in case a WARN_LOGFILE is defined the warning messages will be +# besides being in the defined file also be shown at the end of a run, unless +# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case +# the behavior will remain as with the setting FAIL_ON_WARNINGS. +# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. # The default value is: NO. WARN_AS_ERROR = NO -# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# The WARN_FORMAT tag determines the format of the warning messages that Doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of Doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard -# error (stderr). +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). WARN_LOGFILE = @@ -867,37 +986,52 @@ WARN_LOGFILE = INPUT = Overview.md ../include/Spectra ../include/Spectra/MatOp ../include/Spectra/Util # This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: # https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING # The default value is: UTF-8. INPUT_ENCODING = UTF-8 +# This tag can be used to specify the character encoding of the source files +# that Doxygen parses. The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). +# See also: INPUT_ENCODING for further information on supported encodings. + +INPUT_FILE_ENCODING = + # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not -# read by doxygen. +# read by Doxygen. # # Note the list of default checked file patterns might differ from the list of # default file extension mappings. # -# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, -# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, -# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), -# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, -# *.ucf, *.qsf and *.ice. +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, +# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, +# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, +# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to +# be provided as Doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.c \ *.cc \ *.cxx \ + *.cxxm \ *.cpp \ + *.cppm \ + *.ccm \ *.c++ \ + *.c++m \ *.java \ *.ii \ *.ixx \ @@ -912,6 +1046,8 @@ FILE_PATTERNS = *.c \ *.hxx \ *.hpp \ *.h++ \ + *.ixx \ + *.l \ *.cs \ *.d \ *.php \ @@ -949,7 +1085,7 @@ RECURSIVE = NO # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # -# Note that relative paths are relative to the directory from which doxygen is +# Note that relative paths are relative to the directory from which Doxygen is # run. EXCLUDE = @@ -974,10 +1110,7 @@ EXCLUDE_PATTERNS = # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* +# ANamespace::AClass, ANamespace::*Test EXCLUDE_SYMBOLS = @@ -1007,7 +1140,7 @@ EXAMPLE_RECURSIVE = NO IMAGE_PATH = -# The INPUT_FILTER tag can be used to specify a program that doxygen should +# The INPUT_FILTER tag can be used to specify a program that Doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # @@ -1022,9 +1155,14 @@ IMAGE_PATH = # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # +# Note that Doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not -# properly processed by doxygen. +# properly processed by Doxygen. INPUT_FILTER = @@ -1037,7 +1175,7 @@ INPUT_FILTER = # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not -# properly processed by doxygen. +# properly processed by Doxygen. FILTER_PATTERNS = @@ -1059,10 +1197,28 @@ FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub -# and want to reuse the introduction page also for the doxygen output. +# and want to reuse the introduction page also for the Doxygen output. USE_MDFILE_AS_MAINPAGE = Overview.md +# If the IMPLICIT_DIR_DOCS tag is set to YES, any README.md file found in sub- +# directories of the project's root, is used as the documentation for that sub- +# directory, except when the README.md starts with a \dir, \page or \mainpage +# command. If set to NO, the README.md file needs to start with an explicit \dir +# command in order to be used as directory documentation. +# The default value is: YES. + +IMPLICIT_DIR_DOCS = YES + +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 + #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- @@ -1077,12 +1233,13 @@ USE_MDFILE_AS_MAINPAGE = Overview.md SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body of functions, -# classes and enums directly into the documentation. +# multi-line macros, enums or list initialized variables directly into the +# documentation. # The default value is: NO. INLINE_SOURCES = NO -# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct Doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. @@ -1120,7 +1277,7 @@ REFERENCES_LINK_SOURCE = YES SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will -# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# point to the HTML generated by the htags(1) tool instead of Doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. @@ -1134,14 +1291,14 @@ SOURCE_TOOLTIPS = YES # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # -# The result: instead of the source browser generated by doxygen, the links to +# The result: instead of the source browser generated by Doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO -# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# If the VERBATIM_HEADERS tag is set the YES then Doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. @@ -1149,6 +1306,46 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES +# If the CLANG_ASSISTED_PARSING tag is set to YES then Doxygen will use the +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which Doxygen's built-in parser lacks the necessary type information. +# Note: The availability of this option depends on whether or not Doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS +# tag is set to YES then Doxygen will add the directory of each input to the +# include path. +# The default value is: YES. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_ADD_INC_PATHS = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by Doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not Doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -1160,10 +1357,11 @@ VERBATIM_HEADERS = YES ALPHABETICAL_INDEX = NO -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = @@ -1172,7 +1370,7 @@ IGNORE_PREFIX = # Configuration options related to the HTML output #--------------------------------------------------------------------------- -# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# If the GENERATE_HTML tag is set to YES, Doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES @@ -1193,40 +1391,40 @@ HTML_OUTPUT = html HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for -# each generated HTML page. If the tag is left blank doxygen will generate a +# each generated HTML page. If the tag is left blank Doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets -# that doxygen needs, which is dependent on the configuration options used (e.g. +# that Doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" -# for information on how to generate the default header that doxygen normally +# for information on how to generate the default header that Doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the -# default header when upgrading to a newer version of doxygen. For a description +# default header when upgrading to a newer version of Doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each -# generated HTML page. If the tag is left blank doxygen will generate a standard +# generated HTML page. If the tag is left blank Doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer -# that doxygen normally uses. +# that Doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of -# the HTML output. If left blank doxygen will generate a default style sheet. +# the HTML output. If left blank Doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style -# sheet that doxygen normally uses. +# sheet that Doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. @@ -1236,13 +1434,18 @@ HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets -# created by doxygen. Using this option one can overrule certain style aspects. +# created by Doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the -# list). For an example see the documentation. +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = @@ -1257,9 +1460,22 @@ HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generates light mode output, DARK always +# generates dark mode output, AUTO_LIGHT automatically sets the mode according +# to the user preference, uses light mode if no preference is set (the default), +# AUTO_DARK automatically sets the mode according to the user preference, uses +# dark mode if no preference is set and TOGGLE allows a user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = AUTO_LIGHT + # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see +# this color. Hue is specified as an angle on a color-wheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. @@ -1269,7 +1485,7 @@ HTML_EXTRA_FILES = HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A +# in the HTML output. For a value of 0 the output will use gray-scales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1287,15 +1503,6 @@ HTML_COLORSTYLE_SAT = 100 HTML_COLORSTYLE_GAMMA = 80 -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to YES can help to show when doxygen was last run and thus if the -# documentation is up to date. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_TIMESTAMP = NO - # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via JavaScript. If disabled, the navigation index will @@ -1315,6 +1522,33 @@ HTML_DYNAMIC_MENUS = YES HTML_DYNAMIC_SECTIONS = NO +# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be +# dynamically folded and expanded in the generated HTML source code. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_CODE_FOLDING = YES + +# If the HTML_COPY_CLIPBOARD tag is set to YES then Doxygen will show an icon in +# the top right corner of code and text fragments that allows the user to copy +# its content to the clipboard. Note this only works if supported by the browser +# and the web page is served via a secure context (see: +# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file: +# protocol. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COPY_CLIPBOARD = YES + +# Doxygen stores a couple of settings persistently in the browser (via e.g. +# cookies). By default these settings apply to all HTML pages generated by +# Doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store +# the settings under a project specific key, such that the user preferences will +# be stored separately. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_PROJECT_COOKIE = + # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to @@ -1332,7 +1566,7 @@ HTML_INDEX_NUM_ENTRIES = 100 # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: # https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To -# create a documentation set, doxygen will generate a Makefile in the HTML +# create a documentation set, Doxygen will generate a Makefile in the HTML # output directory. Running make will produce the docset in that directory and # running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at @@ -1351,6 +1585,13 @@ GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. @@ -1373,14 +1614,18 @@ DOCSET_PUBLISHER_ID = org.doxygen.Publisher DOCSET_PUBLISHER_NAME = Publisher -# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# If the GENERATE_HTMLHELP tag is set to YES then Doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: -# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows. +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline (the HTML help workshop was already many +# years in maintenance mode). You can download the HTML help workshop from the +# web archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). # # The HTML Help Workshop contains a compiler that can convert all HTML output -# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# generated by Doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for @@ -1400,7 +1645,7 @@ CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, -# doxygen will try to run the HTML help compiler on the generated index.hhp. +# Doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. @@ -1434,6 +1679,16 @@ BINARY_TOC = NO TOC_EXPAND = NO +# The SITEMAP_URL tag is used to specify the full URL of the place where the +# generated documentation will be placed on the server by the user during the +# deployment of the documentation. The generated sitemap is called sitemap.xml +# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL +# is specified no sitemap is generated. For information about the sitemap +# protocol see https://www.sitemaps.org +# This tag requires that the tag GENERATE_HTML is set to YES. + +SITEMAP_URL = + # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help @@ -1492,7 +1747,7 @@ QHP_CUST_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location (absolute path -# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# including file name) of Qt's qhelpgenerator. If non-empty Doxygen will try to # run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1524,7 +1779,7 @@ ECLIPSE_DOC_ID = org.doxygen.Project # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. -# The default value is: NO. +# The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO @@ -1536,18 +1791,30 @@ DISABLE_INDEX = NO # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. -# The default value is: NO. +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by Doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. +# The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = YES +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that -# doxygen will group on one line in the generated HTML documentation. +# Doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. @@ -1556,6 +1823,12 @@ GENERATE_TREEVIEW = YES ENUM_VALUES_PER_LINE = 1 +# When the SHOW_ENUM_VALUES tag is set doxygen will show the specified +# enumeration values besides the enumeration mnemonics. +# The default value is: NO. + +SHOW_ENUM_VALUES = NO + # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. @@ -1563,14 +1836,21 @@ ENUM_VALUES_PER_LINE = 1 TREEVIEW_WIDTH = 250 -# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# If the EXT_LINKS_IN_WINDOW option is set to YES, Doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO -# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# If the OBFUSCATE_EMAILS tag is set to YES, Doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + +# If the HTML_FORMULA_FORMAT option is set to svg, Doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # https://inkscape.org) to generate formulas as SVG images instead of PNGs for # the HTML output. These images will generally look nicer at scaled resolutions. @@ -1583,24 +1863,13 @@ HTML_FORMULA_FORMAT = png # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful -# doxygen run you need to manually remove any form_*.png images from the HTML +# Doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANSPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_TRANSPARENT = YES - # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands # to create new LaTeX commands to be used in formulas as building blocks. See # the section "Including formulas" for details. @@ -1618,11 +1887,29 @@ FORMULA_MACROFILE = USE_MATHJAX = YES +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + # When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). # Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for MathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1635,20 +1922,26 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from https://www.mathjax.org before deployment. -# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2 # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = -# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# The MATHJAX_CODEFILE tag can be used to specify a file with JavaScript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an @@ -1657,12 +1950,12 @@ MATHJAX_EXTENSIONS = MATHJAX_CODEFILE = -# When the SEARCHENGINE tag is enabled doxygen will generate a search box for -# the HTML output. The underlying search engine uses javascript and DHTML and +# When the SEARCHENGINE tag is enabled Doxygen will generate a search box for +# the HTML output. The underlying search engine uses JavaScript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. -# For large projects the javascript based search engine can be slow, then +# For large projects the JavaScript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically @@ -1681,7 +1974,7 @@ SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a web server instead of a web client using JavaScript. There # are two flavors of web server based searching depending on the EXTERNAL_SEARCH -# setting. When disabled, doxygen will generate a PHP script for searching and +# setting. When disabled, Doxygen will generate a PHP script for searching and # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing # and searching needs to be provided by external tools. See the section # "External Indexing and Searching" for details. @@ -1690,7 +1983,7 @@ SEARCHENGINE = YES SERVER_BASED_SEARCH = NO -# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP +# When EXTERNAL_SEARCH tag is enabled Doxygen will no longer generate the PHP # script for searching. Instead the search results are written to an XML file # which needs to be processed by an external indexer. Doxygen will invoke an # external search engine pointed to by the SEARCHENGINE_URL option to obtain the @@ -1735,7 +2028,7 @@ SEARCHDATA_FILE = searchdata.xml EXTERNAL_SEARCH_ID = -# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through Doxygen # projects other than the one defined by this configuration file, but that are # all added to the same external search index. Each project needs to have a # unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of @@ -1749,7 +2042,7 @@ EXTRA_SEARCH_MAPPINGS = # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- -# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. +# If the GENERATE_LATEX tag is set to YES, Doxygen will generate LaTeX output. # The default value is: YES. GENERATE_LATEX = NO @@ -1794,7 +2087,7 @@ MAKEINDEX_CMD_NAME = makeindex LATEX_MAKEINDEX_CMD = makeindex -# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX +# If the COMPACT_LATEX tag is set to YES, Doxygen generates more compact LaTeX # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -1823,36 +2116,38 @@ PAPER_TYPE = a4 EXTRA_PACKAGES = -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the -# generated LaTeX document. The header should contain everything until the first -# chapter. If it is left blank doxygen will generate a standard header. See -# section "Doxygen usage" for information on how to let doxygen write the -# default header to a separate file. +# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for +# the generated LaTeX document. The header should contain everything until the +# first chapter. If it is left blank Doxygen will generate a standard header. It +# is highly recommended to start with a default header using +# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty +# and then modify the file new_header.tex. See also section "Doxygen usage" for +# information on how to generate the default header that Doxygen normally uses. # -# Note: Only use a user-defined header if you know what you are doing! The -# following commands have a special meaning inside the header: $title, -# $datetime, $date, $doxygenversion, $projectname, $projectnumber, -# $projectbrief, $projectlogo. Doxygen will replace $title with the empty -# string, for the replacement values of the other commands the user is referred -# to HTML_HEADER. +# Note: Only use a user-defined header if you know what you are doing! +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of Doxygen. The following +# commands have a special meaning inside the header (and footer): For a +# description of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HEADER = -# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the -# generated LaTeX document. The footer should contain everything after the last -# chapter. If it is left blank doxygen will generate a standard footer. See +# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for +# the generated LaTeX document. The footer should contain everything after the +# last chapter. If it is left blank Doxygen will generate a standard footer. See # LATEX_HEADER for more information on how to generate a default footer and what -# special commands can be used inside the footer. -# -# Note: Only use a user-defined footer if you know what you are doing! +# special commands can be used inside the footer. See also section "Doxygen +# usage" for information on how to generate the default footer that Doxygen +# normally uses. Note: Only use a user-defined footer if you know what you are +# doing! # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_FOOTER = # The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined # LaTeX style sheets that are included after the standard style sheets created -# by doxygen. Using this option one can overrule certain style aspects. Doxygen +# by Doxygen. Using this option one can overrule certain style aspects. Doxygen # will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the @@ -1878,7 +2173,7 @@ LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES -# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as +# If the USE_PDFLATEX tag is set to YES, Doxygen will use the engine as # specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX # files. Set this option to YES, to get a higher quality PDF documentation. # @@ -1888,47 +2183,35 @@ PDF_HYPERLINKS = YES USE_PDFLATEX = YES -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode -# command to the generated LaTeX files. This will instruct LaTeX to keep running -# if errors occur, instead of asking the user for help. This option is also used -# when generating formulas in HTML. +# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error. +# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch +# mode nothing is printed on the terminal, errors are scrolled as if is +# hit at every error; missing files that TeX tries to input or request from +# keyboard input (\read on a not open input stream) cause the job to abort, +# NON_STOP In nonstop mode the diagnostic message will appear on the terminal, +# but there is no possibility of user interaction just like in batch mode, +# SCROLL In scroll mode, TeX will stop only for missing files to input or if +# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at +# each error, asking for user intervention. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_BATCHMODE = NO -# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the +# If the LATEX_HIDE_INDICES tag is set to YES then Doxygen will not include the # index chapters (such as File Index, Compound Index, etc.) in the output. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HIDE_INDICES = NO -# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source -# code with syntax highlighting in the LaTeX output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_SOURCE_CODE = NO - # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See # https://en.wikipedia.org/wiki/BibTeX and \cite for more info. -# The default value is: plain. +# The default value is: plainnat. # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_BIB_STYLE = plain - -# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated -# page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_TIMESTAMP = NO +LATEX_BIB_STYLE = plainnat # The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) # path from which the emoji images will be read. If a relative path is entered, @@ -1942,7 +2225,7 @@ LATEX_EMOJI_DIRECTORY = # Configuration options related to the RTF output #--------------------------------------------------------------------------- -# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The +# If the GENERATE_RTF tag is set to YES, Doxygen will generate RTF output. The # RTF output is optimized for Word 97 and may not look too pretty with other RTF # readers/editors. # The default value is: NO. @@ -1957,7 +2240,7 @@ GENERATE_RTF = NO RTF_OUTPUT = rtf -# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF +# If the COMPACT_RTF tag is set to YES, Doxygen generates more compact RTF # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -1977,38 +2260,36 @@ COMPACT_RTF = NO RTF_HYPERLINKS = NO -# Load stylesheet definitions from file. Syntax is similar to doxygen's +# Load stylesheet definitions from file. Syntax is similar to Doxygen's # configuration file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. # # See also section "Doxygen usage" for information on how to generate the -# default style sheet that doxygen normally uses. +# default style sheet that Doxygen normally uses. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an RTF document. Syntax is -# similar to doxygen's configuration file. A template extensions file can be +# similar to Doxygen's configuration file. A template extensions file can be # generated using doxygen -e rtf extensionFile. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_EXTENSIONS_FILE = -# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code -# with syntax highlighting in the RTF output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. +# The RTF_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the RTF_OUTPUT output directory. +# Note that the files will be copied as-is; there are no commands or markers +# available. # This tag requires that the tag GENERATE_RTF is set to YES. -RTF_SOURCE_CODE = NO +RTF_EXTRA_FILES = #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- -# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for +# If the GENERATE_MAN tag is set to YES, Doxygen will generate man pages for # classes and files. # The default value is: NO. @@ -2039,7 +2320,7 @@ MAN_EXTENSION = .3 MAN_SUBDIR = -# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, then it # will generate one additional man file for each entity documented in the real # man page(s). These additional files only source the real man page, but without # them the man command would be unable to find the correct page. @@ -2052,7 +2333,7 @@ MAN_LINKS = NO # Configuration options related to the XML output #--------------------------------------------------------------------------- -# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that +# If the GENERATE_XML tag is set to YES, Doxygen will generate an XML file that # captures the structure of the code including all documentation. # The default value is: NO. @@ -2066,7 +2347,7 @@ GENERATE_XML = NO XML_OUTPUT = xml -# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program +# If the XML_PROGRAMLISTING tag is set to YES, Doxygen will dump the program # listings (including syntax highlighting and cross-referencing information) to # the XML output. Note that enabling this will significantly increase the size # of the XML output. @@ -2075,7 +2356,7 @@ XML_OUTPUT = xml XML_PROGRAMLISTING = YES -# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, Doxygen will include # namespace members in file scope as well, matching the HTML output. # The default value is: NO. # This tag requires that the tag GENERATE_XML is set to YES. @@ -2086,7 +2367,7 @@ XML_NS_MEMB_FILE_SCOPE = NO # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- -# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files +# If the GENERATE_DOCBOOK tag is set to YES, Doxygen will generate Docbook files # that can be used to generate PDF. # The default value is: NO. @@ -2100,32 +2381,49 @@ GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook -# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the -# program listings (including syntax highlighting and cross-referencing -# information) to the DOCBOOK output. Note that enabling this will significantly -# increase the size of the DOCBOOK output. -# The default value is: NO. -# This tag requires that the tag GENERATE_DOCBOOK is set to YES. - -DOCBOOK_PROGRAMLISTING = NO - #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- -# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an -# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# If the GENERATE_AUTOGEN_DEF tag is set to YES, Doxygen will generate an +# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures # the structure of the code including all documentation. Note that this feature # is still experimental and incomplete at the moment. # The default value is: NO. GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to Sqlite3 output +#--------------------------------------------------------------------------- + +# If the GENERATE_SQLITE3 tag is set to YES Doxygen will generate a Sqlite3 +# database with symbols found by Doxygen stored in tables. +# The default value is: NO. + +GENERATE_SQLITE3 = NO + +# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be +# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put +# in front of it. +# The default directory is: sqlite3. +# This tag requires that the tag GENERATE_SQLITE3 is set to YES. + +SQLITE3_OUTPUT = sqlite3 + +# The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db +# database file will be recreated with each Doxygen run. If set to NO, Doxygen +# will warn if a database file is already found and not modify it. +# The default value is: YES. +# This tag requires that the tag GENERATE_SQLITE3 is set to YES. + +SQLITE3_RECREATE_DB = YES + #--------------------------------------------------------------------------- # Configuration options related to the Perl module output #--------------------------------------------------------------------------- -# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module +# If the GENERATE_PERLMOD tag is set to YES, Doxygen will generate a Perl module # file that captures the structure of the code including all documentation. # # Note that this feature is still experimental and incomplete at the moment. @@ -2133,7 +2431,7 @@ GENERATE_AUTOGEN_DEF = NO GENERATE_PERLMOD = NO -# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary +# If the PERLMOD_LATEX tag is set to YES, Doxygen will generate the necessary # Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI # output from the Perl module output. # The default value is: NO. @@ -2163,13 +2461,13 @@ PERLMOD_MAKEVAR_PREFIX = # Configuration options related to the preprocessor #--------------------------------------------------------------------------- -# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all +# If the ENABLE_PREPROCESSING tag is set to YES, Doxygen will evaluate all # C-preprocessor directives found in the sources and include files. # The default value is: YES. ENABLE_PREPROCESSING = YES -# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# If the MACRO_EXPANSION tag is set to YES, Doxygen will expand all macro names # in the source code. If set to NO, only conditional compilation will be # performed. Macro expansion can be done in a controlled way by setting # EXPAND_ONLY_PREDEF to YES. @@ -2195,7 +2493,8 @@ SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by the -# preprocessor. +# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of +# RECURSIVE has no effect here. # This tag requires that the tag SEARCH_INCLUDES is set to YES. INCLUDE_PATH = @@ -2227,7 +2526,7 @@ PREDEFINED = protected=private EXPAND_AS_DEFINED = -# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will +# If the SKIP_FUNCTION_MACROS tag is set to YES then Doxygen's preprocessor will # remove all references to function-like macros that are alone on a line, have # an all uppercase name, and do not end with a semicolon. Such function macros # are typically used for boiler-plate code, and will confuse the parser if not @@ -2251,26 +2550,26 @@ SKIP_FUNCTION_MACROS = YES # section "Linking to external documentation" for more information about the use # of tag files. # Note: Each tag file must have a unique name (where the name does NOT include -# the path). If a tag file is not located in the directory in which doxygen is +# the path). If a tag file is not located in the directory in which Doxygen is # run, you must also specify the path to the tagfile here. TAGFILES = -# When a file name is specified after GENERATE_TAGFILE, doxygen will create a +# When a file name is specified after GENERATE_TAGFILE, Doxygen will create a # tag file that is based on the input files it reads. See section "Linking to # external documentation" for more information about the usage of tag files. GENERATE_TAGFILE = -# If the ALLEXTERNALS tag is set to YES, all external class will be listed in -# the class index. If set to NO, only the inherited external classes will be -# listed. +# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces +# will be listed in the class and namespace index. If set to NO, only the +# inherited external classes will be listed. # The default value is: NO. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will be +# in the topic index. If set to NO, only the current project's groups will be # listed. # The default value is: YES. @@ -2284,42 +2583,26 @@ EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES #--------------------------------------------------------------------------- -# Configuration options related to the dot tool +# Configuration options related to diagram generator tools #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram -# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to -# NO turns the diagrams off. Note that this option also works with HAVE_DOT -# disabled, but it is recommended to install and use dot, since it yields more -# powerful graphs. -# The default value is: YES. - -CLASS_DIAGRAMS = YES - -# You can include diagrams made with dia in doxygen documentation. Doxygen will -# then run dia to produce the diagram and insert it in the documentation. The -# DIA_PATH tag allows you to specify the directory where the dia binary resides. -# If left empty dia is assumed to be found in the default search path. - -DIA_PATH = - # If set to YES the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. # The default value is: YES. HIDE_UNDOC_RELATIONS = YES -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# If you set the HAVE_DOT tag to YES then Doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz (see: -# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # Bell Labs. The other options in this section have no effect if this option is # set to NO # The default value is: NO. HAVE_DOT = NO -# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed -# to run in parallel. When set to 0 doxygen will base this on the number of +# The DOT_NUM_THREADS specifies the number of dot invocations Doxygen is allowed +# to run in parallel. When set to 0 Doxygen will base this on the number of # processors available in the system. You can set it explicitly to a value # larger than 0 to get control over the balance between CPU load and processing # speed. @@ -2328,55 +2611,83 @@ HAVE_DOT = NO DOT_NUM_THREADS = 0 -# When you want a differently looking font in the dot files that doxygen -# generates you can specify the font name using DOT_FONTNAME. You need to make -# sure dot is able to find the font, which can be done by putting it in a -# standard location or by setting the DOTFONTPATH environment variable or by -# setting DOT_FONTPATH to the directory containing the font. -# The default value is: Helvetica. +# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of +# subgraphs. When you want a differently looking font in the dot files that +# Doxygen generates you can specify fontname, fontcolor and fontsize attributes. +# For details please see Node, +# Edge and Graph Attributes specification You need to make sure dot is able +# to find the font, which can be done by putting it in a standard location or by +# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. Default graphviz fontsize is 14. +# The default value is: fontname=Helvetica,fontsize=10. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" + +# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can +# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. Complete documentation about +# arrows shapes. +# The default value is: labelfontname=Helvetica,labelfontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTNAME = Helvetica +DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" -# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of -# dot graphs. -# Minimum value: 4, maximum value: 24, default value: 10. +# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes +# around nodes set 'shape=plain' or 'shape=plaintext' Shapes specification +# The default value is: shape=box,height=0.2,width=0.4. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTSIZE = 10 +DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" -# By default doxygen will tell dot to use the default font as specified with -# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set -# the path where dot can find it using this tag. +# You can set the path where dot can find font specified with fontname in +# DOT_COMMON_ATTR and others dot attributes. # This tag requires that the tag HAVE_DOT is set to YES. DOT_FONTPATH = -# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for -# each documented class showing the direct and indirect inheritance relations. -# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then Doxygen will +# generate a graph for each documented class showing the direct and indirect +# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and +# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case +# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the +# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used. +# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance +# relations will be shown as texts / links. Explicit enabling an inheritance +# graph or choosing a different representation for an inheritance graph of a +# specific class, can be accomplished by means of the command \inheritancegraph. +# Disabling an inheritance graph can be accomplished by means of the command +# \hideinheritancegraph. +# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. CLASS_GRAPH = YES -# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a +# If the COLLABORATION_GRAPH tag is set to YES then Doxygen will generate a # graph for each documented class showing the direct and indirect implementation # dependencies (inheritance, containment, and class references variables) of the -# class with other documented classes. +# class with other documented classes. Explicit enabling a collaboration graph, +# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the +# command \collaborationgraph. Disabling a collaboration graph can be +# accomplished by means of the command \hidecollaborationgraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. COLLABORATION_GRAPH = YES -# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for -# groups, showing the direct groups dependencies. +# If the GROUP_GRAPHS tag is set to YES then Doxygen will generate a graph for +# groups, showing the direct groups dependencies. Explicit enabling a group +# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means +# of the command \groupgraph. Disabling a directory graph can be accomplished by +# means of the command \hidegroupgraph. See also the chapter Grouping in the +# manual. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GROUP_GRAPHS = YES -# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and +# If the UML_LOOK tag is set to YES, Doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. # The default value is: NO. @@ -2397,10 +2708,10 @@ UML_LOOK = NO UML_LIMIT_NUM_FIELDS = 10 -# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and +# If the DOT_UML_DETAILS tag is set to NO, Doxygen will show attributes and # methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS -# tag is set to YES, doxygen will add type and arguments for attributes and -# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen +# tag is set to YES, Doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, Doxygen # will not generate fields with class member information in the UML graphs. The # class diagrams will look similar to the default class diagrams but using UML # notation for the relationships. @@ -2412,8 +2723,8 @@ DOT_UML_DETAILS = NO # The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters # to display on a single line. If the actual line length exceeds this threshold -# significantly it will wrapped across multiple lines. Some heuristics are apply -# to avoid ugly line breaks. +# significantly it will be wrapped across multiple lines. Some heuristics are +# applied to avoid ugly line breaks. # Minimum value: 0, maximum value: 1000, default value: 17. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2428,24 +2739,29 @@ DOT_WRAP_THRESHOLD = 17 TEMPLATE_RELATIONS = NO # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to -# YES then doxygen will generate a graph for each documented file showing the +# YES then Doxygen will generate a graph for each documented file showing the # direct and indirect include dependencies of the file with other documented -# files. +# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO, +# can be accomplished by means of the command \includegraph. Disabling an +# include graph can be accomplished by means of the command \hideincludegraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. INCLUDE_GRAPH = YES # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are -# set to YES then doxygen will generate a graph for each documented file showing +# set to YES then Doxygen will generate a graph for each documented file showing # the direct and indirect include dependencies of the file with other documented -# files. +# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set +# to NO, can be accomplished by means of the command \includedbygraph. Disabling +# an included by graph can be accomplished by means of the command +# \hideincludedbygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. INCLUDED_BY_GRAPH = YES -# If the CALL_GRAPH tag is set to YES then doxygen will generate a call +# If the CALL_GRAPH tag is set to YES then Doxygen will generate a call # dependency graph for every global function or class method. # # Note that enabling this option will significantly increase the time of a run. @@ -2457,7 +2773,7 @@ INCLUDED_BY_GRAPH = YES CALL_GRAPH = NO -# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller +# If the CALLER_GRAPH tag is set to YES then Doxygen will generate a caller # dependency graph for every global function or class method. # # Note that enabling this option will significantly increase the time of a run. @@ -2469,26 +2785,36 @@ CALL_GRAPH = NO CALLER_GRAPH = NO -# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical +# If the GRAPHICAL_HIERARCHY tag is set to YES then Doxygen will graphical # hierarchy of all classes instead of a textual one. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GRAPHICAL_HIERARCHY = YES -# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the +# If the DIRECTORY_GRAPH tag is set to YES then Doxygen will show the # dependencies a directory has on other directories in a graphical way. The # dependency relations are determined by the #include relations between the -# files in the directories. +# files in the directories. Explicit enabling a directory graph, when +# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command +# \directorygraph. Disabling a directory graph can be accomplished by means of +# the command \hidedirectorygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. DIRECTORY_GRAPH = YES +# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels +# of child directories generated in directory dependency graphs by dot. +# Minimum value: 1, maximum value: 25, default value: 1. +# This tag requires that the tag DIRECTORY_GRAPH is set to YES. + +DIR_GRAPH_MAX_DEPTH = 1 + # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. For an explanation of the image formats see the section # output formats in the documentation of the dot tool (Graphviz (see: -# http://www.graphviz.org/)). +# https://www.graphviz.org/)). # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). @@ -2525,11 +2851,12 @@ DOT_PATH = DOTFILE_DIRS = -# The MSCFILE_DIRS tag can be used to specify one or more directories that -# contain msc files that are included in the documentation (see the \mscfile -# command). +# You can include diagrams made with dia in Doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. -MSCFILE_DIRS = +DIA_PATH = # The DIAFILE_DIRS tag can be used to specify one or more directories that # contain dia files that are included in the documentation (see the \diafile @@ -2537,28 +2864,34 @@ MSCFILE_DIRS = DIAFILE_DIRS = -# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the -# path where java can find the plantuml.jar file. If left blank, it is assumed -# PlantUML is not used or called during a preprocessing step. Doxygen will -# generate a warning when it encounters a \startuml command in this case and -# will not generate output for the diagram. +# When using PlantUML, the PLANTUML_JAR_PATH tag should be used to specify the +# path where java can find the plantuml.jar file or to the filename of jar file +# to be used. If left blank, it is assumed PlantUML is not used or called during +# a preprocessing step. Doxygen will generate a warning when it encounters a +# \startuml command in this case and will not generate output for the diagram. PLANTUML_JAR_PATH = -# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a -# configuration file for plantuml. +# When using PlantUML, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for PlantUML. PLANTUML_CFG_FILE = -# When using plantuml, the specified paths are searched for files specified by -# the !include statement in a plantuml block. +# When using PlantUML, the specified paths are searched for files specified by +# the !include statement in a PlantUML block. PLANTUML_INCLUDE_PATH = +# The PLANTUMLFILE_DIRS tag can be used to specify one or more directories that +# contain PlantUml files that are included in the documentation (see the +# \plantumlfile command). + +PLANTUMLFILE_DIRS = + # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes -# larger than this value, doxygen will truncate the graph, which is visualized -# by representing a node as a red box. Note that doxygen if the number of direct +# larger than this value, Doxygen will truncate the graph, which is visualized +# by representing a node as a red box. Note that if the number of direct # children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that # the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. @@ -2579,18 +2912,6 @@ DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not seem -# to support this out of the box. -# -# Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to -# read). -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_TRANSPARENT = NO - # Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support @@ -2600,19 +2921,37 @@ DOT_TRANSPARENT = NO DOT_MULTI_TARGETS = NO -# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page +# If the GENERATE_LEGEND tag is set to YES Doxygen will generate a legend page # explaining the meaning of the various boxes and arrows in the dot generated # graphs. +# Note: This tag requires that UML_LOOK isn't set, i.e. the Doxygen internal +# graphical representation for inheritance and collaboration diagrams is used. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate +# If the DOT_CLEANUP tag is set to YES, Doxygen will remove the intermediate # files that are used to generate the various graphs. # -# Note: This setting is not only used for dot files but also for msc and -# plantuml temporary files. +# Note: This setting is not only used for dot files but also for msc temporary +# files. # The default value is: YES. DOT_CLEANUP = YES + +# You can define message sequence charts within Doxygen comments using the \msc +# command. If the MSCGEN_TOOL tag is left empty (the default), then Doxygen will +# use a built-in version of mscgen tool to produce the charts. Alternatively, +# the MSCGEN_TOOL tag can also specify the name an external tool. For instance, +# specifying prog as the value, Doxygen will call the tool as prog -T +# -o . The external tool should support +# output file formats "png", "eps", "svg", and "ismap". + +MSCGEN_TOOL = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = diff --git a/doxygen/Overview.md b/doxygen/Overview.md index 5fe7018..09c66f9 100644 --- a/doxygen/Overview.md +++ b/doxygen/Overview.md @@ -73,6 +73,8 @@ Below is a list of the available eigen solvers in **Spectra**: For generalized eigen solver with real symmetric matrices, using the shift-and-invert mode - \link Spectra::DavidsonSymEigsSolver DavidsonSymEigsSolver\endlink: Jacobi-Davidson eigen solver for real symmetric matrices, with the DPR correction method +- \link Spectra::HermEigsSolver HermEigsSolver\endlink: + For complex Hermitian matrices ## Examples diff --git a/include/Spectra/GenEigsBase.h b/include/Spectra/GenEigsBase.h index 2a512bb..f466c4c 100644 --- a/include/Spectra/GenEigsBase.h +++ b/include/Spectra/GenEigsBase.h @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2023 Yixuan Qiu +// Copyright (C) 2018-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/GenEigsComplexShiftSolver.h b/include/Spectra/GenEigsComplexShiftSolver.h index 098f6d5..2212669 100644 --- a/include/Spectra/GenEigsComplexShiftSolver.h +++ b/include/Spectra/GenEigsComplexShiftSolver.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/GenEigsRealShiftSolver.h b/include/Spectra/GenEigsRealShiftSolver.h index 6d8dfcb..6e694e6 100644 --- a/include/Spectra/GenEigsRealShiftSolver.h +++ b/include/Spectra/GenEigsRealShiftSolver.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/GenEigsSolver.h b/include/Spectra/GenEigsSolver.h index 28b58ea..4e63290 100644 --- a/include/Spectra/GenEigsSolver.h +++ b/include/Spectra/GenEigsSolver.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/SymEigsBase.h b/include/Spectra/HermEigsBase.h similarity index 81% rename from include/Spectra/SymEigsBase.h rename to include/Spectra/HermEigsBase.h index b6648db..2426b5f 100644 --- a/include/Spectra/SymEigsBase.h +++ b/include/Spectra/HermEigsBase.h @@ -1,11 +1,11 @@ -// Copyright (C) 2018-2023 Yixuan Qiu +// Copyright (C) 2018-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. -#ifndef SPECTRA_SYM_EIGS_BASE_H -#define SPECTRA_SYM_EIGS_BASE_H +#ifndef SPECTRA_HERM_EIGS_BASE_H +#define SPECTRA_HERM_EIGS_BASE_H #include #include // std::vector @@ -35,19 +35,26 @@ namespace Spectra { /// /// \ingroup EigenSolver /// -/// This is the base class for symmetric eigen solvers, mainly for internal use. -/// It is kept here to provide the documentation for member functions of concrete eigen solvers -/// such as SymEigsSolver and SymEigsShiftSolver. +/// This is the base class for Hermitian (and real symmetric) eigen solvers, +/// mainly for internal use. +/// It is kept here to provide the documentation for member functions of +/// concrete eigen solvers such as SymEigsSolver, HermEigsSolver, SymEigsShiftSolver, etc. /// template -class SymEigsBase +class HermEigsBase { private: using Scalar = typename OpType::Scalar; + // The real part type of the matrix element, e.g., + // Scalar = double => RealScalar = double + // Scalar = std::complex => RealScalar = double + using RealScalar = typename Eigen::NumTraits::Real; using Index = Eigen::Index; using Matrix = Eigen::Matrix; using Vector = Eigen::Matrix; - using Array = Eigen::Array; + using RealMatrix = Eigen::Matrix; + using RealVector = Eigen::Matrix; + using RealArray = Eigen::Array; using BoolArray = Eigen::Array; using MapMat = Eigen::Map; using MapVec = Eigen::Map; @@ -74,11 +81,11 @@ class SymEigsBase Index m_niter; // number of restarting iterations LanczosFac m_fac; // Lanczos factorization - Vector m_ritz_val; // Ritz values + RealVector m_ritz_val; // Ritz values private: - Matrix m_ritz_vec; // Ritz vectors - Vector m_ritz_est; // last row of m_ritz_vec, also called the Ritz estimates + RealMatrix m_ritz_vec; // Ritz vectors + RealVector m_ritz_est; // last row of m_ritz_vec, also called the Ritz estimates BoolArray m_ritz_conv; // indicator of the convergence of Ritz values CompInfo m_info; // status of the computation // clang-format on @@ -99,18 +106,22 @@ class SymEigsBase if (k >= m_ncv) return; - TridiagQR decomp(m_ncv); - Matrix Q = Matrix::Identity(m_ncv, m_ncv); + // QR decomposition on a real symmetric matrix + TridiagQR decomp(m_ncv); + // Q is a real orthogonal matrix + RealMatrix Q = RealMatrix::Identity(m_ncv, m_ncv); // Apply large shifts first const int nshift = m_ncv - k; - Vector shifts = m_ritz_val.tail(nshift); - std::sort(shifts.data(), shifts.data() + nshift, [](const Scalar& v1, const Scalar& v2) { return abs(v1) > abs(v2); }); + RealVector shifts = m_ritz_val.tail(nshift); + std::sort(shifts.data(), shifts.data() + nshift, + [](const RealScalar& v1, const RealScalar& v2) { return abs(v1) > abs(v2); }); for (Index i = 0; i < nshift; i++) { // QR decomposition of H-mu*I, mu is the shift - decomp.compute(m_fac.matrix_H(), shifts[i]); + // H is known to be a real symmetric matrix + decomp.compute(m_fac.matrix_H().real(), shifts[i]); // Q -> Q * Qi decomp.apply_YQ(Q); @@ -138,19 +149,19 @@ class SymEigsBase } // Calculates the number of converged Ritz values - Index num_converged(const Scalar& tol) + Index num_converged(const RealScalar& tol) { using std::pow; // The machine precision, ~= 1e-16 for the "double" type - const Scalar eps = TypeTraits::epsilon(); + const RealScalar eps = TypeTraits::epsilon(); // std::pow() is not constexpr, so we do not declare eps23 to be constexpr // But most compilers should be able to compute eps23 at compile time - const Scalar eps23 = pow(eps, Scalar(2) / 3); + const RealScalar eps23 = pow(eps, RealScalar(2) / 3); // thresh = tol * max(eps23, abs(theta)), theta for Ritz value - Array thresh = tol * m_ritz_val.head(m_nev).array().abs().max(eps23); - Array resid = m_ritz_est.head(m_nev).array().abs() * m_fac.f_norm(); + RealArray thresh = tol * m_ritz_val.head(m_nev).array().abs().max(eps23); + RealArray resid = m_ritz_est.head(m_nev).array().abs() * m_fac.f_norm(); // Converged "wanted" Ritz values m_ritz_conv = (resid < thresh); @@ -164,7 +175,7 @@ class SymEigsBase // A very small value, but 1.0 / near_0 does not overflow // ~= 1e-307 for the "double" type - const Scalar near_0 = TypeTraits::min() * Scalar(10); + const RealScalar near_0 = TypeTraits::min() * RealScalar(10); Index nev_new = m_nev; for (Index i = m_nev; i < m_ncv; i++) @@ -187,9 +198,9 @@ class SymEigsBase // Retrieves and sorts Ritz values and Ritz vectors void retrieve_ritzpair(SortRule selection) { - TridiagEigen decomp(m_fac.matrix_H()); - const Vector& evals = decomp.eigenvalues(); - const Matrix& evecs = decomp.eigenvectors(); + TridiagEigen decomp(m_fac.matrix_H().real()); + const RealVector& evals = decomp.eigenvalues(); + const RealMatrix& evecs = decomp.eigenvectors(); // Sort Ritz values and put the wanted ones at the beginning std::vector ind = argsort(selection, evals, m_ncv); @@ -217,8 +228,8 @@ class SymEigsBase std::vector ind = argsort(sort_rule, m_ritz_val, m_nev); - Vector new_ritz_val(m_ncv); - Matrix new_ritz_vec(m_ncv, m_nev); + RealVector new_ritz_val(m_ncv); + RealMatrix new_ritz_vec(m_ncv, m_nev); BoolArray new_ritz_conv(m_nev); for (Index i = 0; i < m_nev; i++) @@ -237,7 +248,7 @@ class SymEigsBase /// \cond // If op is an lvalue - SymEigsBase(OpType& op, const BOpType& Bop, Index nev, Index ncv) : + HermEigsBase(OpType& op, const BOpType& Bop, Index nev, Index ncv) : m_op(op), m_n(op.rows()), m_nev(nev), @@ -255,7 +266,7 @@ class SymEigsBase } // If op is an rvalue - SymEigsBase(OpType&& op, const BOpType& Bop, Index nev, Index ncv) : + HermEigsBase(OpType&& op, const BOpType& Bop, Index nev, Index ncv) : m_op_container(create_op_container(std::move(op))), m_op(m_op_container.front()), m_n(m_op.rows()), @@ -276,7 +287,7 @@ class SymEigsBase /// /// Virtual destructor /// - virtual ~SymEigsBase() {} + virtual ~HermEigsBase() {} /// \endcond @@ -347,7 +358,7 @@ class SymEigsBase /// \return Number of converged eigenvalues. /// Index compute(SortRule selection = SortRule::LargestMagn, Index maxit = 1000, - Scalar tol = 1e-10, SortRule sorting = SortRule::LargestAlge) + RealScalar tol = 1e-10, SortRule sorting = SortRule::LargestAlge) { // The m-step Lanczos factorization m_fac.factorize_from(1, m_ncv, m_nmatop); @@ -391,14 +402,16 @@ class SymEigsBase /// /// Returns the converged eigenvalues. /// - /// \return A vector containing the eigenvalues. - /// Returned vector type will be `Eigen::Vector`, depending on - /// the template parameter `Scalar` defined. + /// \return A vector containing the real-valued eigenvalues. + /// Returned vector type will be `Eigen::Vector`, depending on + /// the `Scalar` type defined in the matrix operation class. + /// For example, if `Scalar` is `double` or `std::complex`, + /// then `RealScalar` would be `double`. /// - Vector eigenvalues() const + RealVector eigenvalues() const { const Index nconv = m_ritz_conv.count(); - Vector res(nconv); + RealVector res(nconv); if (!nconv) return res; @@ -423,7 +436,7 @@ class SymEigsBase /// /// \return A matrix containing the eigenvectors. /// Returned matrix type will be `Eigen::Matrix`, - /// depending on the template parameter `Scalar` defined. + /// depending on the `Scalar` type defined in the matrix operation class. /// virtual Matrix eigenvectors(Index nvec) const { @@ -434,7 +447,7 @@ class SymEigsBase if (!nvec) return res; - Matrix ritz_vec_conv(m_ncv, nvec); + RealMatrix ritz_vec_conv(m_ncv, nvec); Index j = 0; for (Index i = 0; i < m_nev && j < nvec; i++) { @@ -461,4 +474,4 @@ class SymEigsBase } // namespace Spectra -#endif // SPECTRA_SYM_EIGS_BASE_H +#endif // SPECTRA_HERM_EIGS_BASE_H diff --git a/include/Spectra/HermEigsSolver.h b/include/Spectra/HermEigsSolver.h new file mode 100644 index 0000000..2b24387 --- /dev/null +++ b/include/Spectra/HermEigsSolver.h @@ -0,0 +1,146 @@ +// Copyright (C) 2024-2025 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef SPECTRA_HERM_EIGS_SOLVER_H +#define SPECTRA_HERM_EIGS_SOLVER_H + +#include + +#include "HermEigsBase.h" +#include "Util/SelectionRule.h" +#include "MatOp/DenseHermMatProd.h" + +namespace Spectra { + +/// +/// \ingroup EigenSolver +/// +/// This class implements the eigen solver for Hermitian matrices, i.e., +/// to solve \f$Ax=\lambda x\f$ where \f$A\f$ is Hermitian. +/// An Hermitian matrix is a complex square matrix that is equal to its +/// own conjugate transpose. It is known that all Hermitian matrices have +/// real-valued eigenvalues. +/// +/// \tparam OpType The name of the matrix operation class. Users could either +/// use the wrapper classes such as DenseHermMatProd and +/// SparseHermMatProd, or define their own that implements the type +/// definition `Scalar` and all the public member functions as in +/// DenseHermMatProd. +/// +/// Below is an example that demonstrates the usage of this class. +/// +/// \code{.cpp} +/// #include +/// #include +/// // is implicitly included +/// #include +/// +/// using namespace Spectra; +/// +/// int main() +/// { +/// // We are going to calculate the eigenvalues of M +/// Eigen::MatrixXcd A = Eigen::MatrixXcd::Random(10, 10); +/// Eigen::MatrixXcd M = A + A.adjoint(); +/// +/// // Construct matrix operation object using the wrapper class DenseHermMatProd +/// using OpType = DenseHermMatProd>; +/// OpType op(M); +/// +/// // Construct eigen solver object, requesting the largest three eigenvalues +/// HermEigsSolver eigs(op, 3, 6); +/// +/// // Initialize and compute +/// eigs.init(); +/// int nconv = eigs.compute(SortRule::LargestAlge); +/// +/// // Retrieve results +/// // Eigenvalues are real-valued, and eigenvectors are complex-valued +/// Eigen::VectorXd evalues; +/// if (eigs.info() == CompInfo::Successful) +/// evalues = eigs.eigenvalues(); +/// +/// std::cout << "Eigenvalues found:\n" << evalues << std::endl; +/// +/// return 0; +/// } +/// \endcode +/// +/// And here is an example for user-supplied matrix operation class. +/// +/// \code{.cpp} +/// #include +/// #include +/// #include +/// +/// using namespace Spectra; +/// +/// // M = diag(1+0i, 2+0i, ..., 10+0i) +/// class MyDiagonalTen +/// { +/// public: +/// using Scalar = std::complex; // A typedef named "Scalar" is required +/// int rows() const { return 10; } +/// int cols() const { return 10; } +/// // y_out = M * x_in +/// void perform_op(Scalar *x_in, Scalar *y_out) const +/// { +/// for (int i = 0; i < rows(); i++) +/// { +/// y_out[i] = x_in[i] * Scalar(i + 1, 0); +/// } +/// } +/// }; +/// +/// int main() +/// { +/// MyDiagonalTen op; +/// HermEigsSolver eigs(op, 3, 6); +/// eigs.init(); +/// eigs.compute(SortRule::LargestAlge); +/// if (eigs.info() == CompInfo::Successful) +/// { +/// Eigen::VectorXd evalues = eigs.eigenvalues(); +/// // Will get (10, 9, 8) +/// std::cout << "Eigenvalues found:\n" << evalues << std::endl; +/// } +/// +/// return 0; +/// } +/// \endcode +/// +template > +class HermEigsSolver : public HermEigsBase +{ +private: + using Index = Eigen::Index; + +public: + /// + /// Constructor to create a solver object. + /// + /// \param op The matrix operation object that implements + /// the matrix-vector multiplication operation of \f$A\f$: + /// calculating \f$Av\f$ for any vector \f$v\f$. Users could either + /// create the object from the wrapper class such as DenseHermMatProd, or + /// define their own that implements all the public members + /// as in DenseHermMatProd. + /// \param nev Number of eigenvalues requested. This should satisfy \f$1\le nev \le n-1\f$, + /// where \f$n\f$ is the size of matrix. + /// \param ncv Parameter that controls the convergence speed of the algorithm. + /// Typically a larger `ncv` means faster convergence, but it may + /// also result in greater memory use and more matrix operations + /// in each iteration. This parameter must satisfy \f$nev < ncv \le n\f$, + /// and is advised to take \f$ncv \ge 2\cdot nev\f$. + /// + HermEigsSolver(OpType& op, Index nev, Index ncv) : + HermEigsBase(op, IdentityBOp(), nev, ncv) + {} +}; + +} // namespace Spectra + +#endif // SPECTRA_HERM_EIGS_SOLVER_H diff --git a/include/Spectra/LinAlg/Arnoldi.h b/include/Spectra/LinAlg/Arnoldi.h index 63a15f5..ca56649 100644 --- a/include/Spectra/LinAlg/Arnoldi.h +++ b/include/Spectra/LinAlg/Arnoldi.h @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2023 Yixuan Qiu +// Copyright (C) 2018-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed @@ -31,6 +31,8 @@ template class Arnoldi { private: + // The real part type of the matrix element + using RealScalar = typename Eigen::NumTraits::Real; using Index = Eigen::Index; using Matrix = Eigen::Matrix; using Vector = Eigen::Matrix; @@ -41,9 +43,9 @@ class Arnoldi protected: // A very small value, but 1.0 / m_near_0 does not overflow // ~= 1e-307 for the "double" type - const Scalar m_near_0 = TypeTraits::min() * Scalar(10); + const RealScalar m_near_0 = TypeTraits::min() * RealScalar(10); // The machine precision, ~= 1e-16 for the "double" type - const Scalar m_eps = TypeTraits::epsilon(); + const RealScalar m_eps = TypeTraits::epsilon(); ArnoldiOpType m_op; // Operators for the Arnoldi factorization const Index m_n; // dimension of A @@ -52,12 +54,12 @@ class Arnoldi Matrix m_fac_V; // V matrix in the Arnoldi factorization Matrix m_fac_H; // H matrix in the Arnoldi factorization Vector m_fac_f; // residual in the Arnoldi factorization - Scalar m_beta; // ||f||, B-norm of f + RealScalar m_beta; // ||f||, B-norm of f - // Given orthonormal basis V (w.r.t. B), find a nonzero vector f such that V'Bf = 0 - // With rounding errors, we hope V'B(f/||f||) < eps + // Given orthonormal basis V (w.r.t. B), find a nonzero vector f such that (V^H)Bf = 0 + // With rounding errors, we hope ||(V^H)Bf|| < eps * ||f|| // Assume that f has been properly allocated - void expand_basis(MapConstMat& V, const Index seed, Vector& f, Scalar& fnorm, Index& op_counter) + void expand_basis(MapConstMat& V, const Index seed, Vector& f, RealScalar& fnorm, Index& op_counter) { using std::sqrt; @@ -77,16 +79,16 @@ class Arnoldi { rng.random_vec(f); } - // f <- f - V * V'Bf, so that f is orthogonal to V in B-norm - m_op.trans_product(V, f, Vf); + // f <- f - V * (V^H)Bf, so that f is orthogonal to V in B-norm + m_op.adjoint_product(V, f, Vf); f.noalias() -= V * Vf; // fnorm <- ||f|| fnorm = m_op.norm(f); - // Compute V'Bf again - m_op.trans_product(V, f, Vf); - // Test whether V'B(f/||f||) < eps - Scalar ortho_err = Vf.cwiseAbs().maxCoeff(); + // Compute (V^H)Bf again + m_op.adjoint_product(V, f, Vf); + // Test whether ||(V^H)Bf|| < eps * ||f|| + RealScalar ortho_err = Vf.cwiseAbs().maxCoeff(); // If not, iteratively correct the residual int count = 0; while (count < 3 && ortho_err >= m_eps * fnorm) @@ -96,7 +98,7 @@ class Arnoldi // beta <- ||f|| fnorm = m_op.norm(f); - m_op.trans_product(V, f, Vf); + m_op.adjoint_product(V, f, Vf); ortho_err = Vf.cwiseAbs().maxCoeff(); count++; } @@ -123,7 +125,7 @@ class Arnoldi const Matrix& matrix_V() const { return m_fac_V; } const Matrix& matrix_H() const { return m_fac_H; } const Vector& vector_f() const { return m_fac_f; } - Scalar f_norm() const { return m_beta; } + RealScalar f_norm() const { return m_beta; } Index subspace_dim() const { return m_k; } // Initialize with an operator and an initial vector @@ -137,7 +139,7 @@ class Arnoldi m_fac_H.setZero(); // Verify the initial vector - const Scalar v0norm = m_op.norm(v0); + const RealScalar v0norm = m_op.norm(v0); if (v0norm < m_near_0) throw std::invalid_argument("initial residual vector cannot be zero"); @@ -148,7 +150,7 @@ class Arnoldi op_counter++; // Normalize - const Scalar vnorm = m_op.norm(v); + const RealScalar vnorm = m_op.norm(v); v /= vnorm; // Compute H and f @@ -166,7 +168,7 @@ class Arnoldi if (m_fac_f.cwiseAbs().maxCoeff() < m_eps * abs(m_fac_H(0, 0))) { m_fac_f.setZero(); - m_beta = Scalar(0); + m_beta = RealScalar(0); } else { @@ -192,7 +194,7 @@ class Arnoldi throw std::invalid_argument(msg); } - const Scalar beta_thresh = m_eps * sqrt(Scalar(m_n)); + const RealScalar beta_thresh = m_eps * sqrt(RealScalar(m_n)); // Pre-allocate vectors Vector Vf(to_m); @@ -219,7 +221,7 @@ class Arnoldi m_fac_V.col(i).noalias() = m_fac_f / m_beta; // The (i+1)-th column // Note that H[i+1, i] equals to the unrestarted beta - m_fac_H(i, i - 1) = restart ? Scalar(0) : m_beta; + m_fac_H(i, i - 1) = restart ? Scalar(0) : Scalar(m_beta); // w <- A * v, v = m_fac_V.col(i) m_op.perform_op(&m_fac_V(0, i), w.data()); @@ -230,20 +232,20 @@ class Arnoldi MapConstMat Vs(m_fac_V.data(), m_n, i1); // h = m_fac_H(0:i, i) MapVec h(&m_fac_H(0, i), i1); - // h <- V'Bw - m_op.trans_product(Vs, w, h); + // h <- (V^H)Bw + m_op.adjoint_product(Vs, w, h); // f <- w - V * h m_fac_f.noalias() = w - Vs * h; m_beta = m_op.norm(m_fac_f); - if (m_beta > Scalar(0.717) * m_op.norm(h)) + if (m_beta > RealScalar(0.717) * m_op.norm(h)) continue; // f/||f|| is going to be the next column of V, so we need to test - // whether V'B(f/||f||) ~= 0 - m_op.trans_product(Vs, m_fac_f, Vf.head(i1)); - Scalar ortho_err = Vf.head(i1).cwiseAbs().maxCoeff(); + // whether (V^H)B(f/||f||) ~= 0 + m_op.adjoint_product(Vs, m_fac_f, Vf.head(i1)); + RealScalar ortho_err = Vf.head(i1).cwiseAbs().maxCoeff(); // If not, iteratively correct the residual int count = 0; while (count < 5 && ortho_err > m_eps * m_beta) @@ -256,7 +258,7 @@ class Arnoldi if (m_beta < beta_thresh) { m_fac_f.setZero(); - m_beta = Scalar(0); + m_beta = RealScalar(0); break; } @@ -267,7 +269,7 @@ class Arnoldi // beta <- ||f|| m_beta = m_op.norm(m_fac_f); - m_op.trans_product(Vs, m_fac_f, Vf.head(i1)); + m_op.adjoint_product(Vs, m_fac_f, Vf.head(i1)); ortho_err = Vf.head(i1).cwiseAbs().maxCoeff(); count++; } @@ -296,13 +298,21 @@ class Arnoldi // Only need to update the first k+1 columns of V // The first (m - k + i) elements of the i-th column of Q are non-zero, // and the rest are zero - void compress_V(const Matrix& Q) + // + // When V has a complex type, Q can be either real or complex + // Hense we use a generic implementation + template + void compress_V(const Eigen::MatrixBase& Q) { + using QScalar = typename Derived::Scalar; + using QVector = Eigen::Matrix; + using QMapConstVec = Eigen::Map; + Matrix Vs(m_n, m_k + 1); for (Index i = 0; i < m_k; i++) { const Index nnz = m_m - m_k + i + 1; - MapConstVec q(&Q(0, i), nnz); + QMapConstVec q(&Q(0, i), nnz); Vs.col(i).noalias() = m_fac_V.leftCols(nnz) * q; } Vs.col(m_k).noalias() = m_fac_V * Q.col(m_k); diff --git a/include/Spectra/LinAlg/BKLDLT.h b/include/Spectra/LinAlg/BKLDLT.h index 7ecd168..386bb07 100644 --- a/include/Spectra/LinAlg/BKLDLT.h +++ b/include/Spectra/LinAlg/BKLDLT.h @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2023 Yixuan Qiu +// Copyright (C) 2019-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed @@ -10,11 +10,49 @@ #include #include #include +#include // std::is_same #include "../Util/CompInfo.h" namespace Spectra { +// We need a generic conj() function for both real and complex values, +// and hope that conj(x) == x if x is real-valued. However, in STL, +// conj(x) == std::complex(x, 0) for such cases, meaning that the +// return value type is not necessarily the same as x. To avoid this +// inconvenience, we define a simple class that does this task +// +// Similarly, define a real(x) function that returns x itself if +// x is real-valued, and returns std::complex(x, 0) if x is complex-valued +template +struct ScalarOp +{ + static Scalar conj(const Scalar& x) + { + return x; + } + + static Scalar real(const Scalar& x) + { + return x; + } +}; +// Specialization for complex values +template +struct ScalarOp> +{ + static std::complex conj(const std::complex& x) + { + using std::conj; + return conj(x); + } + + static std::complex real(const std::complex& x) + { + return std::complex(x.real(), RealScalar(0)); + } +}; + // Bunch-Kaufman LDLT decomposition // References: // 1. Bunch, J. R., & Kaufman, L. (1977). Some stable methods for calculating inertia and solving symmetric linear systems. @@ -27,6 +65,8 @@ template class BKLDLT { private: + // The real part type of the matrix element + using RealScalar = typename Eigen::NumTraits::Real; using Index = Eigen::Index; using Vector = Eigen::Matrix; using MapVec = Eigen::Map; @@ -70,7 +110,7 @@ class BKLDLT // Copy mat - shift * I to m_data template - void copy_data(const Eigen::MatrixBase& mat, int uplo, const Scalar& shift) + void copy_data(const Eigen::MatrixBase& mat, int uplo, const RealScalar& shift) { // If mat is an expression, first evaluate it into a temporary object // This can be achieved by assigning mat to a const Eigen::Ref& @@ -85,22 +125,23 @@ class BKLDLT const Scalar* begin = &src.coeffRef(j, j); const Index len = m_n - j; std::copy(begin, begin + len, col_pointer(j)); - diag_coeff(j) -= shift; + diag_coeff(j) -= Scalar(shift); } - return; } - - Scalar* dest = m_data.data(); - for (Index j = 0; j < m_n; j++) + else { - for (Index i = j; i < m_n; i++, dest++) + Scalar* dest = m_data.data(); + for (Index j = 0; j < m_n; j++) { - if (uplo == Eigen::Lower) - *dest = src.coeff(i, j); - else - *dest = src.coeff(j, i); + for (Index i = j; i < m_n; i++, dest++) + { + if (uplo == Eigen::Lower) + *dest = src.coeff(i, j); + else + *dest = ScalarOp::conj(src.coeff(j, i)); + } + diag_coeff(j) -= Scalar(shift); } - diag_coeff(j) -= shift; } } @@ -121,12 +162,11 @@ class BKLDLT // Assume r >= k void pivoting_1x1(Index k, Index r) { + m_perm[k] = r; + // No permutation if (k == r) - { - m_perm[k] = r; return; - } // A[k, k] <-> A[r, r] std::swap(diag_coeff(k), diag_coeff(r)); @@ -135,13 +175,32 @@ class BKLDLT std::swap_ranges(&coeff(r + 1, k), col_pointer(k + 1), &coeff(r + 1, r)); // A[(k+1):(r-1), k] <-> A[r, (k+1):(r-1)] + // Note: for Hermitian matrices, also need to do conjugate Scalar* src = &coeff(k + 1, k); - for (Index j = k + 1; j < r; j++, src++) + if (std::is_same::value) { - std::swap(*src, coeff(r, j)); + // Simple swapping for real values + for (Index j = k + 1; j < r; j++, src++) + { + std::swap(*src, coeff(r, j)); + } + } + else + { + // For complex values + for (Index j = k + 1; j < r; j++, src++) + { + const Scalar src_conj = ScalarOp::conj(*src); + *src = ScalarOp::conj(coeff(r, j)); + coeff(r, j) = src_conj; + } } - m_perm[k] = r; + // A[r, k] <- Conj(A[r, k]) + if (!std::is_same::value) + { + coeff(r, k) = ScalarOp::conj(coeff(r, k)); + } } // Working on the A[k:end, k:end] submatrix @@ -178,7 +237,7 @@ class BKLDLT // Largest (in magnitude) off-diagonal element in the first column of the current reduced matrix // r is the row index // Assume k < end - Scalar find_lambda(Index k, Index& r) + RealScalar find_lambda(Index k, Index& r) { using std::abs; @@ -186,11 +245,11 @@ class BKLDLT const Scalar* end = col_pointer(k + 1); // Start with r=k+1, lambda=A[k+1, k] r = k + 1; - Scalar lambda = abs(head[1]); + RealScalar lambda = abs(head[1]); // Scan remaining elements for (const Scalar* ptr = head + 2; ptr < end; ptr++) { - const Scalar abs_elem = abs(*ptr); + const RealScalar abs_elem = abs(*ptr); if (lambda < abs_elem) { lambda = abs_elem; @@ -205,20 +264,20 @@ class BKLDLT // Largest (in magnitude) off-diagonal element in the r-th column of the current reduced matrix // p is the row index // Assume k < r < end - Scalar find_sigma(Index k, Index r, Index& p) + RealScalar find_sigma(Index k, Index r, Index& p) { using std::abs; // First search A[r+1, r], ..., A[end, r], which has the same task as find_lambda() // If r == end, we skip this search - Scalar sigma = Scalar(-1); + RealScalar sigma = RealScalar(-1); if (r < m_n - 1) sigma = find_lambda(r, p); // Then search A[k, r], ..., A[r-1, r], which maps to A[r, k], ..., A[r, r-1] for (Index j = k; j < r; j++) { - const Scalar abs_elem = abs(coeff(r, j)); + const RealScalar abs_elem = abs(coeff(r, j)); if (sigma < abs_elem) { sigma = abs_elem; @@ -231,21 +290,21 @@ class BKLDLT // Generate permutations and apply to A // Return true if the resulting pivoting is 1x1, and false if 2x2 - bool permutate_mat(Index k, const Scalar& alpha) + bool permutate_mat(Index k, const RealScalar& alpha) { using std::abs; Index r = k, p = k; - const Scalar lambda = find_lambda(k, r); + const RealScalar lambda = find_lambda(k, r); // If lambda=0, no need to interchange - if (lambda > Scalar(0)) + if (lambda > RealScalar(0)) { - const Scalar abs_akk = abs(diag_coeff(k)); + const RealScalar abs_akk = abs(diag_coeff(k)); // If |A[k, k]| >= alpha * lambda, no need to interchange if (abs_akk < alpha * lambda) { - const Scalar sigma = find_sigma(k, r, p); + const RealScalar sigma = find_sigma(k, r, p); // If sigma * |A[k, k]| >= alpha * lambda^2, no need to interchange if (sigma * abs_akk < alpha * lambda * lambda) @@ -277,7 +336,7 @@ class BKLDLT // r = k+1, so that only one permutation needs to be performed /* const Index rp_min = std::min(r, p); const Index rp_max = std::max(r, p); - if(rp_min == k + 1) + if (rp_min == k + 1) { r = rp_min; p = rp_max; } else { @@ -288,6 +347,7 @@ class BKLDLT // Permutation on A pivoting_2x2(k, r, p); + // Permutation on L interchange_rows(k, p, 0, k - 1); interchange_rows(k + 1, r, 0, k - 1); @@ -307,31 +367,109 @@ class BKLDLT { // inv(E) = [d11, d12], d11 = e22/delta, d21 = -e21/delta, d22 = e11/delta // [d21, d22] - const Scalar delta = e11 * e22 - e21 * e21; + // delta = e11 * e22 - e12 * e21 + const Scalar e12 = ScalarOp::conj(e21); + const Scalar delta = e11 * e22 - e12 * e21; std::swap(e11, e22); e11 /= delta; e22 /= delta; e21 = -e21 / delta; } + // E = [e11, e12] + // [e21, e22] + // Overwrite b with x = inv(E) * b, which is equivalent to solving E * x = b + void solve_inplace_2x2( + const Scalar& e11, const Scalar& e21, const Scalar& e22, + Scalar& b1, Scalar& b2) const + { + using std::abs; + + const Scalar e12 = ScalarOp::conj(e21); + const RealScalar e11_abs = abs(e11); + const RealScalar e21_abs = abs(e21); + // If |e11| >= |e21|, no need to exchange rows + if (e11_abs >= e21_abs) + { + const Scalar fac = e21 / e11; + const Scalar x2 = (b2 - fac * b1) / (e22 - fac * e12); + const Scalar x1 = (b1 - e12 * x2) / e11; + b1 = x1; + b2 = x2; + } + else + { + // Exchange row 1 and row 2, so the system becomes + // E* = [e21, e22], b* = [b2], x* = [x1] + // [e11, e12] [b1] [x2] + const Scalar fac = e11 / e21; + const Scalar x2 = (b1 - fac * b2) / (e12 - fac * e22); + const Scalar x1 = (b2 - e22 * x2) / e21; + b1 = x1; + b2 = x2; + } + } + + // Compute C * inv(E), which is equivalent to solving X * E = C + // X [n x 2], E [2 x 2], C [n x 2] + // X = [x1, x2], E = [e11, e12], C = [c1 c2] + // [e21, e22] + void solve_left_2x2( + const Scalar& e11, const Scalar& e21, const Scalar& e22, + const MapVec& c1, const MapVec& c2, + Eigen::Matrix& x) const + { + using std::abs; + + const Scalar e12 = ScalarOp::conj(e21); + const RealScalar e11_abs = abs(e11); + const RealScalar e12_abs = abs(e12); + // If |e11| >= |e12|, no need to exchange rows + if (e11_abs >= e12_abs) + { + const Scalar fac = e12 / e11; + // const Scalar x2 = (c2 - fac * c1) / (e22 - fac * e21); + // const Scalar x1 = (c1 - e21 * x2) / e11; + x.col(1).array() = (c2 - fac * c1).array() / (e22 - fac * e21); + x.col(0).array() = (c1 - e21 * x.col(1)).array() / e11; + } + else + { + // Exchange column 1 and column 2, so the system becomes + // X* = [x1, x2], E* = [e12, e11], C* = [c2 c1] + // [e22, e21] + const Scalar fac = e11 / e12; + // const Scalar x2 = (c1 - fac * c2) / (e21 - fac * e22); + // const Scalar x1 = (c2 - e22 * x2) / e12; + x.col(1).array() = (c1 - fac * c2).array() / (e21 - fac * e22); + x.col(0).array() = (c2 - e22 * x.col(1)).array() / e12; + } + } + // Return value is the status, CompInfo::Successful/NumericalIssue CompInfo gaussian_elimination_1x1(Index k) { - // D = 1 / A[k, k] - const Scalar akk = diag_coeff(k); + // A[k, k] is known to be real-valued, so we force its imaginary + // part to be zero when Scalar is a complex type + // Interestingly, this has a significant effect on the accuracy + // and numerical stability of the final solution + const Scalar akk = ScalarOp::real(diag_coeff(k)); + diag_coeff(k) = akk; // Return CompInfo::NumericalIssue if not invertible if (akk == Scalar(0)) return CompInfo::NumericalIssue; - diag_coeff(k) = Scalar(1) / akk; + // [inverse] + // diag_coeff(k) = Scalar(1) / akk; - // B -= l * l' / A[k, k], B := A[(k+1):end, (k+1):end], l := L[(k+1):end, k] + // B -= l * l^H / A[k, k], B := A[(k+1):end, (k+1):end], l := L[(k+1):end, k] Scalar* lptr = col_pointer(k) + 1; const Index ldim = m_n - k - 1; MapVec l(lptr, ldim); for (Index j = 0; j < ldim; j++) { - MapVec(col_pointer(j + k + 1), ldim - j).noalias() -= (lptr[j] / akk) * l.tail(ldim - j); + Scalar l_conj = ScalarOp::conj(lptr[j]); + MapVec(col_pointer(j + k + 1), ldim - j).noalias() -= (l_conj / akk) * l.tail(ldim - j); } // l /= A[k, k] @@ -343,15 +481,24 @@ class BKLDLT // Return value is the status, CompInfo::Successful/NumericalIssue CompInfo gaussian_elimination_2x2(Index k) { - // D = inv(E) Scalar& e11 = diag_coeff(k); Scalar& e21 = coeff(k + 1, k); Scalar& e22 = diag_coeff(k + 1); + + // A[k, k] and A[k+1, k+1] are known to be real-valued, + // so we force their imaginary parts to be zero when Scalar + // is a complex type + // Interestingly, this has a significant effect on the accuracy + // and numerical stability of the final solution + e11 = ScalarOp::real(e11); + e22 = ScalarOp::real(e22); + Scalar e12 = ScalarOp::conj(e21); // Return CompInfo::NumericalIssue if not invertible - if (e11 * e22 - e21 * e21 == Scalar(0)) + if (e11 * e22 - e12 * e21 == Scalar(0)) return CompInfo::NumericalIssue; - inverse_inplace_2x2(e11, e21, e22); + // [inverse] + // inverse_inplace_2x2(e11, e21, e22); // X = l * inv(E), l := L[(k+2):end, k:(k+1)] Scalar* l1ptr = &coeff(k + 2, k); @@ -360,13 +507,19 @@ class BKLDLT MapVec l1(l1ptr, ldim), l2(l2ptr, ldim); Eigen::Matrix X(ldim, 2); - X.col(0).noalias() = l1 * e11 + l2 * e21; - X.col(1).noalias() = l1 * e21 + l2 * e22; - - // B -= l * inv(E) * l' = X * l', B = A[(k+2):end, (k+2):end] + // [inverse] + // e12 = ScalarOp::conj(e21); + // X.col(0).noalias() = l1 * e11 + l2 * e21; + // X.col(1).noalias() = l1 * e12 + l2 * e22; + // [solve] + solve_left_2x2(e11, e21, e22, l1, l2, X); + + // B -= l * inv(E) * l^H = X * l^H, B = A[(k+2):end, (k+2):end] for (Index j = 0; j < ldim; j++) { - MapVec(col_pointer(j + k + 2), ldim - j).noalias() -= (X.col(0).tail(ldim - j) * l1ptr[j] + X.col(1).tail(ldim - j) * l2ptr[j]); + const Scalar l1j_conj = ScalarOp::conj(l1ptr[j]); + const Scalar l2j_conj = ScalarOp::conj(l2ptr[j]); + MapVec(col_pointer(j + k + 2), ldim - j).noalias() -= (X.col(0).tail(ldim - j) * l1j_conj + X.col(1).tail(ldim - j) * l2j_conj); } // l = X @@ -383,14 +536,14 @@ class BKLDLT // Factorize mat - shift * I template - BKLDLT(const Eigen::MatrixBase& mat, int uplo = Eigen::Lower, const Scalar& shift = Scalar(0)) : + BKLDLT(const Eigen::MatrixBase& mat, int uplo = Eigen::Lower, const RealScalar& shift = RealScalar(0)) : m_n(mat.rows()), m_computed(false), m_info(CompInfo::NotComputed) { compute(mat, uplo, shift); } template - void compute(const Eigen::MatrixBase& mat, int uplo = Eigen::Lower, const Scalar& shift = Scalar(0)) + void compute(const Eigen::MatrixBase& mat, int uplo = Eigen::Lower, const RealScalar& shift = RealScalar(0)) { using std::abs; @@ -406,7 +559,7 @@ class BKLDLT compute_pointer(); copy_data(mat, uplo, shift); - const Scalar alpha = (1.0 + std::sqrt(17.0)) / 8.0; + const RealScalar alpha = (1.0 + std::sqrt(17.0)) / 8.0; Index k = 0; for (k = 0; k < m_n - 1; k++) { @@ -431,11 +584,13 @@ class BKLDLT // Invert the last 1x1 block if it exists if (k == m_n - 1) { - const Scalar akk = diag_coeff(k); + const Scalar akk = ScalarOp::real(diag_coeff(k)); + diag_coeff(k) = akk; if (akk == Scalar(0)) m_info = CompInfo::NumericalIssue; - diag_coeff(k) = Scalar(1) / diag_coeff(k); + // [inverse] + // diag_coeff(k) = Scalar(1) / diag_coeff(k); } compress_permutation(); @@ -449,7 +604,8 @@ class BKLDLT if (!m_computed) throw std::logic_error("BKLDLT: need to call compute() first"); - // PAP' = LDL' + // PAP' = LD(L^H), A = P'LD(L^H)P + // Ax = b ==> P'LD(L^H)Px = b ==> LD(L^H)Px = Pb // 1. b -> Pb Scalar* x = b.data(); MapVec res(x, m_n); @@ -459,6 +615,7 @@ class BKLDLT std::swap(x[m_permc[i].first], x[m_permc[i].second]); } + // z = D(L^H)Px // 2. Lz = Pb // If m_perm[end] < 0, then end with m_n - 3, otherwise end with m_n - 2 const Index end = (m_perm[m_n - 1] < 0) ? (m_n - 3) : (m_n - 2); @@ -480,37 +637,47 @@ class BKLDLT } } + // w = (L^H)Px // 3. Dw = z for (Index i = 0; i < m_n; i++) { const Scalar e11 = diag_coeff(i); if (m_perm[i] >= 0) { - x[i] *= e11; + // [inverse] + // x[i] *= e11; + // [solve] + x[i] /= e11; } else { const Scalar e21 = coeff(i + 1, i), e22 = diag_coeff(i + 1); - const Scalar wi = x[i] * e11 + x[i + 1] * e21; - x[i + 1] = x[i] * e21 + x[i + 1] * e22; - x[i] = wi; + // [inverse] + // const Scalar e12 = ScalarOp::conj(e21); + // const Scalar wi = x[i] * e11 + x[i + 1] * e12; + // x[i + 1] = x[i] * e21 + x[i + 1] * e22; + // x[i] = wi; + // [solve] + solve_inplace_2x2(e11, e21, e22, x[i], x[i + 1]); + i++; } } - // 4. L'y = w + // y = Px + // 4. (L^H)y = w // If m_perm[end] < 0, then start with m_n - 3, otherwise start with m_n - 2 Index i = (m_perm[m_n - 1] < 0) ? (m_n - 3) : (m_n - 2); for (; i >= 0; i--) { const Index ldim = m_n - i - 1; MapConstVec l(&coeff(i + 1, i), ldim); - x[i] -= res.segment(i + 1, ldim).dot(l); + x[i] -= l.dot(res.segment(i + 1, ldim)); if (m_perm[i] < 0) { MapConstVec l2(&coeff(i + 1, i - 1), ldim); - x[i - 1] -= res.segment(i + 1, ldim).dot(l2); + x[i - 1] -= l2.dot(res.segment(i + 1, ldim)); i--; } } diff --git a/include/Spectra/LinAlg/DoubleShiftQR.h b/include/Spectra/LinAlg/DoubleShiftQR.h index a18bc8b..73b787d 100644 --- a/include/Spectra/LinAlg/DoubleShiftQR.h +++ b/include/Spectra/LinAlg/DoubleShiftQR.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/LinAlg/Lanczos.h b/include/Spectra/LinAlg/Lanczos.h index 1e616ee..da1f089 100644 --- a/include/Spectra/LinAlg/Lanczos.h +++ b/include/Spectra/LinAlg/Lanczos.h @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2023 Yixuan Qiu +// Copyright (C) 2018-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed @@ -27,12 +27,15 @@ template class Lanczos : public Arnoldi { private: + // The real part type of the matrix element + using RealScalar = typename Eigen::NumTraits::Real; using Index = Eigen::Index; using Matrix = Eigen::Matrix; using Vector = Eigen::Matrix; using MapMat = Eigen::Map; using MapVec = Eigen::Map; using MapConstMat = Eigen::Map; + using RealMatrix = Eigen::Matrix; using Arnoldi::m_op; using Arnoldi::m_n; @@ -68,8 +71,8 @@ class Lanczos : public Arnoldi throw std::invalid_argument(msg); } - const Scalar beta_thresh = m_eps * sqrt(Scalar(m_n)); - const Scalar eps_sqrt = sqrt(m_eps); + const RealScalar beta_thresh = m_eps * sqrt(RealScalar(m_n)); + const RealScalar eps_sqrt = sqrt(m_eps); // Pre-allocate vectors Vector Vf(to_m); @@ -86,7 +89,7 @@ class Lanczos : public Arnoldi // to the current V, which we call a restart // // A simple criterion is beta < near_0, but it may be too stringent - // Another heuristic is to test whether V'B(f/||f||) ~= 0 when ||f|| is small, + // Another heuristic is to test whether (V^H)B(f/||f||) ~= 0 when ||f|| is small, // and to reduce the computational cost, we only use the latest Vi // Test the first criterion @@ -100,9 +103,9 @@ class Lanczos : public Arnoldi v.noalias() = m_fac_f / m_beta; if (m_beta < eps_sqrt) { - // Test Vi'v + // Test (Vi^H)v const Scalar Viv = m_op.inner_product(m_fac_V.col(i - 1), v); - // Restart V if Vi'v is much larger than eps + // Restart V if (Vi^H)v is much larger than eps restart = (abs(Viv) > eps_sqrt); } } @@ -118,21 +121,21 @@ class Lanczos : public Arnoldi // contains f / ||f|| // Note that H[i+1, i] equals to the unrestarted beta - m_fac_H(i, i - 1) = restart ? Scalar(0) : m_beta; + m_fac_H(i, i - 1) = restart ? Scalar(0) : Scalar(m_beta); m_fac_H(i - 1, i) = m_fac_H(i, i - 1); // Due to symmetry // w <- A * v m_op.perform_op(v.data(), w.data()); op_counter++; - // f <- w - V * V'Bw = w - H[i+1, i] * V{i} - H[i+1, i+1] * V{i+1} + // f <- w - V * (V^H)Bw = w - H[i+1, i] * V{i} - H[i+1, i+1] * V{i+1} // If restarting, we know that H[i+1, i] = 0 // First do w <- w - H[i+1, i] * V{i}, see the discussions in Section 2.3 of // Cullum and Willoughby (2002). Lanczos Algorithms for Large Symmetric Eigenvalue Computations: Vol. 1 if (!restart) w.noalias() -= m_fac_H(i, i - 1) * m_fac_V.col(i - 1); - // H[i+1, i+1] = = v'Bw + // H[i+1, i+1] = = (v^H)Bw m_fac_H(i, i) = m_op.inner_product(v, w); // f <- w - H[i+1, i+1] * V{i+1} @@ -140,11 +143,11 @@ class Lanczos : public Arnoldi m_beta = m_op.norm(m_fac_f); // f/||f|| is going to be the next column of V, so we need to test - // whether V'B(f/||f||) ~= 0 + // whether (V^H)B(f/||f||) ~= 0 const Index i1 = i + 1; MapMat Vs(m_fac_V.data(), m_n, i1); // The first (i+1) columns - m_op.trans_product(Vs, m_fac_f, Vf.head(i1)); - Scalar ortho_err = Vf.head(i1).cwiseAbs().maxCoeff(); + m_op.adjoint_product(Vs, m_fac_f, Vf.head(i1)); + RealScalar ortho_err = Vf.head(i1).cwiseAbs().maxCoeff(); // If not, iteratively correct the residual int count = 0; while (count < 5 && ortho_err > m_eps * m_beta) @@ -157,7 +160,7 @@ class Lanczos : public Arnoldi if (m_beta < beta_thresh) { m_fac_f.setZero(); - m_beta = Scalar(0); + m_beta = RealScalar(0); break; } @@ -170,7 +173,7 @@ class Lanczos : public Arnoldi // beta <- ||f|| m_beta = m_op.norm(m_fac_f); - m_op.trans_product(Vs, m_fac_f, Vf.head(i1)); + m_op.adjoint_product(Vs, m_fac_f, Vf.head(i1)); ortho_err = Vf.head(i1).cwiseAbs().maxCoeff(); count++; } @@ -182,7 +185,14 @@ class Lanczos : public Arnoldi // Apply H -> Q'HQ, where Q is from a tridiagonal QR decomposition // Function overloading here, not overriding - void compress_H(const TridiagQR& decomp) + // + // Note that H is by nature a real symmetric matrix, but it may be stored + // as a complex matrix (e.g. in HermEigsSolver). + // Therefore, if m_fac_H has a real type (as in SymEigsSolver), then we + // directly overwrite m_fac_H. Otherwise, m_fac_H has a complex type + // (as in HermEigsSolver), so we first compute the real-typed result, + // and then cast to the complex type. This is done in the TridiagQR class + void compress_H(const TridiagQR& decomp) { decomp.matrix_QtHQ(m_fac_H); m_k--; diff --git a/include/Spectra/LinAlg/TridiagEigen.h b/include/Spectra/LinAlg/TridiagEigen.h index c17e617..cc2fb4a 100644 --- a/include/Spectra/LinAlg/TridiagEigen.h +++ b/include/Spectra/LinAlg/TridiagEigen.h @@ -2,7 +2,7 @@ // // Copyright (C) 2008-2010 Gael Guennebaud // Copyright (C) 2010 Jitse Niesen -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/LinAlg/UpperHessenbergEigen.h b/include/Spectra/LinAlg/UpperHessenbergEigen.h index e6ba720..f8ceb8a 100644 --- a/include/Spectra/LinAlg/UpperHessenbergEigen.h +++ b/include/Spectra/LinAlg/UpperHessenbergEigen.h @@ -2,7 +2,7 @@ // // Copyright (C) 2008 Gael Guennebaud // Copyright (C) 2010,2012 Jitse Niesen -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/LinAlg/UpperHessenbergQR.h b/include/Spectra/LinAlg/UpperHessenbergQR.h index 5d6780a..c685a59 100644 --- a/include/Spectra/LinAlg/UpperHessenbergQR.h +++ b/include/Spectra/LinAlg/UpperHessenbergQR.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed @@ -208,7 +208,7 @@ class UpperHessenbergQR /// /// Virtual destructor. /// - virtual ~UpperHessenbergQR(){}; + virtual ~UpperHessenbergQR() {} /// /// Compute the QR decomposition of an upper Hessenberg matrix with @@ -549,6 +549,7 @@ class TridiagQR : public UpperHessenbergQR using Matrix = Eigen::Matrix; using Vector = Eigen::Matrix; using ConstGenericMatrix = const Eigen::Ref; + using ComplexMatrix = Eigen::Matrix, Eigen::Dynamic, Eigen::Dynamic>; using UpperHessenbergQR::m_n; using UpperHessenbergQR::m_shift; @@ -776,6 +777,21 @@ class TridiagQR : public UpperHessenbergQR // Copy the lower subdiagonal to upper subdiagonal dest.diagonal(1).noalias() = dest.diagonal(-1); } + + /// + /// The version of matrix_QtHQ() when `dest` has a complex value type. + /// + /// This is used in Hermitian eigen solvers where the result is stored + /// as a complex matrix. + /// + void matrix_QtHQ(ComplexMatrix& dest) const + { + // Simply compute the real-typed result and copy to the complex one + Matrix dest_real; + this->matrix_QtHQ(dest_real); + dest.resize(m_n, m_n); + dest.noalias() = dest_real.template cast>(); + } }; /// diff --git a/include/Spectra/LinAlg/UpperHessenbergSchur.h b/include/Spectra/LinAlg/UpperHessenbergSchur.h index f3487c2..d4be26e 100644 --- a/include/Spectra/LinAlg/UpperHessenbergSchur.h +++ b/include/Spectra/LinAlg/UpperHessenbergSchur.h @@ -2,7 +2,7 @@ // // Copyright (C) 2008 Gael Guennebaud // Copyright (C) 2010,2012 Jitse Niesen -// Copyright (C) 2021-2023 Yixuan Qiu +// Copyright (C) 2021-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/MatOp/DenseCholesky.h b/include/Spectra/MatOp/DenseCholesky.h index ff4c880..4e8a034 100644 --- a/include/Spectra/MatOp/DenseCholesky.h +++ b/include/Spectra/MatOp/DenseCholesky.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/MatOp/DenseGenComplexShiftSolve.h b/include/Spectra/MatOp/DenseGenComplexShiftSolve.h index 8856aa1..5c17324 100644 --- a/include/Spectra/MatOp/DenseGenComplexShiftSolve.h +++ b/include/Spectra/MatOp/DenseGenComplexShiftSolve.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/MatOp/DenseGenMatProd.h b/include/Spectra/MatOp/DenseGenMatProd.h index 2f0a2f6..249cf18 100644 --- a/include/Spectra/MatOp/DenseGenMatProd.h +++ b/include/Spectra/MatOp/DenseGenMatProd.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/MatOp/DenseGenRealShiftSolve.h b/include/Spectra/MatOp/DenseGenRealShiftSolve.h index 1490da8..c7c7d66 100644 --- a/include/Spectra/MatOp/DenseGenRealShiftSolve.h +++ b/include/Spectra/MatOp/DenseGenRealShiftSolve.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/MatOp/DenseHermMatProd.h b/include/Spectra/MatOp/DenseHermMatProd.h new file mode 100644 index 0000000..669f6c6 --- /dev/null +++ b/include/Spectra/MatOp/DenseHermMatProd.h @@ -0,0 +1,92 @@ +// Copyright (C) 2024-2025 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef SPECTRA_DENSE_HERM_MAT_PROD_H +#define SPECTRA_DENSE_HERM_MAT_PROD_H + +#include + +namespace Spectra { + +/// +/// \ingroup MatOp +/// +/// This class defines the matrix-vector multiplication operation on a +/// Hermitian complex matrix \f$A\f$, i.e., calculating \f$y=Ax\f$ for any vector +/// \f$x\f$. It is mainly used in the HermEigsSolver eigen solver. +/// +/// \tparam Scalar_ The element type of the matrix, for example, +/// `std::complex`, `std::complex`, +/// and `std::complex`. +/// \tparam Uplo Either `Eigen::Lower` or `Eigen::Upper`, indicating which +/// triangular part of the matrix is used. +/// \tparam Flags Either `Eigen::ColMajor` or `Eigen::RowMajor`, indicating +/// the storage format of the input matrix. +/// +template +class DenseHermMatProd +{ +public: + /// + /// Element type of the matrix. + /// + using Scalar = Scalar_; + +private: + using Index = Eigen::Index; + using Matrix = Eigen::Matrix; + using Vector = Eigen::Matrix; + using MapConstVec = Eigen::Map; + using MapVec = Eigen::Map; + using ConstGenericMatrix = const Eigen::Ref; + + ConstGenericMatrix m_mat; + +public: + /// + /// Constructor to create the matrix operation object. + /// + /// \param mat An **Eigen** matrix object, whose type can be + /// `Eigen::Matrix` (e.g. `Eigen::MatrixXcd` and + /// `Eigen::MatrixXcf`), or its mapped version + /// (e.g. `Eigen::Map`). + /// + template + DenseHermMatProd(const Eigen::MatrixBase& mat) : + m_mat(mat) + { + static_assert( + static_cast(Derived::PlainObject::IsRowMajor) == static_cast(Matrix::IsRowMajor), + "DenseHermMatProd: the \"Flags\" template parameter does not match the input matrix (Eigen::ColMajor/Eigen::RowMajor)"); + } + + /// + /// Return the number of rows of the underlying matrix. + /// + Index rows() const { return m_mat.rows(); } + /// + /// Return the number of columns of the underlying matrix. + /// + Index cols() const { return m_mat.cols(); } + + /// + /// Perform the matrix-vector multiplication operation \f$y=Ax\f$. + /// + /// \param x_in Pointer to the \f$x\f$ vector. + /// \param y_out Pointer to the \f$y\f$ vector. + /// + // y_out = A * x_in + void perform_op(const Scalar* x_in, Scalar* y_out) const + { + MapConstVec x(x_in, m_mat.cols()); + MapVec y(y_out, m_mat.rows()); + y.noalias() = m_mat.template selfadjointView() * x; + } +}; + +} // namespace Spectra + +#endif // SPECTRA_DENSE_HERM_MAT_PROD_H diff --git a/include/Spectra/MatOp/DenseSymMatProd.h b/include/Spectra/MatOp/DenseSymMatProd.h index d335504..d31a14b 100644 --- a/include/Spectra/MatOp/DenseSymMatProd.h +++ b/include/Spectra/MatOp/DenseSymMatProd.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/MatOp/DenseSymShiftSolve.h b/include/Spectra/MatOp/DenseSymShiftSolve.h index 51e9c6a..77aa191 100644 --- a/include/Spectra/MatOp/DenseSymShiftSolve.h +++ b/include/Spectra/MatOp/DenseSymShiftSolve.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/MatOp/SparseCholesky.h b/include/Spectra/MatOp/SparseCholesky.h index be65913..e2e4bc8 100644 --- a/include/Spectra/MatOp/SparseCholesky.h +++ b/include/Spectra/MatOp/SparseCholesky.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/MatOp/SparseGenComplexShiftSolve.h b/include/Spectra/MatOp/SparseGenComplexShiftSolve.h index af387a3..5ead9e6 100644 --- a/include/Spectra/MatOp/SparseGenComplexShiftSolve.h +++ b/include/Spectra/MatOp/SparseGenComplexShiftSolve.h @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2023 Yixuan Qiu +// Copyright (C) 2020-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/MatOp/SparseGenMatProd.h b/include/Spectra/MatOp/SparseGenMatProd.h index 3df02a6..b65cbc6 100644 --- a/include/Spectra/MatOp/SparseGenMatProd.h +++ b/include/Spectra/MatOp/SparseGenMatProd.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/MatOp/SparseGenRealShiftSolve.h b/include/Spectra/MatOp/SparseGenRealShiftSolve.h index 879b057..f89376f 100644 --- a/include/Spectra/MatOp/SparseGenRealShiftSolve.h +++ b/include/Spectra/MatOp/SparseGenRealShiftSolve.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/MatOp/SparseHermMatProd.h b/include/Spectra/MatOp/SparseHermMatProd.h new file mode 100644 index 0000000..47d8493 --- /dev/null +++ b/include/Spectra/MatOp/SparseHermMatProd.h @@ -0,0 +1,92 @@ +// Copyright (C) 2024-2025 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef SPECTRA_SPARSE_HERM_MAT_PROD_H +#define SPECTRA_SPARSE_HERM_MAT_PROD_H + +#include +#include + +namespace Spectra { + +/// +/// \ingroup MatOp +/// +/// This class defines the matrix-vector multiplication operation on a +/// sparse real symmetric matrix \f$A\f$, i.e., calculating \f$y=Ax\f$ for any vector +/// \f$x\f$. It is mainly used in the SymEigsSolver eigen solver. +/// +/// \tparam Scalar_ The element type of the matrix, for example, +/// `float`, `double`, and `long double`. +/// \tparam Uplo Either `Eigen::Lower` or `Eigen::Upper`, indicating which +/// triangular part of the matrix is used. +/// \tparam Flags Either `Eigen::ColMajor` or `Eigen::RowMajor`, indicating +/// the storage format of the input matrix. +/// \tparam StorageIndex The type of the indices for the sparse matrix. +/// +template +class SparseHermMatProd +{ +public: + /// + /// Element type of the matrix. + /// + using Scalar = Scalar_; + +private: + using Index = Eigen::Index; + using Vector = Eigen::Matrix; + using MapConstVec = Eigen::Map; + using MapVec = Eigen::Map; + using Matrix = Eigen::Matrix; + using SparseMatrix = Eigen::SparseMatrix; + using ConstGenericSparseMatrix = const Eigen::Ref; + + ConstGenericSparseMatrix m_mat; + +public: + /// + /// Constructor to create the matrix operation object. + /// + /// \param mat An **Eigen** sparse matrix object, whose type can be + /// `Eigen::SparseMatrix` or its mapped version + /// `Eigen::Map >`. + /// + template + SparseHermMatProd(const Eigen::SparseMatrixBase& mat) : + m_mat(mat) + { + static_assert( + static_cast(Derived::PlainObject::IsRowMajor) == static_cast(SparseMatrix::IsRowMajor), + "SparseHermMatProd: the \"Flags\" template parameter does not match the input matrix (Eigen::ColMajor/Eigen::RowMajor)"); + } + + /// + /// Return the number of rows of the underlying matrix. + /// + Index rows() const { return m_mat.rows(); } + /// + /// Return the number of columns of the underlying matrix. + /// + Index cols() const { return m_mat.cols(); } + + /// + /// Perform the matrix-vector multiplication operation \f$y=Ax\f$. + /// + /// \param x_in Pointer to the \f$x\f$ vector. + /// \param y_out Pointer to the \f$y\f$ vector. + /// + // y_out = A * x_in + void perform_op(const Scalar* x_in, Scalar* y_out) const + { + MapConstVec x(x_in, m_mat.cols()); + MapVec y(y_out, m_mat.rows()); + y.noalias() = m_mat.template selfadjointView() * x; + } +}; +} // namespace Spectra + +#endif // SPECTRA_SPARSE_HERM_MAT_PROD_H diff --git a/include/Spectra/MatOp/SparseRegularInverse.h b/include/Spectra/MatOp/SparseRegularInverse.h index e528f83..03562a8 100644 --- a/include/Spectra/MatOp/SparseRegularInverse.h +++ b/include/Spectra/MatOp/SparseRegularInverse.h @@ -1,4 +1,4 @@ -// Copyright (C) 2017-2023 Yixuan Qiu +// Copyright (C) 2017-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/MatOp/SparseSymMatProd.h b/include/Spectra/MatOp/SparseSymMatProd.h index 9917f7b..9ac4784 100644 --- a/include/Spectra/MatOp/SparseSymMatProd.h +++ b/include/Spectra/MatOp/SparseSymMatProd.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/MatOp/SparseSymShiftSolve.h b/include/Spectra/MatOp/SparseSymShiftSolve.h index 0184b89..ddf3194 100644 --- a/include/Spectra/MatOp/SparseSymShiftSolve.h +++ b/include/Spectra/MatOp/SparseSymShiftSolve.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/MatOp/SymShiftInvert.h b/include/Spectra/MatOp/SymShiftInvert.h index 2522351..2b6eacc 100644 --- a/include/Spectra/MatOp/SymShiftInvert.h +++ b/include/Spectra/MatOp/SymShiftInvert.h @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2023 Yixuan Qiu +// Copyright (C) 2020-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/MatOp/internal/ArnoldiOp.h b/include/Spectra/MatOp/internal/ArnoldiOp.h index 92d5f71..0188f6b 100644 --- a/include/Spectra/MatOp/internal/ArnoldiOp.h +++ b/include/Spectra/MatOp/internal/ArnoldiOp.h @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2023 Yixuan Qiu +// Copyright (C) 2018-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed @@ -8,7 +8,8 @@ #define SPECTRA_ARNOLDI_OP_H #include -#include // std::sqrt +#include // std::sqrt +#include // std::real namespace Spectra { @@ -32,6 +33,8 @@ template class ArnoldiOp { private: + // The real part type of the matrix element + using RealScalar = typename Eigen::NumTraits::Real; using Index = Eigen::Index; using Vector = Eigen::Matrix; @@ -54,10 +57,10 @@ class ArnoldiOp inline Index rows() const { return m_op.rows(); } - // In generalized eigenvalue problem Ax=lambda*Bx, define the inner product to be = x'By. - // For regular eigenvalue problems, it is the usual inner product = x'y + // In generalized eigenvalue problem Ax=lambda*Bx, define the inner product to be = (x^H)By. + // For regular eigenvalue problems, it is the usual inner product = (x^H)y - // Compute = x'By + // Compute = (x^H)By // x and y are two vectors template Scalar inner_product(const Arg1& x, const Arg2& y) const @@ -66,21 +69,22 @@ class ArnoldiOp return x.dot(m_cache); } - // Compute res = = X'By + // Compute res = = (X^H)By // X is a matrix, y is a vector, res is a vector template - void trans_product(const Arg1& x, const Arg2& y, Eigen::Ref res) const + void adjoint_product(const Arg1& x, const Arg2& y, Eigen::Ref res) const { m_Bop.perform_op(y.data(), m_cache.data()); - res.noalias() = x.transpose() * m_cache; + res.noalias() = x.adjoint() * m_cache; } - // B-norm of a vector, ||x||_B = sqrt(x'Bx) + // B-norm of a vector, ||x||_B = sqrt((x^H)Bx) template - Scalar norm(const Arg& x) const + RealScalar norm(const Arg& x) const { using std::sqrt; - return sqrt(inner_product(x, x)); + using std::real; + return sqrt(real(inner_product(x, x))); } // The "A" operator to generate the Krylov subspace @@ -107,6 +111,8 @@ template class ArnoldiOp { private: + // The real part type of the matrix element + using RealScalar = typename Eigen::NumTraits::Real; using Index = Eigen::Index; using Vector = Eigen::Matrix; @@ -119,7 +125,7 @@ class ArnoldiOp inline Index rows() const { return m_op.rows(); } - // Compute = x'y + // Compute = (x^H)y // x and y are two vectors template Scalar inner_product(const Arg1& x, const Arg2& y) const @@ -127,17 +133,17 @@ class ArnoldiOp return x.dot(y); } - // Compute res = = X'y + // Compute res = = (X^H)y // X is a matrix, y is a vector, res is a vector template - void trans_product(const Arg1& x, const Arg2& y, Eigen::Ref res) const + void adjoint_product(const Arg1& x, const Arg2& y, Eigen::Ref res) const { - res.noalias() = x.transpose() * y; + res.noalias() = x.adjoint() * y; } // B-norm of a vector. For regular eigenvalue problems it is simply the L2 norm template - Scalar norm(const Arg& x) const + RealScalar norm(const Arg& x) const { return x.norm(); } diff --git a/include/Spectra/MatOp/internal/SymGEigsBucklingOp.h b/include/Spectra/MatOp/internal/SymGEigsBucklingOp.h index 7200001..b380381 100644 --- a/include/Spectra/MatOp/internal/SymGEigsBucklingOp.h +++ b/include/Spectra/MatOp/internal/SymGEigsBucklingOp.h @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2023 Yixuan Qiu +// Copyright (C) 2020-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/MatOp/internal/SymGEigsCayleyOp.h b/include/Spectra/MatOp/internal/SymGEigsCayleyOp.h index 952bbfc..df71507 100644 --- a/include/Spectra/MatOp/internal/SymGEigsCayleyOp.h +++ b/include/Spectra/MatOp/internal/SymGEigsCayleyOp.h @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2023 Yixuan Qiu +// Copyright (C) 2020-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/MatOp/internal/SymGEigsCholeskyOp.h b/include/Spectra/MatOp/internal/SymGEigsCholeskyOp.h index ac21b0d..7c1d6bd 100644 --- a/include/Spectra/MatOp/internal/SymGEigsCholeskyOp.h +++ b/include/Spectra/MatOp/internal/SymGEigsCholeskyOp.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/MatOp/internal/SymGEigsRegInvOp.h b/include/Spectra/MatOp/internal/SymGEigsRegInvOp.h index 73cf77f..a8b8482 100644 --- a/include/Spectra/MatOp/internal/SymGEigsRegInvOp.h +++ b/include/Spectra/MatOp/internal/SymGEigsRegInvOp.h @@ -1,4 +1,4 @@ -// Copyright (C) 2017-2023 Yixuan Qiu +// Copyright (C) 2017-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/MatOp/internal/SymGEigsShiftInvertOp.h b/include/Spectra/MatOp/internal/SymGEigsShiftInvertOp.h index ab9bd36..c9e6793 100644 --- a/include/Spectra/MatOp/internal/SymGEigsShiftInvertOp.h +++ b/include/Spectra/MatOp/internal/SymGEigsShiftInvertOp.h @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2023 Yixuan Qiu +// Copyright (C) 2020-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/SymEigsShiftSolver.h b/include/Spectra/SymEigsShiftSolver.h index 90fd86c..3733601 100644 --- a/include/Spectra/SymEigsShiftSolver.h +++ b/include/Spectra/SymEigsShiftSolver.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed @@ -9,7 +9,7 @@ #include -#include "SymEigsBase.h" +#include "HermEigsBase.h" #include "Util/SelectionRule.h" #include "MatOp/DenseSymShiftSolve.h" @@ -146,14 +146,14 @@ namespace Spectra { /// \endcode /// template > -class SymEigsShiftSolver : public SymEigsBase +class SymEigsShiftSolver : public HermEigsBase { private: using Scalar = typename OpType::Scalar; using Index = Eigen::Index; using Array = Eigen::Array; - using Base = SymEigsBase; + using Base = HermEigsBase; using Base::m_nev; using Base::m_ritz_val; diff --git a/include/Spectra/SymEigsSolver.h b/include/Spectra/SymEigsSolver.h index 323c172..a19956a 100644 --- a/include/Spectra/SymEigsSolver.h +++ b/include/Spectra/SymEigsSolver.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed @@ -9,7 +9,7 @@ #include -#include "SymEigsBase.h" +#include "HermEigsBase.h" #include "Util/SelectionRule.h" #include "MatOp/DenseSymMatProd.h" @@ -131,7 +131,7 @@ namespace Spectra { /// \endcode /// template > -class SymEigsSolver : public SymEigsBase +class SymEigsSolver : public HermEigsBase { private: using Index = Eigen::Index; @@ -155,7 +155,7 @@ class SymEigsSolver : public SymEigsBase /// and is advised to take \f$ncv \ge 2\cdot nev\f$. /// SymEigsSolver(OpType& op, Index nev, Index ncv) : - SymEigsBase(op, IdentityBOp(), nev, ncv) + HermEigsBase(op, IdentityBOp(), nev, ncv) {} }; diff --git a/include/Spectra/SymGEigsShiftSolver.h b/include/Spectra/SymGEigsShiftSolver.h index 72c9774..cca5b7e 100644 --- a/include/Spectra/SymGEigsShiftSolver.h +++ b/include/Spectra/SymGEigsShiftSolver.h @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2023 Yixuan Qiu +// Copyright (C) 2020-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed @@ -8,7 +8,7 @@ #define SPECTRA_SYM_GEIGS_SHIFT_SOLVER_H #include // std::move -#include "SymEigsBase.h" +#include "HermEigsBase.h" #include "Util/GEigsMode.h" #include "MatOp/internal/SymGEigsShiftInvertOp.h" #include "MatOp/internal/SymGEigsBucklingOp.h" @@ -145,7 +145,7 @@ class SymGEigsShiftSolver // Partial specialization for mode = GEigsMode::ShiftInvert template class SymGEigsShiftSolver : - public SymEigsBase, BOpType> + public HermEigsBase, BOpType> { private: using Scalar = typename OpType::Scalar; @@ -153,7 +153,7 @@ class SymGEigsShiftSolver : using Array = Eigen::Array; using ModeMatOp = SymGEigsShiftInvertOp; - using Base = SymEigsBase; + using Base = HermEigsBase; using Base::m_nev; using Base::m_ritz_val; @@ -305,7 +305,7 @@ class SymGEigsShiftSolver : // Partial specialization for mode = GEigsMode::Buckling template class SymGEigsShiftSolver : - public SymEigsBase, BOpType> + public HermEigsBase, BOpType> { private: using Scalar = typename OpType::Scalar; @@ -313,7 +313,7 @@ class SymGEigsShiftSolver : using Array = Eigen::Array; using ModeMatOp = SymGEigsBucklingOp; - using Base = SymEigsBase; + using Base = HermEigsBase; using Base::m_nev; using Base::m_ritz_val; @@ -397,7 +397,7 @@ class SymGEigsShiftSolver : // Partial specialization for mode = GEigsMode::Cayley template class SymGEigsShiftSolver : - public SymEigsBase, BOpType> + public HermEigsBase, BOpType> { private: using Scalar = typename OpType::Scalar; @@ -405,7 +405,7 @@ class SymGEigsShiftSolver : using Array = Eigen::Array; using ModeMatOp = SymGEigsCayleyOp; - using Base = SymEigsBase; + using Base = HermEigsBase; using Base::m_nev; using Base::m_ritz_val; diff --git a/include/Spectra/SymGEigsSolver.h b/include/Spectra/SymGEigsSolver.h index ee4cc68..caea165 100644 --- a/include/Spectra/SymGEigsSolver.h +++ b/include/Spectra/SymGEigsSolver.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed @@ -7,7 +7,7 @@ #ifndef SPECTRA_SYM_GEIGS_SOLVER_H #define SPECTRA_SYM_GEIGS_SOLVER_H -#include "SymEigsBase.h" +#include "HermEigsBase.h" #include "Util/GEigsMode.h" #include "MatOp/internal/SymGEigsCholeskyOp.h" #include "MatOp/internal/SymGEigsRegInvOp.h" @@ -147,7 +147,7 @@ class SymGEigsSolver // Partial specialization for mode = GEigsMode::Cholesky template class SymGEigsSolver : - public SymEigsBase, IdentityBOp> + public HermEigsBase, IdentityBOp> { private: using Scalar = typename OpType::Scalar; @@ -156,7 +156,7 @@ class SymGEigsSolver : using Vector = Eigen::Matrix; using ModeMatOp = SymGEigsCholeskyOp; - using Base = SymEigsBase; + using Base = HermEigsBase; const BOpType& m_Bop; @@ -249,13 +249,13 @@ class SymGEigsSolver : // Partial specialization for mode = GEigsMode::RegularInverse template class SymGEigsSolver : - public SymEigsBase, BOpType> + public HermEigsBase, BOpType> { private: using Index = Eigen::Index; using ModeMatOp = SymGEigsRegInvOp; - using Base = SymEigsBase; + using Base = HermEigsBase; public: /// diff --git a/include/Spectra/Util/CompInfo.h b/include/Spectra/Util/CompInfo.h index 22a730e..bd2108a 100644 --- a/include/Spectra/Util/CompInfo.h +++ b/include/Spectra/Util/CompInfo.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/Util/GEigsMode.h b/include/Spectra/Util/GEigsMode.h index 1b9ad4a..5c622ac 100644 --- a/include/Spectra/Util/GEigsMode.h +++ b/include/Spectra/Util/GEigsMode.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/Util/SelectionRule.h b/include/Spectra/Util/SelectionRule.h index 3cbba8e..d67da6d 100644 --- a/include/Spectra/Util/SelectionRule.h +++ b/include/Spectra/Util/SelectionRule.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/Util/SimpleRandom.h b/include/Spectra/Util/SimpleRandom.h index 0c4d454..0b7dda7 100644 --- a/include/Spectra/Util/SimpleRandom.h +++ b/include/Spectra/Util/SimpleRandom.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2023 Yixuan Qiu +// Copyright (C) 2016-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed @@ -8,6 +8,7 @@ #define SPECTRA_SIMPLE_RANDOM_H #include +#include /// \cond @@ -25,48 +26,79 @@ namespace Spectra { // 5. Based on public domain code by Ray Gardner // http://stjarnhimlen.se/snippets/rg_rand.c +// Given a 32-bit integer as the seed, generate a pseudo random 32-bit integer +inline long next_long_rand(long seed) +{ + constexpr unsigned int m_a = 16807; // multiplier + constexpr unsigned long m_max = 2147483647L; // 2^31 - 1 + + unsigned long lo, hi; + + lo = m_a * (long) (seed & 0xFFFF); + hi = m_a * (long) ((unsigned long) seed >> 16); + lo += (hi & 0x7FFF) << 16; + if (lo > m_max) + { + lo &= m_max; + ++lo; + } + lo += hi >> 15; + if (lo > m_max) + { + lo &= m_max; + ++lo; + } + return (long) lo; +} + +// Generate a random scalar from the given random seed +// Also overwrite seed with the new random integer +template +struct RandomScalar +{ + static Scalar run(long& seed) + { + constexpr unsigned long m_max = 2147483647L; // 2^31 - 1 + + seed = next_long_rand(seed); + return Scalar(seed) / Scalar(m_max) - Scalar(0.5); + } +}; +// Specialization for complex values +template +struct RandomScalar> +{ + static std::complex run(long& seed) + { + RealScalar r = RandomScalar::run(seed); + RealScalar i = RandomScalar::run(seed); + return std::complex(r, i); + } +}; + +// A simple random generator class template class SimpleRandom { private: + // The real part type of the matrix element + using RealScalar = typename Eigen::NumTraits::Real; using Index = Eigen::Index; using Vector = Eigen::Matrix; - static constexpr unsigned int m_a = 16807; // multiplier - static constexpr unsigned long m_max = 2147483647L; // 2^31 - 1 - long m_rand; // RNG state + long m_rand; // RNG state - inline long next_long_rand(long seed) const +public: + SimpleRandom(unsigned long init_seed) { - unsigned long lo, hi; - - lo = m_a * (long) (seed & 0xFFFF); - hi = m_a * (long) ((unsigned long) seed >> 16); - lo += (hi & 0x7FFF) << 16; - if (lo > m_max) - { - lo &= m_max; - ++lo; - } - lo += hi >> 15; - if (lo > m_max) - { - lo &= m_max; - ++lo; - } - return (long) lo; + constexpr unsigned long m_max = 2147483647L; // 2^31 - 1 + m_rand = init_seed ? (init_seed & m_max) : 1; } -public: - SimpleRandom(unsigned long init_seed) : - m_rand(init_seed ? (init_seed & m_max) : 1) - {} - // Return a single random number, ranging from -0.5 to 0.5 Scalar random() { - m_rand = next_long_rand(m_rand); - return Scalar(m_rand) / Scalar(m_max) - Scalar(0.5); + return RandomScalar::run(m_rand); } // Fill the given vector with random numbers @@ -76,10 +108,8 @@ class SimpleRandom const Index len = vec.size(); for (Index i = 0; i < len; i++) { - m_rand = next_long_rand(m_rand); - vec[i] = Scalar(m_rand); + vec[i] = random(); } - vec.array() = vec.array() / Scalar(m_max) - Scalar(0.5); } // Return a vector of random numbers diff --git a/include/Spectra/Util/TypeTraits.h b/include/Spectra/Util/TypeTraits.h index ecf0307..a413f00 100644 --- a/include/Spectra/Util/TypeTraits.h +++ b/include/Spectra/Util/TypeTraits.h @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2023 Yixuan Qiu +// Copyright (C) 2018-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/include/Spectra/Util/Version.h b/include/Spectra/Util/Version.h index 488df6e..39b267c 100644 --- a/include/Spectra/Util/Version.h +++ b/include/Spectra/Util/Version.h @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2023 Yixuan Qiu +// Copyright (C) 2020-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed @@ -8,8 +8,8 @@ #define SPECTRA_VERSION_H #define SPECTRA_MAJOR_VERSION 1 -#define SPECTRA_MINOR_VERSION 0 -#define SPECTRA_PATCH_VERSION 1 +#define SPECTRA_MINOR_VERSION 1 +#define SPECTRA_PATCH_VERSION 0 #define SPECTRA_VERSION (SPECTRA_MAJOR_VERSION * 10000 + SPECTRA_MINOR_VERSION * 100 + SPECTRA_PATCH_VERSION) diff --git a/include/Spectra/contrib/PartialSVDSolver.h b/include/Spectra/contrib/PartialSVDSolver.h index 275c3e5..1ecf475 100644 --- a/include/Spectra/contrib/PartialSVDSolver.h +++ b/include/Spectra/contrib/PartialSVDSolver.h @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2023 Yixuan Qiu +// Copyright (C) 2018-2025 Yixuan Qiu // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed diff --git a/test/Arnoldi.cpp b/test/Arnoldi.cpp new file mode 100644 index 0000000..4533aa5 --- /dev/null +++ b/test/Arnoldi.cpp @@ -0,0 +1,158 @@ +// Test ../include/Spectra/LinAlg/Arnoldi.h and +// ../include/Spectra/LinAlg/Lanczos.h +#include +#include +#include +#include +#include +#include + +using namespace Spectra; + +#include "catch.hpp" + +using Matrix = Eigen::MatrixXd; +using Vector = Eigen::VectorXd; +using Index = Eigen::Index; + +// clang-format off +template +void run_test(FacType& fac, const MatType& A, int m) +{ + using VecType = Eigen::Matrix; + + // Initialization + const int n = A.rows(); + VecType v0 = VecType::Random(n); + Eigen::Map v0map(v0.data(), n); + Index op_counter = 0; + fac.init(v0map, op_counter); + + // After initialization, the subspace dimension is one + const int k1 = fac.subspace_dim(); + INFO("k1 = " << k1); + REQUIRE(k1 == 1); + + // A*V = V*H + f*e' + constexpr double tol = 1e-12; + MatType V = fac.matrix_V().leftCols(k1); + MatType H = fac.matrix_H().topLeftCorner(k1, k1); + MatType resid = A * V - V * H; + VecType f = fac.vector_f(); + INFO("AV - VH = \n" << resid); + INFO("f = " << f.transpose()); + // Test residual + REQUIRE((resid.template rightCols<1>() - f).cwiseAbs().maxCoeff() == Approx(0.0).margin(tol)); + + // Factorization to m/2 + fac.factorize_from(1, m / 2, op_counter); + const int k2 = fac.subspace_dim(); + INFO("k2 = " << k2); + REQUIRE(k2 == m / 2); + + V = fac.matrix_V().leftCols(k2); + H = fac.matrix_H().topLeftCorner(k2, k2); + resid = A * V - V * H; + f = fac.vector_f(); + INFO("AV - VH = \n" << resid); + INFO("f = " << f.transpose()); + // Test residual + REQUIRE(resid.leftCols(k2 - 1).cwiseAbs().maxCoeff() == Approx(0.0).margin(tol)); + REQUIRE((resid.template rightCols<1>() - f).cwiseAbs().maxCoeff() == Approx(0.0).margin(tol)); + + // Factorization to m + fac.factorize_from(m / 2, m, op_counter); + const int k3 = fac.subspace_dim(); + INFO("k3 = " << k3); + REQUIRE(k3 == m); + + V = fac.matrix_V().leftCols(k3); + H = fac.matrix_H().topLeftCorner(k3, k3); + resid = A * V - V * H; + f = fac.vector_f(); + MatType VHV = V.adjoint() * V; + INFO("V = \n" << V); + INFO("H = \n" << H); + INFO("V^H * V = \n" << VHV); + INFO("AV - VH = \n" << resid); + INFO("f = " << f.transpose()); + // Test orthogonality of V + MatType iden = MatType::Identity(m, m); + REQUIRE((VHV - iden).cwiseAbs().maxCoeff() == Approx(0.0).margin(tol)); + // Test residual + REQUIRE(resid.leftCols(k3 - 1).cwiseAbs().maxCoeff() == Approx(0.0).margin(tol)); + REQUIRE((resid.template rightCols<1>() - f).cwiseAbs().maxCoeff() == Approx(0.0).margin(tol)); +} +// clang-format on + +TEST_CASE("Arnoldi factorization of general real matrix", "[Arnoldi]") +{ + std::srand(123); + const int n = 10; + Matrix A = Matrix::Random(n, n); + const int m = 6; + + using AOpType = DenseGenMatProd; + using ArnoldiOpType = ArnoldiOp; + + AOpType Aop(A); + ArnoldiOpType arn(Aop, IdentityBOp()); + Arnoldi fac(arn, m); + + run_test(fac, A, m); +} + +TEST_CASE("Lanczos factorization of symmetric real matrix", "[Lanczos]") +{ + std::srand(123); + const int n = 10; + Matrix A = Matrix::Random(n, n); + A = (A + A.transpose()).eval(); + const int m = 6; + + using AOpType = DenseSymMatProd; + using ArnoldiOpType = ArnoldiOp; + + AOpType Aop(A); + ArnoldiOpType arn(Aop, IdentityBOp()); + Lanczos fac(arn, m); + + run_test(fac, A, m); +} + +TEST_CASE("Arnoldi factorization of general complex matrix", "[Arnoldi]") +{ + std::srand(123); + const int n = 10; + Eigen::MatrixXcd A = Eigen::MatrixXcd::Random(n, n); + const int m = 6; + + using Scalar = std::complex; + using AOpType = DenseGenMatProd; + using ArnoldiOpType = ArnoldiOp; + + AOpType Aop(A); + ArnoldiOpType arn(Aop, IdentityBOp()); + Arnoldi fac(arn, m); + + run_test(fac, A, m); +} + +TEST_CASE("Lanczos factorization of Hermitian complex matrix", "[Lanczos]") +{ + std::srand(123); + const int n = 10; + Eigen::MatrixXcd A = Eigen::MatrixXcd::Random(n, n); + A = (A + A.adjoint()).eval(); + const int m = 6; + + using Scalar = std::complex; + using AOpType = DenseHermMatProd; + using ArnoldiOpType = ArnoldiOp; + + AOpType Aop(A); + ArnoldiOpType arn(Aop, IdentityBOp()); + Lanczos fac(arn, m); + + run_test(fac, A, m); +} diff --git a/test/BKLDLT.cpp b/test/BKLDLT.cpp index a2473ac..110dbcc 100644 --- a/test/BKLDLT.cpp +++ b/test/BKLDLT.cpp @@ -8,26 +8,31 @@ using namespace Spectra; using Matrix = Eigen::MatrixXd; using Vector = Eigen::VectorXd; +using ComplexMatrix = Eigen::MatrixXcd; +using ComplexVector = Eigen::VectorXcd; // Solve (A - s * I)x = b -void run_test(const Matrix& A, const Vector& b, double s) +template +void run_test(const MatType& A, const VecType& b, double s) { + using Scalar = typename MatType::Scalar; + // Test decomposition using only the lower triangular part - BKLDLT decompL(A, Eigen::Lower, s); + BKLDLT decompL(A, Eigen::Lower, s); REQUIRE(decompL.info() == CompInfo::Successful); // Test decomposition using only the upper triangular part - BKLDLT decompU(A, Eigen::Upper, s); + BKLDLT decompU(A, Eigen::Upper, s); REQUIRE(decompU.info() == CompInfo::Successful); // Test whether the solutions are identical - Vector solL = decompL.solve(b); - Vector solU = decompU.solve(b); + VecType solL = decompL.solve(b); + VecType solU = decompU.solve(b); REQUIRE((solL - solU).cwiseAbs().maxCoeff() == 0.0); // Test the accuracy of the solution constexpr double tol = 1e-9; - Vector resid = A * solL - s * solL - b; + VecType resid = A * solL - s * solL - b; INFO("||(A - s * I)x - b||_inf = " << resid.cwiseAbs().maxCoeff()); REQUIRE(resid.cwiseAbs().maxCoeff() == Approx(0.0).margin(tol)); } @@ -67,3 +72,39 @@ TEST_CASE("BKLDLT decomposition of symmetric real matrix [1000x1000]", "[BKLDLT] run_test(A, b, shift); } + +TEST_CASE("BKLDLT decomposition of Hermitian matrix [10x10]", "[BKLDLT]") +{ + std::srand(123); + const int n = 10; + ComplexMatrix A = ComplexMatrix::Random(n, n); + A = (A + A.adjoint()).eval(); + ComplexVector b = ComplexVector::Random(n); + const double shift = 1.0; + + run_test(A, b, shift); +} + +TEST_CASE("BKLDLT decomposition of Hermitian matrix [100x100]", "[BKLDLT]") +{ + std::srand(123); + const int n = 100; + ComplexMatrix A = ComplexMatrix::Random(n, n); + A = (A + A.adjoint()).eval(); + ComplexVector b = ComplexVector::Random(n); + const double shift = 1.0; + + run_test(A, b, shift); +} + +TEST_CASE("BKLDLT decomposition of Hermitian matrix [1000x1000]", "[BKLDLT]") +{ + std::srand(123); + const int n = 1000; + ComplexMatrix A = ComplexMatrix::Random(n, n); + A = (A + A.adjoint()).eval(); + ComplexVector b = ComplexVector::Random(n); + const double shift = 1.0; + + run_test(A, b, shift); +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 38db671..33b5268 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,31 +1,54 @@ set(test_target_sources) - add_library (tests-main tests-main.cpp) list(APPEND test_target_sources - BKLDLT.cpp - DavidsonSymEigs.cpp - DenseGenMatProd.cpp - DenseSymMatProd.cpp + # Linear algebra + QR.cpp Eigen.cpp - GenEigs.cpp - GenEigsRealShift.cpp - GenEigsComplexShift.cpp + Schur.cpp + BKLDLT.cpp + Arnoldi.cpp + + # JD linear algebra Orthogonalization.cpp - JDSymEigsBase.cpp - JDSymEigsDPRConstructor.cpp - QR.cpp RitzPairs.cpp - Schur.cpp SearchSpace.cpp + + # Matrix operators + DenseGenMatProd.cpp + DenseSymMatProd.cpp SparseGenMatProd.cpp SparseSymMatProd.cpp - SVD.cpp + + # Symmetric eigen solver SymEigs.cpp SymEigsShift.cpp + + # Hermitian eigen solver + HermEigs.cpp + + # General eigen solver + GenEigs.cpp + GenEigsRealShift.cpp + GenEigsComplexShift.cpp + + # Symmetric generalized eigen solver SymGEigsCholesky.cpp SymGEigsRegInv.cpp SymGEigsShift.cpp + + # SVD + SVD.cpp + + # JD eigen solver + JDSymEigsBase.cpp + JDSymEigsDPRConstructor.cpp + DavidsonSymEigs.cpp + + # Examples from bug reports + Example1.cpp + Example2.cpp + Example3.cpp ) foreach(TEST_SOURCE ${test_target_sources}) @@ -41,4 +64,3 @@ foreach(TEST_SOURCE ${test_target_sources}) add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) # the working directory is the out-of-source build directory endforeach() - diff --git a/test/HermEigs.cpp b/test/HermEigs.cpp new file mode 100644 index 0000000..22afb01 --- /dev/null +++ b/test/HermEigs.cpp @@ -0,0 +1,175 @@ +#include +#include +#include +#include +#include // Requires C++ 11 + +#include +#include +#include + +using namespace Spectra; + +#include "catch.hpp" + +using Matrix = Eigen::MatrixXcd; +using Vector = Eigen::VectorXcd; +using SpMatrix = Eigen::SparseMatrix>; +using RealMatrix = Eigen::MatrixXd; +using RealVector = Eigen::VectorXd; + +// Generate data for testing +Matrix gen_dense_data(int n) +{ + const Matrix mat = Matrix::Random(n, n); + return mat + mat.adjoint(); +} + +SpMatrix gen_sparse_data(int n, double prob = 0.5) +{ + // Eigen solver only uses the lower triangular part of mat, + // so we don't need to make mat an Hermitian matrix here, + // but diagonal elements must have a zero imaginary part + SpMatrix mat(n, n); + std::default_random_engine gen; + gen.seed(0); + std::uniform_real_distribution distr(0.0, 1.0); + for (int i = 0; i < n; i++) + { + for (int j = 0; j < n; j++) + { + if (distr(gen) < prob) + { + double re = distr(gen) - 0.5; + double im = (i == j) ? 0.0 : (distr(gen) - 0.5); + mat.insert(i, j) = std::complex(re, im); + } + } + } + return mat; +} + +template +void run_test(const MatType& mat, Solver& eigs, SortRule selection) +{ + eigs.init(); + int nconv = eigs.compute(selection); + int niter = eigs.num_iterations(); + int nops = eigs.num_operations(); + + INFO("nconv = " << nconv); + INFO("niter = " << niter); + INFO("nops = " << nops); + REQUIRE(eigs.info() == CompInfo::Successful); + + RealVector evals = eigs.eigenvalues(); + Matrix evecs = eigs.eigenvectors(); + + Matrix resid = mat.template selfadjointView() * evecs - evecs * evals.asDiagonal(); + const double err = resid.array().abs().maxCoeff(); + + INFO("||AU - UD||_inf = " << err); + REQUIRE(err == Approx(0.0).margin(1e-9)); +} + +template +void run_test_sets(const MatType& mat, int k, int m) +{ + constexpr bool is_dense = std::is_same::value; + using DenseOp = DenseHermMatProd>; + using SparseOp = SparseHermMatProd>; + using OpType = typename std::conditional::type; + + OpType op(mat); + HermEigsSolver eigs(op, k, m); + + SECTION("Largest Magnitude") + { + run_test(mat, eigs, SortRule::LargestMagn); + } + SECTION("Largest Value") + { + run_test(mat, eigs, SortRule::LargestAlge); + } + SECTION("Smallest Magnitude") + { + run_test(mat, eigs, SortRule::SmallestMagn); + } + SECTION("Smallest Value") + { + run_test(mat, eigs, SortRule::SmallestAlge); + } + SECTION("Both Ends") + { + run_test(mat, eigs, SortRule::BothEnds); + } +} + +TEST_CASE("Eigensolver of Hermitian matrix [10x10]", "[eigs_herm]") +{ + std::srand(123); + + const Matrix A = gen_dense_data(10); + int k = 3; + int m = 6; + + run_test_sets(A, k, m); +} + +TEST_CASE("Eigensolver of Hermitian matrix [100x100]", "[eigs_herm]") +{ + std::srand(123); + + const Matrix A = gen_dense_data(100); + int k = 10; + int m = 20; + + run_test_sets(A, k, m); +} + +TEST_CASE("Eigensolver of Hermitian matrix [1000x1000]", "[eigs_herm]") +{ + std::srand(123); + + const Matrix A = gen_dense_data(1000); + int k = 20; + int m = 50; + + run_test_sets(A, k, m); +} + +TEST_CASE("Eigensolver of sparse Hermitian matrix [10x10]", "[eigs_herm]") +{ + std::srand(123); + + // Eigen solver only uses the lower triangle + const SpMatrix A = gen_sparse_data(10, 0.5); + int k = 3; + int m = 6; + + run_test_sets(A, k, m); +} + +TEST_CASE("Eigensolver of sparse Hermitian matrix [100x100]", "[eigs_herm]") +{ + std::srand(123); + + // Eigen solver only uses the lower triangle + const SpMatrix A = gen_sparse_data(100, 0.1); + int k = 10; + int m = 20; + + run_test_sets(A, k, m); +} + +TEST_CASE("Eigensolver of sparse Hermitian matrix [1000x1000]", "[eigs_herm]") +{ + std::srand(123); + + // Eigen solver only uses the lower triangle + const SpMatrix A = gen_sparse_data(1000, 0.01); + int k = 20; + int m = 50; + + run_test_sets(A, k, m); +} diff --git a/test/Makefile b/test/Makefile index bfa1801..b424068 100644 --- a/test/Makefile +++ b/test/Makefile @@ -10,10 +10,11 @@ HEADERS = $(wildcard ../include/Spectra/MatOp/*.h) \ $(wildcard ../include/Spectra/*.h) \ $(wildcard ../include/Spectra/contrib/*.h) -OUTPUT = QR.out Eigen.out Schur.out BKLDLT.out \ +OUTPUT = QR.out Eigen.out Schur.out BKLDLT.out Arnoldi.out \ Orthogonalization.out RitzPairs.out SearchSpace.out \ DenseGenMatProd.out DenseSymMatProd.out SparseGenMatProd.out SparseSymMatProd.out \ SymEigs.out SymEigsShift.out \ + HermEigs.out \ GenEigs.out GenEigsRealShift.out GenEigsComplexShift.out \ SymGEigsCholesky.out SymGEigsRegInv.out SymGEigsShift.out \ SVD.out \ @@ -29,6 +30,7 @@ test: -./Eigen.out -./Schur.out -./BKLDLT.out + -./Arnoldi.out -./Orthogonalization.out -./RitzPairs.out -./SearchSpace.out @@ -38,6 +40,7 @@ test: -./SparseSymMatProd.out -./SymEigs.out -./SymEigsShift.out + -./HermEigs.out -./GenEigs.out -./GenEigsRealShift.out -./GenEigsComplexShift.out diff --git a/test/SymEigs.cpp b/test/SymEigs.cpp index 7255fb2..d695c64 100644 --- a/test/SymEigs.cpp +++ b/test/SymEigs.cpp @@ -19,13 +19,13 @@ using SpMatrix = Eigen::SparseMatrix; // Generate data for testing Matrix gen_dense_data(int n) { - const Matrix mat = Eigen::MatrixXd::Random(n, n); + const Matrix mat = Matrix::Random(n, n); return mat + mat.transpose(); } SpMatrix gen_sparse_data(int n, double prob = 0.5) { - // Eigen solver only uses the lower triangle of mat, + // Eigen solver only uses the lower triangular part of mat, // so we don't need to make mat symmetric here. SpMatrix mat(n, n); std::default_random_engine gen;