├── CMake ├── AddCompilerFlag.cmake ├── CheckCXXCompilerFlag.cmake ├── DownloadProject.CMakeLists.cmake.in ├── DownloadProject.LICENSE ├── DownloadProject.cmake ├── FindEigen3.cmake ├── FindHalf.cmake ├── FindLIBIGL.cmake ├── FindMKL.cmake ├── FindMetis.cmake ├── FindSuiteSparse.cmake ├── FindTBB.cmake └── OptimizeForArchitecture.cmake ├── CMakeLists.txt ├── LICENSE ├── Libs ├── CMakeLists.txt └── spectra │ ├── .gitignore │ ├── .travis.yml │ ├── AUTHORS.md │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── benchmark │ ├── ArpackFun.h │ ├── Cpp.cpp │ ├── F77.cpp │ ├── Makefile │ ├── main.cpp │ ├── result_analyzer.R │ ├── timer.h │ └── wrapper.f │ ├── doxygen │ ├── Doxyfile │ └── Overview.md │ ├── include │ └── Spectra │ │ ├── GenEigsBase.h │ │ ├── GenEigsComplexShiftSolver.h │ │ ├── GenEigsRealShiftSolver.h │ │ ├── GenEigsSolver.h │ │ ├── LinAlg │ │ ├── Arnoldi.h │ │ ├── BKLDLT.h │ │ ├── DoubleShiftQR.h │ │ ├── Lanczos.h │ │ ├── TridiagEigen.h │ │ ├── UpperHessenbergEigen.h │ │ └── UpperHessenbergQR.h │ │ ├── MatOp │ │ ├── DenseCholesky.h │ │ ├── DenseGenComplexShiftSolve.h │ │ ├── DenseGenMatProd.h │ │ ├── DenseGenRealShiftSolve.h │ │ ├── DenseSymMatProd.h │ │ ├── DenseSymShiftSolve.h │ │ ├── SparseCholesky.h │ │ ├── SparseGenMatProd.h │ │ ├── SparseGenRealShiftSolve.h │ │ ├── SparseRegularInverse.h │ │ ├── SparseSymMatProd.h │ │ ├── SparseSymShiftSolve.h │ │ └── internal │ │ │ ├── ArnoldiOp.h │ │ │ ├── SymGEigsCholeskyOp.h │ │ │ └── SymGEigsRegInvOp.h │ │ ├── SymEigsBase.h │ │ ├── SymEigsShiftSolver.h │ │ ├── SymEigsSolver.h │ │ ├── SymGEigsSolver.h │ │ ├── Util │ │ ├── CompInfo.h │ │ ├── GEigsMode.h │ │ ├── SelectionRule.h │ │ ├── SimpleRandom.h │ │ └── TypeTraits.h │ │ └── contrib │ │ ├── LOBPCGSolver.h │ │ └── PartialSVDSolver.h │ └── test │ ├── BKLDLT.cpp │ ├── Eigen.cpp │ ├── GenEigs.cpp │ ├── GenEigsComplexShift.cpp │ ├── GenEigsRealShift.cpp │ ├── Makefile │ ├── QR.cpp │ ├── SVD.cpp │ ├── SymEigs.cpp │ ├── SymEigsShift.cpp │ ├── SymGEigsCholesky.cpp │ ├── SymGEigsRegInv.cpp │ └── catch.hpp ├── Projects ├── CMakeLists.txt └── ProgrammableDigitalWeaves │ ├── CMakeLists.txt │ ├── Data │ ├── sphere162.obj │ └── torus.obj │ ├── DrawPattern.py │ ├── GCode │ ├── Actuation.gcode │ ├── CircleFused.gcode │ ├── CircleSliding.gcode │ ├── FusedGrid.gcode │ ├── SlidingGrid.gcode │ ├── Type1.gcode │ ├── Type2.gcode │ ├── Type3.gcode │ └── Type4.gcode │ ├── autodiff │ ├── EoLRodBendingAndTwisting.h │ ├── EoLRodBendingAndTwistingPBC.h │ ├── EoLRodEulerAngleBendingAndTwisting.h │ ├── EoLRodQuaternionBendingAndTwisting.h │ ├── EoLRodStretchingEnergy.h │ ├── JointBendingAndTwisting.h │ └── RotationPenalty.h │ ├── include │ ├── EoLRodSim.h │ ├── GCodeGenerator.h │ ├── Homogenization.h │ ├── HybridC2Curve.h │ ├── IO.h │ ├── RestState.h │ ├── Rod.h │ ├── UI.h │ ├── UnitPatch.h │ ├── Util.h │ └── VecMatDef.h │ └── src │ ├── BendingAndTwisting.cpp │ ├── DerivativeTest.cpp │ ├── Elasticity.cpp │ ├── EoLRodSim.cpp │ ├── GCodeGenerator.cpp │ ├── Homogenization.cpp │ ├── HybridC2Curve.cpp │ ├── Joint.cpp │ ├── PBCBendingAndTwisting.cpp │ ├── ParallelContact.cpp │ ├── PeriodicBC.cpp │ ├── Regularizer.cpp │ ├── RestState.cpp │ ├── Rod.cpp │ ├── Scene.cpp │ ├── Stretching.cpp │ ├── UI.cpp │ ├── UnitPatch.cpp │ ├── Visualization.cpp │ └── main.cpp ├── README.md ├── build.sh └── img └── teaser.png /CMake/CheckCXXCompilerFlag.cmake: -------------------------------------------------------------------------------- 1 | # - Check whether the CXX compiler supports a given flag. 2 | # CHECK_CXX_COMPILER_FLAG( ) 3 | # - the compiler flag 4 | # - variable to store the result 5 | # This internally calls the check_cxx_source_compiles macro. See help 6 | # for CheckCXXSourceCompiles for a listing of variables that can 7 | # modify the build. 8 | 9 | #============================================================================= 10 | # Copyright 2006-2009 Kitware, Inc. 11 | # Copyright 2006 Alexander Neundorf 12 | # Copyright 2011-2013 Matthias Kretz 13 | # 14 | # Redistribution and use in source and binary forms, with or without 15 | # modification, are permitted provided that the following conditions are 16 | # met: 17 | # 18 | # * Redistributions of source code must retain the above copyright notice, 19 | # this list of conditions and the following disclaimer. 20 | # 21 | # * Redistributions in binary form must reproduce the above copyright notice, 22 | # this list of conditions and the following disclaimer in the documentation 23 | # and/or other materials provided with the distribution. 24 | # 25 | # * The names of Kitware, Inc., the Insight Consortium, or the names of 26 | # any consortium members, or of any contributors, may not be used to 27 | # endorse or promote products derived from this software without 28 | # specific prior written permission. 29 | # 30 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' 31 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 | # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 34 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 36 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 37 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 38 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 39 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40 | #============================================================================= 41 | 42 | INCLUDE(CheckCXXSourceCompiles) 43 | 44 | MACRO(CHECK_CXX_COMPILER_FLAG _FLAG _RESULT) 45 | SET(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}") 46 | SET(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}") 47 | if (${ARGC} GREATER 2) 48 | SET(TEST_SOURCE "${ARGV2}") 49 | else () 50 | SET(TEST_SOURCE "int main() { return 0;}") 51 | endif () 52 | CHECK_CXX_SOURCE_COMPILES("${TEST_SOURCE}" ${_RESULT} 53 | # Some compilers do not fail with a bad flag 54 | FAIL_REGEX "error: bad value (.*) for .* switch" # GNU 55 | FAIL_REGEX "argument unused during compilation" # clang 56 | FAIL_REGEX "is valid for .* but not for C\\\\+\\\\+" # GNU 57 | FAIL_REGEX "unrecognized .*option" # GNU 58 | FAIL_REGEX "ignored for target" # GNU 59 | FAIL_REGEX "ignoring unknown option" # MSVC 60 | FAIL_REGEX "warning D9002" # MSVC 61 | FAIL_REGEX "[Uu]nknown option" # HP 62 | FAIL_REGEX "[Ww]arning: [Oo]ption" # SunPro 63 | FAIL_REGEX "command option .* is not recognized" # XL 64 | FAIL_REGEX "WARNING: unknown flag:" # Open64 65 | FAIL_REGEX "command line error" # ICC 66 | FAIL_REGEX "command line warning" # ICC 67 | FAIL_REGEX "#10236:" # ICC: File not found 68 | FAIL_REGEX " #10159: " # ICC 69 | FAIL_REGEX " #10353: " # ICC: option '-mfma' ignored, suggest using '-march=core-avx2' 70 | ) 71 | SET(CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}") 72 | ENDMACRO(CHECK_CXX_COMPILER_FLAG) 73 | 74 | -------------------------------------------------------------------------------- /CMake/DownloadProject.CMakeLists.cmake.in: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.2) 2 | 3 | project(${DL_ARGS_PROJ}-download NONE) 4 | 5 | include(ExternalProject) 6 | ExternalProject_Add(${DL_ARGS_PROJ}-download 7 | ${DL_ARGS_UNPARSED_ARGUMENTS} 8 | SOURCE_DIR "${DL_ARGS_SOURCE_DIR}" 9 | BINARY_DIR "${DL_ARGS_BINARY_DIR}" 10 | CONFIGURE_COMMAND "" 11 | BUILD_COMMAND "" 12 | INSTALL_COMMAND "" 13 | TEST_COMMAND "" 14 | ) 15 | -------------------------------------------------------------------------------- /CMake/DownloadProject.LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Crascit 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /CMake/DownloadProject.cmake: -------------------------------------------------------------------------------- 1 | # MODULE: DownloadProject 2 | # 3 | # PROVIDES: 4 | # download_project( PROJ projectName 5 | # [PREFIX prefixDir] 6 | # [DOWNLOAD_DIR downloadDir] 7 | # [SOURCE_DIR srcDir] 8 | # [BINARY_DIR binDir] 9 | # [QUIET] 10 | # ... 11 | # ) 12 | # 13 | # Provides the ability to download and unpack a tarball, zip file, git repository, 14 | # etc. at configure time (i.e. when the cmake command is run). How the downloaded 15 | # and unpacked contents are used is up to the caller, but the motivating case is 16 | # to download source code which can then be included directly in the build with 17 | # add_subdirectory() after the call to download_project(). Source and build 18 | # directories are set up with this in mind. 19 | # 20 | # The PROJ argument is required. The projectName value will be used to construct 21 | # the following variables upon exit (obviously replace projectName with its actual 22 | # value): 23 | # 24 | # projectName_SOURCE_DIR 25 | # projectName_BINARY_DIR 26 | # 27 | # The SOURCE_DIR and BINARY_DIR arguments are optional and would not typically 28 | # need to be provided. They can be specified if you want the downloaded source 29 | # and build directories to be located in a specific place. The contents of 30 | # projectName_SOURCE_DIR and projectName_BINARY_DIR will be populated with the 31 | # locations used whether you provide SOURCE_DIR/BINARY_DIR or not. 32 | # 33 | # The DOWNLOAD_DIR argument does not normally need to be set. It controls the 34 | # location of the temporary CMake build used to perform the download. 35 | # 36 | # The PREFIX argument can be provided to change the base location of the default 37 | # values of DOWNLOAD_DIR, SOURCE_DIR and BINARY_DIR. If all of those three arguments 38 | # are provided, then PREFIX will have no effect. The default value for PREFIX is 39 | # CMAKE_BINARY_DIR. 40 | # 41 | # The QUIET option can be given if you do not want to show the output associated 42 | # with downloading the specified project. 43 | # 44 | # In addition to the above, any other options are passed through unmodified to 45 | # ExternalProject_Add() to perform the actual download, patch and update steps. 46 | # The following ExternalProject_Add() options are explicitly prohibited (they 47 | # are reserved for use by the download_project() command): 48 | # 49 | # CONFIGURE_COMMAND 50 | # BUILD_COMMAND 51 | # INSTALL_COMMAND 52 | # TEST_COMMAND 53 | # 54 | # Only those ExternalProject_Add() arguments which relate to downloading, patching 55 | # and updating of the project sources are intended to be used. Also note that at 56 | # least one set of download-related arguments are required. 57 | # 58 | # If using CMake 3.2 or later, the UPDATE_DISCONNECTED option can be used to 59 | # prevent a check at the remote end for changes every time CMake is run 60 | # after the first successful download. See the documentation of the ExternalProject 61 | # module for more information. It is likely you will want to use this option if it 62 | # is available to you. 63 | # 64 | # EXAMPLE USAGE: 65 | # 66 | # include(download_project.cmake) 67 | # download_project(PROJ googletest 68 | # GIT_REPOSITORY https://github.com/google/googletest.git 69 | # GIT_TAG master 70 | # UPDATE_DISCONNECTED 1 71 | # QUIET 72 | # ) 73 | # 74 | # add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR}) 75 | # 76 | #======================================================================================== 77 | 78 | 79 | set(_DownloadProjectDir "${CMAKE_CURRENT_LIST_DIR}") 80 | 81 | include(CMakeParseArguments) 82 | 83 | function(download_project) 84 | 85 | set(options QUIET) 86 | set(oneValueArgs 87 | PROJ 88 | PREFIX 89 | DOWNLOAD_DIR 90 | SOURCE_DIR 91 | BINARY_DIR 92 | # Prevent the following from being passed through 93 | CONFIGURE_COMMAND 94 | BUILD_COMMAND 95 | INSTALL_COMMAND 96 | TEST_COMMAND 97 | ) 98 | set(multiValueArgs "") 99 | 100 | cmake_parse_arguments(DL_ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 101 | 102 | # Hide output if requested 103 | if (DL_ARGS_QUIET) 104 | set(OUTPUT_QUIET "OUTPUT_QUIET") 105 | else() 106 | unset(OUTPUT_QUIET) 107 | message(STATUS "Downloading/updating ${DL_ARGS_PROJ}") 108 | endif() 109 | 110 | # Set up where we will put our temporary CMakeLists.txt file and also 111 | # the base point below which the default source and binary dirs will be 112 | if (NOT DL_ARGS_PREFIX) 113 | set(DL_ARGS_PREFIX "${CMAKE_BINARY_DIR}") 114 | endif() 115 | if (NOT DL_ARGS_DOWNLOAD_DIR) 116 | set(DL_ARGS_DOWNLOAD_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-download") 117 | endif() 118 | 119 | # Ensure the caller can know where to find the source and build directories 120 | if (NOT DL_ARGS_SOURCE_DIR) 121 | set(DL_ARGS_SOURCE_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-src") 122 | endif() 123 | if (NOT DL_ARGS_BINARY_DIR) 124 | set(DL_ARGS_BINARY_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-build") 125 | endif() 126 | set(${DL_ARGS_PROJ}_SOURCE_DIR "${DL_ARGS_SOURCE_DIR}" PARENT_SCOPE) 127 | set(${DL_ARGS_PROJ}_BINARY_DIR "${DL_ARGS_BINARY_DIR}" PARENT_SCOPE) 128 | 129 | # Create and build a separate CMake project to carry out the download. 130 | # If we've already previously done these steps, they will not cause 131 | # anything to be updated, so extra rebuilds of the project won't occur. 132 | configure_file("${_DownloadProjectDir}/DownloadProject.CMakeLists.cmake.in" 133 | "${DL_ARGS_DOWNLOAD_DIR}/CMakeLists.txt") 134 | execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . 135 | ${OUTPUT_QUIET} 136 | WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}" 137 | ) 138 | execute_process(COMMAND ${CMAKE_COMMAND} --build . 139 | ${OUTPUT_QUIET} 140 | WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}" 141 | ) 142 | 143 | endfunction() 144 | -------------------------------------------------------------------------------- /CMake/FindEigen3.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find Eigen3 lib 2 | # 3 | # This module supports requiring a minimum version, e.g. you can do 4 | # find_package(Eigen3 3.1.2) 5 | # to require version 3.1.2 or newer of Eigen3. 6 | # 7 | # Once done this will define 8 | # 9 | # EIGEN3_FOUND - system has eigen lib with correct version 10 | # EIGEN3_INCLUDE_DIR - the eigen include directory 11 | # EIGEN3_VERSION - eigen version 12 | 13 | # Copyright (c) 2006, 2007 Montel Laurent, 14 | # Copyright (c) 2008, 2009 Gael Guennebaud, 15 | # Copyright (c) 2009 Benoit Jacob 16 | # Redistribution and use is allowed according to the terms of the 2-clause BSD license. 17 | 18 | if(NOT Eigen3_FIND_VERSION) 19 | if(NOT Eigen3_FIND_VERSION_MAJOR) 20 | set(Eigen3_FIND_VERSION_MAJOR 2) 21 | endif(NOT Eigen3_FIND_VERSION_MAJOR) 22 | if(NOT Eigen3_FIND_VERSION_MINOR) 23 | set(Eigen3_FIND_VERSION_MINOR 91) 24 | endif(NOT Eigen3_FIND_VERSION_MINOR) 25 | if(NOT Eigen3_FIND_VERSION_PATCH) 26 | set(Eigen3_FIND_VERSION_PATCH 0) 27 | endif(NOT Eigen3_FIND_VERSION_PATCH) 28 | 29 | set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}") 30 | endif(NOT Eigen3_FIND_VERSION) 31 | 32 | macro(_eigen3_check_version) 33 | file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header) 34 | 35 | string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}") 36 | set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}") 37 | string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}") 38 | set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}") 39 | string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}") 40 | set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}") 41 | 42 | set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION}) 43 | if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) 44 | set(EIGEN3_VERSION_OK FALSE) 45 | else(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) 46 | set(EIGEN3_VERSION_OK TRUE) 47 | endif(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) 48 | 49 | if(NOT EIGEN3_VERSION_OK) 50 | 51 | message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, " 52 | "but at least version ${Eigen3_FIND_VERSION} is required") 53 | endif(NOT EIGEN3_VERSION_OK) 54 | endmacro(_eigen3_check_version) 55 | 56 | if (EIGEN3_INCLUDE_DIR) 57 | 58 | # in cache already 59 | _eigen3_check_version() 60 | set(EIGEN3_FOUND ${EIGEN3_VERSION_OK}) 61 | 62 | else (EIGEN3_INCLUDE_DIR) 63 | 64 | find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library 65 | PATHS 66 | ${CMAKE_INSTALL_PREFIX}/include 67 | ${KDE4_INCLUDE_DIR} 68 | PATH_SUFFIXES eigen3 eigen 69 | ) 70 | 71 | if(EIGEN3_INCLUDE_DIR) 72 | _eigen3_check_version() 73 | endif(EIGEN3_INCLUDE_DIR) 74 | 75 | include(FindPackageHandleStandardArgs) 76 | find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK) 77 | 78 | mark_as_advanced(EIGEN3_INCLUDE_DIR) 79 | 80 | endif(EIGEN3_INCLUDE_DIR) 81 | get_filename_component(EIGEN_ABSOLUTE ${EIGEN3_INCLUDE_DIR} ABSOLUTE) 82 | if(${EIGEN_ABSOLUTE} STREQUAL /usr/include) 83 | set(EIGEN3_INCLUDE_DIR "") 84 | endif() 85 | -------------------------------------------------------------------------------- /CMake/FindHalf.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find Half 2 | # Once done, this will define 3 | # 4 | # HALF_FOUND - system has Half 5 | # HALF_INCLUDE_DIRS - the Half include directories 6 | # HALF_LIBRARIES - link these to use libHalf 7 | 8 | find_path(HALF_ROOT_DIR 9 | NAMES include/OpenEXR/half.h 10 | HINTS ENV HT 11 | ) 12 | 13 | find_library(HALF_LIBRARIES 14 | NAMES Half libHalf 15 | HINTS ${HALF_ROOT_DIR}/lib 16 | ENV HDSO 17 | ) 18 | 19 | find_path(HALF_INCLUDE_DIRS 20 | NAMES OpenEXR/half.h 21 | HINTS ${HALF_ROOT_DIR}/include 22 | ) 23 | 24 | include(FindPackageHandleStandardArgs) 25 | find_package_handle_standard_args(HALF DEFAULT_MSG 26 | HALF_LIBRARIES 27 | HALF_INCLUDE_DIRS 28 | ) 29 | 30 | mark_as_advanced( 31 | HALF_ROOT_DIR 32 | HALF_LIBRARIES 33 | HALF_INCLUDE_DIRS 34 | ) 35 | -------------------------------------------------------------------------------- /CMake/FindLIBIGL.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find the LIBIGL library 2 | # Once done this will define 3 | # 4 | # LIBIGL_FOUND - system has LIBIGL 5 | # LIBIGL_INCLUDE_DIR - **the** LIBIGL include directory 6 | if(LIBIGL_FOUND) 7 | return() 8 | endif() 9 | 10 | find_path(LIBIGL_INCLUDE_DIR igl/readOBJ.h 11 | HINTS 12 | ${LIBIGL_DIR} 13 | ENV LIBIGL_DIR 14 | PATHS 15 | ${CMAKE_SOURCE_DIR}/../.. 16 | ${CMAKE_SOURCE_DIR}/.. 17 | ${CMAKE_SOURCE_DIR} 18 | ${CMAKE_SOURCE_DIR}/libigl 19 | ${CMAKE_SOURCE_DIR}/../libigl 20 | ${CMAKE_SOURCE_DIR}/../../libigl 21 | /usr 22 | /usr/local 23 | /usr/local/igl/libigl 24 | PATH_SUFFIXES include 25 | ) 26 | 27 | include(FindPackageHandleStandardArgs) 28 | find_package_handle_standard_args(LIBIGL 29 | "\nlibigl not found --- You can download it using:\n\tgit clone https://github.com/libigl/libigl.git ${CMAKE_SOURCE_DIR}/../libigl" 30 | LIBIGL_INCLUDE_DIR) 31 | mark_as_advanced(LIBIGL_INCLUDE_DIR) 32 | 33 | list(APPEND CMAKE_MODULE_PATH "${LIBIGL_INCLUDE_DIR}/../cmake") 34 | include(libigl) 35 | -------------------------------------------------------------------------------- /CMake/FindMetis.cmake: -------------------------------------------------------------------------------- 1 | # Pastix requires METIS or METIS (partitioning and reordering tools) 2 | 3 | if (METIS_INCLUDE_DIRS AND METIS_LIBRARIES) 4 | set(METIS_FIND_QUIETLY TRUE) 5 | endif (METIS_INCLUDE_DIRS AND METIS_LIBRARIES) 6 | 7 | find_path(METIS_INCLUDE_DIRS 8 | NAMES 9 | metis.h 10 | PATHS 11 | $ENV{METISDIR} 12 | ${INCLUDE_INSTALL_DIR} 13 | metis 14 | include 15 | ) 16 | 17 | macro(_metis_check_version) 18 | file(READ "${METIS_INCLUDE_DIRS}/metis.h" _metis_version_header) 19 | 20 | string(REGEX MATCH "define[ \t]+METIS_VER_MAJOR[ \t]+([0-9]+)" _metis_major_version_match "${_metis_version_header}") 21 | set(METIS_MAJOR_VERSION "${CMAKE_MATCH_1}") 22 | string(REGEX MATCH "define[ \t]+METIS_VER_MINOR[ \t]+([0-9]+)" _metis_minor_version_match "${_metis_version_header}") 23 | set(METIS_MINOR_VERSION "${CMAKE_MATCH_1}") 24 | string(REGEX MATCH "define[ \t]+METIS_VER_SUBMINOR[ \t]+([0-9]+)" _metis_subminor_version_match "${_metis_version_header}") 25 | set(METIS_SUBMINOR_VERSION "${CMAKE_MATCH_1}") 26 | if(NOT METIS_MAJOR_VERSION) 27 | message(STATUS "Could not determine Metis version. Assuming version 4.0.0") 28 | set(METIS_VERSION 4.0.0) 29 | else() 30 | set(METIS_VERSION ${METIS_MAJOR_VERSION}.${METIS_MINOR_VERSION}.${METIS_SUBMINOR_VERSION}) 31 | endif() 32 | if(${METIS_VERSION} VERSION_LESS ${Metis_FIND_VERSION}) 33 | set(METIS_VERSION_OK FALSE) 34 | else() 35 | set(METIS_VERSION_OK TRUE) 36 | endif() 37 | 38 | if(NOT METIS_VERSION_OK) 39 | message(STATUS "Metis version ${METIS_VERSION} found in ${METIS_INCLUDE_DIRS}, " 40 | "but at least version ${Metis_FIND_VERSION} is required") 41 | endif(NOT METIS_VERSION_OK) 42 | endmacro(_metis_check_version) 43 | 44 | if(METIS_INCLUDE_DIRS AND Metis_FIND_VERSION) 45 | _metis_check_version() 46 | else() 47 | set(METIS_VERSION_OK TRUE) 48 | endif() 49 | 50 | 51 | find_library(METIS_LIBRARIES metis PATHS $ENV{METISDIR} ${LIB_INSTALL_DIR} PATH_SUFFIXES lib) 52 | 53 | include(FindPackageHandleStandardArgs) 54 | find_package_handle_standard_args(METIS DEFAULT_MSG 55 | METIS_INCLUDE_DIRS METIS_LIBRARIES METIS_VERSION_OK) 56 | 57 | mark_as_advanced(METIS_INCLUDE_DIRS METIS_LIBRARIES) 58 | list(REMOVE_ITEM METIS_INCLUDE_DIRS /usr/include) 59 | -------------------------------------------------------------------------------- /CMake/FindTBB.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find tbb 2 | # Once done, this will define 3 | # 4 | # TBB_FOUND - system has tbb 5 | # TBB_INCLUDE_DIRS - the tbb include directories 6 | # TBB_LIBRARIES - link these to use libtbb 7 | 8 | find_path(TBB_ROOT_DIR 9 | NAMES include/tbb/tbb.h 10 | HINTS 11 | ENV HT 12 | ) 13 | 14 | find_library(TBB_LIBRARIES 15 | NAMES tbb libtbb 16 | HINTS 17 | ${TBB_ROOT_DIR}/lib 18 | ENV HDSO 19 | ) 20 | 21 | find_library(TBB_MALLOC 22 | NAMES tbbmalloc libtbbmalloc 23 | HINTS 24 | ${TBB_ROOT_DIR}/lib 25 | ENV HDSO 26 | ) 27 | 28 | find_library(TBB_MALLOC_PROXY 29 | NAMES tbbmalloc_proxy libtbbmalloc_proxy 30 | HINTS 31 | ${TBB_ROOT_DIR}/lib 32 | ENV HDSO 33 | ) 34 | 35 | find_path(TBB_INCLUDE_DIRS 36 | NAMES tbb/tbb.h 37 | HINTS ${TBB_ROOT_DIR}/include 38 | ) 39 | 40 | include(FindPackageHandleStandardArgs) 41 | find_package_handle_standard_args(TBB DEFAULT_MSG 42 | TBB_LIBRARIES 43 | TBB_INCLUDE_DIRS 44 | ) 45 | 46 | mark_as_advanced( 47 | TBB_ROOT_DIR 48 | TBB_LIBRARIES 49 | TBB_MALLOC 50 | TBB_MALLOC_PROXY 51 | TBB_INCLUDE_DIRS 52 | ) 53 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | 3 | if (APPLE) 4 | message(STATUS "Switch to homebrew clang for openmp") 5 | set(CMAKE_C_COMPILER "/opt/homebrew/opt/llvm/bin/clang") 6 | set(CMAKE_CXX_COMPILER "/opt/homebrew/opt/llvm/bin/clang++") 7 | endif() 8 | 9 | if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) 10 | message( FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there. You may need to remove CMakeCache.txt." ) 11 | endif() 12 | 13 | project(ProgrammableDigitalWeaves CXX) 14 | 15 | message(STATUS "${CMAKE_BUILD_TYPE} Build") 16 | 17 | # set(CMAKE_BUILD_TYPE Release) 18 | 19 | set(VERSION_MAJOR 0) 20 | set(VERSION_MINOR 1) 21 | set(VERSION_PATCH 0) 22 | 23 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMake) 24 | 25 | set(CMAKE_EXPORT_COMPILE_COMMANDS "ON") 26 | set(CMAKE_CXX_STANDARD 17) 27 | 28 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC -O0 -fopenmp") 29 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -O0 -fopenmp -Wno-unused-variable -Wno-unused-but-set-variable -Wsign-compare -Wreorder") 30 | # set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wconversion -Wshadow -Wpedantic -fsanitize=undefined,address") 31 | 32 | # libigl 33 | option(LIBIGL_WITH_OPENGL "Use OpenGL" ON) 34 | option(LIBIGL_WITH_OPENGL_GLFW "Use GLFW" ON) 35 | option(LIBIGL_WITH_OPENGL_GLFW_IMGUI "Use ImGui" ON) 36 | option(LIBIGL_WITH_PNG "Use PNG" ON) 37 | option(LIBIGL_WITH_XML "Use XML" ON) 38 | 39 | 40 | find_package (Eigen3 3.3 REQUIRED NO_MODULE) 41 | find_package (SuiteSparse REQUIRED) 42 | find_package (OpenMP REQUIRED) 43 | if (OPENMP_FOUND) 44 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") 45 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") 46 | set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") 47 | endif() 48 | 49 | find_package (LIBIGL REQUIRED) 50 | 51 | # include(FetchContent) 52 | # FetchContent_Declare( 53 | # ipc_toolkit 54 | # GIT_REPOSITORY https://github.com/ipc-sim/ipc-toolkit.git 55 | # GIT_TAG "main" 56 | # ) 57 | # FetchContent_MakeAvailable(ipc_toolkit) 58 | 59 | 60 | # include(libigl) 61 | # igl_include(glfw) 62 | # igl_include(opengl) 63 | # igl_include(imgui) 64 | # igl_include(copyleft tetgen) 65 | # igl_include(restricted triangle) 66 | # igl_include(stb) 67 | 68 | 69 | find_package(CGAL REQUIRED COMPONENTS Core) 70 | 71 | 72 | add_definitions(-DEIGEN_USE_MKL_ALL) 73 | add_definitions(-DUSE_CHOLMOD) 74 | add_definitions(-DMKL_LP64) 75 | 76 | include_directories(${CMAKE_SOURCE_DIR}) 77 | # include_directories(/opt/intel/oneapi/mkl/2022.1.0/include) 78 | set(SuiteSparse_ROOT ${CMAKE_SOURCE_DIR}/../SuiteSparse-5.12.0) 79 | include_directories(${SuiteSparse_ROOT}/include) 80 | 81 | add_subdirectory(Libs) 82 | add_subdirectory(Projects) 83 | 84 | 85 | 86 | message("**************************************************") 87 | message("C++ Cmake Flags: ${CMAKE_CXX_FLAGS}") 88 | message("**************************************************") 89 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Yue Li 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Libs/CMakeLists.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liyuesolo/ProgrammableDigitalWeaves/20d9431e67f9b9262adc478c7d99833e01eafea7/Libs/CMakeLists.txt -------------------------------------------------------------------------------- /Libs/spectra/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.out 3 | include/Eigen/* 4 | -------------------------------------------------------------------------------- /Libs/spectra/.travis.yml: -------------------------------------------------------------------------------- 1 | branches: 2 | only: 3 | - master 4 | - develop 5 | 6 | language: cpp 7 | 8 | compiler: 9 | - clang 10 | - gcc 11 | 12 | dist: xenial 13 | 14 | addons: 15 | apt: 16 | sources: 17 | - ubuntu-toolchain-r-test 18 | - llvm-toolchain-xenial-7 19 | packages: 20 | - g++-8 21 | - clang-7 22 | 23 | before_install: 24 | - if [ "$CXX" = "g++" ]; then export CXX="g++-8"; fi 25 | - if [ "$CXX" = "clang++" ]; then export CXX="clang++-7"; fi 26 | - $CXX --version 27 | 28 | install: 29 | - wget https://bitbucket.org/eigen/eigen/get/3.3.7.tar.gz 30 | - tar xzf 3.3.7.tar.gz 31 | - mv eigen-eigen-323c052e1731/Eigen include/ 32 | 33 | script: 34 | - cd test 35 | - make -j 2 all 36 | - make test 37 | -------------------------------------------------------------------------------- /Libs/spectra/AUTHORS.md: -------------------------------------------------------------------------------- 1 | The files 2 | 3 | - `include/Spectra/LinAlg/TridiagEigen.h` 4 | - `include/Spectra/LinAlg/UpperHessenbergEigen.h` 5 | 6 | were adapted from 7 | 8 | - `Eigen/src/Eigenvaleus/SelfAdjointEigenSolver.h` 9 | - `Eigen/src/Eigenvaleus/EigenSolver.h` 10 | 11 | in the [Eigen](http://eigen.tuxfamily.org/) library. 12 | 13 | The authors for these two files were Gael Guennebaud 14 | and Jitse Niesen . 15 | 16 | The file `include/contrib/LOBPCGSolver.h` was originally contributed by Anna Araslanova. 17 | 18 | The [Catch](https://github.com/philsquared/Catch) library included for unit testing 19 | was written by Phil Nash . 20 | 21 | Other part of Spectra was written by Yixuan Qiu . 22 | -------------------------------------------------------------------------------- /Libs/spectra/benchmark/Cpp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "timer.h" 4 | 5 | #include 6 | #include 7 | 8 | using namespace Spectra; 9 | 10 | using Eigen::MatrixXd; 11 | using Eigen::VectorXd; 12 | using Eigen::MatrixXcd; 13 | using Eigen::VectorXcd; 14 | 15 | void eigs_sym_Cpp(MatrixXd &M, VectorXd &init_resid, int k, int m, 16 | double &time_used, double &prec_err, int &nops) 17 | { 18 | double start, end; 19 | start = get_wall_time(); 20 | 21 | DenseSymMatProd op(M); 22 | SymEigsSolver< double, LARGEST_MAGN, DenseSymMatProd > eigs(&op, k, m); 23 | eigs.init(init_resid.data()); 24 | 25 | int nconv = eigs.compute(); 26 | int niter = eigs.num_iterations(); 27 | nops = eigs.num_operations(); 28 | 29 | VectorXd evals = eigs.eigenvalues(); 30 | MatrixXd evecs = eigs.eigenvectors(); 31 | 32 | /* 33 | std::cout << "computed eigenvalues D = \n" << evals.transpose() << std::endl; 34 | std::cout << "first 5 rows of computed eigenvectors U = \n" << evecs.topRows<5>() << std::endl; 35 | std::cout << "nconv = " << nconv << std::endl; 36 | std::cout << "niter = " << niter << std::endl; 37 | std::cout << "nops = " << nops << std::endl; 38 | */ 39 | 40 | end = get_wall_time(); 41 | time_used = (end - start) * 1000; 42 | 43 | MatrixXd err = M * evecs - evecs * evals.asDiagonal(); 44 | prec_err = err.cwiseAbs().maxCoeff(); 45 | } 46 | 47 | 48 | 49 | void eigs_gen_Cpp(MatrixXd &M, VectorXd &init_resid, int k, int m, 50 | double &time_used, double &prec_err, int &nops) 51 | { 52 | double start, end; 53 | start = get_wall_time(); 54 | 55 | DenseGenMatProd op(M); 56 | GenEigsSolver< double, LARGEST_MAGN, DenseGenMatProd > eigs(&op, k, m); 57 | eigs.init(init_resid.data()); 58 | 59 | int nconv = eigs.compute(); 60 | int niter = eigs.num_iterations(); 61 | nops = eigs.num_operations(); 62 | 63 | VectorXcd evals = eigs.eigenvalues(); 64 | MatrixXcd evecs = eigs.eigenvectors(); 65 | 66 | /* 67 | std::cout << "computed eigenvalues D = \n" << evals.transpose() << std::endl; 68 | std::cout << "first 5 rows of computed eigenvectors U = \n" << evecs.topRows<5>() << std::endl; 69 | std::cout << "nconv = " << nconv << std::endl; 70 | std::cout << "niter = " << niter << std::endl; 71 | std::cout << "nops = " << nops << std::endl; 72 | 73 | MatrixXcd err = M * evecs - evecs * evals.asDiagonal(); 74 | std::cout << "||AU - UD||_inf = " << err.array().abs().maxCoeff() << std::endl; 75 | */ 76 | 77 | end = get_wall_time(); 78 | time_used = (end - start) * 1000; 79 | 80 | MatrixXcd err = M * evecs - evecs * evals.asDiagonal(); 81 | prec_err = err.cwiseAbs().maxCoeff(); 82 | } 83 | -------------------------------------------------------------------------------- /Libs/spectra/benchmark/Makefile: -------------------------------------------------------------------------------- 1 | CXX = g++ 2 | CXXFLAGS = -Wall -O2 3 | CPPFLAGS = -I../include -I. 4 | FC = gfortran 5 | FFLAGS = -O3 6 | LDFLAGS = 7 | LIBS = -larpack -llapack -lblas -lgfortran 8 | 9 | HEADERS = $(wildcard ../include/Spectra/MatOp/*.h) \ 10 | $(wildcard ../include/Spectra/LinAlg/*.h) \ 11 | $(wildcard ../include/Spectra/Util/*.h) \ 12 | $(wildcard ../include/Spectra/*.h) 13 | OBJS = F77.o Cpp.o wrapper.o 14 | 15 | .PHONY: all clean 16 | 17 | all: benchmark.out 18 | 19 | benchmark.out: $(OBJS) main.cpp 20 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) main.cpp $(OBJS) -o benchmark.out $(LDFLAGS) $(LIBS) 21 | 22 | %.o: %.cpp $(HEADERS) 23 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@ 24 | 25 | %.o: %.f 26 | $(FC) $(FFLAGS) -c $< -o $@ 27 | 28 | clean: 29 | -rm *.out *.o 30 | -------------------------------------------------------------------------------- /Libs/spectra/benchmark/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using Eigen::MatrixXd; 7 | using Eigen::VectorXd; 8 | 9 | void eigs_sym_F77(MatrixXd &M, VectorXd &init_resid, int k, int m, double &time_used, double &prec_err, int &nops); 10 | void eigs_gen_F77(MatrixXd &M, VectorXd &init_resid, int k, int m, double &time_used, double &prec_err, int &nops); 11 | void eigs_sym_Cpp(MatrixXd &M, VectorXd &init_resid, int k, int m, double &time_used, double &prec_err, int &nops); 12 | void eigs_gen_Cpp(MatrixXd &M, VectorXd &init_resid, int k, int m, double &time_used, double &prec_err, int &nops); 13 | 14 | 15 | void print_header(std::string title) 16 | { 17 | const int width = 80; 18 | const char sep = ' '; 19 | 20 | std::cout << std::endl << std::string(width, '=') << std::endl; 21 | std::cout << std::string((width - title.length()) / 2, ' ') << title << std::endl; 22 | std::cout << std::string(width, '-') << std::endl; 23 | 24 | std::cout << std::left << std::setw(7) << std::setfill(sep) << "size"; 25 | std::cout << std::left << std::setw(10) << std::setfill(sep) << "dataset"; 26 | std::cout << std::left << std::setw(11) << std::setfill(sep) << "F77/time"; 27 | std::cout << std::left << std::setw(13) << std::setfill(sep) << "error"; 28 | std::cout << std::left << std::setw(7) << std::setfill(sep) << "nops"; 29 | std::cout << std::left << std::setw(11) << std::setfill(sep) << "C++/time"; 30 | std::cout << std::left << std::setw(13) << std::setfill(sep) << "error"; 31 | std::cout << std::left << std::setw(7) << std::setfill(sep) << "nops"; 32 | std::cout << std::endl; 33 | 34 | std::cout << std::string(width, '-') << std::endl; 35 | } 36 | 37 | void print_row(int n, int dataset, 38 | double time_f77, double err_f77, int nops_f77, 39 | double time_cpp, double err_cpp, int nops_cpp) 40 | { 41 | const char sep = ' '; 42 | 43 | std::cout.precision(5); 44 | 45 | std::cout << std::left << std::setw(7) << std::setfill(sep) << n; 46 | std::cout << std::left << std::setw(10) << std::setfill(sep) << dataset; 47 | std::cout << std::left << std::setw(11) << std::setfill(sep) << time_f77; 48 | std::cout << std::left << std::setw(13) << std::setfill(sep) << err_f77; 49 | std::cout << std::left << std::setw(7) << std::setfill(sep) << nops_f77; 50 | std::cout << std::left << std::setw(11) << std::setfill(sep) << time_cpp; 51 | std::cout << std::left << std::setw(13) << std::setfill(sep) << err_cpp; 52 | std::cout << std::left << std::setw(7) << std::setfill(sep) << nops_cpp; 53 | std::cout << std::endl; 54 | } 55 | 56 | void print_footer() 57 | { 58 | const int width = 80; 59 | std::cout << std::string(width, '=') << std::endl << std::endl; 60 | } 61 | 62 | void run_eigs_sym(int n_experiment, int n_replicate, int n, int k, int m) 63 | { 64 | double time_f77, time_cpp; 65 | double err_f77, err_cpp; 66 | int nops_f77, nops_cpp; 67 | 68 | for(int i = 0; i < n_experiment; i++) 69 | { 70 | MatrixXd A = MatrixXd::Random(n, n); 71 | MatrixXd M = A.transpose() + A; 72 | 73 | VectorXd init_resid = VectorXd::Random(M.cols()); 74 | init_resid.array() -= 0.5; 75 | init_resid = M * init_resid; 76 | 77 | for(int j = 0; j < n_replicate; j++) 78 | { 79 | eigs_sym_F77(M, init_resid, k, m, time_f77, err_f77, nops_f77); 80 | eigs_sym_Cpp(M, init_resid, k, m, time_cpp, err_cpp, nops_cpp); 81 | print_row(n, i + 1, time_f77, err_f77, nops_f77, time_cpp, err_cpp, nops_cpp); 82 | } 83 | } 84 | } 85 | 86 | void run_eigs_gen(int n_experiment, int n_replicate, int n, int k, int m) 87 | { 88 | double time_f77, time_cpp; 89 | double err_f77, err_cpp; 90 | int nops_f77, nops_cpp; 91 | 92 | for(int i = 0; i < n_experiment; i++) 93 | { 94 | MatrixXd A = MatrixXd::Random(n, n); 95 | 96 | VectorXd init_resid = VectorXd::Random(A.cols()); 97 | init_resid.array() -= 0.5; 98 | init_resid = A * init_resid; 99 | 100 | for(int j = 0; j < n_replicate; j++) 101 | { 102 | eigs_gen_F77(A, init_resid, k, m, time_f77, err_f77, nops_f77); 103 | eigs_gen_Cpp(A, init_resid, k, m, time_cpp, err_cpp, nops_cpp); 104 | print_row(n, i + 1, time_f77, err_f77, nops_f77, time_cpp, err_cpp, nops_cpp); 105 | } 106 | } 107 | } 108 | 109 | int main() 110 | { 111 | std::srand(123); 112 | int n_experiment = 5; 113 | int n_replicate = 10; 114 | 115 | print_header("eigs_sym"); 116 | run_eigs_sym(n_experiment, n_replicate, 100, 10, 20); 117 | run_eigs_sym(n_experiment, n_replicate, 1000, 10, 30); 118 | print_footer(); 119 | 120 | print_header("eigs_gen"); 121 | run_eigs_gen(n_experiment, n_replicate, 100, 10, 20); 122 | run_eigs_gen(n_experiment, n_replicate, 1000, 10, 30); 123 | print_footer(); 124 | 125 | return 0; 126 | } 127 | -------------------------------------------------------------------------------- /Libs/spectra/benchmark/result_analyzer.R: -------------------------------------------------------------------------------- 1 | library(dplyr) 2 | library(reshape2) 3 | library(ggplot2) 4 | 5 | res = readLines("benchmark_res.txt") 6 | header = c("Size", "Dataset", "F77time", "F77err", "F77nops", "Cpptime", "Cpperr", "Cppnops") 7 | 8 | ## Benchmark result for symmetric solver 9 | sym_start = grep("eigs_sym", res) + 4 10 | sym_end = grep("========", res)[2] - 1 11 | sym_dat = strsplit(res[sym_start:sym_end], " ") 12 | sym_dat = lapply(sym_dat, function(x) as.numeric(x[x != ""])) 13 | sym_dat = as.data.frame(do.call(rbind, sym_dat)) 14 | colnames(sym_dat) = header 15 | 16 | sym_medtime = sym_dat %>% 17 | select(Size, Dataset, F77time, Cpptime) %>% 18 | melt(id.vars = c("Size", "Dataset"), 19 | variable.name = "Package", value.name = "Time") %>% 20 | group_by(Size, Dataset, Package) %>% 21 | summarize(Medtime = median(Time)) %>% 22 | mutate(Package = ifelse(grepl("F77", Package), "ARPACK", "Spectra")) 23 | 24 | sym_medtime$Size = paste("Matrix size:", sym_medtime$Size) 25 | 26 | ggplot(sym_medtime, aes(x = factor(Dataset), y = Medtime)) + 27 | geom_bar(aes(fill = Package), position = "dodge", stat = "identity") + 28 | facet_wrap(~ Size, scales = "free", ncol = 2) + 29 | xlab("Matrix ID") + ylab("Median Elapsed Time (ms)") + 30 | ggtitle("Symmetric Eigen Solver") + 31 | theme_bw(base_size = 20) + 32 | theme(plot.title = element_text(hjust = 0.5)) 33 | 34 | ## Benchmark result for general solver 35 | gen_start = grep("eigs_gen", res) + 4 36 | gen_end = grep("========", res)[4] - 1 37 | gen_dat = strsplit(res[gen_start:gen_end], " ") 38 | gen_dat = lapply(gen_dat, function(x) as.numeric(x[x != ""])) 39 | gen_dat = as.data.frame(do.call(rbind, gen_dat)) 40 | colnames(gen_dat) = header 41 | 42 | gen_medtime = gen_dat %>% 43 | select(Size, Dataset, F77time, Cpptime) %>% 44 | melt(id.vars = c("Size", "Dataset"), 45 | variable.name = "Package", value.name = "Time") %>% 46 | group_by(Size, Dataset, Package) %>% 47 | summarize(Medtime = median(Time)) %>% 48 | mutate(Package = ifelse(grepl("F77", Package), "ARPACK", "Spectra")) 49 | 50 | gen_medtime$Size = paste("Matrix size:", gen_medtime$Size) 51 | 52 | ggplot(gen_medtime, aes(x = factor(Dataset), y = Medtime)) + 53 | geom_bar(aes(fill = Package), position = "dodge", stat = "identity") + 54 | facet_wrap(~ Size, scales = "free", ncol = 2) + 55 | xlab("Matrix ID") + ylab("Median Elapsed Time (ms)") + 56 | ggtitle("General Eigen Solver") + 57 | theme_bw(base_size = 20) + 58 | theme(plot.title = element_text(hjust = 0.5)) 59 | -------------------------------------------------------------------------------- /Libs/spectra/benchmark/timer.h: -------------------------------------------------------------------------------- 1 | // http://stackoverflow.com/questions/17432502/how-can-i-measure-cpu-time-and-wall-clock-time-on-both-linux-windows 2 | 3 | // Windows 4 | #ifdef _WIN32 5 | #include 6 | 7 | inline double get_wall_time(){ 8 | LARGE_INTEGER time,freq; 9 | if (!QueryPerformanceFrequency(&freq)){ 10 | // Handle error 11 | return 0; 12 | } 13 | if (!QueryPerformanceCounter(&time)){ 14 | // Handle error 15 | return 0; 16 | } 17 | return (double)time.QuadPart / freq.QuadPart; 18 | } 19 | 20 | inline double get_cpu_time(){ 21 | FILETIME a,b,c,d; 22 | if (GetProcessTimes(GetCurrentProcess(),&a,&b,&c,&d) != 0){ 23 | // Returns total user time. 24 | // Can be tweaked to include kernel times as well. 25 | return 26 | (double)(d.dwLowDateTime | 27 | ((unsigned long long)d.dwHighDateTime << 32)) * 0.0000001; 28 | }else{ 29 | // Handle error 30 | return 0; 31 | } 32 | } 33 | 34 | // Posix/Linux 35 | #else 36 | #include 37 | #include 38 | 39 | inline double get_wall_time(){ 40 | struct timeval time; 41 | if (gettimeofday(&time,NULL)){ 42 | // Handle error 43 | return 0; 44 | } 45 | return (double)time.tv_sec + (double)time.tv_usec * .000001; 46 | } 47 | 48 | inline double get_cpu_time(){ 49 | return (double)clock() / CLOCKS_PER_SEC; 50 | } 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /Libs/spectra/benchmark/wrapper.f: -------------------------------------------------------------------------------- 1 | subroutine getbmat(bmati, bmat) 2 | 3 | integer bmati 4 | character bmat 5 | 6 | select case (bmati) 7 | case (0) 8 | bmat = 'I' 9 | case (1) 10 | bmat = 'G' 11 | case default 12 | bmat = 'I' 13 | end select 14 | 15 | end 16 | 17 | 18 | 19 | subroutine getwhich(whichi, which) 20 | 21 | integer whichi 22 | character which*2 23 | 24 | select case (whichi) 25 | case (0) 26 | which = 'LM' 27 | case (1) 28 | which = 'SM' 29 | case (2) 30 | which = 'LR' 31 | case (3) 32 | which = 'SR' 33 | case (4) 34 | which = 'LI' 35 | case (5) 36 | which = 'SI' 37 | case (6) 38 | which = 'LA' 39 | case (7) 40 | which = 'SA' 41 | case (8) 42 | which = 'BE' 43 | case default 44 | which = 'LM' 45 | end select 46 | 47 | end 48 | 49 | 50 | 51 | subroutine gethowmny(howmnyi, howmny) 52 | 53 | integer howmnyi 54 | character howmny 55 | 56 | select case (howmnyi) 57 | case (0) 58 | howmny = 'A' 59 | case (1) 60 | howmny = 'P' 61 | case (2) 62 | howmny = 'S' 63 | case default 64 | howmny = 'A' 65 | end select 66 | 67 | end 68 | 69 | 70 | 71 | subroutine dsaupdwr 72 | & ( ido, bmati, n, whichi, nev, tol, resid, ncv, v, ldv, iparam, 73 | & ipntr, workd, workl, lworkl, info ) 74 | 75 | implicit none 76 | 77 | integer ido, n, nev, ncv, ldv, iparam(11), ipntr(11), lworkl, info 78 | integer bmati, whichi 79 | character bmat, which*2 80 | double precision tol, resid(n), v(ldv, ncv), workd(3*n), 81 | & workl(lworkl) 82 | 83 | external dsaupd, getbmat, getwhich 84 | 85 | call getbmat(bmati, bmat) 86 | call getwhich(whichi, which) 87 | 88 | call dsaupd 89 | & ( ido, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, 90 | & ipntr, workd, workl, lworkl, info ) 91 | 92 | end 93 | 94 | 95 | 96 | subroutine dseupdwr(rvec , howmnyi, select, d , 97 | & z , ldz , sigma , bmati, 98 | & n , whichi , nev , tol , 99 | & resid , ncv , v , ldv , 100 | & iparam, ipntr , workd , workl, 101 | & lworkl, info ) 102 | 103 | implicit none 104 | 105 | integer ldz, n, nev, ncv, ldv, iparam(7), ipntr(11), lworkl, info 106 | double precision d(nev), z(ldz, nev), sigma, tol, resid(n) 107 | double precision v(ldv, ncv), workd(2*n), workl(lworkl) 108 | integer howmnyi, bmati, whichi 109 | character howmny, bmat, which*2 110 | logical rvec, select(ncv) 111 | 112 | external dseupd, gethowmny, getbmat, getwhich 113 | 114 | call gethowmny(howmnyi, howmny) 115 | call getbmat(bmati, bmat) 116 | call getwhich(whichi, which) 117 | 118 | call dseupd(rvec , howmny, select, d , 119 | & z , ldz , sigma , bmat , 120 | & n , which , nev , tol , 121 | & resid , ncv , v , ldv , 122 | & iparam, ipntr , workd , workl, 123 | & lworkl, info ) 124 | 125 | end 126 | 127 | 128 | 129 | subroutine dnaupdwr 130 | & ( ido, bmati, n, whichi, nev, tol, resid, ncv, v, ldv, iparam, 131 | & ipntr, workd, workl, lworkl, info ) 132 | 133 | implicit none 134 | 135 | integer ido, n, nev, ncv, ldv, iparam(11), ipntr(14), lworkl, info 136 | integer bmati, whichi 137 | character bmat, which*2 138 | double precision tol, resid(n), v(ldv, ncv), workd(3*n), 139 | & workl(lworkl) 140 | 141 | external dnaupd, getbmat, getwhich 142 | 143 | call getbmat(bmati, bmat) 144 | call getwhich(whichi, which) 145 | 146 | call dnaupd 147 | & ( ido, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, 148 | & ipntr, workd, workl, lworkl, info ) 149 | 150 | end 151 | 152 | 153 | 154 | subroutine dneupdwr(rvec , howmnyi, select, dr , di, 155 | & z , ldz , sigmar, sigmai, workev, 156 | & bmati, n , whichi, nev , tol, 157 | & resid, ncv , v , ldv , iparam, 158 | & ipntr, workd , workl , lworkl, info) 159 | 160 | implicit none 161 | 162 | integer ldz, n, nev, ncv, ldv, iparam(11), ipntr(14), lworkl, info 163 | double precision dr(nev+1), di(nev+1), z(ldz,*), sigmar, sigmai 164 | double precision workev(3*ncv), tol, resid(n), v(ldv, ncv) 165 | double precision workd(3*n), workl(lworkl) 166 | integer howmnyi, bmati, whichi 167 | character howmny, bmat, which*2 168 | logical rvec, select(ncv) 169 | 170 | external dneupd, gethowmny, getbmat, getwhich 171 | 172 | call gethowmny(howmnyi, howmny) 173 | call getbmat(bmati, bmat) 174 | call getwhich(whichi, which) 175 | 176 | call dneupd (rvec , howmny, select, dr , di, 177 | & z , ldz , sigmar, sigmai, workev, 178 | & bmat , n , which , nev , tol, 179 | & resid, ncv , v , ldv , iparam, 180 | & ipntr, workd , workl , lworkl, info) 181 | 182 | end 183 | -------------------------------------------------------------------------------- /Libs/spectra/include/Spectra/GenEigsRealShiftSolver.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Yixuan Qiu 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla 4 | // Public License v. 2.0. If a copy of the MPL was not distributed 5 | // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #ifndef GEN_EIGS_REAL_SHIFT_SOLVER_H 8 | #define GEN_EIGS_REAL_SHIFT_SOLVER_H 9 | 10 | #include 11 | 12 | #include "GenEigsBase.h" 13 | #include "Util/SelectionRule.h" 14 | #include "MatOp/DenseGenRealShiftSolve.h" 15 | 16 | namespace Spectra { 17 | 18 | 19 | /// 20 | /// \ingroup EigenSolver 21 | /// 22 | /// This class implements the eigen solver for general real matrices with 23 | /// a real shift value in the **shift-and-invert mode**. The background 24 | /// knowledge of the shift-and-invert mode can be found in the documentation 25 | /// of the SymEigsShiftSolver class. 26 | /// 27 | /// \tparam Scalar The element type of the matrix. 28 | /// Currently supported types are `float`, `double` and `long double`. 29 | /// \tparam SelectionRule An enumeration value indicating the selection rule of 30 | /// the shifted-and-inverted eigenvalues. 31 | /// The full list of enumeration values can be found in 32 | /// \ref Enumerations. 33 | /// \tparam OpType The name of the matrix operation class. Users could either 34 | /// use the wrapper classes such as DenseGenRealShiftSolve and 35 | /// SparseGenRealShiftSolve, or define their 36 | /// own that implements all the public member functions as in 37 | /// DenseGenRealShiftSolve. 38 | /// 39 | template > 42 | class GenEigsRealShiftSolver: public GenEigsBase 43 | { 44 | private: 45 | typedef Eigen::Index Index; 46 | typedef std::complex Complex; 47 | typedef Eigen::Array ComplexArray; 48 | 49 | const Scalar m_sigma; 50 | 51 | // First transform back the Ritz values, and then sort 52 | void sort_ritzpair(int sort_rule) 53 | { 54 | // The eigenvalues we get from the iteration is nu = 1 / (lambda - sigma) 55 | // So the eigenvalues of the original problem is lambda = 1 / nu + sigma 56 | ComplexArray ritz_val_org = Scalar(1.0) / this->m_ritz_val.head(this->m_nev).array() + m_sigma; 57 | this->m_ritz_val.head(this->m_nev) = ritz_val_org; 58 | GenEigsBase::sort_ritzpair(sort_rule); 59 | } 60 | public: 61 | /// 62 | /// Constructor to create a eigen solver object using the shift-and-invert mode. 63 | /// 64 | /// \param op Pointer to the matrix operation object. This class should implement 65 | /// the shift-solve operation of \f$A\f$: calculating 66 | /// \f$(A-\sigma I)^{-1}v\f$ for any vector \f$v\f$. Users could either 67 | /// create the object from the wrapper class such as DenseGenRealShiftSolve, or 68 | /// define their own that implements all the public member functions 69 | /// as in DenseGenRealShiftSolve. 70 | /// \param nev Number of eigenvalues requested. This should satisfy \f$1\le nev \le n-2\f$, 71 | /// where \f$n\f$ is the size of matrix. 72 | /// \param ncv Parameter that controls the convergence speed of the algorithm. 73 | /// Typically a larger `ncv` means faster convergence, but it may 74 | /// also result in greater memory use and more matrix operations 75 | /// in each iteration. This parameter must satisfy \f$nev+2 \le ncv \le n\f$, 76 | /// and is advised to take \f$ncv \ge 2\cdot nev + 1\f$. 77 | /// \param sigma The real-valued shift. 78 | /// 79 | GenEigsRealShiftSolver(OpType* op, Index nev, Index ncv, Scalar sigma) : 80 | GenEigsBase(op, NULL, nev, ncv), 81 | m_sigma(sigma) 82 | { 83 | this->m_op->set_shift(m_sigma); 84 | } 85 | }; 86 | 87 | 88 | } // namespace Spectra 89 | 90 | #endif // GEN_EIGS_REAL_SHIFT_SOLVER_H 91 | -------------------------------------------------------------------------------- /Libs/spectra/include/Spectra/GenEigsSolver.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Yixuan Qiu 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla 4 | // Public License v. 2.0. If a copy of the MPL was not distributed 5 | // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #ifndef GEN_EIGS_SOLVER_H 8 | #define GEN_EIGS_SOLVER_H 9 | 10 | #include 11 | 12 | #include "GenEigsBase.h" 13 | #include "Util/SelectionRule.h" 14 | #include "MatOp/DenseGenMatProd.h" 15 | 16 | namespace Spectra { 17 | 18 | 19 | /// 20 | /// \ingroup EigenSolver 21 | /// 22 | /// This class implements the eigen solver for general real matrices, i.e., 23 | /// to solve \f$Ax=\lambda x\f$ for a possibly non-symmetric \f$A\f$ matrix. 24 | /// 25 | /// Most of the background information documented in the SymEigsSolver class 26 | /// also applies to the GenEigsSolver class here, except that the eigenvalues 27 | /// and eigenvectors of a general matrix can now be complex-valued. 28 | /// 29 | /// \tparam Scalar The element type of the matrix. 30 | /// Currently supported types are `float`, `double` and `long double`. 31 | /// \tparam SelectionRule An enumeration value indicating the selection rule of 32 | /// the requested eigenvalues, for example `LARGEST_MAGN` 33 | /// to retrieve eigenvalues with the largest magnitude. 34 | /// The full list of enumeration values can be found in 35 | /// \ref Enumerations. 36 | /// \tparam OpType The name of the matrix operation class. Users could either 37 | /// use the wrapper classes such as DenseGenMatProd and 38 | /// SparseGenMatProd, or define their 39 | /// own that implements all the public member functions as in 40 | /// DenseGenMatProd. 41 | /// 42 | /// An example that illustrates the usage of GenEigsSolver is give below: 43 | /// 44 | /// \code{.cpp} 45 | /// #include 46 | /// #include 47 | /// // is implicitly included 48 | /// #include 49 | /// 50 | /// using namespace Spectra; 51 | /// 52 | /// int main() 53 | /// { 54 | /// // We are going to calculate the eigenvalues of M 55 | /// Eigen::MatrixXd M = Eigen::MatrixXd::Random(10, 10); 56 | /// 57 | /// // Construct matrix operation object using the wrapper class 58 | /// DenseGenMatProd op(M); 59 | /// 60 | /// // Construct eigen solver object, requesting the largest 61 | /// // (in magnitude, or norm) three eigenvalues 62 | /// GenEigsSolver< double, LARGEST_MAGN, DenseGenMatProd > eigs(&op, 3, 6); 63 | /// 64 | /// // Initialize and compute 65 | /// eigs.init(); 66 | /// int nconv = eigs.compute(); 67 | /// 68 | /// // Retrieve results 69 | /// Eigen::VectorXcd evalues; 70 | /// if(eigs.info() == SUCCESSFUL) 71 | /// evalues = eigs.eigenvalues(); 72 | /// 73 | /// std::cout << "Eigenvalues found:\n" << evalues << std::endl; 74 | /// 75 | /// return 0; 76 | /// } 77 | /// \endcode 78 | /// 79 | /// And also an example for sparse matrices: 80 | /// 81 | /// \code{.cpp} 82 | /// #include 83 | /// #include 84 | /// #include 85 | /// #include 86 | /// #include 87 | /// 88 | /// using namespace Spectra; 89 | /// 90 | /// int main() 91 | /// { 92 | /// // A band matrix with 1 on the main diagonal, 2 on the below-main subdiagonal, 93 | /// // and 3 on the above-main subdiagonal 94 | /// const int n = 10; 95 | /// Eigen::SparseMatrix M(n, n); 96 | /// M.reserve(Eigen::VectorXi::Constant(n, 3)); 97 | /// for(int i = 0; i < n; i++) 98 | /// { 99 | /// M.insert(i, i) = 1.0; 100 | /// if(i > 0) 101 | /// M.insert(i - 1, i) = 3.0; 102 | /// if(i < n - 1) 103 | /// M.insert(i + 1, i) = 2.0; 104 | /// } 105 | /// 106 | /// // Construct matrix operation object using the wrapper class SparseGenMatProd 107 | /// SparseGenMatProd op(M); 108 | /// 109 | /// // Construct eigen solver object, requesting the largest three eigenvalues 110 | /// GenEigsSolver< double, LARGEST_MAGN, SparseGenMatProd > eigs(&op, 3, 6); 111 | /// 112 | /// // Initialize and compute 113 | /// eigs.init(); 114 | /// int nconv = eigs.compute(); 115 | /// 116 | /// // Retrieve results 117 | /// Eigen::VectorXcd evalues; 118 | /// if(eigs.info() == SUCCESSFUL) 119 | /// evalues = eigs.eigenvalues(); 120 | /// 121 | /// std::cout << "Eigenvalues found:\n" << evalues << std::endl; 122 | /// 123 | /// return 0; 124 | /// } 125 | /// \endcode 126 | template < typename Scalar = double, 127 | int SelectionRule = LARGEST_MAGN, 128 | typename OpType = DenseGenMatProd > 129 | class GenEigsSolver: public GenEigsBase 130 | { 131 | private: 132 | typedef Eigen::Index Index; 133 | 134 | public: 135 | /// 136 | /// Constructor to create a solver object. 137 | /// 138 | /// \param op Pointer to the matrix operation object, which should implement 139 | /// the matrix-vector multiplication operation of \f$A\f$: 140 | /// calculating \f$Av\f$ for any vector \f$v\f$. Users could either 141 | /// create the object from the wrapper class such as DenseGenMatProd, or 142 | /// define their own that implements all the public member functions 143 | /// as in DenseGenMatProd. 144 | /// \param nev Number of eigenvalues requested. This should satisfy \f$1\le nev \le n-2\f$, 145 | /// where \f$n\f$ is the size of matrix. 146 | /// \param ncv Parameter that controls the convergence speed of the algorithm. 147 | /// Typically a larger `ncv` means faster convergence, but it may 148 | /// also result in greater memory use and more matrix operations 149 | /// in each iteration. This parameter must satisfy \f$nev+2 \le ncv \le n\f$, 150 | /// and is advised to take \f$ncv \ge 2\cdot nev + 1\f$. 151 | /// 152 | GenEigsSolver(OpType* op, Index nev, Index ncv) : 153 | GenEigsBase(op, NULL, nev, ncv) 154 | {} 155 | }; 156 | 157 | 158 | } // namespace Spectra 159 | 160 | #endif // GEN_EIGS_SOLVER_H 161 | -------------------------------------------------------------------------------- /Libs/spectra/include/Spectra/LinAlg/Lanczos.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018-2019 Yixuan Qiu 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla 4 | // Public License v. 2.0. If a copy of the MPL was not distributed 5 | // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #ifndef LANCZOS_H 8 | #define LANCZOS_H 9 | 10 | #include 11 | #include // std::sqrt 12 | #include // std::invalid_argument 13 | #include // std::stringstream 14 | 15 | #include "Arnoldi.h" 16 | 17 | namespace Spectra { 18 | 19 | 20 | // Lanczos factorization A * V = V * H + f * e' 21 | // A: n x n 22 | // V: n x k 23 | // H: k x k 24 | // f: n x 1 25 | // e: [0, ..., 0, 1] 26 | // V and H are allocated of dimension m, so the maximum value of k is m 27 | template 28 | class Lanczos: public Arnoldi 29 | { 30 | private: 31 | typedef Eigen::Index Index; 32 | typedef Eigen::Matrix Matrix; 33 | typedef Eigen::Matrix Vector; 34 | typedef Eigen::Map MapMat; 35 | typedef Eigen::Map MapVec; 36 | typedef Eigen::Map MapConstMat; 37 | typedef Eigen::Map MapConstVec; 38 | 39 | using Arnoldi::m_op; 40 | using Arnoldi::m_n; 41 | using Arnoldi::m_m; 42 | using Arnoldi::m_k; 43 | using Arnoldi::m_fac_V; 44 | using Arnoldi::m_fac_H; 45 | using Arnoldi::m_fac_f; 46 | using Arnoldi::m_beta; 47 | using Arnoldi::m_near_0; 48 | using Arnoldi::m_eps; 49 | 50 | public: 51 | Lanczos(const ArnoldiOpType& op, Index m) : 52 | Arnoldi(op, m) 53 | {} 54 | 55 | // Lanczos factorization starting from step-k 56 | void factorize_from(Index from_k, Index to_m, Index& op_counter) 57 | { 58 | using std::sqrt; 59 | 60 | if(to_m <= from_k) return; 61 | 62 | if(from_k > m_k) 63 | { 64 | std::stringstream msg; 65 | msg << "Lanczos: from_k (= " << from_k << 66 | ") is larger than the current subspace dimension (= " << 67 | m_k << ")"; 68 | throw std::invalid_argument(msg.str()); 69 | } 70 | 71 | const Scalar beta_thresh = m_eps * sqrt(Scalar(m_n)); 72 | 73 | // Pre-allocate vectors 74 | Vector Vf(to_m); 75 | Vector w(m_n); 76 | 77 | // Keep the upperleft k x k submatrix of H and set other elements to 0 78 | m_fac_H.rightCols(m_m - from_k).setZero(); 79 | m_fac_H.block(from_k, 0, m_m - from_k, from_k).setZero(); 80 | 81 | for(Index i = from_k; i <= to_m - 1; i++) 82 | { 83 | bool restart = false; 84 | // If beta = 0, then the next V is not full rank 85 | // We need to generate a new residual vector that is orthogonal 86 | // to the current V, which we call a restart 87 | if(m_beta < m_near_0) 88 | { 89 | MapConstMat V(m_fac_V.data(), m_n, i); // The first i columns 90 | this->expand_basis(V, 2 * i, m_fac_f, m_beta); 91 | restart = true; 92 | } 93 | 94 | // v <- f / ||f|| 95 | MapVec v(&m_fac_V(0, i), m_n); // The (i+1)-th column 96 | v.noalias() = m_fac_f / m_beta; 97 | 98 | // Note that H[i+1, i] equals to the unrestarted beta 99 | m_fac_H(i, i - 1) = restart ? Scalar(0) : m_beta; 100 | 101 | // w <- A * v 102 | m_op.perform_op(v.data(), w.data()); 103 | op_counter++; 104 | 105 | // H[i+1, i+1] = = v'Bw 106 | m_fac_H(i - 1, i) = m_fac_H(i, i - 1); // Due to symmetry 107 | m_fac_H(i, i) = m_op.inner_product(v, w); 108 | 109 | // f <- w - V * V'Bw = w - H[i+1, i] * V{i} - H[i+1, i+1] * V{i+1} 110 | // If restarting, we know that H[i+1, i] = 0 111 | if(restart) 112 | m_fac_f.noalias() = w - m_fac_H(i, i) * v; 113 | else 114 | m_fac_f.noalias() = w - m_fac_H(i, i - 1) * m_fac_V.col(i - 1) - m_fac_H(i, i) * v; 115 | 116 | m_beta = m_op.norm(m_fac_f); 117 | 118 | // f/||f|| is going to be the next column of V, so we need to test 119 | // whether V'B(f/||f||) ~= 0 120 | const Index i1 = i + 1; 121 | MapMat Vs(m_fac_V.data(), m_n, i1); // The first (i+1) columns 122 | m_op.trans_product(Vs, m_fac_f, Vf.head(i1)); 123 | Scalar ortho_err = Vf.head(i1).cwiseAbs().maxCoeff(); 124 | // If not, iteratively correct the residual 125 | int count = 0; 126 | while(count < 5 && ortho_err > m_eps * m_beta) 127 | { 128 | // There is an edge case: when beta=||f|| is close to zero, f mostly consists 129 | // of noises of rounding errors, so the test [ortho_err < eps * beta] is very 130 | // likely to fail. In particular, if beta=0, then the test is ensured to fail. 131 | // Hence when this happens, we force f to be zero, and then restart in the 132 | // next iteration. 133 | if(m_beta < beta_thresh) 134 | { 135 | m_fac_f.setZero(); 136 | m_beta = Scalar(0); 137 | break; 138 | } 139 | 140 | // f <- f - V * Vf 141 | m_fac_f.noalias() -= Vs * Vf.head(i1); 142 | // h <- h + Vf 143 | m_fac_H(i - 1, i) += Vf[i - 1]; 144 | m_fac_H(i, i - 1) = m_fac_H(i - 1, i); 145 | m_fac_H(i, i) += Vf[i]; 146 | // beta <- ||f|| 147 | m_beta = m_op.norm(m_fac_f); 148 | 149 | m_op.trans_product(Vs, m_fac_f, Vf.head(i1)); 150 | ortho_err = Vf.head(i1).cwiseAbs().maxCoeff(); 151 | count++; 152 | } 153 | } 154 | 155 | // Indicate that this is a step-m factorization 156 | m_k = to_m; 157 | } 158 | 159 | // Apply H -> Q'HQ, where Q is from a tridiagonal QR decomposition 160 | void compress_H(const TridiagQR& decomp) 161 | { 162 | decomp.matrix_QtHQ(m_fac_H); 163 | m_k--; 164 | } 165 | }; 166 | 167 | 168 | } // namespace Spectra 169 | 170 | #endif // LANCZOS_H 171 | -------------------------------------------------------------------------------- /Libs/spectra/include/Spectra/MatOp/DenseCholesky.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Yixuan Qiu 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla 4 | // Public License v. 2.0. If a copy of the MPL was not distributed 5 | // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #ifndef DENSE_CHOLESKY_H 8 | #define DENSE_CHOLESKY_H 9 | 10 | #include 11 | #include 12 | #include 13 | #include "../Util/CompInfo.h" 14 | 15 | namespace Spectra { 16 | 17 | 18 | /// 19 | /// \ingroup MatOp 20 | /// 21 | /// This class defines the operations related to Cholesky decomposition on a 22 | /// positive definite matrix, \f$B=LL'\f$, where \f$L\f$ is a lower triangular 23 | /// matrix. It is mainly used in the SymGEigsSolver generalized eigen solver 24 | /// in the Cholesky decomposition mode. 25 | /// 26 | template 27 | class DenseCholesky 28 | { 29 | private: 30 | typedef Eigen::Index Index; 31 | typedef Eigen::Matrix Matrix; 32 | typedef Eigen::Matrix Vector; 33 | typedef Eigen::Map MapConstMat; 34 | typedef Eigen::Map MapConstVec; 35 | typedef Eigen::Map MapVec; 36 | typedef const Eigen::Ref ConstGenericMatrix; 37 | 38 | const Index m_n; 39 | Eigen::LLT m_decomp; 40 | int m_info; // status of the decomposition 41 | 42 | public: 43 | /// 44 | /// Constructor to create the matrix operation object. 45 | /// 46 | /// \param mat An **Eigen** matrix object, whose type can be 47 | /// `Eigen::Matrix` (e.g. `Eigen::MatrixXd` and 48 | /// `Eigen::MatrixXf`), or its mapped version 49 | /// (e.g. `Eigen::Map`). 50 | /// 51 | DenseCholesky(ConstGenericMatrix& mat) : 52 | m_n(mat.rows()), m_info(NOT_COMPUTED) 53 | { 54 | if(mat.rows() != mat.cols()) 55 | throw std::invalid_argument("DenseCholesky: matrix must be square"); 56 | 57 | m_decomp.compute(mat); 58 | m_info = (m_decomp.info() == Eigen::Success) ? 59 | SUCCESSFUL : 60 | NUMERICAL_ISSUE; 61 | } 62 | 63 | /// 64 | /// Returns the number of rows of the underlying matrix. 65 | /// 66 | Index rows() const { return m_n; } 67 | /// 68 | /// Returns the number of columns of the underlying matrix. 69 | /// 70 | Index cols() const { return m_n; } 71 | 72 | /// 73 | /// Returns the status of the computation. 74 | /// The full list of enumeration values can be found in \ref Enumerations. 75 | /// 76 | int info() const { return m_info; } 77 | 78 | /// 79 | /// Performs the lower triangular solving operation \f$y=L^{-1}x\f$. 80 | /// 81 | /// \param x_in Pointer to the \f$x\f$ vector. 82 | /// \param y_out Pointer to the \f$y\f$ vector. 83 | /// 84 | // y_out = inv(L) * x_in 85 | void lower_triangular_solve(const Scalar* x_in, Scalar* y_out) const 86 | { 87 | MapConstVec x(x_in, m_n); 88 | MapVec y(y_out, m_n); 89 | y.noalias() = m_decomp.matrixL().solve(x); 90 | } 91 | 92 | /// 93 | /// Performs the upper triangular solving operation \f$y=(L')^{-1}x\f$. 94 | /// 95 | /// \param x_in Pointer to the \f$x\f$ vector. 96 | /// \param y_out Pointer to the \f$y\f$ vector. 97 | /// 98 | // y_out = inv(L') * x_in 99 | void upper_triangular_solve(const Scalar* x_in, Scalar* y_out) const 100 | { 101 | MapConstVec x(x_in, m_n); 102 | MapVec y(y_out, m_n); 103 | y.noalias() = m_decomp.matrixU().solve(x); 104 | } 105 | }; 106 | 107 | 108 | } // namespace Spectra 109 | 110 | #endif // DENSE_CHOLESKY_H 111 | -------------------------------------------------------------------------------- /Libs/spectra/include/Spectra/MatOp/DenseGenComplexShiftSolve.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Yixuan Qiu 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla 4 | // Public License v. 2.0. If a copy of the MPL was not distributed 5 | // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #ifndef DENSE_GEN_COMPLEX_SHIFT_SOLVE_H 8 | #define DENSE_GEN_COMPLEX_SHIFT_SOLVE_H 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace Spectra { 15 | 16 | 17 | /// 18 | /// \ingroup MatOp 19 | /// 20 | /// This class defines the complex shift-solve operation on a general real matrix \f$A\f$, 21 | /// i.e., calculating \f$y=\mathrm{Re}\{(A-\sigma I)^{-1}x\}\f$ for any complex-valued 22 | /// \f$\sigma\f$ and real-valued vector \f$x\f$. It is mainly used in the 23 | /// GenEigsComplexShiftSolver eigen solver. 24 | /// 25 | template 26 | class DenseGenComplexShiftSolve 27 | { 28 | private: 29 | typedef Eigen::Index Index; 30 | typedef Eigen::Matrix Matrix; 31 | typedef Eigen::Matrix Vector; 32 | typedef Eigen::Map MapConstVec; 33 | typedef Eigen::Map MapVec; 34 | typedef const Eigen::Ref ConstGenericMatrix; 35 | 36 | typedef std::complex Complex; 37 | typedef Eigen::Matrix ComplexMatrix; 38 | typedef Eigen::Matrix ComplexVector; 39 | 40 | typedef Eigen::PartialPivLU ComplexSolver; 41 | 42 | ConstGenericMatrix m_mat; 43 | const Index m_n; 44 | ComplexSolver m_solver; 45 | ComplexVector m_x_cache; 46 | 47 | public: 48 | /// 49 | /// Constructor to create the matrix operation object. 50 | /// 51 | /// \param mat An **Eigen** matrix object, whose type can be 52 | /// `Eigen::Matrix` (e.g. `Eigen::MatrixXd` and 53 | /// `Eigen::MatrixXf`), or its mapped version 54 | /// (e.g. `Eigen::Map`). 55 | /// 56 | DenseGenComplexShiftSolve(ConstGenericMatrix& mat) : 57 | m_mat(mat), m_n(mat.rows()) 58 | { 59 | if(mat.rows() != mat.cols()) 60 | throw std::invalid_argument("DenseGenComplexShiftSolve: matrix must be square"); 61 | } 62 | 63 | /// 64 | /// Return the number of rows of the underlying matrix. 65 | /// 66 | Index rows() const { return m_n; } 67 | /// 68 | /// Return the number of columns of the underlying matrix. 69 | /// 70 | Index cols() const { return m_n; } 71 | 72 | /// 73 | /// Set the complex shift \f$\sigma\f$. 74 | /// 75 | /// \param sigmar Real part of \f$\sigma\f$. 76 | /// \param sigmai Imaginary part of \f$\sigma\f$. 77 | /// 78 | void set_shift(Scalar sigmar, Scalar sigmai) 79 | { 80 | m_solver.compute(m_mat.template cast() - Complex(sigmar, sigmai) * ComplexMatrix::Identity(m_n, m_n)); 81 | m_x_cache.resize(m_n); 82 | m_x_cache.setZero(); 83 | } 84 | 85 | /// 86 | /// Perform the complex shift-solve operation 87 | /// \f$y=\mathrm{Re}\{(A-\sigma I)^{-1}x\}\f$. 88 | /// 89 | /// \param x_in Pointer to the \f$x\f$ vector. 90 | /// \param y_out Pointer to the \f$y\f$ vector. 91 | /// 92 | // y_out = Re( inv(A - sigma * I) * x_in ) 93 | void perform_op(const Scalar* x_in, Scalar* y_out) 94 | { 95 | m_x_cache.real() = MapConstVec(x_in, m_n); 96 | MapVec y(y_out, m_n); 97 | y.noalias() = m_solver.solve(m_x_cache).real(); 98 | } 99 | }; 100 | 101 | 102 | } // namespace Spectra 103 | 104 | #endif // DENSE_GEN_COMPLEX_SHIFT_SOLVE_H 105 | -------------------------------------------------------------------------------- /Libs/spectra/include/Spectra/MatOp/DenseGenMatProd.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Yixuan Qiu 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla 4 | // Public License v. 2.0. If a copy of the MPL was not distributed 5 | // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #ifndef DENSE_GEN_MAT_PROD_H 8 | #define DENSE_GEN_MAT_PROD_H 9 | 10 | #include 11 | 12 | namespace Spectra { 13 | 14 | 15 | /// 16 | /// \defgroup MatOp Matrix Operations 17 | /// 18 | /// Define matrix operations on existing matrix objects 19 | /// 20 | 21 | /// 22 | /// \ingroup MatOp 23 | /// 24 | /// This class defines the matrix-vector multiplication operation on a 25 | /// general real matrix \f$A\f$, i.e., calculating \f$y=Ax\f$ for any vector 26 | /// \f$x\f$. It is mainly used in the GenEigsSolver and 27 | /// SymEigsSolver eigen solvers. 28 | /// 29 | template 30 | class DenseGenMatProd 31 | { 32 | private: 33 | typedef Eigen::Index Index; 34 | typedef Eigen::Matrix Matrix; 35 | typedef Eigen::Matrix Vector; 36 | typedef Eigen::Map MapConstVec; 37 | typedef Eigen::Map MapVec; 38 | typedef const Eigen::Ref ConstGenericMatrix; 39 | 40 | ConstGenericMatrix m_mat; 41 | 42 | public: 43 | /// 44 | /// Constructor to create the matrix operation object. 45 | /// 46 | /// \param mat An **Eigen** matrix object, whose type can be 47 | /// `Eigen::Matrix` (e.g. `Eigen::MatrixXd` and 48 | /// `Eigen::MatrixXf`), or its mapped version 49 | /// (e.g. `Eigen::Map`). 50 | /// 51 | DenseGenMatProd(ConstGenericMatrix& mat) : 52 | m_mat(mat) 53 | {} 54 | 55 | /// 56 | /// Return the number of rows of the underlying matrix. 57 | /// 58 | Index rows() const { return m_mat.rows(); } 59 | /// 60 | /// Return the number of columns of the underlying matrix. 61 | /// 62 | Index cols() const { return m_mat.cols(); } 63 | 64 | /// 65 | /// Perform the matrix-vector multiplication operation \f$y=Ax\f$. 66 | /// 67 | /// \param x_in Pointer to the \f$x\f$ vector. 68 | /// \param y_out Pointer to the \f$y\f$ vector. 69 | /// 70 | // y_out = A * x_in 71 | void perform_op(const Scalar* x_in, Scalar* y_out) const 72 | { 73 | MapConstVec x(x_in, m_mat.cols()); 74 | MapVec y(y_out, m_mat.rows()); 75 | y.noalias() = m_mat * x; 76 | } 77 | }; 78 | 79 | 80 | } // namespace Spectra 81 | 82 | #endif // DENSE_GEN_MAT_PROD_H 83 | -------------------------------------------------------------------------------- /Libs/spectra/include/Spectra/MatOp/DenseGenRealShiftSolve.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Yixuan Qiu 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla 4 | // Public License v. 2.0. If a copy of the MPL was not distributed 5 | // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #ifndef DENSE_GEN_REAL_SHIFT_SOLVE_H 8 | #define DENSE_GEN_REAL_SHIFT_SOLVE_H 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace Spectra { 15 | 16 | 17 | /// 18 | /// \ingroup MatOp 19 | /// 20 | /// This class defines the shift-solve operation on a general real matrix \f$A\f$, 21 | /// i.e., calculating \f$y=(A-\sigma I)^{-1}x\f$ for any real \f$\sigma\f$ and 22 | /// vector \f$x\f$. It is mainly used in the GenEigsRealShiftSolver eigen solver. 23 | /// 24 | template 25 | class DenseGenRealShiftSolve 26 | { 27 | private: 28 | typedef Eigen::Index Index; 29 | typedef Eigen::Matrix Matrix; 30 | typedef Eigen::Matrix Vector; 31 | typedef Eigen::Map MapConstVec; 32 | typedef Eigen::Map MapVec; 33 | typedef const Eigen::Ref ConstGenericMatrix; 34 | 35 | ConstGenericMatrix m_mat; 36 | const Index m_n; 37 | Eigen::PartialPivLU m_solver; 38 | 39 | public: 40 | /// 41 | /// Constructor to create the matrix operation object. 42 | /// 43 | /// \param mat An **Eigen** matrix object, whose type can be 44 | /// `Eigen::Matrix` (e.g. `Eigen::MatrixXd` and 45 | /// `Eigen::MatrixXf`), or its mapped version 46 | /// (e.g. `Eigen::Map`). 47 | /// 48 | DenseGenRealShiftSolve(ConstGenericMatrix& mat) : 49 | m_mat(mat), m_n(mat.rows()) 50 | { 51 | if(mat.rows() != mat.cols()) 52 | throw std::invalid_argument("DenseGenRealShiftSolve: matrix must be square"); 53 | } 54 | 55 | /// 56 | /// Return the number of rows of the underlying matrix. 57 | /// 58 | Index rows() const { return m_n; } 59 | /// 60 | /// Return the number of columns of the underlying matrix. 61 | /// 62 | Index cols() const { return m_n; } 63 | 64 | /// 65 | /// Set the real shift \f$\sigma\f$. 66 | /// 67 | void set_shift(Scalar sigma) 68 | { 69 | m_solver.compute(m_mat - sigma * Matrix::Identity(m_n, m_n)); 70 | } 71 | 72 | /// 73 | /// Perform the shift-solve operation \f$y=(A-\sigma I)^{-1}x\f$. 74 | /// 75 | /// \param x_in Pointer to the \f$x\f$ vector. 76 | /// \param y_out Pointer to the \f$y\f$ vector. 77 | /// 78 | // y_out = inv(A - sigma * I) * x_in 79 | void perform_op(const Scalar* x_in, Scalar* y_out) const 80 | { 81 | MapConstVec x(x_in, m_n); 82 | MapVec y(y_out, m_n); 83 | y.noalias() = m_solver.solve(x); 84 | } 85 | }; 86 | 87 | 88 | } // namespace Spectra 89 | 90 | #endif // DENSE_GEN_REAL_SHIFT_SOLVE_H 91 | -------------------------------------------------------------------------------- /Libs/spectra/include/Spectra/MatOp/DenseSymMatProd.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Yixuan Qiu 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla 4 | // Public License v. 2.0. If a copy of the MPL was not distributed 5 | // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #ifndef DENSE_SYM_MAT_PROD_H 8 | #define DENSE_SYM_MAT_PROD_H 9 | 10 | #include 11 | 12 | namespace Spectra { 13 | 14 | 15 | /// 16 | /// \ingroup MatOp 17 | /// 18 | /// This class defines the matrix-vector multiplication operation on a 19 | /// symmetric real matrix \f$A\f$, i.e., calculating \f$y=Ax\f$ for any vector 20 | /// \f$x\f$. It is mainly used in the SymEigsSolver eigen solver. 21 | /// 22 | template 23 | class DenseSymMatProd 24 | { 25 | private: 26 | typedef Eigen::Index Index; 27 | typedef Eigen::Matrix Matrix; 28 | typedef Eigen::Matrix Vector; 29 | typedef Eigen::Map MapConstVec; 30 | typedef Eigen::Map MapVec; 31 | typedef const Eigen::Ref ConstGenericMatrix; 32 | 33 | ConstGenericMatrix m_mat; 34 | 35 | public: 36 | /// 37 | /// Constructor to create the matrix operation object. 38 | /// 39 | /// \param mat An **Eigen** matrix object, whose type can be 40 | /// `Eigen::Matrix` (e.g. `Eigen::MatrixXd` and 41 | /// `Eigen::MatrixXf`), or its mapped version 42 | /// (e.g. `Eigen::Map`). 43 | /// 44 | DenseSymMatProd(ConstGenericMatrix& mat) : 45 | m_mat(mat) 46 | {} 47 | 48 | /// 49 | /// Return the number of rows of the underlying matrix. 50 | /// 51 | Index rows() const { return m_mat.rows(); } 52 | /// 53 | /// Return the number of columns of the underlying matrix. 54 | /// 55 | Index cols() const { return m_mat.cols(); } 56 | 57 | /// 58 | /// Perform the matrix-vector multiplication operation \f$y=Ax\f$. 59 | /// 60 | /// \param x_in Pointer to the \f$x\f$ vector. 61 | /// \param y_out Pointer to the \f$y\f$ vector. 62 | /// 63 | // y_out = A * x_in 64 | void perform_op(const Scalar* x_in, Scalar* y_out) const 65 | { 66 | MapConstVec x(x_in, m_mat.cols()); 67 | MapVec y(y_out, m_mat.rows()); 68 | y.noalias() = m_mat.template selfadjointView() * x; 69 | } 70 | }; 71 | 72 | 73 | } // namespace Spectra 74 | 75 | #endif // DENSE_SYM_MAT_PROD_H 76 | -------------------------------------------------------------------------------- /Libs/spectra/include/Spectra/MatOp/DenseSymShiftSolve.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Yixuan Qiu 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla 4 | // Public License v. 2.0. If a copy of the MPL was not distributed 5 | // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #ifndef DENSE_SYM_SHIFT_SOLVE_H 8 | #define DENSE_SYM_SHIFT_SOLVE_H 9 | 10 | #include 11 | #include 12 | 13 | #include "../LinAlg/BKLDLT.h" 14 | #include "../Util/CompInfo.h" 15 | 16 | namespace Spectra { 17 | 18 | 19 | /// 20 | /// \ingroup MatOp 21 | /// 22 | /// This class defines the shift-solve operation on a real symmetric matrix \f$A\f$, 23 | /// i.e., calculating \f$y=(A-\sigma I)^{-1}x\f$ for any real \f$\sigma\f$ and 24 | /// vector \f$x\f$. It is mainly used in the SymEigsShiftSolver eigen solver. 25 | /// 26 | template 27 | class DenseSymShiftSolve 28 | { 29 | private: 30 | typedef Eigen::Index Index; 31 | typedef Eigen::Matrix Matrix; 32 | typedef Eigen::Matrix Vector; 33 | typedef Eigen::Map MapConstVec; 34 | typedef Eigen::Map MapVec; 35 | typedef const Eigen::Ref ConstGenericMatrix; 36 | 37 | ConstGenericMatrix m_mat; 38 | const int m_n; 39 | BKLDLT m_solver; 40 | 41 | public: 42 | /// 43 | /// Constructor to create the matrix operation object. 44 | /// 45 | /// \param mat An **Eigen** matrix object, whose type can be 46 | /// `Eigen::Matrix` (e.g. `Eigen::MatrixXd` and 47 | /// `Eigen::MatrixXf`), or its mapped version 48 | /// (e.g. `Eigen::Map`). 49 | /// 50 | DenseSymShiftSolve(ConstGenericMatrix& mat) : 51 | m_mat(mat), m_n(mat.rows()) 52 | { 53 | if(mat.rows() != mat.cols()) 54 | throw std::invalid_argument("DenseSymShiftSolve: matrix must be square"); 55 | } 56 | 57 | /// 58 | /// Return the number of rows of the underlying matrix. 59 | /// 60 | Index rows() const { return m_n; } 61 | /// 62 | /// Return the number of columns of the underlying matrix. 63 | /// 64 | Index cols() const { return m_n; } 65 | 66 | /// 67 | /// Set the real shift \f$\sigma\f$. 68 | /// 69 | void set_shift(Scalar sigma) 70 | { 71 | m_solver.compute(m_mat, Uplo, sigma); 72 | if(m_solver.info() != SUCCESSFUL) 73 | throw std::invalid_argument("DenseSymShiftSolve: factorization failed with the given shift"); 74 | } 75 | 76 | /// 77 | /// Perform the shift-solve operation \f$y=(A-\sigma I)^{-1}x\f$. 78 | /// 79 | /// \param x_in Pointer to the \f$x\f$ vector. 80 | /// \param y_out Pointer to the \f$y\f$ vector. 81 | /// 82 | // y_out = inv(A - sigma * I) * x_in 83 | void perform_op(const Scalar* x_in, Scalar* y_out) const 84 | { 85 | MapConstVec x(x_in, m_n); 86 | MapVec y(y_out, m_n); 87 | y.noalias() = m_solver.solve(x); 88 | } 89 | }; 90 | 91 | 92 | } // namespace Spectra 93 | 94 | #endif // DENSE_SYM_SHIFT_SOLVE_H 95 | -------------------------------------------------------------------------------- /Libs/spectra/include/Spectra/MatOp/SparseCholesky.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Yixuan Qiu 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla 4 | // Public License v. 2.0. If a copy of the MPL was not distributed 5 | // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #ifndef SPARSE_CHOLESKY_H 8 | #define SPARSE_CHOLESKY_H 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "../Util/CompInfo.h" 15 | 16 | namespace Spectra { 17 | 18 | 19 | /// 20 | /// \ingroup MatOp 21 | /// 22 | /// This class defines the operations related to Cholesky decomposition on a 23 | /// sparse positive definite matrix, \f$B=LL'\f$, where \f$L\f$ is a lower triangular 24 | /// matrix. It is mainly used in the SymGEigsSolver generalized eigen solver 25 | /// in the Cholesky decomposition mode. 26 | /// 27 | template 28 | class SparseCholesky 29 | { 30 | private: 31 | typedef Eigen::Index Index; 32 | typedef Eigen::Matrix Vector; 33 | typedef Eigen::Map MapConstVec; 34 | typedef Eigen::Map MapVec; 35 | typedef Eigen::SparseMatrix SparseMatrix; 36 | typedef const Eigen::Ref ConstGenericSparseMatrix; 37 | 38 | const Index m_n; 39 | Eigen::SimplicialLLT m_decomp; 40 | int m_info; // status of the decomposition 41 | 42 | public: 43 | /// 44 | /// Constructor to create the matrix operation object. 45 | /// 46 | /// \param mat An **Eigen** sparse matrix object, whose type can be 47 | /// `Eigen::SparseMatrix` or its mapped version 48 | /// `Eigen::Map >`. 49 | /// 50 | SparseCholesky(ConstGenericSparseMatrix& mat) : 51 | m_n(mat.rows()) 52 | { 53 | if(mat.rows() != mat.cols()) 54 | throw std::invalid_argument("SparseCholesky: matrix must be square"); 55 | 56 | m_decomp.compute(mat); 57 | m_info = (m_decomp.info() == Eigen::Success) ? 58 | SUCCESSFUL : 59 | NUMERICAL_ISSUE; 60 | } 61 | 62 | /// 63 | /// Returns the number of rows of the underlying matrix. 64 | /// 65 | Index rows() const { return m_n; } 66 | /// 67 | /// Returns the number of columns of the underlying matrix. 68 | /// 69 | Index cols() const { return m_n; } 70 | 71 | /// 72 | /// Returns the status of the computation. 73 | /// The full list of enumeration values can be found in \ref Enumerations. 74 | /// 75 | int info() const { return m_info; } 76 | 77 | /// 78 | /// Performs the lower triangular solving operation \f$y=L^{-1}x\f$. 79 | /// 80 | /// \param x_in Pointer to the \f$x\f$ vector. 81 | /// \param y_out Pointer to the \f$y\f$ vector. 82 | /// 83 | // y_out = inv(L) * x_in 84 | void lower_triangular_solve(const Scalar* x_in, Scalar* y_out) const 85 | { 86 | MapConstVec x(x_in, m_n); 87 | MapVec y(y_out, m_n); 88 | y.noalias() = m_decomp.permutationP() * x; 89 | m_decomp.matrixL().solveInPlace(y); 90 | } 91 | 92 | /// 93 | /// Performs the upper triangular solving operation \f$y=(L')^{-1}x\f$. 94 | /// 95 | /// \param x_in Pointer to the \f$x\f$ vector. 96 | /// \param y_out Pointer to the \f$y\f$ vector. 97 | /// 98 | // y_out = inv(L') * x_in 99 | void upper_triangular_solve(const Scalar* x_in, Scalar* y_out) const 100 | { 101 | MapConstVec x(x_in, m_n); 102 | MapVec y(y_out, m_n); 103 | y.noalias() = m_decomp.matrixU().solve(x); 104 | y = m_decomp.permutationPinv() * y; 105 | } 106 | }; 107 | 108 | 109 | } // namespace Spectra 110 | 111 | #endif // SPARSE_CHOLESKY_H 112 | -------------------------------------------------------------------------------- /Libs/spectra/include/Spectra/MatOp/SparseGenMatProd.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Yixuan Qiu 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla 4 | // Public License v. 2.0. If a copy of the MPL was not distributed 5 | // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #ifndef SPARSE_GEN_MAT_PROD_H 8 | #define SPARSE_GEN_MAT_PROD_H 9 | 10 | #include 11 | #include 12 | 13 | namespace Spectra { 14 | 15 | 16 | /// 17 | /// \ingroup MatOp 18 | /// 19 | /// This class defines the matrix-vector multiplication operation on a 20 | /// sparse real matrix \f$A\f$, i.e., calculating \f$y=Ax\f$ for any vector 21 | /// \f$x\f$. It is mainly used in the GenEigsSolver and SymEigsSolver 22 | /// eigen solvers. 23 | /// 24 | template 25 | class SparseGenMatProd 26 | { 27 | private: 28 | typedef Eigen::Index Index; 29 | typedef Eigen::Matrix Vector; 30 | typedef Eigen::Map MapConstVec; 31 | typedef Eigen::Map MapVec; 32 | typedef Eigen::SparseMatrix SparseMatrix; 33 | typedef const Eigen::Ref ConstGenericSparseMatrix; 34 | 35 | ConstGenericSparseMatrix m_mat; 36 | 37 | public: 38 | /// 39 | /// Constructor to create the matrix operation object. 40 | /// 41 | /// \param mat An **Eigen** sparse matrix object, whose type can be 42 | /// `Eigen::SparseMatrix` or its mapped version 43 | /// `Eigen::Map >`. 44 | /// 45 | SparseGenMatProd(ConstGenericSparseMatrix& mat) : 46 | m_mat(mat) 47 | {} 48 | 49 | /// 50 | /// Return the number of rows of the underlying matrix. 51 | /// 52 | Index rows() const { return m_mat.rows(); } 53 | /// 54 | /// Return the number of columns of the underlying matrix. 55 | /// 56 | Index cols() const { return m_mat.cols(); } 57 | 58 | /// 59 | /// Perform the matrix-vector multiplication operation \f$y=Ax\f$. 60 | /// 61 | /// \param x_in Pointer to the \f$x\f$ vector. 62 | /// \param y_out Pointer to the \f$y\f$ vector. 63 | /// 64 | // y_out = A * x_in 65 | void perform_op(const Scalar* x_in, Scalar* y_out) const 66 | { 67 | MapConstVec x(x_in, m_mat.cols()); 68 | MapVec y(y_out, m_mat.rows()); 69 | y.noalias() = m_mat * x; 70 | } 71 | }; 72 | 73 | 74 | } // namespace Spectra 75 | 76 | #endif // SPARSE_GEN_MAT_PROD_H 77 | -------------------------------------------------------------------------------- /Libs/spectra/include/Spectra/MatOp/SparseGenRealShiftSolve.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Yixuan Qiu 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla 4 | // Public License v. 2.0. If a copy of the MPL was not distributed 5 | // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #ifndef SPARSE_GEN_REAL_SHIFT_SOLVE_H 8 | #define SPARSE_GEN_REAL_SHIFT_SOLVE_H 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace Spectra { 16 | 17 | 18 | /// 19 | /// \ingroup MatOp 20 | /// 21 | /// This class defines the shift-solve operation on a sparse real matrix \f$A\f$, 22 | /// i.e., calculating \f$y=(A-\sigma I)^{-1}x\f$ for any real \f$\sigma\f$ and 23 | /// vector \f$x\f$. It is mainly used in the GenEigsRealShiftSolver eigen solver. 24 | /// 25 | template 26 | class SparseGenRealShiftSolve 27 | { 28 | private: 29 | typedef Eigen::Index Index; 30 | typedef Eigen::Matrix Vector; 31 | typedef Eigen::Map MapConstVec; 32 | typedef Eigen::Map MapVec; 33 | typedef Eigen::SparseMatrix SparseMatrix; 34 | typedef const Eigen::Ref ConstGenericSparseMatrix; 35 | 36 | ConstGenericSparseMatrix m_mat; 37 | const int m_n; 38 | Eigen::SparseLU m_solver; 39 | 40 | public: 41 | /// 42 | /// Constructor to create the matrix operation object. 43 | /// 44 | /// \param mat An **Eigen** sparse matrix object, whose type can be 45 | /// `Eigen::SparseMatrix` or its mapped version 46 | /// `Eigen::Map >`. 47 | /// 48 | SparseGenRealShiftSolve(ConstGenericSparseMatrix& mat) : 49 | m_mat(mat), m_n(mat.rows()) 50 | { 51 | if(mat.rows() != mat.cols()) 52 | throw std::invalid_argument("SparseGenRealShiftSolve: matrix must be square"); 53 | } 54 | 55 | /// 56 | /// Return the number of rows of the underlying matrix. 57 | /// 58 | Index rows() const { return m_n; } 59 | /// 60 | /// Return the number of columns of the underlying matrix. 61 | /// 62 | Index cols() const { return m_n; } 63 | 64 | /// 65 | /// Set the real shift \f$\sigma\f$. 66 | /// 67 | void set_shift(Scalar sigma) 68 | { 69 | SparseMatrix I(m_n, m_n); 70 | I.setIdentity(); 71 | 72 | m_solver.compute(m_mat - sigma * I); 73 | if(m_solver.info() != Eigen::Success) 74 | throw std::invalid_argument("SparseGenRealShiftSolve: factorization failed with the given shift"); 75 | } 76 | 77 | /// 78 | /// Perform the shift-solve operation \f$y=(A-\sigma I)^{-1}x\f$. 79 | /// 80 | /// \param x_in Pointer to the \f$x\f$ vector. 81 | /// \param y_out Pointer to the \f$y\f$ vector. 82 | /// 83 | // y_out = inv(A - sigma * I) * x_in 84 | void perform_op(const Scalar* x_in, Scalar* y_out) const 85 | { 86 | MapConstVec x(x_in, m_n); 87 | MapVec y(y_out, m_n); 88 | y.noalias() = m_solver.solve(x); 89 | } 90 | }; 91 | 92 | 93 | } // namespace Spectra 94 | 95 | #endif // SPARSE_GEN_REAL_SHIFT_SOLVE_H 96 | -------------------------------------------------------------------------------- /Libs/spectra/include/Spectra/MatOp/SparseRegularInverse.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017-2019 Yixuan Qiu 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla 4 | // Public License v. 2.0. If a copy of the MPL was not distributed 5 | // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #ifndef SPARSE_REGULAR_INVERSE_H 8 | #define SPARSE_REGULAR_INVERSE_H 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace Spectra { 16 | 17 | 18 | /// 19 | /// \ingroup MatOp 20 | /// 21 | /// This class defines matrix operations required by the generalized eigen solver 22 | /// in the regular inverse mode. For a sparse and positive definite matrix \f$B\f$, 23 | /// it implements the matrix-vector product \f$y=Bx\f$ and the linear equation 24 | /// solving operation \f$y=B^{-1}x\f$. 25 | /// 26 | /// This class is intended to be used with the SymGEigsSolver generalized eigen solver 27 | /// in the regular inverse mode. 28 | /// 29 | template 30 | class SparseRegularInverse 31 | { 32 | private: 33 | typedef Eigen::Index Index; 34 | typedef Eigen::Matrix Vector; 35 | typedef Eigen::Map MapConstVec; 36 | typedef Eigen::Map MapVec; 37 | typedef Eigen::SparseMatrix SparseMatrix; 38 | typedef const Eigen::Ref ConstGenericSparseMatrix; 39 | 40 | ConstGenericSparseMatrix m_mat; 41 | const int m_n; 42 | Eigen::ConjugateGradient m_cg; 43 | 44 | public: 45 | /// 46 | /// Constructor to create the matrix operation object. 47 | /// 48 | /// \param mat An **Eigen** sparse matrix object, whose type can be 49 | /// `Eigen::SparseMatrix` or its mapped version 50 | /// `Eigen::Map >`. 51 | /// 52 | SparseRegularInverse(ConstGenericSparseMatrix& mat) : 53 | m_mat(mat), m_n(mat.rows()) 54 | { 55 | if(mat.rows() != mat.cols()) 56 | throw std::invalid_argument("SparseRegularInverse: matrix must be square"); 57 | 58 | m_cg.compute(mat); 59 | } 60 | 61 | /// 62 | /// Return the number of rows of the underlying matrix. 63 | /// 64 | Index rows() const { return m_n; } 65 | /// 66 | /// Return the number of columns of the underlying matrix. 67 | /// 68 | Index cols() const { return m_n; } 69 | 70 | /// 71 | /// Perform the solving operation \f$y=B^{-1}x\f$. 72 | /// 73 | /// \param x_in Pointer to the \f$x\f$ vector. 74 | /// \param y_out Pointer to the \f$y\f$ vector. 75 | /// 76 | // y_out = inv(B) * x_in 77 | void solve(const Scalar* x_in, Scalar* y_out) const 78 | { 79 | MapConstVec x(x_in, m_n); 80 | MapVec y(y_out, m_n); 81 | y.noalias() = m_cg.solve(x); 82 | } 83 | 84 | /// 85 | /// Perform the matrix-vector multiplication operation \f$y=Bx\f$. 86 | /// 87 | /// \param x_in Pointer to the \f$x\f$ vector. 88 | /// \param y_out Pointer to the \f$y\f$ vector. 89 | /// 90 | // y_out = B * x_in 91 | void mat_prod(const Scalar* x_in, Scalar* y_out) const 92 | { 93 | MapConstVec x(x_in, m_n); 94 | MapVec y(y_out, m_n); 95 | y.noalias() = m_mat.template selfadjointView() * x; 96 | } 97 | }; 98 | 99 | 100 | } // namespace Spectra 101 | 102 | #endif // SPARSE_REGULAR_INVERSE_H 103 | -------------------------------------------------------------------------------- /Libs/spectra/include/Spectra/MatOp/SparseSymMatProd.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Yixuan Qiu 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla 4 | // Public License v. 2.0. If a copy of the MPL was not distributed 5 | // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #ifndef SPARSE_SYM_MAT_PROD_H 8 | #define SPARSE_SYM_MAT_PROD_H 9 | 10 | #include 11 | #include 12 | 13 | namespace Spectra { 14 | 15 | 16 | /// 17 | /// \ingroup MatOp 18 | /// 19 | /// This class defines the matrix-vector multiplication operation on a 20 | /// sparse real symmetric matrix \f$A\f$, i.e., calculating \f$y=Ax\f$ for any vector 21 | /// \f$x\f$. It is mainly used in the SymEigsSolver eigen solver. 22 | /// 23 | template 24 | class SparseSymMatProd 25 | { 26 | private: 27 | typedef Eigen::Index Index; 28 | typedef Eigen::Matrix Vector; 29 | typedef Eigen::Map MapConstVec; 30 | typedef Eigen::Map MapVec; 31 | typedef Eigen::SparseMatrix SparseMatrix; 32 | typedef const Eigen::Ref ConstGenericSparseMatrix; 33 | 34 | ConstGenericSparseMatrix m_mat; 35 | 36 | public: 37 | /// 38 | /// Constructor to create the matrix operation object. 39 | /// 40 | /// \param mat An **Eigen** sparse matrix object, whose type can be 41 | /// `Eigen::SparseMatrix` or its mapped version 42 | /// `Eigen::Map >`. 43 | /// 44 | SparseSymMatProd(ConstGenericSparseMatrix& mat) : 45 | m_mat(mat) 46 | {} 47 | 48 | /// 49 | /// Return the number of rows of the underlying matrix. 50 | /// 51 | Index rows() const { return m_mat.rows(); } 52 | /// 53 | /// Return the number of columns of the underlying matrix. 54 | /// 55 | Index cols() const { return m_mat.cols(); } 56 | 57 | /// 58 | /// Perform the matrix-vector multiplication operation \f$y=Ax\f$. 59 | /// 60 | /// \param x_in Pointer to the \f$x\f$ vector. 61 | /// \param y_out Pointer to the \f$y\f$ vector. 62 | /// 63 | // y_out = A * x_in 64 | void perform_op(const Scalar* x_in, Scalar* y_out) const 65 | { 66 | MapConstVec x(x_in, m_mat.cols()); 67 | MapVec y(y_out, m_mat.rows()); 68 | y.noalias() = m_mat.template selfadjointView() * x; 69 | } 70 | }; 71 | 72 | 73 | } // namespace Spectra 74 | 75 | #endif // SPARSE_SYM_MAT_PROD_H 76 | -------------------------------------------------------------------------------- /Libs/spectra/include/Spectra/MatOp/SparseSymShiftSolve.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Yixuan Qiu 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla 4 | // Public License v. 2.0. If a copy of the MPL was not distributed 5 | // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #ifndef SPARSE_SYM_SHIFT_SOLVE_H 8 | #define SPARSE_SYM_SHIFT_SOLVE_H 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace Spectra { 16 | 17 | 18 | /// 19 | /// \ingroup MatOp 20 | /// 21 | /// This class defines the shift-solve operation on a sparse real symmetric matrix \f$A\f$, 22 | /// i.e., calculating \f$y=(A-\sigma I)^{-1}x\f$ for any real \f$\sigma\f$ and 23 | /// vector \f$x\f$. It is mainly used in the SymEigsShiftSolver eigen solver. 24 | /// 25 | template 26 | class SparseSymShiftSolve 27 | { 28 | private: 29 | typedef Eigen::Index Index; 30 | typedef Eigen::Matrix Vector; 31 | typedef Eigen::Map MapConstVec; 32 | typedef Eigen::Map MapVec; 33 | typedef Eigen::SparseMatrix SparseMatrix; 34 | typedef const Eigen::Ref ConstGenericSparseMatrix; 35 | 36 | ConstGenericSparseMatrix m_mat; 37 | const int m_n; 38 | Eigen::SparseLU m_solver; 39 | 40 | public: 41 | /// 42 | /// Constructor to create the matrix operation object. 43 | /// 44 | /// \param mat An **Eigen** sparse matrix object, whose type can be 45 | /// `Eigen::SparseMatrix` or its mapped version 46 | /// `Eigen::Map >`. 47 | /// 48 | SparseSymShiftSolve(ConstGenericSparseMatrix& mat) : 49 | m_mat(mat), m_n(mat.rows()) 50 | { 51 | if(mat.rows() != mat.cols()) 52 | throw std::invalid_argument("SparseSymShiftSolve: matrix must be square"); 53 | } 54 | 55 | /// 56 | /// Return the number of rows of the underlying matrix. 57 | /// 58 | Index rows() const { return m_n; } 59 | /// 60 | /// Return the number of columns of the underlying matrix. 61 | /// 62 | Index cols() const { return m_n; } 63 | 64 | /// 65 | /// Set the real shift \f$\sigma\f$. 66 | /// 67 | void set_shift(Scalar sigma) 68 | { 69 | SparseMatrix mat = m_mat.template selfadjointView(); 70 | SparseMatrix identity(m_n, m_n); 71 | identity.setIdentity(); 72 | mat = mat - sigma * identity; 73 | m_solver.isSymmetric(true); 74 | m_solver.compute(mat); 75 | if(m_solver.info() != Eigen::Success) 76 | throw std::invalid_argument("SparseSymShiftSolve: factorization failed with the given shift"); 77 | } 78 | 79 | /// 80 | /// Perform the shift-solve operation \f$y=(A-\sigma I)^{-1}x\f$. 81 | /// 82 | /// \param x_in Pointer to the \f$x\f$ vector. 83 | /// \param y_out Pointer to the \f$y\f$ vector. 84 | /// 85 | // y_out = inv(A - sigma * I) * x_in 86 | void perform_op(const Scalar* x_in, Scalar* y_out) const 87 | { 88 | MapConstVec x(x_in, m_n); 89 | MapVec y(y_out, m_n); 90 | y.noalias() = m_solver.solve(x); 91 | } 92 | }; 93 | 94 | 95 | } // namespace Spectra 96 | 97 | #endif // SPARSE_SYM_SHIFT_SOLVE_H 98 | -------------------------------------------------------------------------------- /Libs/spectra/include/Spectra/MatOp/internal/ArnoldiOp.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018-2019 Yixuan Qiu 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla 4 | // Public License v. 2.0. If a copy of the MPL was not distributed 5 | // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #ifndef ARNOLDI_OP_H 8 | #define ARNOLDI_OP_H 9 | 10 | #include 11 | #include // std::sqrt 12 | 13 | namespace Spectra { 14 | 15 | 16 | /// 17 | /// \ingroup Internals 18 | /// @{ 19 | /// 20 | 21 | /// 22 | /// \defgroup Operators Operators 23 | /// 24 | /// Different types of operators. 25 | /// 26 | 27 | /// 28 | /// \ingroup Operators 29 | /// 30 | /// Operators used in the Arnoldi factorization. 31 | /// 32 | template 33 | class ArnoldiOp 34 | { 35 | private: 36 | typedef Eigen::Index Index; 37 | typedef Eigen::Matrix Vector; 38 | 39 | OpType& m_op; 40 | BOpType& m_Bop; 41 | Vector m_cache; 42 | 43 | public: 44 | ArnoldiOp(OpType* op, BOpType* Bop) : 45 | m_op(*op), m_Bop(*Bop), m_cache(op->rows()) 46 | {} 47 | 48 | inline Index rows() const { return m_op.rows(); } 49 | 50 | // In generalized eigenvalue problem Ax=lambda*Bx, define the inner product to be = x'By. 51 | // For regular eigenvalue problems, it is the usual inner product = x'y 52 | 53 | // Compute = x'By 54 | // x and y are two vectors 55 | template 56 | Scalar inner_product(const Arg1& x, const Arg2& y) 57 | { 58 | m_Bop.mat_prod(y.data(), m_cache.data()); 59 | return x.dot(m_cache); 60 | } 61 | 62 | // Compute res = = X'By 63 | // X is a matrix, y is a vector, res is a vector 64 | template 65 | void trans_product(const Arg1& x, const Arg2& y, Eigen::Ref res) 66 | { 67 | m_Bop.mat_prod(y.data(), m_cache.data()); 68 | res.noalias() = x.transpose() * m_cache; 69 | } 70 | 71 | // B-norm of a vector, ||x||_B = sqrt(x'Bx) 72 | template 73 | Scalar norm(const Arg& x) 74 | { 75 | using std::sqrt; 76 | return sqrt(inner_product(x, x)); 77 | } 78 | 79 | // The "A" operator to generate the Krylov subspace 80 | inline void perform_op(const Scalar* x_in, Scalar* y_out) 81 | { 82 | m_op.perform_op(x_in, y_out); 83 | } 84 | }; 85 | 86 | 87 | 88 | /// 89 | /// \ingroup Operators 90 | /// 91 | /// Placeholder for the B-operator when \f$B = I\f$. 92 | /// 93 | class IdentityBOp {}; 94 | 95 | 96 | 97 | /// 98 | /// \ingroup Operators 99 | /// 100 | /// Partial specialization for the case \f$B = I\f$. 101 | /// 102 | template 103 | class ArnoldiOp 104 | { 105 | private: 106 | typedef Eigen::Index Index; 107 | typedef Eigen::Matrix Vector; 108 | 109 | OpType& m_op; 110 | 111 | public: 112 | ArnoldiOp(OpType* op, IdentityBOp* Bop) : 113 | m_op(*op) 114 | {} 115 | 116 | inline Index rows() const { return m_op.rows(); } 117 | 118 | // Compute = x'y 119 | // x and y are two vectors 120 | template 121 | Scalar inner_product(const Arg1& x, const Arg2& y) const 122 | { 123 | return x.dot(y); 124 | } 125 | 126 | // Compute res = = X'y 127 | // X is a matrix, y is a vector, res is a vector 128 | template 129 | void trans_product(const Arg1& x, const Arg2& y, Eigen::Ref res) const 130 | { 131 | res.noalias() = x.transpose() * y; 132 | } 133 | 134 | // B-norm of a vector. For regular eigenvalue problems it is simply the L2 norm 135 | template 136 | Scalar norm(const Arg& x) 137 | { 138 | return x.norm(); 139 | } 140 | 141 | // The "A" operator to generate the Krylov subspace 142 | inline void perform_op(const Scalar* x_in, Scalar* y_out) 143 | { 144 | m_op.perform_op(x_in, y_out); 145 | } 146 | }; 147 | 148 | /// 149 | /// @} 150 | /// 151 | 152 | 153 | } // namespace Spectra 154 | 155 | #endif // ARNOLDI_OP_H 156 | -------------------------------------------------------------------------------- /Libs/spectra/include/Spectra/MatOp/internal/SymGEigsCholeskyOp.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Yixuan Qiu 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla 4 | // Public License v. 2.0. If a copy of the MPL was not distributed 5 | // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #ifndef SYM_GEIGS_CHOLESKY_OP_H 8 | #define SYM_GEIGS_CHOLESKY_OP_H 9 | 10 | #include 11 | #include "../DenseSymMatProd.h" 12 | #include "../DenseCholesky.h" 13 | 14 | namespace Spectra { 15 | 16 | 17 | /// 18 | /// \ingroup Operators 19 | /// 20 | /// This class defines the matrix operation for generalized eigen solver in the 21 | /// Cholesky decomposition mode. It calculates \f$y=L^{-1}A(L')^{-1}x\f$ for any 22 | /// vector \f$x\f$, where \f$L\f$ is the Cholesky decomposition of \f$B\f$. 23 | /// This class is intended for internal use. 24 | /// 25 | template < typename Scalar = double, 26 | typename OpType = DenseSymMatProd, 27 | typename BOpType = DenseCholesky > 28 | class SymGEigsCholeskyOp 29 | { 30 | private: 31 | typedef Eigen::Index Index; 32 | typedef Eigen::Matrix Matrix; 33 | typedef Eigen::Matrix Vector; 34 | 35 | OpType& m_op; 36 | BOpType& m_Bop; 37 | Vector m_cache; // temporary working space 38 | 39 | public: 40 | /// 41 | /// Constructor to create the matrix operation object. 42 | /// 43 | /// \param op Pointer to the \f$A\f$ matrix operation object. 44 | /// \param Bop Pointer to the \f$B\f$ matrix operation object. 45 | /// 46 | SymGEigsCholeskyOp(OpType& op, BOpType& Bop) : 47 | m_op(op), m_Bop(Bop), m_cache(op.rows()) 48 | {} 49 | 50 | /// 51 | /// Return the number of rows of the underlying matrix. 52 | /// 53 | Index rows() const { return m_Bop.rows(); } 54 | /// 55 | /// Return the number of columns of the underlying matrix. 56 | /// 57 | Index cols() const { return m_Bop.rows(); } 58 | 59 | /// 60 | /// Perform the matrix operation \f$y=L^{-1}A(L')^{-1}x\f$. 61 | /// 62 | /// \param x_in Pointer to the \f$x\f$ vector. 63 | /// \param y_out Pointer to the \f$y\f$ vector. 64 | /// 65 | // y_out = inv(L) * A * inv(L') * x_in 66 | void perform_op(const Scalar* x_in, Scalar* y_out) 67 | { 68 | m_Bop.upper_triangular_solve(x_in, y_out); 69 | m_op.perform_op(y_out, m_cache.data()); 70 | m_Bop.lower_triangular_solve(m_cache.data(), y_out); 71 | } 72 | }; 73 | 74 | 75 | } // namespace Spectra 76 | 77 | #endif // SYM_GEIGS_CHOLESKY_OP_H 78 | -------------------------------------------------------------------------------- /Libs/spectra/include/Spectra/MatOp/internal/SymGEigsRegInvOp.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017-2019 Yixuan Qiu 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla 4 | // Public License v. 2.0. If a copy of the MPL was not distributed 5 | // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #ifndef SYM_GEIGS_REG_INV_OP_H 8 | #define SYM_GEIGS_REG_INV_OP_H 9 | 10 | #include 11 | #include "../SparseSymMatProd.h" 12 | #include "../SparseRegularInverse.h" 13 | 14 | namespace Spectra { 15 | 16 | 17 | /// 18 | /// \ingroup Operators 19 | /// 20 | /// This class defines the matrix operation for generalized eigen solver in the 21 | /// regular inverse mode. This class is intended for internal use. 22 | /// 23 | template < typename Scalar = double, 24 | typename OpType = SparseSymMatProd, 25 | typename BOpType = SparseRegularInverse > 26 | class SymGEigsRegInvOp 27 | { 28 | private: 29 | typedef Eigen::Index Index; 30 | typedef Eigen::Matrix Matrix; 31 | typedef Eigen::Matrix Vector; 32 | 33 | OpType& m_op; 34 | BOpType& m_Bop; 35 | Vector m_cache; // temporary working space 36 | 37 | public: 38 | /// 39 | /// Constructor to create the matrix operation object. 40 | /// 41 | /// \param op Pointer to the \f$A\f$ matrix operation object. 42 | /// \param Bop Pointer to the \f$B\f$ matrix operation object. 43 | /// 44 | SymGEigsRegInvOp(OpType& op, BOpType& Bop) : 45 | m_op(op), m_Bop(Bop), m_cache(op.rows()) 46 | {} 47 | 48 | /// 49 | /// Return the number of rows of the underlying matrix. 50 | /// 51 | Index rows() const { return m_Bop.rows(); } 52 | /// 53 | /// Return the number of columns of the underlying matrix. 54 | /// 55 | Index cols() const { return m_Bop.rows(); } 56 | 57 | /// 58 | /// Perform the matrix operation \f$y=B^{-1}Ax\f$. 59 | /// 60 | /// \param x_in Pointer to the \f$x\f$ vector. 61 | /// \param y_out Pointer to the \f$y\f$ vector. 62 | /// 63 | // y_out = inv(B) * A * x_in 64 | void perform_op(const Scalar* x_in, Scalar* y_out) 65 | { 66 | m_op.perform_op(x_in, m_cache.data()); 67 | m_Bop.solve(m_cache.data(), y_out); 68 | } 69 | }; 70 | 71 | 72 | } // namespace Spectra 73 | 74 | #endif // SYM_GEIGS_REG_INV_OP_H 75 | -------------------------------------------------------------------------------- /Libs/spectra/include/Spectra/Util/CompInfo.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Yixuan Qiu 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla 4 | // Public License v. 2.0. If a copy of the MPL was not distributed 5 | // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #ifndef COMP_INFO_H 8 | #define COMP_INFO_H 9 | 10 | namespace Spectra { 11 | 12 | 13 | /// 14 | /// \ingroup Enumerations 15 | /// 16 | /// The enumeration to report the status of computation. 17 | /// 18 | enum COMPUTATION_INFO 19 | { 20 | SUCCESSFUL = 0, ///< Computation was successful. 21 | 22 | NOT_COMPUTED, ///< Used in eigen solvers, indicating that computation 23 | ///< has not been conducted. Users should call 24 | ///< the `compute()` member function of solvers. 25 | 26 | NOT_CONVERGING, ///< Used in eigen solvers, indicating that some eigenvalues 27 | ///< did not converge. The `compute()` 28 | ///< function returns the number of converged eigenvalues. 29 | 30 | NUMERICAL_ISSUE ///< Used in Cholesky decomposition, indicating that the 31 | ///< matrix is not positive definite. 32 | }; 33 | 34 | 35 | } // namespace Spectra 36 | 37 | #endif // COMP_INFO_H 38 | -------------------------------------------------------------------------------- /Libs/spectra/include/Spectra/Util/GEigsMode.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Yixuan Qiu 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla 4 | // Public License v. 2.0. If a copy of the MPL was not distributed 5 | // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #ifndef GEIGS_MODE_H 8 | #define GEIGS_MODE_H 9 | 10 | namespace Spectra { 11 | 12 | 13 | /// 14 | /// \ingroup Enumerations 15 | /// 16 | /// The enumeration to specify the mode of generalized eigenvalue solver. 17 | /// 18 | enum GEIGS_MODE 19 | { 20 | GEIGS_CHOLESKY = 0, ///< Using Cholesky decomposition to solve generalized eigenvalues. 21 | 22 | GEIGS_REGULAR_INVERSE, ///< Regular inverse mode for generalized eigenvalue solver. 23 | 24 | GEIGS_SHIFT_INVERT, ///< Shift-and-invert mode for generalized eigenvalue solver. 25 | 26 | GEIGS_BUCKLING, ///< Buckling mode for generalized eigenvalue solver. 27 | 28 | GEIGS_CAYLEY ///< Cayley transformation mode for generalized eigenvalue solver. 29 | }; 30 | 31 | 32 | } // namespace Spectra 33 | 34 | #endif // GEIGS_MODE_H 35 | -------------------------------------------------------------------------------- /Libs/spectra/include/Spectra/Util/SimpleRandom.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Yixuan Qiu 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla 4 | // Public License v. 2.0. If a copy of the MPL was not distributed 5 | // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #ifndef SIMPLE_RANDOM_H 8 | #define SIMPLE_RANDOM_H 9 | 10 | #include 11 | 12 | /// \cond 13 | 14 | namespace Spectra { 15 | 16 | 17 | // We need a simple pseudo random number generator here: 18 | // 1. It is used to generate initial and restarted residual vector. 19 | // 2. It is not necessary to be so "random" and advanced. All we hope 20 | // is that the residual vector is not in the space spanned by the 21 | // current Krylov space. This should be met almost surely. 22 | // 3. We don't want to call RNG in C++, since we actually want the 23 | // algorithm to be deterministic. Also, calling RNG in C/C++ is not 24 | // allowed in R packages submitted to CRAN. 25 | // 4. The method should be as simple as possible, so an LCG is enough. 26 | // 5. Based on public domain code by Ray Gardner 27 | // http://stjarnhimlen.se/snippets/rg_rand.c 28 | 29 | 30 | template 31 | class SimpleRandom 32 | { 33 | private: 34 | typedef Eigen::Index Index; 35 | typedef Eigen::Matrix Vector; 36 | 37 | const unsigned int m_a; // multiplier 38 | const unsigned long m_max; // 2^31 - 1 39 | long m_rand; 40 | 41 | inline long next_long_rand(long seed) 42 | { 43 | unsigned long lo, hi; 44 | 45 | lo = m_a * (long)(seed & 0xFFFF); 46 | hi = m_a * (long)((unsigned long)seed >> 16); 47 | lo += (hi & 0x7FFF) << 16; 48 | if(lo > m_max) 49 | { 50 | lo &= m_max; 51 | ++lo; 52 | } 53 | lo += hi >> 15; 54 | if(lo > m_max) 55 | { 56 | lo &= m_max; 57 | ++lo; 58 | } 59 | return (long)lo; 60 | } 61 | public: 62 | SimpleRandom(unsigned long init_seed) : 63 | m_a(16807), 64 | m_max(2147483647L), 65 | m_rand(init_seed ? (init_seed & m_max) : 1) 66 | {} 67 | 68 | Scalar random() 69 | { 70 | m_rand = next_long_rand(m_rand); 71 | return Scalar(m_rand) / Scalar(m_max) - Scalar(0.5); 72 | } 73 | 74 | // Vector of random numbers of type Scalar 75 | // Ranging from -0.5 to 0.5 76 | Vector random_vec(const Index len) 77 | { 78 | Vector res(len); 79 | for(Index i = 0; i < len; i++) 80 | { 81 | m_rand = next_long_rand(m_rand); 82 | res[i] = Scalar(m_rand) / Scalar(m_max) - Scalar(0.5); 83 | } 84 | return res; 85 | } 86 | }; 87 | 88 | 89 | } // namespace Spectra 90 | 91 | /// \endcond 92 | 93 | #endif // SIMPLE_RANDOM_H 94 | -------------------------------------------------------------------------------- /Libs/spectra/include/Spectra/Util/TypeTraits.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018-2019 Yixuan Qiu 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla 4 | // Public License v. 2.0. If a copy of the MPL was not distributed 5 | // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #ifndef TYPE_TRAITS_H 8 | #define TYPE_TRAITS_H 9 | 10 | #include 11 | #include 12 | 13 | /// \cond 14 | 15 | namespace Spectra { 16 | 17 | 18 | // For a real value type "Scalar", we want to know its smallest 19 | // positive value, i.e., std::numeric_limits::min(). 20 | // However, we must take non-standard value types into account, 21 | // so we rely on Eigen::NumTraits. 22 | // 23 | // Eigen::NumTraits has defined epsilon() and lowest(), but 24 | // lowest() means negative highest(), which is a very small 25 | // negative value. 26 | // 27 | // Therefore, we manually define this limit, and use eplison()^3 28 | // to mimic it for non-standard types. 29 | 30 | // Generic definition 31 | template 32 | struct TypeTraits 33 | { 34 | static inline Scalar min() 35 | { 36 | return Eigen::numext::pow(Eigen::NumTraits::epsilon(), Scalar(3)); 37 | } 38 | }; 39 | 40 | // Full specialization 41 | template <> 42 | struct TypeTraits 43 | { 44 | static inline float min() 45 | { 46 | return std::numeric_limits::min(); 47 | } 48 | }; 49 | 50 | template <> 51 | struct TypeTraits 52 | { 53 | static inline double min() 54 | { 55 | return std::numeric_limits::min(); 56 | } 57 | }; 58 | 59 | template <> 60 | struct TypeTraits 61 | { 62 | static inline long double min() 63 | { 64 | return std::numeric_limits::min(); 65 | } 66 | }; 67 | 68 | 69 | } // namespace Spectra 70 | 71 | /// \endcond 72 | 73 | #endif // TYPE_TRAITS_H 74 | -------------------------------------------------------------------------------- /Libs/spectra/include/Spectra/contrib/PartialSVDSolver.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Yixuan Qiu 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla 4 | // Public License v. 2.0. If a copy of the MPL was not distributed 5 | // with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #ifndef PARTIAL_SVD_SOLVER_H 8 | #define PARTIAL_SVD_SOLVER_H 9 | 10 | #include 11 | #include "../SymEigsSolver.h" 12 | 13 | 14 | namespace Spectra { 15 | 16 | 17 | // Abstract class for matrix operation 18 | template 19 | class SVDMatOp 20 | { 21 | public: 22 | virtual int rows() const = 0; 23 | virtual int cols() const = 0; 24 | 25 | // y_out = A' * A * x_in or y_out = A * A' * x_in 26 | virtual void perform_op(const Scalar* x_in, Scalar* y_out) = 0; 27 | 28 | virtual ~SVDMatOp() {} 29 | }; 30 | 31 | // Operation of a tall matrix in SVD 32 | // We compute the eigenvalues of A' * A 33 | // MatrixType is either Eigen::Matrix or Eigen::SparseMatrix 34 | template 35 | class SVDTallMatOp: public SVDMatOp 36 | { 37 | private: 38 | typedef Eigen::Matrix Vector; 39 | typedef Eigen::Map MapConstVec; 40 | typedef Eigen::Map MapVec; 41 | typedef const Eigen::Ref ConstGenericMatrix; 42 | 43 | ConstGenericMatrix m_mat; 44 | const int m_dim; 45 | Vector m_cache; 46 | 47 | public: 48 | // Constructor 49 | SVDTallMatOp(ConstGenericMatrix& mat) : 50 | m_mat(mat), 51 | m_dim(std::min(mat.rows(), mat.cols())), 52 | m_cache(mat.rows()) 53 | {} 54 | 55 | // These are the rows and columns of A' * A 56 | int rows() const { return m_dim; } 57 | int cols() const { return m_dim; } 58 | 59 | // y_out = A' * A * x_in 60 | void perform_op(const Scalar* x_in, Scalar* y_out) 61 | { 62 | MapConstVec x(x_in, m_mat.cols()); 63 | MapVec y(y_out, m_mat.cols()); 64 | m_cache.noalias() = m_mat * x; 65 | y.noalias() = m_mat.transpose() * m_cache; 66 | } 67 | }; 68 | 69 | // Operation of a wide matrix in SVD 70 | // We compute the eigenvalues of A * A' 71 | // MatrixType is either Eigen::Matrix or Eigen::SparseMatrix 72 | template 73 | class SVDWideMatOp: public SVDMatOp 74 | { 75 | private: 76 | typedef Eigen::Matrix Vector; 77 | typedef Eigen::Map MapConstVec; 78 | typedef Eigen::Map MapVec; 79 | typedef const Eigen::Ref ConstGenericMatrix; 80 | 81 | ConstGenericMatrix m_mat; 82 | const int m_dim; 83 | Vector m_cache; 84 | 85 | public: 86 | // Constructor 87 | SVDWideMatOp(ConstGenericMatrix& mat) : 88 | m_mat(mat), 89 | m_dim(std::min(mat.rows(), mat.cols())), 90 | m_cache(mat.cols()) 91 | {} 92 | 93 | // These are the rows and columns of A * A' 94 | int rows() const { return m_dim; } 95 | int cols() const { return m_dim; } 96 | 97 | // y_out = A * A' * x_in 98 | void perform_op(const Scalar* x_in, Scalar* y_out) 99 | { 100 | MapConstVec x(x_in, m_mat.rows()); 101 | MapVec y(y_out, m_mat.rows()); 102 | m_cache.noalias() = m_mat.transpose() * x; 103 | y.noalias() = m_mat * m_cache; 104 | } 105 | }; 106 | 107 | // Partial SVD solver 108 | // MatrixType is either Eigen::Matrix or Eigen::SparseMatrix 109 | template < typename Scalar = double, 110 | typename MatrixType = Eigen::Matrix > 111 | class PartialSVDSolver 112 | { 113 | private: 114 | typedef Eigen::Matrix Matrix; 115 | typedef Eigen::Matrix Vector; 116 | typedef const Eigen::Ref ConstGenericMatrix; 117 | 118 | ConstGenericMatrix m_mat; 119 | const int m_m; 120 | const int m_n; 121 | SVDMatOp* m_op; 122 | SymEigsSolver< Scalar, LARGEST_ALGE, SVDMatOp >* m_eigs; 123 | int m_nconv; 124 | Matrix m_evecs; 125 | 126 | public: 127 | // Constructor 128 | PartialSVDSolver(ConstGenericMatrix& mat, int ncomp, int ncv) : 129 | m_mat(mat), m_m(mat.rows()), m_n(mat.cols()), m_evecs(0, 0) 130 | { 131 | // Determine the matrix type, tall or wide 132 | if(m_m > m_n) 133 | { 134 | m_op = new SVDTallMatOp(mat); 135 | } else { 136 | m_op = new SVDWideMatOp(mat); 137 | } 138 | 139 | // Solver object 140 | m_eigs = new SymEigsSolver< Scalar, LARGEST_ALGE, SVDMatOp >(m_op, ncomp, ncv); 141 | } 142 | 143 | // Destructor 144 | virtual ~PartialSVDSolver() 145 | { 146 | delete m_eigs; 147 | delete m_op; 148 | } 149 | 150 | // Computation 151 | int compute(int maxit = 1000, Scalar tol = 1e-10) 152 | { 153 | m_eigs->init(); 154 | m_nconv = m_eigs->compute(maxit, tol); 155 | 156 | return m_nconv; 157 | } 158 | 159 | // The converged singular values 160 | Vector singular_values() const 161 | { 162 | Vector svals = m_eigs->eigenvalues().cwiseSqrt(); 163 | 164 | return svals; 165 | } 166 | 167 | // The converged left singular vectors 168 | Matrix matrix_U(int nu) 169 | { 170 | if(m_evecs.cols() < 1) 171 | { 172 | m_evecs = m_eigs->eigenvectors(); 173 | } 174 | nu = std::min(nu, m_nconv); 175 | if(m_m <= m_n) 176 | { 177 | return m_evecs.leftCols(nu); 178 | } 179 | 180 | return m_mat * (m_evecs.leftCols(nu).array().rowwise() / m_eigs->eigenvalues().head(nu).transpose().array().sqrt()).matrix(); 181 | } 182 | 183 | // The converged right singular vectors 184 | Matrix matrix_V(int nv) 185 | { 186 | if(m_evecs.cols() < 1) 187 | { 188 | m_evecs = m_eigs->eigenvectors(); 189 | } 190 | nv = std::min(nv, m_nconv); 191 | if(m_m > m_n) 192 | { 193 | return m_evecs.leftCols(nv); 194 | } 195 | 196 | return m_mat.transpose() * (m_evecs.leftCols(nv).array().rowwise() / m_eigs->eigenvalues().head(nv).transpose().array().sqrt()).matrix(); 197 | } 198 | }; 199 | 200 | 201 | } // namespace Spectra 202 | 203 | #endif // PARTIAL_SVD_SOLVER_H 204 | -------------------------------------------------------------------------------- /Libs/spectra/test/BKLDLT.cpp: -------------------------------------------------------------------------------- 1 | // Test ../include/Spectra/LinAlg/BKLDLT.h 2 | #include 3 | #include 4 | 5 | using namespace Spectra; 6 | 7 | #define CATCH_CONFIG_MAIN 8 | #include "catch.hpp" 9 | 10 | using Eigen::MatrixXd; 11 | using Eigen::VectorXd; 12 | 13 | // Solve (A - s * I)x = b 14 | void run_test(const MatrixXd& A, const VectorXd& b, double s) 15 | { 16 | BKLDLT decompL(A, Eigen::Lower, s); 17 | REQUIRE(decompL.info() == SUCCESSFUL); 18 | 19 | BKLDLT decompU(A, Eigen::Upper, s); 20 | REQUIRE( decompU.info() == SUCCESSFUL ); 21 | 22 | VectorXd solL = decompL.solve(b); 23 | VectorXd solU = decompU.solve(b); 24 | REQUIRE( (solL - solU).cwiseAbs().maxCoeff() == 0.0 ); 25 | 26 | const double tol = 1e-9; 27 | VectorXd resid = A * solL - s * solL - b; 28 | INFO( "||(A - s * I)x - b||_inf = " << resid.cwiseAbs().maxCoeff() ); 29 | REQUIRE( resid.cwiseAbs().maxCoeff() == Approx(0.0).margin(tol) ); 30 | } 31 | 32 | TEST_CASE("BKLDLT decomposition of symmetric real matrix [10x10]", "[BKLDLT]") 33 | { 34 | std::srand(123); 35 | const int n = 10; 36 | MatrixXd A = MatrixXd::Random(n, n); 37 | A = (A + A.transpose()).eval(); 38 | VectorXd b = VectorXd::Random(n); 39 | const double shift = 1.0; 40 | 41 | run_test(A, b, shift); 42 | } 43 | 44 | TEST_CASE("BKLDLT decomposition of symmetric real matrix [100x100]", "[BKLDLT]") 45 | { 46 | std::srand(123); 47 | const int n = 100; 48 | MatrixXd A = MatrixXd::Random(n, n); 49 | A = (A + A.transpose()).eval(); 50 | VectorXd b = VectorXd::Random(n); 51 | const double shift = 1.0; 52 | 53 | run_test(A, b, shift); 54 | } 55 | 56 | TEST_CASE("BKLDLT decomposition of symmetric real matrix [1000x1000]", "[BKLDLT]") 57 | { 58 | std::srand(123); 59 | const int n = 1000; 60 | MatrixXd A = MatrixXd::Random(n, n); 61 | A = (A + A.transpose()).eval(); 62 | VectorXd b = VectorXd::Random(n); 63 | const double shift = 1.0; 64 | 65 | run_test(A, b, shift); 66 | } 67 | -------------------------------------------------------------------------------- /Libs/spectra/test/Eigen.cpp: -------------------------------------------------------------------------------- 1 | // Test ../include/Spectra/LinAlg/UpperHessenbergEigen.h and 2 | // ../include/Spectra/LinAlg/TridiagEigen.h 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace Spectra; 9 | 10 | #define CATCH_CONFIG_MAIN 11 | #include "catch.hpp" 12 | 13 | using Eigen::MatrixXd; 14 | using Eigen::VectorXd; 15 | using Eigen::MatrixXcd; 16 | using Eigen::VectorXcd; 17 | 18 | TEST_CASE("Eigen decomposition of upper Hessenberg matrix", "[Eigen]") 19 | { 20 | std::srand(123); 21 | int n = 100; 22 | MatrixXd m = MatrixXd::Random(n, n); 23 | m.array() -= 0.5; 24 | MatrixXd H = m.triangularView(); 25 | H.diagonal(-1) = m.diagonal(-1); 26 | 27 | UpperHessenbergEigen decomp(H); 28 | VectorXcd evals = decomp.eigenvalues(); 29 | MatrixXcd evecs = decomp.eigenvectors(); 30 | 31 | MatrixXcd err = H * evecs - evecs * evals.asDiagonal(); 32 | 33 | INFO( "||HU - UD||_inf = " << err.cwiseAbs().maxCoeff() ); 34 | REQUIRE( err.cwiseAbs().maxCoeff() == Approx(0.0).margin(1e-12) ); 35 | 36 | clock_t t1, t2; 37 | t1 = clock(); 38 | for(int i = 0; i < 100; i++) 39 | { 40 | UpperHessenbergEigen decomp(H); 41 | VectorXcd evals = decomp.eigenvalues(); 42 | MatrixXcd evecs = decomp.eigenvectors(); 43 | } 44 | t2 = clock(); 45 | std::cout << "elapsed time for UpperHessenbergEigen: " 46 | << double(t2 - t1) / CLOCKS_PER_SEC << " secs\n"; 47 | 48 | t1 = clock(); 49 | for(int i = 0; i < 100; i++) 50 | { 51 | Eigen::EigenSolver decomp(H); 52 | VectorXcd evals = decomp.eigenvalues(); 53 | MatrixXcd evecs = decomp.eigenvectors(); 54 | } 55 | t2 = clock(); 56 | std::cout << "elapsed time for Eigen::EigenSolver: " 57 | << double(t2 - t1) / CLOCKS_PER_SEC << " secs\n"; 58 | } 59 | 60 | TEST_CASE("Eigen decomposition of symmetric tridiagonal matrix", "[Eigen]") 61 | { 62 | std::srand(123); 63 | int n = 100; 64 | MatrixXd m = MatrixXd::Random(n, n); 65 | m.array() -= 0.5; 66 | MatrixXd H = MatrixXd::Zero(n, n); 67 | H.diagonal() = m.diagonal(); 68 | H.diagonal(-1) = m.diagonal(-1); 69 | H.diagonal(1) = m.diagonal(-1); 70 | 71 | TridiagEigen decomp(H); 72 | VectorXd evals = decomp.eigenvalues(); 73 | MatrixXd evecs = decomp.eigenvectors(); 74 | 75 | MatrixXd err = H * evecs - evecs * evals.asDiagonal(); 76 | 77 | INFO( "||HU - UD||_inf = " << err.cwiseAbs().maxCoeff() ); 78 | REQUIRE( err.cwiseAbs().maxCoeff() == Approx(0.0).margin(1e-12) ); 79 | 80 | clock_t t1, t2; 81 | t1 = clock(); 82 | for(int i = 0; i < 100; i++) 83 | { 84 | TridiagEigen decomp(H); 85 | VectorXd evals = decomp.eigenvalues(); 86 | MatrixXd evecs = decomp.eigenvectors(); 87 | } 88 | t2 = clock(); 89 | std::cout << "elapsed time for TridiagEigen: " 90 | << double(t2 - t1) / CLOCKS_PER_SEC << " secs\n"; 91 | 92 | t1 = clock(); 93 | for(int i = 0; i < 100; i++) 94 | { 95 | Eigen::SelfAdjointEigenSolver decomp(H); 96 | VectorXd evals = decomp.eigenvalues(); 97 | MatrixXd evecs = decomp.eigenvectors(); 98 | } 99 | t2 = clock(); 100 | std::cout << "elapsed time for Eigen::SelfAdjointEigenSolver: " 101 | << double(t2 - t1) / CLOCKS_PER_SEC << " secs\n"; 102 | } 103 | -------------------------------------------------------------------------------- /Libs/spectra/test/GenEigs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include // Requires C++ 11 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace Spectra; 11 | 12 | #define CATCH_CONFIG_MAIN 13 | #include "catch.hpp" 14 | 15 | typedef Eigen::MatrixXd Matrix; 16 | typedef Eigen::VectorXd Vector; 17 | typedef Eigen::MatrixXcd ComplexMatrix; 18 | typedef Eigen::VectorXcd ComplexVector; 19 | typedef Eigen::SparseMatrix SpMatrix; 20 | 21 | // Traits to obtain operation type from matrix type 22 | template 23 | struct OpTypeTrait 24 | { 25 | typedef DenseGenMatProd OpType; 26 | }; 27 | 28 | template <> 29 | struct OpTypeTrait 30 | { 31 | typedef SparseGenMatProd OpType; 32 | }; 33 | 34 | // Generate random sparse matrix 35 | SpMatrix gen_sparse_data(int n, double prob = 0.5) 36 | { 37 | SpMatrix mat(n, n); 38 | std::default_random_engine gen; 39 | gen.seed(0); 40 | std::uniform_real_distribution distr(0.0, 1.0); 41 | for(int i = 0; i < n; i++) 42 | { 43 | for(int j = 0; j < n; j++) 44 | { 45 | if(distr(gen) < prob) 46 | mat.insert(i, j) = distr(gen) - 0.5; 47 | } 48 | } 49 | return mat; 50 | } 51 | 52 | 53 | 54 | template 55 | void run_test(const MatType& mat, int k, int m, bool allow_fail = false) 56 | { 57 | typename OpTypeTrait::OpType op(mat); 58 | GenEigsSolver::OpType> 59 | eigs(&op, k, m); 60 | eigs.init(); 61 | int nconv = eigs.compute(500); // maxit = 500 to reduce running time for failed cases 62 | int niter = eigs.num_iterations(); 63 | int nops = eigs.num_operations(); 64 | 65 | if(allow_fail) 66 | { 67 | if( eigs.info() != SUCCESSFUL ) 68 | { 69 | WARN( "FAILED on this test" ); 70 | std::cout << "nconv = " << nconv << std::endl; 71 | std::cout << "niter = " << niter << std::endl; 72 | std::cout << "nops = " << nops << std::endl; 73 | return; 74 | } 75 | } else { 76 | INFO( "nconv = " << nconv ); 77 | INFO( "niter = " << niter ); 78 | INFO( "nops = " << nops ); 79 | REQUIRE( eigs.info() == SUCCESSFUL ); 80 | } 81 | 82 | ComplexVector evals = eigs.eigenvalues(); 83 | ComplexMatrix evecs = eigs.eigenvectors(); 84 | 85 | ComplexMatrix resid = mat * evecs - evecs * evals.asDiagonal(); 86 | const double err = resid.array().abs().maxCoeff(); 87 | 88 | INFO( "||AU - UD||_inf = " << err ); 89 | REQUIRE( err == Approx(0.0).margin(1e-9) ); 90 | } 91 | 92 | template 93 | void run_test_sets(const MatType& A, int k, int m) 94 | { 95 | SECTION( "Largest Magnitude" ) 96 | { 97 | run_test(A, k, m); 98 | } 99 | SECTION( "Largest Real Part" ) 100 | { 101 | run_test(A, k, m); 102 | } 103 | SECTION( "Largest Imaginary Part" ) 104 | { 105 | run_test(A, k, m); 106 | } 107 | SECTION( "Smallest Magnitude" ) 108 | { 109 | run_test(A, k, m, true); 110 | } 111 | SECTION( "Smallest Real Part" ) 112 | { 113 | run_test(A, k, m); 114 | } 115 | SECTION( "Smallest Imaginary Part" ) 116 | { 117 | run_test(A, k, m, true); 118 | } 119 | } 120 | 121 | TEST_CASE("Eigensolver of general real matrix [10x10]", "[eigs_gen]") 122 | { 123 | std::srand(123); 124 | 125 | const Matrix A = Eigen::MatrixXd::Random(10, 10); 126 | int k = 3; 127 | int m = 6; 128 | 129 | run_test_sets(A, k, m); 130 | } 131 | 132 | TEST_CASE("Eigensolver of general real matrix [100x100]", "[eigs_gen]") 133 | { 134 | std::srand(123); 135 | 136 | const Matrix A = Eigen::MatrixXd::Random(100, 100); 137 | int k = 10; 138 | int m = 20; 139 | 140 | run_test_sets(A, k, m); 141 | } 142 | 143 | TEST_CASE("Eigensolver of general real matrix [1000x1000]", "[eigs_gen]") 144 | { 145 | std::srand(123); 146 | 147 | const Matrix A = Eigen::MatrixXd::Random(1000, 1000); 148 | int k = 20; 149 | int m = 50; 150 | 151 | run_test_sets(A, k, m); 152 | } 153 | 154 | TEST_CASE("Eigensolver of sparse real matrix [10x10]", "[eigs_gen]") 155 | { 156 | std::srand(123); 157 | 158 | const SpMatrix A = gen_sparse_data(10, 0.5); 159 | int k = 3; 160 | int m = 6; 161 | 162 | run_test_sets(A, k, m); 163 | } 164 | 165 | TEST_CASE("Eigensolver of sparse real matrix [100x100]", "[eigs_gen]") 166 | { 167 | std::srand(123); 168 | 169 | const SpMatrix A = gen_sparse_data(100, 0.5); 170 | int k = 10; 171 | int m = 20; 172 | 173 | run_test_sets(A, k, m); 174 | } 175 | 176 | TEST_CASE("Eigensolver of sparse real matrix [1000x1000]", "[eigs_gen]") 177 | { 178 | std::srand(123); 179 | 180 | const SpMatrix A = gen_sparse_data(1000, 0.5); 181 | int k = 20; 182 | int m = 50; 183 | 184 | run_test_sets(A, k, m); 185 | } 186 | -------------------------------------------------------------------------------- /Libs/spectra/test/GenEigsComplexShift.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | using namespace Spectra; 8 | 9 | #define CATCH_CONFIG_MAIN 10 | #include "catch.hpp" 11 | 12 | typedef Eigen::MatrixXd Matrix; 13 | typedef Eigen::VectorXd Vector; 14 | typedef Eigen::MatrixXcd ComplexMatrix; 15 | typedef Eigen::VectorXcd ComplexVector; 16 | 17 | template 18 | void run_test(const Matrix &mat, int k, int m, double sigmar, double sigmai, bool allow_fail = false) 19 | { 20 | DenseGenComplexShiftSolve op(mat); 21 | GenEigsComplexShiftSolver> eigs(&op, k, m, sigmar, sigmai); 22 | eigs.init(); 23 | int nconv = eigs.compute(100); // maxit = 500 to reduce running time for failed cases 24 | int niter = eigs.num_iterations(); 25 | int nops = eigs.num_operations(); 26 | 27 | if(allow_fail) 28 | { 29 | if( eigs.info() != SUCCESSFUL ) 30 | { 31 | WARN( "FAILED on this test" ); 32 | std::cout << "nconv = " << nconv << std::endl; 33 | std::cout << "niter = " << niter << std::endl; 34 | std::cout << "nops = " << nops << std::endl; 35 | return; 36 | } 37 | } else { 38 | INFO( "nconv = " << nconv ); 39 | INFO( "niter = " << niter ); 40 | INFO( "nops = " << nops ); 41 | REQUIRE( eigs.info() == SUCCESSFUL ); 42 | } 43 | 44 | ComplexVector evals = eigs.eigenvalues(); 45 | ComplexMatrix evecs = eigs.eigenvectors(); 46 | 47 | ComplexMatrix resid = mat * evecs - evecs * evals.asDiagonal(); 48 | const double err = resid.array().abs().maxCoeff(); 49 | 50 | INFO( "||AU - UD||_inf = " << err ); 51 | INFO( "||AU - UD||_2 colwise =" << resid.colwise().norm() ); 52 | REQUIRE( err == Approx(0.0).margin(1e-8) ); 53 | } 54 | 55 | 56 | void run_test_sets(const Matrix &A, int k, int m, double sigmar, double sigmai) 57 | { 58 | SECTION( "Largest Magnitude" ) 59 | { 60 | run_test(A, k, m, sigmar, sigmai); 61 | } 62 | SECTION( "Largest Real Part" ) 63 | { 64 | run_test(A, k, m, sigmar, sigmai); 65 | } 66 | SECTION( "Largest Imaginary Part" ) 67 | { 68 | run_test(A, k, m, sigmar, sigmai); 69 | } 70 | SECTION( "Smallest Magnitude" ) 71 | { 72 | run_test(A, k, m, sigmar, sigmai, true); 73 | } 74 | SECTION( "Smallest Real Part" ) 75 | { 76 | run_test(A, k, m, sigmar, sigmai); 77 | } 78 | SECTION( "Smallest Imaginary Part" ) 79 | { 80 | run_test(A, k, m, sigmar, sigmai, true); 81 | } 82 | } 83 | 84 | TEST_CASE("Eigensolver of general real matrix [10x10]", "[eigs_gen]") 85 | { 86 | std::srand(123); 87 | 88 | Matrix A = Eigen::MatrixXd::Random(10, 10); 89 | int k = 3; 90 | int m = 8; 91 | double sigmar = 2.0; 92 | double sigmai = 1.0; 93 | 94 | run_test_sets(A, k, m, sigmar, sigmai); 95 | } 96 | 97 | TEST_CASE("Eigensolver of general real matrix [100x100]", "[eigs_gen]") 98 | { 99 | std::srand(123); 100 | 101 | Matrix A = Eigen::MatrixXd::Random(100, 100); 102 | int k = 10; 103 | int m = 20; 104 | double sigmar = 20.0; 105 | double sigmai = 10.0; 106 | 107 | run_test_sets(A, k, m, sigmar, sigmai); 108 | } 109 | 110 | TEST_CASE("Eigensolver of general real matrix [1000x1000]", "[eigs_gen]") 111 | { 112 | std::srand(123); 113 | 114 | Matrix A = Eigen::MatrixXd::Random(1000, 1000); 115 | int k = 20; 116 | int m = 50; 117 | double sigmar = 200.0; 118 | double sigmai = 100.0; 119 | 120 | run_test_sets(A, k, m, sigmar, sigmai); 121 | } 122 | -------------------------------------------------------------------------------- /Libs/spectra/test/GenEigsRealShift.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include // Requires C++ 11 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace Spectra; 11 | 12 | #define CATCH_CONFIG_MAIN 13 | #include "catch.hpp" 14 | 15 | typedef Eigen::MatrixXd Matrix; 16 | typedef Eigen::VectorXd Vector; 17 | typedef Eigen::MatrixXcd ComplexMatrix; 18 | typedef Eigen::VectorXcd ComplexVector; 19 | typedef Eigen::SparseMatrix SpMatrix; 20 | 21 | // Traits to obtain operation type from matrix type 22 | template 23 | struct OpTypeTrait 24 | { 25 | typedef DenseGenRealShiftSolve OpType; 26 | }; 27 | 28 | template <> 29 | struct OpTypeTrait 30 | { 31 | typedef SparseGenRealShiftSolve OpType; 32 | }; 33 | 34 | // Generate random sparse matrix 35 | SpMatrix gen_sparse_data(int n, double prob = 0.5) 36 | { 37 | SpMatrix mat(n, n); 38 | std::default_random_engine gen; 39 | gen.seed(0); 40 | std::uniform_real_distribution distr(0.0, 1.0); 41 | for(int i = 0; i < n; i++) 42 | { 43 | for(int j = 0; j < n; j++) 44 | { 45 | if(distr(gen) < prob) 46 | mat.insert(i, j) = distr(gen) - 0.5; 47 | } 48 | } 49 | return mat; 50 | } 51 | 52 | 53 | template 54 | void run_test(const MatType& mat, int k, int m, double sigma, bool allow_fail = false) 55 | { 56 | typename OpTypeTrait::OpType op(mat); 57 | GenEigsRealShiftSolver::OpType> 58 | eigs(&op, k, m, sigma); 59 | eigs.init(); 60 | int nconv = eigs.compute(300); // maxit = 300 to reduce running time for failed cases 61 | int niter = eigs.num_iterations(); 62 | int nops = eigs.num_operations(); 63 | 64 | if(allow_fail) 65 | { 66 | if( eigs.info() != SUCCESSFUL ) 67 | { 68 | WARN( "FAILED on this test" ); 69 | std::cout << "nconv = " << nconv << std::endl; 70 | std::cout << "niter = " << niter << std::endl; 71 | std::cout << "nops = " << nops << std::endl; 72 | return; 73 | } 74 | } else { 75 | INFO( "nconv = " << nconv ); 76 | INFO( "niter = " << niter ); 77 | INFO( "nops = " << nops ); 78 | REQUIRE( eigs.info() == SUCCESSFUL ); 79 | } 80 | 81 | ComplexVector evals = eigs.eigenvalues(); 82 | ComplexMatrix evecs = eigs.eigenvectors(); 83 | 84 | ComplexMatrix resid = mat * evecs - evecs * evals.asDiagonal(); 85 | const double err = resid.array().abs().maxCoeff(); 86 | 87 | INFO( "||AU - UD||_inf = " << err ); 88 | REQUIRE( err == Approx(0.0).margin(1e-8) ); 89 | } 90 | 91 | template 92 | void run_test_sets(const MatType& A, int k, int m, double sigma) 93 | { 94 | SECTION( "Largest Magnitude" ) 95 | { 96 | run_test(A, k, m, sigma); 97 | } 98 | SECTION( "Largest Real Part" ) 99 | { 100 | run_test(A, k, m, sigma); 101 | } 102 | SECTION( "Largest Imaginary Part" ) 103 | { 104 | run_test(A, k, m, sigma); 105 | } 106 | SECTION( "Smallest Magnitude" ) 107 | { 108 | run_test(A, k, m, sigma, true); 109 | } 110 | SECTION( "Smallest Real Part" ) 111 | { 112 | run_test(A, k, m, sigma); 113 | } 114 | SECTION( "Smallest Imaginary Part" ) 115 | { 116 | run_test(A, k, m, sigma, true); 117 | } 118 | } 119 | 120 | TEST_CASE("Eigensolver of general real matrix [10x10]", "[eigs_gen]") 121 | { 122 | std::srand(123); 123 | 124 | const Matrix A = Eigen::MatrixXd::Random(10, 10); 125 | int k = 3; 126 | int m = 8; 127 | double sigma = 1.0; 128 | 129 | run_test_sets(A, k, m, sigma); 130 | } 131 | 132 | TEST_CASE("Eigensolver of general real matrix [100x100]", "[eigs_gen]") 133 | { 134 | std::srand(123); 135 | 136 | const Matrix A = Eigen::MatrixXd::Random(100, 100); 137 | int k = 10; 138 | int m = 20; 139 | double sigma = 10.0; 140 | 141 | run_test_sets(A, k, m, sigma); 142 | } 143 | 144 | TEST_CASE("Eigensolver of general real matrix [1000x1000]", "[eigs_gen]") 145 | { 146 | std::srand(123); 147 | 148 | const Matrix A = Eigen::MatrixXd::Random(1000, 1000); 149 | int k = 20; 150 | int m = 50; 151 | double sigma = 100.0; 152 | 153 | run_test_sets(A, k, m, sigma); 154 | } 155 | 156 | TEST_CASE("Eigensolver of sparse real matrix [10x10]", "[eigs_gen]") 157 | { 158 | std::srand(123); 159 | 160 | const SpMatrix A = gen_sparse_data(10, 0.5); 161 | int k = 3; 162 | int m = 6; 163 | double sigma = 1.0; 164 | 165 | run_test_sets(A, k, m, sigma); 166 | } 167 | 168 | TEST_CASE("Eigensolver of sparse real matrix [100x100]", "[eigs_gen]") 169 | { 170 | std::srand(123); 171 | 172 | const SpMatrix A = gen_sparse_data(100, 0.5); 173 | int k = 10; 174 | int m = 30; 175 | double sigma = 10.0; 176 | 177 | run_test_sets(A, k, m, sigma); 178 | } 179 | 180 | TEST_CASE("Eigensolver of sparse real matrix [1000x1000]", "[eigs_gen]") 181 | { 182 | std::srand(123); 183 | 184 | const SpMatrix A = gen_sparse_data(1000, 0.5); 185 | int k = 20; 186 | int m = 50; 187 | double sigma = 100.0; 188 | 189 | run_test_sets(A, k, m, sigma); 190 | } 191 | -------------------------------------------------------------------------------- /Libs/spectra/test/Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS = -std=c++11 -Wall -O2 -Wno-parentheses -Wno-misleading-indentation -Wno-int-in-bool-context 2 | CPPFLAGS = -I../include 3 | LDFLAGS = 4 | LIBS = 5 | 6 | HEADERS = $(wildcard ../include/Spectra/MatOp/*.h) \ 7 | $(wildcard ../include/Spectra/LinAlg/*.h) \ 8 | $(wildcard ../include/Spectra/Util/*.h) \ 9 | $(wildcard ../include/Spectra/*.h) \ 10 | $(wildcard ../include/Spectra/contrib/*.h) 11 | 12 | 13 | .PHONY: all test clean 14 | 15 | all: QR.out Eigen.out BKLDLT.out SymEigs.out SymEigsShift.out GenEigs.out GenEigsRealShift.out GenEigsComplexShift.out \ 16 | SymGEigsCholesky.out SymGEigsRegInv.out SVD.out 17 | 18 | test: 19 | -./QR.out 20 | -./Eigen.out 21 | -./BKLDLT.out 22 | -./SymEigs.out 23 | -./SymEigsShift.out 24 | -./GenEigs.out 25 | -./GenEigsRealShift.out 26 | -./GenEigsComplexShift.out 27 | -./SymGEigsCholesky.out 28 | -./SymGEigsRegInv.out 29 | -./SVD.out 30 | 31 | %.out: %.cpp $(HEADERS) 32 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) $< -o $@ $(LDFLAGS) $(LIBS) 33 | 34 | clean: 35 | -rm *.out 36 | 37 | -------------------------------------------------------------------------------- /Libs/spectra/test/QR.cpp: -------------------------------------------------------------------------------- 1 | // Test ../include/Spectra/LinAlg/UpperHessenbergQR.h and 2 | // ../include/Spectra/LinAlg/DoubleShiftQR.h 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace Spectra; 9 | 10 | #define CATCH_CONFIG_MAIN 11 | #include "catch.hpp" 12 | 13 | using Eigen::MatrixXd; 14 | using Eigen::VectorXd; 15 | typedef Eigen::Map MapMat; 16 | 17 | template 18 | void run_test(MatrixType &H, double shift) 19 | { 20 | Solver decomp(H, shift); 21 | const int n = H.rows(); 22 | const double tol = 1e-12; 23 | MatrixXd Hs = H - shift * MatrixXd::Identity(n, n); 24 | 25 | // Obtain Q matrix 26 | MatrixXd I = MatrixXd::Identity(n, n); 27 | MatrixXd Q = I; 28 | decomp.apply_QY(Q); 29 | 30 | // Test orthogonality 31 | MatrixXd QtQ = Q.transpose() * Q; 32 | INFO( "||Q'Q - I||_inf = " << (QtQ - I).cwiseAbs().maxCoeff() ); 33 | REQUIRE( (QtQ - I).cwiseAbs().maxCoeff() == Approx(0.0).margin(tol) ); 34 | 35 | MatrixXd QQt = Q * Q.transpose(); 36 | INFO( "||QQ' - I||_inf = " << (QQt - I).cwiseAbs().maxCoeff() ); 37 | REQUIRE( (QQt - I).cwiseAbs().maxCoeff() == Approx(0.0).margin(tol) ); 38 | 39 | // Obtain R matrix and test whether it is upper triangular 40 | MatrixXd R = decomp.matrix_R(); 41 | MatrixXd Rlower = R.triangularView(); 42 | INFO( "Whether R is upper triangular, error = " << Rlower.cwiseAbs().maxCoeff() ); 43 | REQUIRE( Rlower.cwiseAbs().maxCoeff() == Approx(0.0).margin(tol) ); 44 | 45 | // Compare Hs = H - s * I and QR 46 | INFO( "||Hs - QR||_inf = " << (Hs - Q * R).cwiseAbs().maxCoeff() ); 47 | REQUIRE( (Hs - Q * R).cwiseAbs().maxCoeff() == Approx(0.0).margin(tol) ); 48 | 49 | // Obtain Q'HQ 50 | MatrixXd QtHQ_true = Q.transpose() * H * Q; 51 | MatrixXd QtHQ; 52 | decomp.matrix_QtHQ(QtHQ); 53 | INFO( "max error of Q'HQ = " << (QtHQ - QtHQ_true).cwiseAbs().maxCoeff() ); 54 | REQUIRE( (QtHQ - QtHQ_true).cwiseAbs().maxCoeff() == Approx(0.0).margin(tol) ); 55 | 56 | // Test "apply" functions 57 | MatrixXd Y = MatrixXd::Random(n, n); 58 | 59 | MatrixXd QY = Y; 60 | decomp.apply_QY(QY); 61 | INFO( "max error of QY = " << (QY - Q * Y).cwiseAbs().maxCoeff() ); 62 | REQUIRE( (QY - Q * Y).cwiseAbs().maxCoeff() == Approx(0.0).margin(tol) ); 63 | 64 | MatrixXd YQ = Y; 65 | decomp.apply_YQ(YQ); 66 | INFO( "max error of YQ = " << (YQ - Y * Q).cwiseAbs().maxCoeff() ); 67 | REQUIRE( (YQ - Y * Q).cwiseAbs().maxCoeff() == Approx(0.0).margin(tol) ); 68 | 69 | MatrixXd QtY = Y; 70 | decomp.apply_QtY(QtY); 71 | INFO( "max error of Q'Y = " << (QtY - Q.transpose() * Y).cwiseAbs().maxCoeff() ); 72 | REQUIRE( (QtY - Q.transpose() * Y).cwiseAbs().maxCoeff() == Approx(0.0).margin(tol) ); 73 | 74 | MatrixXd YQt = Y; 75 | decomp.apply_YQt(YQt); 76 | INFO( "max error of YQ' = " << (YQt - Y * Q.transpose()).cwiseAbs().maxCoeff() ); 77 | REQUIRE( (YQt - Y * Q.transpose()).cwiseAbs().maxCoeff() == Approx(0.0).margin(tol) ); 78 | 79 | // Test "apply" functions for vectors 80 | VectorXd y = VectorXd::Random(n); 81 | 82 | VectorXd Qy = y; 83 | decomp.apply_QY(Qy); 84 | INFO( "max error of Qy = " << (Qy - Q * y).cwiseAbs().maxCoeff() ); 85 | REQUIRE( (Qy - Q * y).cwiseAbs().maxCoeff() == Approx(0.0).margin(tol) ); 86 | 87 | VectorXd Qty = y; 88 | decomp.apply_QtY(Qty); 89 | INFO( "max error of Q'y = " << (Qty - Q.transpose() * y).cwiseAbs().maxCoeff() ); 90 | REQUIRE( (Qty - Q.transpose() * y).cwiseAbs().maxCoeff() == Approx(0.0).margin(tol) ); 91 | } 92 | 93 | TEST_CASE("QR of upper Hessenberg matrix", "[QR]") 94 | { 95 | std::srand(123); 96 | int n = 100; 97 | MatrixXd m = MatrixXd::Random(n, n); 98 | m.array() -= 0.5; 99 | MatrixXd H = m.triangularView(); 100 | H.diagonal(-1) = m.diagonal(-1); 101 | 102 | run_test< UpperHessenbergQR >(H, 1.2345); 103 | 104 | MapMat Hmap(H.data(), H.rows(), H.cols()); 105 | run_test< UpperHessenbergQR >(Hmap, 0.6789); 106 | } 107 | 108 | TEST_CASE("QR of Tridiagonal matrix", "[QR]") 109 | { 110 | std::srand(123); 111 | int n = 100; 112 | MatrixXd m = MatrixXd::Random(n, n); 113 | m.array() -= 0.5; 114 | MatrixXd H = MatrixXd::Zero(n, n); 115 | H.diagonal() = m.diagonal(); 116 | H.diagonal(-1) = m.diagonal(-1); 117 | H.diagonal(1) = m.diagonal(-1); 118 | 119 | run_test< TridiagQR >(H, 1.2345); 120 | 121 | MapMat Hmap(H.data(), H.rows(), H.cols()); 122 | run_test< TridiagQR >(Hmap, 0.6789); 123 | } 124 | 125 | TEST_CASE("QR decomposition with double shifts", "[QR]") 126 | { 127 | std::srand(123); 128 | const int n = 100; 129 | const double tol = 1e-12; 130 | 131 | MatrixXd m = MatrixXd::Random(n, n); 132 | m.array() -= 0.5; 133 | MatrixXd H = m.triangularView(); 134 | H.diagonal(-1) = m.diagonal(-1); 135 | H(1, 0) = 0; // Test for the case when sub-diagonal element is zero 136 | 137 | const double s = 2, t = 3; 138 | 139 | MatrixXd M = H * H - s * H + t * MatrixXd::Identity(n, n); 140 | Eigen::HouseholderQR qr(M); 141 | MatrixXd Q0 = qr.householderQ(); 142 | 143 | DoubleShiftQR decomp(H, s, t); 144 | MatrixXd Q = MatrixXd::Identity(n, n); 145 | decomp.apply_YQ(Q); 146 | 147 | // Equal up to signs 148 | INFO( "max error of Q = " << (Q.cwiseAbs() - Q0.cwiseAbs()).cwiseAbs().maxCoeff() ); 149 | REQUIRE( (Q.cwiseAbs() - Q0.cwiseAbs()).cwiseAbs().maxCoeff() == Approx(0.0).margin(tol) ); 150 | 151 | // Test Q'HQ 152 | MatrixXd QtHQ; 153 | decomp.matrix_QtHQ(QtHQ); 154 | INFO( "max error of Q'HQ = " << (QtHQ - Q.transpose() * H * Q).cwiseAbs().maxCoeff() ); 155 | REQUIRE( (QtHQ - Q.transpose() * H * Q).cwiseAbs().maxCoeff() == Approx(0.0).margin(tol) ); 156 | 157 | // Test apply functions 158 | VectorXd y = VectorXd::Random(n); 159 | MatrixXd Y = MatrixXd::Random(n / 2, n); 160 | 161 | VectorXd Qty = y; 162 | decomp.apply_QtY(Qty); 163 | INFO( "max error of Q'y = " << (Qty - Q.transpose() * y).cwiseAbs().maxCoeff() ); 164 | REQUIRE( (Qty - Q.transpose() * y).cwiseAbs().maxCoeff() == Approx(0.0).margin(tol) ); 165 | 166 | MatrixXd YQ = Y; 167 | decomp.apply_YQ(YQ); 168 | INFO( "max error of YQ = " << (YQ- Y * Q).cwiseAbs().maxCoeff() ); 169 | REQUIRE( (YQ- Y * Q).cwiseAbs().maxCoeff() == Approx(0.0).margin(tol) ); 170 | } 171 | -------------------------------------------------------------------------------- /Libs/spectra/test/SVD.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include // Requires C++ 11 6 | 7 | #include 8 | 9 | using namespace Spectra; 10 | 11 | #define CATCH_CONFIG_MAIN 12 | #include "catch.hpp" 13 | 14 | typedef Eigen::MatrixXd Matrix; 15 | typedef Eigen::VectorXd Vector; 16 | typedef Eigen::SparseMatrix SpMatrix; 17 | 18 | // Generate random sparse matrix 19 | SpMatrix gen_sparse_data(int m, int n, double prob = 0.5) 20 | { 21 | SpMatrix mat(m, n); 22 | std::default_random_engine gen; 23 | gen.seed(0); 24 | std::uniform_real_distribution distr(0.0, 1.0); 25 | for(int i = 0; i < m; i++) 26 | { 27 | for(int j = 0; j < n; j++) 28 | { 29 | if(distr(gen) < prob) 30 | mat.insert(i, j) = distr(gen) - 0.5; 31 | } 32 | } 33 | return mat; 34 | } 35 | 36 | 37 | 38 | template 39 | void run_test(const MatType& mat, int k, int m) 40 | { 41 | PartialSVDSolver svds(mat, k, m); 42 | int nconv = svds.compute(); 43 | 44 | INFO( "nconv = " << nconv ); 45 | REQUIRE( nconv == k ); 46 | 47 | Vector svals = svds.singular_values(); 48 | Matrix U = svds.matrix_U(k); 49 | Matrix V = svds.matrix_V(k); 50 | 51 | // SVD solver from Eigen 52 | // Requires dense matrices 53 | Matrix mat_dense = Matrix(mat); 54 | Eigen::JacobiSVD svd(mat, Eigen::ComputeThinU | Eigen::ComputeThinV); 55 | Vector svals_eigen = svd.singularValues(); 56 | Matrix U_eigen = svd.matrixU(); 57 | Matrix V_eigen = svd.matrixV(); 58 | 59 | double err = (svals - svals_eigen.head(k)).array().abs().maxCoeff(); 60 | INFO( "Residual of singular values = " << err ); 61 | REQUIRE( err == Approx(0.0).margin(1e-9) ); 62 | 63 | err = (U.array().abs() - U_eigen.leftCols(k).array().abs()).abs().maxCoeff(); 64 | INFO( "Residual of left singular vectors = " << err ); 65 | REQUIRE( err == Approx(0.0).margin(1e-9) ); 66 | 67 | err = (V.array().abs() - V_eigen.leftCols(k).array().abs()).abs().maxCoeff(); 68 | INFO( "Residual of right singular vectors = " << err ); 69 | REQUIRE( err == Approx(0.0).margin(1e-9) ); 70 | } 71 | 72 | TEST_CASE("Partial SVD of tall dense matrix [1000x100]", "[svds_dense_tall]") 73 | { 74 | std::srand(123); 75 | 76 | const Matrix A = Matrix::Random(1000, 100); 77 | int k = 5; 78 | int m = 10; 79 | 80 | run_test(A, k, m); 81 | } 82 | 83 | TEST_CASE("Partial SVD of wide dense matrix [1000x100]", "[svds_dense_wide]") 84 | { 85 | std::srand(123); 86 | 87 | const Matrix A = Matrix::Random(100, 1000); 88 | int k = 5; 89 | int m = 10; 90 | 91 | run_test(A, k, m); 92 | } 93 | 94 | TEST_CASE("Partial SVD of tall sparse matrix [1000x100]", "[svds_sparse_tall]") 95 | { 96 | std::srand(123); 97 | 98 | const SpMatrix A = gen_sparse_data(1000, 100, 0.1); 99 | int k = 5; 100 | int m = 10; 101 | 102 | run_test(A, k, m); 103 | } 104 | 105 | TEST_CASE("Partial SVD of wide sparse matrix [1000x100]", "[svds_sparse_wide]") 106 | { 107 | std::srand(123); 108 | 109 | const SpMatrix A = gen_sparse_data(100, 1000, 0.1); 110 | int k = 5; 111 | int m = 10; 112 | 113 | run_test(A, k, m); 114 | } 115 | 116 | -------------------------------------------------------------------------------- /Libs/spectra/test/SymEigs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include // Requires C++ 11 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace Spectra; 11 | 12 | #define CATCH_CONFIG_MAIN 13 | #include "catch.hpp" 14 | 15 | typedef Eigen::MatrixXd Matrix; 16 | typedef Eigen::VectorXd Vector; 17 | typedef Eigen::SparseMatrix SpMatrix; 18 | 19 | // Traits to obtain operation type from matrix type 20 | template 21 | struct OpTypeTrait 22 | { 23 | typedef DenseSymMatProd OpType; 24 | }; 25 | 26 | template <> 27 | struct OpTypeTrait 28 | { 29 | typedef SparseSymMatProd OpType; 30 | }; 31 | 32 | // Generate data for testing 33 | Matrix gen_dense_data(int n) 34 | { 35 | const Matrix mat = Eigen::MatrixXd::Random(n, n); 36 | return mat + mat.transpose(); 37 | } 38 | 39 | SpMatrix gen_sparse_data(int n, double prob = 0.5) 40 | { 41 | // Eigen solver only uses the lower triangle of mat, 42 | // so we don't need to make mat symmetric here. 43 | SpMatrix mat(n, n); 44 | std::default_random_engine gen; 45 | gen.seed(0); 46 | std::uniform_real_distribution distr(0.0, 1.0); 47 | for(int i = 0; i < n; i++) 48 | { 49 | for(int j = 0; j < n; j++) 50 | { 51 | if(distr(gen) < prob) 52 | mat.insert(i, j) = distr(gen) - 0.5; 53 | } 54 | } 55 | return mat; 56 | } 57 | 58 | 59 | 60 | template 61 | void run_test(const MatType& mat, int k, int m) 62 | { 63 | typename OpTypeTrait::OpType op(mat); 64 | SymEigsSolver::OpType> 65 | eigs(&op, k, m); 66 | eigs.init(); 67 | int nconv = eigs.compute(); 68 | int niter = eigs.num_iterations(); 69 | int nops = eigs.num_operations(); 70 | 71 | INFO( "nconv = " << nconv ); 72 | INFO( "niter = " << niter ); 73 | INFO( "nops = " << nops ); 74 | REQUIRE( eigs.info() == SUCCESSFUL ); 75 | 76 | Vector evals = eigs.eigenvalues(); 77 | Matrix evecs = eigs.eigenvectors(); 78 | 79 | Matrix resid = mat.template selfadjointView() * evecs - evecs * evals.asDiagonal(); 80 | const double err = resid.array().abs().maxCoeff(); 81 | 82 | INFO( "||AU - UD||_inf = " << err ); 83 | REQUIRE( err == Approx(0.0).margin(1e-9) ); 84 | } 85 | 86 | template 87 | void run_test_sets(const MatType& mat, int k, int m) 88 | { 89 | SECTION( "Largest Magnitude" ) 90 | { 91 | run_test(mat, k, m); 92 | } 93 | SECTION( "Largest Value" ) 94 | { 95 | run_test(mat, k, m); 96 | } 97 | SECTION( "Smallest Magnitude" ) 98 | { 99 | run_test(mat, k, m); 100 | } 101 | SECTION( "Smallest Value" ) 102 | { 103 | run_test(mat, k, m); 104 | } 105 | SECTION( "Both Ends" ) 106 | { 107 | run_test(mat, k, m); 108 | } 109 | } 110 | 111 | TEST_CASE("Eigensolver of symmetric real matrix [10x10]", "[eigs_sym]") 112 | { 113 | std::srand(123); 114 | 115 | const Matrix A = gen_dense_data(10); 116 | int k = 3; 117 | int m = 6; 118 | 119 | run_test_sets(A, k, m); 120 | } 121 | 122 | TEST_CASE("Eigensolver of symmetric real matrix [100x100]", "[eigs_sym]") 123 | { 124 | std::srand(123); 125 | 126 | const Matrix A = gen_dense_data(100); 127 | int k = 10; 128 | int m = 20; 129 | 130 | run_test_sets(A, k, m); 131 | } 132 | 133 | TEST_CASE("Eigensolver of symmetric real matrix [1000x1000]", "[eigs_sym]") 134 | { 135 | std::srand(123); 136 | 137 | const Matrix A = gen_dense_data(1000); 138 | int k = 20; 139 | int m = 50; 140 | 141 | run_test_sets(A, k, m); 142 | } 143 | 144 | TEST_CASE("Eigensolver of sparse symmetric real matrix [10x10]", "[eigs_sym]") 145 | { 146 | std::srand(123); 147 | 148 | // Eigen solver only uses the lower triangle 149 | const SpMatrix A = gen_sparse_data(10, 0.5); 150 | int k = 3; 151 | int m = 6; 152 | 153 | run_test_sets(A, k, m); 154 | } 155 | 156 | TEST_CASE("Eigensolver of sparse symmetric real matrix [100x100]", "[eigs_sym]") 157 | { 158 | std::srand(123); 159 | 160 | // Eigen solver only uses the lower triangle 161 | const SpMatrix A = gen_sparse_data(100, 0.5); 162 | int k = 10; 163 | int m = 20; 164 | 165 | run_test_sets(A, k, m); 166 | } 167 | 168 | TEST_CASE("Eigensolver of sparse symmetric real matrix [1000x1000]", "[eigs_sym]") 169 | { 170 | std::srand(123); 171 | 172 | // Eigen solver only uses the lower triangle 173 | const SpMatrix A = gen_sparse_data(1000, 0.5); 174 | int k = 20; 175 | int m = 50; 176 | 177 | run_test_sets(A, k, m); 178 | } 179 | -------------------------------------------------------------------------------- /Libs/spectra/test/SymEigsShift.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include // Requires C++ 11 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace Spectra; 11 | 12 | #define CATCH_CONFIG_MAIN 13 | #include "catch.hpp" 14 | 15 | typedef Eigen::MatrixXd Matrix; 16 | typedef Eigen::VectorXd Vector; 17 | typedef Eigen::SparseMatrix SpMatrix; 18 | 19 | // Traits to obtain operation type from matrix type 20 | template 21 | struct OpTypeTrait 22 | { 23 | typedef DenseSymShiftSolve OpType; 24 | }; 25 | 26 | template <> 27 | struct OpTypeTrait 28 | { 29 | typedef SparseSymShiftSolve OpType; 30 | }; 31 | 32 | // Generate data for testing 33 | Matrix gen_dense_data(int n) 34 | { 35 | const Matrix mat = Eigen::MatrixXd::Random(n, n); 36 | return mat + mat.transpose(); 37 | } 38 | 39 | SpMatrix gen_sparse_data(int n, double prob = 0.5) 40 | { 41 | // Eigen solver only uses the lower triangle of mat, 42 | // so we don't need to make mat symmetric here. 43 | SpMatrix mat(n, n); 44 | std::default_random_engine gen; 45 | gen.seed(0); 46 | std::uniform_real_distribution distr(0.0, 1.0); 47 | for(int i = 0; i < n; i++) 48 | { 49 | for(int j = 0; j < n; j++) 50 | { 51 | if(distr(gen) < prob) 52 | mat.insert(i, j) = distr(gen) - 0.5; 53 | } 54 | } 55 | return mat; 56 | } 57 | 58 | 59 | 60 | template 61 | void run_test(const MatType& mat, int k, int m, double sigma, bool allow_fail = false) 62 | { 63 | typename OpTypeTrait::OpType op(mat); 64 | SymEigsShiftSolver::OpType> 65 | eigs(&op, k, m, sigma); 66 | eigs.init(); 67 | int nconv = eigs.compute(150); // maxit = 150 to reduce running time for failed cases 68 | int niter = eigs.num_iterations(); 69 | int nops = eigs.num_operations(); 70 | 71 | if(allow_fail) 72 | { 73 | if( eigs.info() != SUCCESSFUL ) 74 | { 75 | WARN( "FAILED on this test" ); 76 | std::cout << "nconv = " << nconv << std::endl; 77 | std::cout << "niter = " << niter << std::endl; 78 | std::cout << "nops = " << nops << std::endl; 79 | return; 80 | } 81 | } else { 82 | INFO( "nconv = " << nconv ); 83 | INFO( "niter = " << niter ); 84 | INFO( "nops = " << nops ); 85 | REQUIRE( eigs.info() == SUCCESSFUL ); 86 | } 87 | 88 | Vector evals = eigs.eigenvalues(); 89 | Matrix evecs = eigs.eigenvectors(); 90 | 91 | Matrix resid = mat.template selfadjointView() * evecs - evecs * evals.asDiagonal(); 92 | const double err = resid.array().abs().maxCoeff(); 93 | 94 | INFO( "||AU - UD||_inf = " << err ); 95 | REQUIRE( err == Approx(0.0).margin(1e-9) ); 96 | } 97 | 98 | template 99 | void run_test_sets(const MatType& mat, int k, int m, double sigma) 100 | { 101 | SECTION( "Largest Magnitude" ) 102 | { 103 | run_test(mat, k, m, sigma); 104 | } 105 | SECTION( "Largest Value" ) 106 | { 107 | run_test(mat, k, m, sigma); 108 | } 109 | SECTION( "Smallest Magnitude" ) 110 | { 111 | run_test(mat, k, m, sigma, true); 112 | } 113 | SECTION( "Smallest Value" ) 114 | { 115 | run_test(mat, k, m, sigma); 116 | } 117 | SECTION( "Both Ends" ) 118 | { 119 | run_test(mat, k, m, sigma); 120 | } 121 | } 122 | 123 | TEST_CASE("Eigensolver of symmetric real matrix [10x10]", "[eigs_sym]") 124 | { 125 | std::srand(123); 126 | 127 | const Matrix A = gen_dense_data(10); 128 | int k = 3; 129 | int m = 6; 130 | double sigma = 1.0; 131 | 132 | run_test_sets(A, k, m, sigma); 133 | } 134 | 135 | TEST_CASE("Eigensolver of symmetric real matrix [100x100]", "[eigs_sym]") 136 | { 137 | std::srand(123); 138 | 139 | const Matrix A = gen_dense_data(100); 140 | int k = 10; 141 | int m = 20; 142 | double sigma = 10.0; 143 | 144 | run_test_sets(A, k, m, sigma); 145 | } 146 | 147 | TEST_CASE("Eigensolver of symmetric real matrix [1000x1000]", "[eigs_sym]") 148 | { 149 | std::srand(123); 150 | 151 | const Matrix A = gen_dense_data(1000); 152 | int k = 20; 153 | int m = 50; 154 | double sigma = 100.0; 155 | 156 | run_test_sets(A, k, m, sigma); 157 | } 158 | 159 | TEST_CASE("Eigensolver of sparse symmetric real matrix [10x10]", "[eigs_sym]") 160 | { 161 | std::srand(123); 162 | 163 | // Eigen solver only uses the lower triangle 164 | const SpMatrix A = gen_sparse_data(10, 0.5); 165 | int k = 3; 166 | int m = 6; 167 | double sigma = 1.0; 168 | 169 | run_test_sets(A, k, m, sigma); 170 | } 171 | 172 | TEST_CASE("Eigensolver of sparse symmetric real matrix [100x100]", "[eigs_sym]") 173 | { 174 | std::srand(123); 175 | 176 | // Eigen solver only uses the lower triangle 177 | const SpMatrix A = gen_sparse_data(100, 0.5); 178 | int k = 10; 179 | int m = 20; 180 | double sigma = 10.0; 181 | 182 | run_test_sets(A, k, m, sigma); 183 | } 184 | 185 | TEST_CASE("Eigensolver of sparse symmetric real matrix [1000x1000]", "[eigs_sym]") 186 | { 187 | std::srand(123); 188 | 189 | // Eigen solver only uses the lower triangle 190 | const SpMatrix A = gen_sparse_data(1000, 0.5); 191 | int k = 20; 192 | int m = 50; 193 | double sigma = 100.0; 194 | 195 | run_test_sets(A, k, m, sigma); 196 | } 197 | -------------------------------------------------------------------------------- /Libs/spectra/test/SymGEigsRegInv.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include // Requires C++ 11 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace Spectra; 11 | 12 | #define CATCH_CONFIG_MAIN 13 | #include "catch.hpp" 14 | 15 | typedef Eigen::MatrixXd Matrix; 16 | typedef Eigen::VectorXd Vector; 17 | typedef Eigen::SparseMatrix SpMatrix; 18 | 19 | // Generate random sparse matrix 20 | SpMatrix sprand(int size, double prob = 0.5) 21 | { 22 | SpMatrix mat(size, size); 23 | std::default_random_engine gen; 24 | gen.seed(0); 25 | std::uniform_real_distribution distr(0.0, 1.0); 26 | for(int i = 0; i < size; i++) 27 | { 28 | for(int j = 0; j < size; j++) 29 | { 30 | if(distr(gen) < prob) 31 | mat.insert(i, j) = distr(gen) - 0.5; 32 | } 33 | } 34 | return mat; 35 | } 36 | 37 | void gen_sparse_data(int n, SpMatrix& A, SpMatrix& B) 38 | { 39 | // Eigen solver only uses the lower triangle of A, 40 | // so we don't need to make A symmetric here. 41 | A = sprand(n, 0.1); 42 | B = A.transpose() * A; 43 | // To make sure B is positive definite 44 | for(int i = 0; i < n; i++) 45 | B.coeffRef(i, i) += 0.1; 46 | } 47 | 48 | 49 | 50 | template 51 | void run_test(const SpMatrix& A, const SpMatrix& B, int k, int m, bool allow_fail = false) 52 | { 53 | typedef SparseSymMatProd OpType; 54 | typedef SparseRegularInverse BOpType; 55 | OpType op(A); 56 | BOpType Bop(B); 57 | SymGEigsSolver eigs(&op, &Bop, k, m); 58 | eigs.init(); 59 | int nconv = eigs.compute(100); // maxit = 100 to reduce running time for failed cases 60 | int niter = eigs.num_iterations(); 61 | int nops = eigs.num_operations(); 62 | 63 | if(allow_fail) 64 | { 65 | if( eigs.info() != SUCCESSFUL ) 66 | { 67 | WARN( "FAILED on this test" ); 68 | std::cout << "nconv = " << nconv << std::endl; 69 | std::cout << "niter = " << niter << std::endl; 70 | std::cout << "nops = " << nops << std::endl; 71 | return; 72 | } 73 | } else { 74 | INFO( "nconv = " << nconv ); 75 | INFO( "niter = " << niter ); 76 | INFO( "nops = " << nops ); 77 | REQUIRE( eigs.info() == SUCCESSFUL ); 78 | } 79 | 80 | Vector evals = eigs.eigenvalues(); 81 | Matrix evecs = eigs.eigenvectors(); 82 | 83 | Matrix resid = A.template selfadjointView() * evecs - 84 | B.template selfadjointView() * evecs * evals.asDiagonal(); 85 | const double err = resid.array().abs().maxCoeff(); 86 | 87 | INFO( "||AU - BUD||_inf = " << err ); 88 | REQUIRE( err == Approx(0.0).margin(1e-9) ); 89 | } 90 | 91 | void run_test_sets(const SpMatrix& A, const SpMatrix& B, int k, int m) 92 | { 93 | SECTION( "Largest Magnitude" ) 94 | { 95 | run_test(A, B, k, m); 96 | } 97 | SECTION( "Largest Value" ) 98 | { 99 | run_test(A, B, k, m); 100 | } 101 | SECTION( "Smallest Magnitude" ) 102 | { 103 | run_test(A, B, k, m, true); 104 | } 105 | SECTION( "Smallest Value" ) 106 | { 107 | run_test(A, B, k, m); 108 | } 109 | SECTION( "Both Ends" ) 110 | { 111 | run_test(A, B, k, m); 112 | } 113 | } 114 | 115 | TEST_CASE("Generalized eigensolver of sparse symmetric real matrix [10x10]", "[geigs_sym]") 116 | { 117 | std::srand(123); 118 | 119 | // Eigen solver only uses the lower triangle 120 | SpMatrix A, B; 121 | gen_sparse_data(10, A, B); 122 | int k = 3; 123 | int m = 6; 124 | 125 | run_test_sets(A, B, k, m); 126 | } 127 | 128 | TEST_CASE("Generalized eigensolver of sparse symmetric real matrix [100x100]", "[geigs_sym]") 129 | { 130 | std::srand(123); 131 | 132 | // Eigen solver only uses the lower triangle 133 | SpMatrix A, B; 134 | gen_sparse_data(100, A, B); 135 | int k = 10; 136 | int m = 20; 137 | 138 | run_test_sets(A, B, k, m); 139 | } 140 | 141 | // Too time-consuming 142 | /* 143 | TEST_CASE("Generalized eigensolver of sparse symmetric real matrix [1000x1000]", "[geigs_sym]") 144 | { 145 | std::srand(123); 146 | 147 | // Eigen solver only uses the lower triangle 148 | SpMatrix A, B; 149 | gen_sparse_data(1000, A, B); 150 | int k = 20; 151 | int m = 50; 152 | 153 | run_test_sets(A, B, k, m); 154 | } 155 | */ 156 | -------------------------------------------------------------------------------- /Projects/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(ProgrammableDigitalWeaves) -------------------------------------------------------------------------------- /Projects/ProgrammableDigitalWeaves/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(DEPS tbb tbbmalloc tbbmalloc_proxy Eigen3::Eigen igl::core igl::opengl_glfw igl::opengl_glfw_imgui igl::png gmp mpfr mkl_intel_lp64 mkl_sequential mkl_core) 2 | 3 | file(GLOB HEADERS "*.h" "include/*.hpp" ) 4 | file(GLOB SOURCES "*.c" "src/*.cpp" ) 5 | 6 | add_executable(ProgrammableDigitalWeaves ${HEADERS} ${SOURCES}) 7 | target_include_directories(ProgrammableDigitalWeaves PUBLIC ../../Libs/spectra/include) 8 | target_link_libraries(ProgrammableDigitalWeaves PUBLIC ${DEPS}) 9 | -------------------------------------------------------------------------------- /Projects/ProgrammableDigitalWeaves/DrawPattern.py: -------------------------------------------------------------------------------- 1 | # import tkinter as tk 2 | # from tkinter import font 3 | 4 | # def draw(event): 5 | # x, y = event[0], event[1] 6 | # if canvas.old_coords: 7 | # x1, y1 = canvas.old_coords 8 | # canvas.create_line(x, y, x1, y1) 9 | # canvas.old_coords = x, y 10 | 11 | # def draw_line(event): 12 | 13 | # if str(event.type) == 'ButtonPress': 14 | # canvas.old_coords = event[0], event[1] 15 | 16 | # elif str(event.type) == 'ButtonRelease': 17 | # x, y = event[0], event[1] 18 | # x1, y1 = canvas.old_coords 19 | # canvas.create_line(x, y, x1, y1) 20 | 21 | # def reset_coords(event): 22 | # canvas.old_coords = None 23 | 24 | # def exitApp(event): 25 | # window.destroy() 26 | 27 | # window = tk.Tk() 28 | # window.title("WuKong GUI") 29 | 30 | # canvas = tk.Canvas(window, width=1024, height=1024) 31 | # canvas.pack() 32 | # canvas.old_coords = None 33 | 34 | # selected = tk.IntVar() 35 | 36 | # line_button = tk.Radiobutton(window, text='Line', value=1, variable=selected) 37 | # Beizier_button = tk.Radiobutton(window, text='Bizier Curve', value=2, variable=selected) 38 | # line_button.pack(anchor=tk.W) 39 | # Beizier_button.pack(anchor=tk.W) 40 | 41 | # ### BIND KEY FUNCTION HERE 42 | # window.bind('', draw_line) 43 | # window.bind('', draw_line) 44 | # window.bind('', exitApp) 45 | 46 | 47 | # window.mainloop() 48 | 49 | 50 | from typing_extensions import ParamSpecArgs 51 | import numpy as np 52 | import matplotlib.pyplot as plt 53 | import math 54 | 55 | def bezier(p0,p1,p2): 56 | ti=np.roots([np.linalg.norm(p2-p0)**2, 57 | 3*np.dot(p2-p0,p0-p1), 58 | np.dot(3*p0-2*p1-p2,p0-p1), 59 | -np.linalg.norm(p0-p1)**2]) 60 | for i in ti: 61 | if isinstance(i,complex) and i.imag<0.01: 62 | i=i.real 63 | if i>0 and i<1: 64 | ti=i 65 | break 66 | if(isinstance(ti,float)==False): 67 | raise Exception(f"Error compute t_i:{ti}") 68 | 69 | b1=(p1-(1-ti)**2*p0-ti**2*p2)/(2*(1-ti)*ti) 70 | 71 | def _bezier(t): 72 | if isinstance(t,float): 73 | return (1-t)**2 * p0+2*(1-t)*t*b1 + t**2*p2 74 | elif isinstance(t,np.ndarray): 75 | ans = np.zeros((len(t),len(p0))) 76 | for index,i in enumerate(t): 77 | ans[index,:]= _bezier(i) 78 | return ans 79 | else: 80 | raise Exception("type error") 81 | 82 | return _bezier 83 | 84 | 85 | def bezier_interpolate(pts,circle=False): 86 | if isinstance(pts,np.ndarray)==False: 87 | pts=np.array(pts) 88 | 89 | n=len(pts) 90 | bezier_functions=[] 91 | for i in range(n-2): 92 | bezier_functions.append(bezier(pts[i],pts[i+1],pts[i+2])) 93 | if circle: 94 | bezier_functions.append(bezier(pts[-2],pts[-1],pts[1])) 95 | 96 | def scaled_bezier(func): 97 | def _scaled_bezier(theta): 98 | return func(theta/math.pi) 99 | return _scaled_bezier 100 | for i,func in enumerate(bezier_functions): 101 | bezier_functions[i]= scaled_bezier(func) 102 | 103 | def blend(f1,f2): 104 | def _blend(theta): 105 | if isinstance(theta,float): 106 | return math.cos(theta)**2 * f1(theta+math.pi/2) + math.sin(theta)**2 * f2(theta) 107 | elif isinstance(theta,int): 108 | return _blend(float(theta)) 109 | elif isinstance(theta,np.ndarray): 110 | return np.array(tuple(_blend(_theta) for _theta in theta)) 111 | else: 112 | raise Exception('type error.') 113 | return _blend 114 | 115 | C_functions = [] 116 | if circle: 117 | C_functions.append(blend(bezier_functions[-1],bezier_functions[0])) 118 | else: 119 | C_functions.append(bezier_functions[0]) 120 | for i in range(0,len(bezier_functions)-1): 121 | C_functions.append( 122 | blend(bezier_functions[i],bezier_functions[i+1]) 123 | ) # theta in [0,pi/2] 124 | if not circle: 125 | def _f_last(theta): 126 | return bezier_functions[-1](theta+math.pi/2) 127 | C_functions.append(_f_last) 128 | 129 | return C_functions 130 | 131 | 132 | # pts = np.array([[0,0],[1,1],[2,0],[3,1],[4,0],[5,1]]) 133 | # C_list = bezier_interpolate(pts) 134 | # plot_pts = np.concatenate(tuple(C(np.linspace(0,math.pi/2,10)) for C in C_list)) 135 | 136 | # plt.scatter(pts[:,0],pts[:,1]) 137 | # plt.plot(plot_pts[:,0],plot_pts[:,1]) 138 | # plt.show() 139 | 140 | 141 | # pts = np.array([[1,0],[0,1],[-1,0],[0,-1],[1,0]]) 142 | # C_list = bezier_interpolate(pts,circle=True) 143 | # plot_pts = np.concatenate(tuple(C(np.linspace(0,math.pi/2,10)) for C in C_list)) 144 | 145 | # plt.axes().set_aspect(1) 146 | # plt.scatter(pts[:,0],pts[:,1]) 147 | # plt.plot(plot_pts[:,0],plot_pts[:,1]) 148 | # plt.show() 149 | 150 | pts = np.array([[1,0],[0,1],[-1,0],[0,-1],[1,0]]) 151 | C_list = bezier_interpolate(pts,circle=True) 152 | plot_pts = np.concatenate(tuple(C(np.linspace(0,math.pi/2,10)) for C in C_list)) 153 | 154 | plt.axes().set_aspect(1) 155 | plt.scatter(pts[:,0],pts[:,1]) 156 | plt.plot(plot_pts[:,0],plot_pts[:,1]) 157 | plt.show() 158 | 159 | # pts = np.array([[1,0],[0,1],[-1,0],[0,-1],[1,-2],[0,-3]]) 160 | # C_list = bezier_interpolate(pts) 161 | # plot_pts = np.concatenate(tuple(C(np.linspace(0,math.pi/2,10)) for C in C_list)) 162 | 163 | # plt.axes().set_aspect(1) 164 | # plt.scatter(pts[:,0],pts[:,1]) 165 | # plt.plot(plot_pts[:,0],plot_pts[:,1]) 166 | # plt.show() 167 | 168 | -------------------------------------------------------------------------------- /Projects/ProgrammableDigitalWeaves/include/GCodeGenerator.h: -------------------------------------------------------------------------------- 1 | #ifndef GCODE_GENERATOR_H 2 | #define GCODE_GENERATOR_H 3 | 4 | #include 5 | 6 | #include "EoLRodSim.h" 7 | template 8 | class EoLRodSim; 9 | 10 | enum PrinterType 11 | { 12 | PrusaI3, UltiMaker 13 | }; 14 | 15 | enum ExtrusionMode 16 | { 17 | Absolute, Relative 18 | }; 19 | 20 | template 21 | class GCodeGenerator 22 | { 23 | public: 24 | using TV = Vector; 25 | using TV3 = Vector; 26 | using TV2 = Vector; 27 | using Range = Vector; 28 | using Offset = Vector; 29 | 30 | // generic printing stuff 31 | private: 32 | const EoLRodSim& sim; 33 | std::string gcode_file; 34 | T nozzle_diameter; 35 | T filament_diameter; 36 | T extruder_temperature; 37 | T bed_temperature; 38 | T first_layer_height; 39 | T layer_height; 40 | T feed_rate_move; 41 | T feed_rate_print; 42 | PrinterType printer; 43 | ExtrusionMode extrusion_mode; 44 | 45 | T current_E; 46 | TV current_position = TV::Zero(); 47 | 48 | std::ofstream gcode; 49 | 50 | // specific parameters 51 | private: 52 | T tunnel_height = 0.2; //0.2mm 53 | public: 54 | GCodeGenerator(const EoLRodSim& _sim) : sim(_sim), gcode_file("./rod.gcode") {} 55 | GCodeGenerator(const EoLRodSim& _sim, const std::string& filename); 56 | ~GCodeGenerator() {} 57 | 58 | public: 59 | 60 | void buildGridScene(int n_row, int n_col, int type); 61 | 62 | void generateShelterScene(); 63 | 64 | void generateGCodeShelter(); 65 | void generateGCodeSingleStrand(); 66 | 67 | void generateGCodeClosedGrid(int n_row, int n_col, int type); 68 | 69 | void circlePatchGCode(int n_row, int n_col, int type, bool add_bar); 70 | 71 | void slidingBlocksGCode(int n_row, int n_col, int type, bool add_bar); 72 | 73 | void activeTexticleGCode(bool fused = false); 74 | void activeTexticleGCode2(bool fused = false); 75 | 76 | void crossingTest(); 77 | 78 | void generateGCodeFromRodsCurveGripperHardCoded(); 79 | 80 | void generateGCodeFromRodsGridGripperHardCoded(); 81 | void generateGCodeFromRodsFixedGridGripperHardCoded(); 82 | 83 | void generateGCodeFromRodsShelterHardCoded(); 84 | void generateGCodeFromRodsGridHardCoded(int n_row, int n_col, int type); 85 | 86 | void generateGCodeFromRodsNoTunnel(); 87 | 88 | void writeCircle(const TV& center, T r, const TV& start, const Vector& range, 89 | const std::vector& lifting_points, int sub_div, T rod_diameter); 90 | 91 | void addRecBar(const TV& border_a, const TV& border_b, T width, T rod_diameter); 92 | 93 | void writeLine(const TV& from, const TV& to, T rod_radius, T speed = 800.0); 94 | void moveTo(const TV& to, T speed = 2000.0, bool do_retract = true); 95 | 96 | void addSingleTunnel(const TV& from, const TV& to, T height, T rod_diameter = 0.2); 97 | void addSingleTunnel(const TV& left, const TV& center, const TV& right, T rod_diameter); 98 | 99 | void addSingleTunnelOnCrossing(int crossing_id, const TV3& heights, 100 | int direction, std::function scaleAndShift); 101 | 102 | void addSingleTunnelOnCrossingWithFixedRange(int crossing_id, const TV3& heights, 103 | int direction, std::function scaleAndShift, 104 | const Range& range, T extend_right = 0.3, T speed_first_half = 100, T speed_second_half = 300); 105 | 106 | void addTunnelsAlongRod(int rod_idx, const TV3& heights, 107 | std::function scaleAndShift, int cross_n_seg, 108 | T extend_right = 0.3, T speed_first_half = 100, T speed_second_half = 300); 109 | 110 | void generateCodeSingleRod(int rod_idx, std::function scaleAndShift, 111 | bool is_first_layer, 112 | T bd_height = 0.3, T inner_height = 0.3, T buffer_percentage = 0.3, T less = false, T extend = false); 113 | 114 | void generateCodeSingleRodMoveUpNozzle(int rod_idx, std::function scaleAndShift, 115 | bool is_first_layer, 116 | T bd_height = 0.3, T inner_height = 0.3, T buffer_percentage = 0.3, T less = false, T extend = false); 117 | private: 118 | 119 | T computeExtrusionAmount() const; 120 | T crossSectionAreaFromRod(T rod_radius) const; 121 | 122 | T extrusionWidth() const; 123 | T crossSectionArea(bool is_first_layer) const; 124 | void retract(T E); 125 | void extrude(T E); 126 | void writeHeader(); 127 | void writeFooter(); 128 | 129 | 130 | }; 131 | 132 | 133 | #endif -------------------------------------------------------------------------------- /Projects/ProgrammableDigitalWeaves/include/Homogenization.h: -------------------------------------------------------------------------------- 1 | #ifndef HOMOGENIZATION_H 2 | #define HOMOGENIZATION_H 3 | 4 | #include "EoLRodSim.h" 5 | 6 | template 7 | class EoLRodSim; 8 | 9 | template 10 | class Homogenization 11 | { 12 | using TVDOF = Vector; 13 | using DOFStack = Matrix; 14 | using IV2 = Vector; 15 | using TV2 = Vector; 16 | using TV = Vector; 17 | using TM = Matrix; 18 | using TM2 = Matrix; 19 | using CDoF2D = Vector; 20 | using CHessian2D = Matrix; 21 | using ComplianceTensor = Matrix; 22 | using TVEntry = Vector; 23 | 24 | using ComplianceTensorFull = Matrix; 25 | using TVEntryFull = Vector; 26 | 27 | using VectorXT = Matrix; 28 | using Offset = Vector; 29 | 30 | public: 31 | EoLRodSim& sim; 32 | 33 | const VectorXT& rest_states = sim.rest_states; 34 | const VectorXT& deformed_states = sim.deformed_states; 35 | 36 | T s1 = 1.01; 37 | T s2 = 1.0; 38 | 39 | bool biaxial = false; 40 | 41 | public: 42 | Homogenization(EoLRodSim& eol_sim) : sim(eol_sim) {} 43 | ~Homogenization() {} 44 | 45 | void initialize(); 46 | void testOneSample(); 47 | 48 | void marcoMaterialParametersFitting(); 49 | void materialParametersFromUniaxialStrain(T theta, T s, TV2& E_nu); 50 | void computeYoungsModulusPoissonRatioBatch(); 51 | 52 | void computeMacroStressStrain(TM2& stress_marco, TM2& strain_marco); 53 | 54 | }; 55 | 56 | #endif -------------------------------------------------------------------------------- /Projects/ProgrammableDigitalWeaves/include/HybridC2Curve.h: -------------------------------------------------------------------------------- 1 | #ifndef HYBRID_C2_CURVE_H 2 | #define HYBRID_C2_CURVE_H 3 | 4 | 5 | // reference 6 | // view-source:http://www.cemyuksel.com/research/interpolating_splines/curves.html 7 | // A Class of C2 Interpolating Splines ACM Transactions on Graphics, 39, 5, 2020 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "VecMatDef.h" 16 | 17 | template 18 | struct CurveData 19 | { 20 | CurveData(Vector c, Vector a1, Vector a2, Vector bound) 21 | : center(c), axis1(a1), axis2(a2), limits(bound) {} 22 | Vector center, axis1, axis2; 23 | Vector limits; 24 | }; 25 | 26 | template 27 | class HybridC2Curve 28 | { 29 | public: 30 | using TV = Vector; 31 | 32 | int sub_div; 33 | 34 | std::vector data_points; 35 | std::vector points_on_curve; 36 | std::vector*> curve_data; 37 | 38 | public: 39 | HybridC2Curve(int _sub_div) : sub_div(_sub_div) {} 40 | HybridC2Curve() : sub_div(64) {} 41 | ~HybridC2Curve() 42 | { 43 | for(auto data : curve_data) 44 | { 45 | delete data; 46 | } 47 | } 48 | 49 | void getLinearSegments(std::vector& points) 50 | { 51 | 52 | if (data_points.size() <= 2) 53 | points = data_points; 54 | else 55 | { 56 | points = points_on_curve; 57 | } 58 | } 59 | 60 | void constructIndividualCurves(); 61 | 62 | void sampleCurves(std::vector& points); 63 | 64 | void normalizeDataPoints(); 65 | 66 | void getPosOnCurve(int curve_idx, T t, TV& pos, bool interpolate); 67 | 68 | void derivativeTestdCdt(); 69 | 70 | void getPosOnCurveWithDerivatives(int curve_idx, T t, 71 | TV& ci, TV& dci, TV& ddci, 72 | bool compute_dci, bool compute_ddci, 73 | bool interpolate); 74 | 75 | // adapted from line 1143 curvePos 76 | void F(T t, int idx, TV& pos) 77 | { 78 | T tt = curve_data[idx]->limits[0] + t * (curve_data[idx]->limits[1] - curve_data[idx]->limits[0]); 79 | pos = curve_data[idx]->center + 80 | curve_data[idx]->axis1 * std::cos(tt) + 81 | curve_data[idx]->axis2 * std::sin(tt); 82 | } 83 | 84 | 85 | void dF(T t, int idx, TV& dpos) 86 | { 87 | T theta = curve_data[idx]->limits[0] + t * (curve_data[idx]->limits[1] - curve_data[idx]->limits[0]); 88 | T dtheta = (curve_data[idx]->limits[1] - curve_data[idx]->limits[0]); 89 | 90 | dpos = (-std::sin(theta) * curve_data[idx]->axis1 + 91 | curve_data[idx]->axis2 * std::cos(theta)) * dtheta; 92 | } 93 | 94 | void ddF(T t, int idx, TV& ddpos) 95 | { 96 | T theta = curve_data[idx]->limits[0] + t * (curve_data[idx]->limits[1] - curve_data[idx]->limits[0]); 97 | T dtheta = (curve_data[idx]->limits[1] - curve_data[idx]->limits[0]); 98 | 99 | ddpos = -dtheta * dtheta * (std::cos(theta) * curve_data[idx]->axis1 + 100 | curve_data[idx]->axis2 * std::sin(theta)); 101 | } 102 | 103 | private: 104 | 105 | // adapted from source code 106 | void circularInterpolation(int i, TV& center, TV& axis1, TV& axis2, Vector& limits); 107 | 108 | // adapted from source code 109 | void ellipticalInterpolation(int i, TV& center, TV& axis1, TV& axis2, Vector& limits); 110 | 111 | // adapted from source code 112 | void hybridInterpolation(int i, TV& center, TV& axis1, TV& axis2, Vector& limits); 113 | 114 | }; 115 | 116 | 117 | 118 | #endif -------------------------------------------------------------------------------- /Projects/ProgrammableDigitalWeaves/include/IO.h: -------------------------------------------------------------------------------- 1 | #ifndef IO_H 2 | #define IO_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "VecMatDef.h" 10 | 11 | template 12 | void extrudeMeshToObj(const std::vector>& points, 13 | const std::vector>& edges, 14 | const std::string& filename, T z) 15 | { 16 | using TV = Vector; 17 | 18 | std::ofstream out(filename); 19 | for (auto & point : points) 20 | { 21 | out << "v " << point[0] << " " << point[1] << " 0" << std::endl; 22 | } 23 | for (auto & point : points) 24 | { 25 | out << "v " << point[0] << " " << point[1] << " " << z << std::endl; 26 | } 27 | for (auto& edge: edges) 28 | { 29 | int vi = edge[0], vj = edge[1], vk = edge[0] + points.size(), vl = edge[1] + points.size(); 30 | vi++; vj++; vk++; vl++; 31 | out << "f " << vi << " " << vj << " " << vl << std::endl; 32 | out << "f " << vl << " " << vk << " " << vi << std::endl; 33 | } 34 | out.close(); 35 | } 36 | 37 | #endif -------------------------------------------------------------------------------- /Projects/ProgrammableDigitalWeaves/include/RestState.h: -------------------------------------------------------------------------------- 1 | #ifndef CURVATURE_FUNCTION_H 2 | #define CURVATURE_FUNCTION_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "HybridC2Curve.h" 10 | // #include "VecMatDef.h" 11 | 12 | template 13 | class RestState 14 | { 15 | public: 16 | using TV = Vector; 17 | using TV2 = Vector; 18 | 19 | Vector starting_point, ending_point; 20 | 21 | public: 22 | RestState(Vector q0, Vector q1) 23 | : starting_point(q0), ending_point(q1) {} 24 | RestState() 25 | : starting_point(Vector::Zero()), ending_point(Vector::Ones()) {} 26 | ~RestState() {} 27 | 28 | virtual T value(T u) {return 0;} 29 | virtual void gradient(T u, T& dedu) { dedu = 0; } 30 | virtual void hessian(T u, T& de2du2) { de2du2 = 0; } 31 | 32 | virtual void getMaterialPos(T u, TV& X, TV& dXdu, TV& d2Xdu2, bool g, bool h) 33 | { 34 | X = starting_point.template segment(0) + 35 | (u - starting_point[dim]) / (ending_point[dim] - starting_point[dim]) * (ending_point.template segment(0) - starting_point.template segment(0)); 36 | dXdu = (ending_point.template segment(0) - starting_point.template segment(0)) / (ending_point[dim] - starting_point[dim]) ; 37 | d2Xdu2 = TV::Zero(); 38 | } 39 | }; 40 | 41 | template 42 | class LineCurvature : public RestState 43 | { 44 | using TV = Vector; 45 | 46 | public: 47 | LineCurvature(Vector q0, 48 | Vector q1) : RestState(q0, q1) {} 49 | LineCurvature() : RestState(){} 50 | 51 | virtual void getMaterialPos(T u, TV& X, TV& dXdu, TV& d2Xdu2, bool g, bool h); 52 | }; 53 | 54 | template 55 | class DiscreteHybridCurvature : public RestState 56 | { 57 | public: 58 | using TV = Vector; 59 | using TV2 = Vector; 60 | HybridC2Curve* curve; 61 | std::vector data_points_discrete_arc_length; 62 | 63 | public: 64 | DiscreteHybridCurvature() : RestState(){} 65 | 66 | DiscreteHybridCurvature(Vector q0, 67 | Vector q1) : RestState(q0, q1) {} 68 | virtual void getMaterialPos(T u, TV& X, TV& dXdu, TV& d2Xdu2, bool g, bool h); 69 | 70 | void setData(HybridC2Curve* c, const std::vector& v) 71 | { 72 | curve = c; 73 | data_points_discrete_arc_length = v; 74 | } 75 | 76 | }; 77 | 78 | template 79 | class PreBendCurvaure : public RestState 80 | { 81 | T length; 82 | T theta; 83 | public: 84 | PreBendCurvaure(T _length, T _theta) : length(_length), theta(_theta) {} 85 | virtual T value(T u) 86 | { 87 | return theta / length; 88 | } 89 | }; 90 | 91 | template 92 | class CircleCurvature : public RestState 93 | { 94 | private: 95 | T r; 96 | public: 97 | CircleCurvature(T _r) : r(_r) {} 98 | 99 | virtual T value(T u); 100 | virtual void gradient(T u, T& dedu); 101 | virtual void hessian(T u, T& de2du2); 102 | }; 103 | 104 | template 105 | class SineCurvature : public RestState 106 | { 107 | private: 108 | T amp; 109 | T phi; 110 | T period; 111 | 112 | public: 113 | SineCurvature(T _amp, T _phi, T _period) : amp(_amp), phi(_phi), period(_period) {} 114 | 115 | virtual T value(T u); 116 | //-g -> f 117 | virtual void gradient(T u, T& dedu); 118 | // -J -> Hessian 119 | virtual void hessian(T u, T& de2du2); 120 | }; 121 | 122 | #endif -------------------------------------------------------------------------------- /Projects/ProgrammableDigitalWeaves/include/UI.h: -------------------------------------------------------------------------------- 1 | #ifndef UI_H 2 | #define UI_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "VecMatDef.h" 17 | 18 | void appendSphereMesh(Eigen::MatrixXd& V, Eigen::MatrixXi& F, 19 | double scale = 1.0, Vector shift = Vector::Zero()); 20 | 21 | void appendSphereMeshWithColor(Eigen::MatrixXd& V, Eigen::MatrixXi& F, Eigen::MatrixXd& C, 22 | double scale = 1.0, Vector shift = Vector::Zero()); 23 | 24 | void appendTorusMeshWithColor(Eigen::MatrixXd& V, Eigen::MatrixXi& F, Eigen::MatrixXd& C, 25 | double scale = 1.0, 26 | Vector shift = Vector::Zero(), 27 | Eigen::Matrix3d R = Eigen::Matrix3d::Identity()); 28 | 29 | void removeSphereMesh(Eigen::MatrixXd& V, Eigen::MatrixXi& F); 30 | 31 | void removeSphereMeshWithColor(Eigen::MatrixXd& V, Eigen::MatrixXi& F, Eigen::MatrixXd& C); 32 | 33 | template 34 | void appendCylinderMesh(igl::opengl::glfw::Viewer& viewer, 35 | Eigen::MatrixXd& V, Eigen::MatrixXi& F, 36 | std::vector>& points_on_curve, bool backward = false, int n_div = 8) 37 | { 38 | using T = double; 39 | using TV2 = Vector; 40 | using TV = Vector; 41 | using TV3 = Vector; 42 | using IV3 = Vector; 43 | using TM3 = Matrix; 44 | 45 | Eigen::Vector4f eye_n = (viewer.core().view).inverse().col(3); 46 | 47 | if (points_on_curve.size() < 2) 48 | { 49 | if(backward) 50 | { 51 | V.setZero(); 52 | F.setZero(); 53 | return; 54 | } 55 | else 56 | return; 57 | } 58 | 59 | 60 | 61 | int rod_offset_v = n_div * 2; 62 | int rod_offset_f = n_div * 2; 63 | 64 | int n_rods = points_on_curve.size() - 1; 65 | 66 | 67 | V.resize(n_rods * rod_offset_v, 3); 68 | V.setZero(); 69 | F.resize(n_rods * rod_offset_f, 3); 70 | F.setZero(); 71 | 72 | T theta = 2.0 * M_PI / T(n_div); 73 | 74 | T visual_R = 0.01; 75 | Eigen::MatrixXd points(n_div, 3); 76 | // bottom face vertices 77 | for(int i = 0; i < n_div; i++) 78 | points.row(i) = Eigen::Vector3d(visual_R * std::cos(theta * T(i)), 0.0, visual_R*std::sin(theta*T(i))); 79 | 80 | tbb::parallel_for(0, n_rods, [&](int rod_cnt){ 81 | int rov = rod_cnt * rod_offset_v; 82 | int rof = rod_cnt * rod_offset_f; 83 | 84 | TV from2d = points_on_curve[rod_cnt]; 85 | TV to2d = points_on_curve[rod_cnt+1]; 86 | 87 | TV3 from3d, to3d; 88 | 89 | igl::unproject_on_plane(from2d.template segment<2>(0), viewer.core().proj*viewer.core().view, 90 | viewer.core().viewport, eye_n, from3d); 91 | igl::unproject_on_plane(to2d.template segment<2>(0), viewer.core().proj*viewer.core().view, 92 | viewer.core().viewport, eye_n, to3d); 93 | 94 | TV3 axis_world = to3d - from3d; 95 | TV3 axis_local(0, axis_world.norm(), 0); 96 | 97 | TM3 R = Eigen::Quaternion().setFromTwoVectors(axis_world, axis_local).toRotationMatrix(); 98 | for(int i = 0; i < n_div; i++) 99 | { 100 | for(int d = 0; d < 3; d++) 101 | { 102 | V(rov + i, d) = points(i, d); 103 | V(rov + i+n_div, d) = points(i, d); 104 | if (d == 1) 105 | V(rov + i+n_div, d) += axis_world.norm(); 106 | } 107 | 108 | V.row(rov + i) = (V.row(rov + i) * R).transpose() + from3d; 109 | V.row(rov + i + n_div) = (V.row(rov + i + n_div) * R).transpose() + from3d; 110 | 111 | F.row(rof + i*2 ) = IV3(rov + i, rov + i+n_div, rov + (i+1)%(n_div)); 112 | F.row(rof + i*2 + 1) = IV3(rov + (i+1)%(n_div), rov + i+n_div, rov + (i+1)%(n_div) + n_div); 113 | 114 | } 115 | }); 116 | } 117 | 118 | #endif -------------------------------------------------------------------------------- /Projects/ProgrammableDigitalWeaves/include/UnitPatch.h: -------------------------------------------------------------------------------- 1 | #ifndef UNIT_PATCH_H 2 | #define UNIT_PATCH_H 3 | 4 | #include "EoLRodSim.h" 5 | 6 | template 7 | class EoLRodSim; 8 | 9 | template 10 | class UnitPatch 11 | { 12 | public: 13 | using TV = Vector; 14 | using TV2 = Vector; 15 | 16 | using VectorXT = Matrix; 17 | 18 | 19 | using StiffnessMatrix = Eigen::SparseMatrix; 20 | using Entry = Eigen::Triplet; 21 | 22 | using Offset = Vector; 23 | using Range = Vector; 24 | using Mask = Vector; 25 | using Mask2 = Vector; 26 | 27 | 28 | private: 29 | EoLRodSim& sim; 30 | 31 | VectorXT& deformed_states = sim.deformed_states; 32 | 33 | 34 | public: 35 | UnitPatch(EoLRodSim& eol_sim) : sim(eol_sim) {} 36 | ~UnitPatch() {} 37 | 38 | void buildScene(int patch_type); 39 | void buildGridClosed(int sub_div); 40 | 41 | //Mechanisim 42 | void buildFingerScene(int sub_div); 43 | void buildTennisBallWrapperScene(int sub_div); 44 | void buildShoeScene(int sub_div); 45 | 46 | void buildActuationSingleStrandScene(int sub_div); 47 | void buildActuationSingleStrandSceneWithoutCrossing(int sub_div); 48 | 49 | void buildPeriodicCircleScene(int sub_div); 50 | void buildFullCircleScene(int sub_div); 51 | 52 | void buildShelterAcutationScene(int sub_div); 53 | 54 | void buildRandomPatchScene(int sub_div); 55 | 56 | void buildSaddleScene(int sub_div); 57 | void buildGripperScene(int sub_div); 58 | void buildGridLayoutGripper(int sub_div); 59 | void buildSquareCrossJointScene(int sub_div); 60 | 61 | void buildInterlockingSquareScene(int sub_div); 62 | void buildDenseInterlockingSquarePeriodicScene(int sub_div); 63 | 64 | void buildDenseInterlockingSquareScene(int sub_div); 65 | 66 | void buildActiveTextileScene(int sub_div); 67 | 68 | void buildTestSceneJuan(int sub_div); 69 | 70 | void buildDomeScene(int sub_div); 71 | 72 | 73 | void buildTestJoint(int sub_div); 74 | void buildXJointsScene(int sub_div); 75 | void buildXJointsScene2(int sub_div); 76 | 77 | void buildShelterScene(int sub_div); 78 | void buildGridScene2(int sub_div); 79 | 80 | void build3DtestScene(int sub_div); 81 | void buildOneCrossScene(int sub_div); 82 | void buildGridScene(int sub_div); 83 | 84 | void buildOmegaScene(int sub_div); 85 | void buildStraightRodScene(int sub_div); 86 | 87 | private: 88 | 89 | void cropTranslationalUnitByparallelogram(const std::vector>& input_points, 90 | std::vector& output_points, const TV2& top_left, const TV2& top_right, 91 | const TV2& bottom_right, const TV2& bottom_left, std::vector>& edge_pairs, 92 | std::unordered_map>& crossing_tracker, 93 | std::vector>>& boundary_pairs, 94 | std::vector>& boundary_pair_rod_idx); 95 | 96 | void clearSimData(); 97 | 98 | void appendThetaAndJointDoF(std::vector& w_entry, 99 | int& full_dof_cnt, int& dof_cnt); 100 | 101 | void addAStraightRod(const TV& from, const TV& to, 102 | const std::vector& passing_points, 103 | const std::vector& passing_points_id, 104 | int sub_div, 105 | int& full_dof_cnt, int& node_cnt, int& rod_cnt); 106 | 107 | void addCurvedRod(const std::vector& data_points, 108 | const std::vector& passing_points, 109 | const std::vector& passing_points_id, 110 | int sub_div, int& full_dof_cnt, int& node_cnt, int& rod_cnt, bool closed); 111 | 112 | void addStraightYarnCrossNPoints(const TV& from, const TV& to, 113 | const std::vector& passing_points, 114 | const std::vector& passing_points_id, 115 | int sub_div, 116 | std::vector& sub_points, std::vector& node_idx, 117 | std::vector& key_points_location, 118 | int start, bool pbc = false); 119 | 120 | 121 | void markCrossingDoF( 122 | std::vector>& w_entry, 123 | int& dof_cnt); 124 | 125 | void addPoint(const TV& point, int& full_dof_cnt, int& node_cnt); 126 | 127 | void addCrossingPoint(std::vector& existing_nodes, 128 | const TV& point, int& full_dof_cnt, int& node_cnt); 129 | }; 130 | 131 | #endif -------------------------------------------------------------------------------- /Projects/ProgrammableDigitalWeaves/include/VecMatDef.h: -------------------------------------------------------------------------------- 1 | template 2 | using Vector = Eigen::Matrix; 3 | 4 | template 5 | using Matrix = Eigen::Matrix; 6 | 7 | 8 | -------------------------------------------------------------------------------- /Projects/ProgrammableDigitalWeaves/src/Regularizer.cpp: -------------------------------------------------------------------------------- 1 | #include "../include/EoLRodSim.h" 2 | 3 | template 4 | void EoLRodSim::addRegularizingK(std::vector>& entry_K) 5 | { 6 | for (auto& crossing : rod_crossings) 7 | { 8 | if (crossing->is_fixed) 9 | continue; 10 | int node_idx = crossing->node_idx; 11 | std::vector rods_involved = crossing->rods_involved; 12 | for (int rod_idx : rods_involved) 13 | { 14 | Offset offset; 15 | Rods[rod_idx]->getEntry(node_idx, offset); 16 | entry_K.push_back(Entry(offset[dim], offset[dim], ke)); 17 | } 18 | } 19 | } 20 | 21 | template 22 | void EoLRodSim::addRegularizingForce(Eigen::Ref residual) 23 | { 24 | 25 | VectorXT residual_cp = residual; 26 | for (auto& crossing : rod_crossings) 27 | { 28 | if (crossing->is_fixed) 29 | continue; 30 | 31 | int node_idx = crossing->node_idx; 32 | std::vector rods_involved = crossing->rods_involved; 33 | for (int rod_idx : rods_involved) 34 | { 35 | Offset offset; 36 | Rods[rod_idx]->getEntry(node_idx, offset); 37 | T u, U; 38 | Rods[rod_idx]->u(node_idx, u); 39 | Rods[rod_idx]->U(node_idx, U); 40 | 41 | residual[offset[dim]] += -ke * (u-U); 42 | } 43 | } 44 | 45 | 46 | if(print_force_mag) 47 | std::cout << "Eulerian penalty norm: " << (residual - residual_cp).norm() << std::endl; 48 | } 49 | 50 | 51 | template 52 | T EoLRodSim::addRegularizingEnergy() 53 | { 54 | T energy = 0.0; 55 | for (auto& crossing : rod_crossings) 56 | { 57 | if (crossing->is_fixed) 58 | continue; 59 | int node_idx = crossing->node_idx; 60 | std::vector rods_involved = crossing->rods_involved; 61 | for (int rod_idx : rods_involved) 62 | { 63 | Offset offset; 64 | Rods[rod_idx]->getEntry(node_idx, offset); 65 | T u, U; 66 | Rods[rod_idx]->u(node_idx, u); 67 | Rods[rod_idx]->U(node_idx, U); 68 | energy += 0.5 * ke * std::pow(u - U, 2); 69 | } 70 | } 71 | 72 | 73 | return energy; 74 | } 75 | 76 | template class EoLRodSim; 77 | template class EoLRodSim; -------------------------------------------------------------------------------- /Projects/ProgrammableDigitalWeaves/src/RestState.cpp: -------------------------------------------------------------------------------- 1 | #include "../include/RestState.h" 2 | 3 | template 4 | void DiscreteHybridCurvature::getMaterialPos(T u, TV& X, 5 | TV& dXdu, TV& d2Xdu2, bool g, bool h) 6 | { 7 | bool debug = false; 8 | 9 | if (u < 0) 10 | { 11 | X = this->starting_point.template segment(0); 12 | dXdu = TV::Zero(); 13 | d2Xdu2 = TV::Zero(); 14 | return; 15 | } 16 | 17 | if (debug) 18 | std::cout << "getMaterialPos" << std::endl; 19 | T dx = 1.0; 20 | 21 | int left_node = 0; 22 | // find which curve the current points lies on 23 | for (int i = 0; i < curve->data_points.size(); i++) 24 | { 25 | T fraction_data_point = i; 26 | 27 | if( fraction_data_point > u || i == curve->data_points.size() - 1) 28 | { 29 | left_node = i - 1; 30 | break; 31 | } 32 | } 33 | 34 | int curve_id = 0; 35 | 36 | if(left_node > 1) 37 | curve_id = left_node - 1; 38 | 39 | T t = u - T(left_node * dx); 40 | 41 | bool interpolate = false; 42 | if ( left_node != 0 && left_node != curve->data_points.size() - 2) 43 | interpolate = true; 44 | if (curve->data_points.size() == 3 && u >= 1) 45 | { 46 | curve_id += 1; 47 | interpolate= false; 48 | } 49 | 50 | if (debug) 51 | std::cout << "t " << t << " u " << u << " curve idx " << curve_id << " left node " << left_node << " interpolate " << interpolate << std::endl; 52 | 53 | // curve->getPosOnCurve(curve_id, t, X, interpolate); 54 | 55 | if constexpr (dim == 3) 56 | { 57 | TV2 _X, _dXdu, _d2Xdu2; 58 | curve->getPosOnCurveWithDerivatives(curve_id, t, _X, _dXdu, _d2Xdu2, g, h, interpolate); 59 | X = TV(_X[0], _X[1], 0); 60 | dXdu = TV(_dXdu[0], _dXdu[1], 0); 61 | d2Xdu2 = TV(_d2Xdu2[0], _d2Xdu2[1], 0); 62 | } 63 | else if constexpr (dim == 2) 64 | curve->getPosOnCurveWithDerivatives(curve_id, t, X, dXdu, d2Xdu2, g, h, interpolate); 65 | 66 | // T shifted = shift ? t * 0.5 + 0.5 : t * 0.5; 67 | // X *= 0.03; 68 | if (debug) 69 | { 70 | std::cout << u << " " << X.transpose() << std::endl; 71 | std::cout << "getMaterialPos done" << std::endl; 72 | // std::getchar(); 73 | } 74 | } 75 | template 76 | void LineCurvature::getMaterialPos(T u, TV& X, TV& dXdu, TV& d2Xdu2, bool g, bool h) 77 | { 78 | T scale = (this->ending_point[dim] - this->starting_point[dim]); 79 | X = this->starting_point.template segment(0) + 80 | (u - this->starting_point[dim]) / scale 81 | * (this->ending_point.template segment(0) - this->starting_point.template segment(0)); 82 | 83 | dXdu = (this->ending_point.template segment(0) - this->starting_point.template segment(0)) / scale; 84 | d2Xdu2 = TV::Zero(); 85 | } 86 | 87 | template 88 | T CircleCurvature::value(T u) 89 | { 90 | // left most / right most boundary 91 | if (u < 1e-6 || std::abs(u - 2* M_PI * r) < 1e-6) 92 | return 0; 93 | T middle_point_arclength = M_PI * r; 94 | T dis = (u - middle_point_arclength); 95 | if (dis < -1e-6) 96 | return 1.0 / r; 97 | else if(std::abs(dis) < 1e-6) 98 | return 0; 99 | else if(dis > 1e-6) 100 | return -1.0 / r; 101 | else 102 | std::cout << "undefined curvature for u=" << u << std::endl; 103 | } 104 | 105 | template 106 | void CircleCurvature::gradient(T u, T& dedu) 107 | { 108 | dedu = 0; 109 | } 110 | template 111 | void CircleCurvature::hessian(T u, T& de2du2) 112 | { 113 | de2du2 = 0; 114 | } 115 | 116 | template 117 | T SineCurvature::value(T u) 118 | { 119 | T t1 = period*period; 120 | T t3 = period*u; 121 | T t4 = std::sin(t3); 122 | T t5 = amp*amp; 123 | T t7 = std::cos(t3); 124 | T t8 = t7*t7; 125 | T t11 = std::pow(t5*t1*t8+1.0,-0.15E1); 126 | return -amp*t1*t4*t11; 127 | } 128 | 129 | //-g -> f 130 | template 131 | void SineCurvature::gradient(T u, T& dedu) 132 | { 133 | T t1 = period*period; 134 | T t4 = period*u; 135 | T t5 = std::cos(t4); 136 | T t6 = amp*amp; 137 | T t8 = t5*t5; 138 | T t10 = t6*t1*t8+1.0; 139 | T t11 = std::pow(t10,-0.15E1); 140 | T t15 = t1*t1; 141 | T t18 = std::sin(t4); 142 | T t19 = t18*t18; 143 | T t20 = std::pow(t10,-0.25E1); 144 | dedu = amp*t1*period*t5*t11+0.3E1*t6*amp*t15*period*t19*t20*t5; 145 | } 146 | // -J -> Hessian 147 | template 148 | void SineCurvature::hessian(T u, T& de2du2) 149 | { 150 | T t1 = period*period; 151 | T t2 = t1*t1; 152 | T t4 = period*u; 153 | T t5 = std::sin(t4); 154 | T t6 = amp*amp; 155 | T t8 = std::cos(t4); 156 | T t9 = t8*t8; 157 | T t11 = t6*t1*t9+1.0; 158 | T t12 = std::pow(t11,-0.15E1); 159 | T t17 = t6*amp*t2*t1; 160 | T t18 = std::pow(t11,-0.25E1); 161 | T t23 = t6*t6; 162 | T t25 = t2*t2; 163 | T t27 = t5*t5; 164 | T t28 = t27*t5; 165 | T t29 = std::pow(t11,-0.35E1); 166 | de2du2 = amp*t2*t5*t12+0.9E1*t17*t9*t18*t5+0.15E2*t23*amp*t25*t28*t29*t9-0.3E1*t17*t28*t18; 167 | } 168 | 169 | template class SineCurvature; 170 | template class SineCurvature; 171 | 172 | template class CircleCurvature; 173 | template class CircleCurvature; 174 | 175 | template class LineCurvature; 176 | template class LineCurvature; 177 | 178 | template class DiscreteHybridCurvature; 179 | template class DiscreteHybridCurvature; 180 | -------------------------------------------------------------------------------- /Projects/ProgrammableDigitalWeaves/src/Scene.cpp: -------------------------------------------------------------------------------- 1 | #include "../include/EoLRodSim.h" 2 | #include "../include/UnitPatch.h" 3 | 4 | template 5 | void EoLRodSim::buildPeriodicNetwork(Eigen::MatrixXd& V, Eigen::MatrixXi& F, 6 | Eigen::MatrixXd& C, bool show_rest) 7 | { 8 | V.resize(0, 0); F.resize(0, 0); 9 | int n_tile_x = 2, n_tile_y = 2; 10 | int n_faces = 20; 11 | 12 | if constexpr (dim == 3) 13 | { 14 | TV xi = deformed_states.template segment(pbc_pairs_reference[0].first.first[0]); 15 | TV xj = deformed_states.template segment(pbc_pairs_reference[0].first.second[0]); 16 | TV xk = deformed_states.template segment(pbc_pairs_reference[1].first.first[0]); 17 | TV xl = deformed_states.template segment(pbc_pairs_reference[1].first.second[0]); 18 | 19 | TV ref0_shift = xj - xi, ref1_shift = xl - xk; 20 | // for (int i = -n_tile_x + 1; i < n_tile_x; i++) 21 | for (int i = 0; i < n_tile_x; i++) 22 | { 23 | for (int j = 0; j < n_tile_y; j++) 24 | { 25 | Eigen::MatrixXd V_tile; 26 | Eigen::MatrixXi F_tile; 27 | 28 | generateMeshForRendering(V_tile, F_tile, (T(i) * ref0_shift + T(j) * ref1_shift), show_rest); 29 | for (int row = 0; row < F_tile.rows(); row++) 30 | { 31 | for (int d = 0; d < dim; d++) 32 | { 33 | F_tile(row, d) += V.rows(); 34 | } 35 | } 36 | // std::cout << "V " << V_tile.row(0) << " F " << F_tile.row(0) << std::endl; 37 | 38 | int v_row_current = V.rows(); 39 | // std::cout << "V rows " << V.rows() << " V_tile rows " << V_tile.rows() << std::endl; 40 | V.conservativeResize(v_row_current + V_tile.rows(), 3); 41 | V.block(v_row_current, 0, V_tile.rows(), 3) = V_tile; 42 | 43 | int f_row_current = F.rows(); 44 | // std::cout << "F rows " << F.rows() << " F_tile rows " << F_tile.rows() << std::endl; 45 | F.conservativeResize(f_row_current+ F_tile.rows(), 3); 46 | F.block(f_row_current, 0, F_tile.rows(), 3) = F_tile; 47 | } 48 | } 49 | 50 | } 51 | 52 | C.resize(F.rows(), 3); 53 | tbb::parallel_for(0, int(F.rows()), [&](int i){ 54 | // if( i % 2 == 0) 55 | // C.row(i) = Eigen::Vector3d(0, 1, 0); 56 | // else 57 | // C.row(i) = Eigen::Vector3d(0, 0, 1); 58 | C.row(i) = Eigen::Vector3d(0, 0.3, 1); 59 | }); 60 | 61 | // std::cout << V << std::endl; 62 | // std::cout << F << std::endl; 63 | // std::cout << C << std::endl; 64 | } 65 | 66 | template 67 | void EoLRodSim::buildSceneFromUnitPatch(int patch_id) 68 | { 69 | UnitPatch unit_patch(*this); 70 | unit_patch.buildScene(patch_id); 71 | } 72 | 73 | template class EoLRodSim; 74 | template class EoLRodSim; 75 | 76 | //not working for float yet 77 | // template class EoLRodSim; -------------------------------------------------------------------------------- /Projects/ProgrammableDigitalWeaves/src/UI.cpp: -------------------------------------------------------------------------------- 1 | #include "../include/UI.h" 2 | 3 | void appendSphereMesh(Eigen::MatrixXd& V, Eigen::MatrixXi& F, double scale, Vector shift) 4 | { 5 | Eigen::MatrixXd v_sphere; 6 | Eigen::MatrixXi f_sphere; 7 | 8 | igl::readOBJ("../../../Projects/ProgrammableDigitalWeaves/Data/sphere162.obj", v_sphere, f_sphere); 9 | 10 | v_sphere = v_sphere * scale; 11 | 12 | tbb::parallel_for(0, (int)v_sphere.rows(), [&](int row_idx){ 13 | v_sphere.row(row_idx) += shift; 14 | }); 15 | 16 | int n_vtx_prev = V.rows(); 17 | int n_face_prev = F.rows(); 18 | 19 | tbb::parallel_for(0, (int)f_sphere.rows(), [&](int row_idx){ 20 | f_sphere.row(row_idx) += Eigen::Vector3i(n_vtx_prev, n_vtx_prev, n_vtx_prev); 21 | }); 22 | 23 | V.conservativeResize(V.rows() + v_sphere.rows(), 3); 24 | F.conservativeResize(F.rows() + f_sphere.rows(), 3); 25 | 26 | V.block(n_vtx_prev, 0, v_sphere.rows(), 3) = v_sphere; 27 | F.block(n_face_prev, 0, f_sphere.rows(), 3) = f_sphere; 28 | } 29 | 30 | void appendSphereMeshWithColor(Eigen::MatrixXd& V, Eigen::MatrixXi& F, Eigen::MatrixXd& C, 31 | double scale, Vector shift) 32 | { 33 | Eigen::MatrixXd v_sphere; 34 | Eigen::MatrixXi f_sphere; 35 | 36 | igl::readOBJ("../../../Projects/ProgrammableDigitalWeaves/Data/sphere162.obj", v_sphere, f_sphere); 37 | 38 | Eigen::MatrixXd c_sphere(f_sphere.rows(), f_sphere.cols()); 39 | 40 | v_sphere = v_sphere * scale; 41 | 42 | tbb::parallel_for(0, (int)v_sphere.rows(), [&](int row_idx){ 43 | v_sphere.row(row_idx) += shift; 44 | }); 45 | 46 | int n_vtx_prev = V.rows(); 47 | int n_face_prev = F.rows(); 48 | 49 | tbb::parallel_for(0, (int)f_sphere.rows(), [&](int row_idx){ 50 | f_sphere.row(row_idx) += Eigen::Vector3i(n_vtx_prev, n_vtx_prev, n_vtx_prev); 51 | }); 52 | tbb::parallel_for(0, (int)f_sphere.rows(), [&](int row_idx){ 53 | c_sphere.row(row_idx) = Eigen::Vector3d(1, 0, 0); 54 | }); 55 | 56 | V.conservativeResize(V.rows() + v_sphere.rows(), 3); 57 | F.conservativeResize(F.rows() + f_sphere.rows(), 3); 58 | C.conservativeResize(C.rows() + f_sphere.rows(), 3); 59 | 60 | 61 | V.block(n_vtx_prev, 0, v_sphere.rows(), 3) = v_sphere; 62 | F.block(n_face_prev, 0, f_sphere.rows(), 3) = f_sphere; 63 | C.block(n_face_prev, 0, f_sphere.rows(), 3) = c_sphere; 64 | 65 | } 66 | 67 | void appendTorusMeshWithColor(Eigen::MatrixXd& V, Eigen::MatrixXi& F, Eigen::MatrixXd& C, 68 | double scale, Vector shift, Eigen::Matrix3d R) 69 | { 70 | Eigen::MatrixXd v_sphere; 71 | Eigen::MatrixXi f_sphere; 72 | 73 | 74 | // igl::readOBJ("/home/yueli/Documents/ETH/WuKong/Projects/DigitalFabrics/Data/sphere.obj", v_sphere, f_sphere); 75 | igl::readOBJ("../../../Projects/ProgrammableDigitalWeaves/Data/torus.obj", v_sphere, f_sphere); 76 | 77 | Eigen::MatrixXd c_sphere(f_sphere.rows(), f_sphere.cols()); 78 | 79 | v_sphere = v_sphere * scale; 80 | 81 | tbb::parallel_for(0, (int)v_sphere.rows(), [&](int row_idx){ 82 | v_sphere.row(row_idx) = (R * v_sphere.row(row_idx).transpose()) + shift; 83 | }); 84 | 85 | int n_vtx_prev = V.rows(); 86 | int n_face_prev = F.rows(); 87 | 88 | tbb::parallel_for(0, (int)f_sphere.rows(), [&](int row_idx){ 89 | f_sphere.row(row_idx) += Eigen::Vector3i(n_vtx_prev, n_vtx_prev, n_vtx_prev); 90 | }); 91 | 92 | tbb::parallel_for(0, (int)f_sphere.rows(), [&](int row_idx){ 93 | c_sphere.row(row_idx) = Eigen::Vector3d(177, 156, 217) / 255.0; 94 | }); 95 | 96 | V.conservativeResize(V.rows() + v_sphere.rows(), 3); 97 | F.conservativeResize(F.rows() + f_sphere.rows(), 3); 98 | C.conservativeResize(C.rows() + f_sphere.rows(), 3); 99 | 100 | 101 | V.block(n_vtx_prev, 0, v_sphere.rows(), 3) = v_sphere; 102 | F.block(n_face_prev, 0, f_sphere.rows(), 3) = f_sphere; 103 | C.block(n_face_prev, 0, f_sphere.rows(), 3) = c_sphere; 104 | } 105 | 106 | void removeSphereMesh(Eigen::MatrixXd& V, Eigen::MatrixXi& F) 107 | { 108 | if(V.rows() > 0) 109 | { 110 | Eigen::MatrixXd v_sphere; 111 | Eigen::MatrixXi f_sphere; 112 | 113 | igl::readOBJ("../../../Projects/ProgrammableDigitalWeaves/Data/sphere162.obj", v_sphere, f_sphere); 114 | 115 | int n_vtx_prev = V.rows(); 116 | int n_face_prev = F.rows(); 117 | 118 | V.conservativeResize(V.rows() - v_sphere.rows(), 3); 119 | F.conservativeResize(F.rows() - f_sphere.rows(), 3); 120 | } 121 | } 122 | 123 | void removeSphereMeshWithColor(Eigen::MatrixXd& V, Eigen::MatrixXi& F, Eigen::MatrixXd& C) 124 | { 125 | if(V.rows() > 0) 126 | { 127 | Eigen::MatrixXd v_sphere; 128 | Eigen::MatrixXi f_sphere; 129 | 130 | igl::readOBJ("../../../Projects/ProgrammableDigitalWeaves/Data/sphere162.obj", v_sphere, f_sphere); 131 | 132 | int n_vtx_prev = V.rows(); 133 | int n_face_prev = F.rows(); 134 | 135 | V.conservativeResize(V.rows() - v_sphere.rows(), 3); 136 | F.conservativeResize(F.rows() - f_sphere.rows(), 3); 137 | C.conservativeResize(C.rows() - f_sphere.rows(), 3); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Programmable Digital Weaves 2 | ![image](img/teaser.png)\ 3 | [Yue Li](https://liyuesolo.github.io/), [Juan Montes Maestre](https://dl.acm.org/profile/99660986570), [Bernhard Thomaszewski](https://n.ethz.ch/~bthomasz/), [Stelian Coros](https://crl.ethz.ch/people/coros/index.html)\ 4 | IEEE Robotics and Automation Letters (RA-L), 2022 5 | ### [Paper](https://crl.ethz.ch/papers/PDWPreprintRAL2022.pdf) [Video](https://www.youtube.com/watch?v=OQfsXoXeAwg) 6 | 7 | ## Code Structure 8 | 9 | Projects/ProgrammbleDigitalWeaves/ 10 | - [include & src] contains header and source files 11 | - [GCode] contains sample GCodes 12 | - [Data] meshes for rendering 13 | 14 | ## Compile and Run 15 | Simply execute the build.sh file 16 | 17 | 18 | ## Third Party Library Required 19 | 20 | Libigl (https://libigl.github.io/) 21 | 22 | These folder should be in the same folder as the base folder. 23 | 24 | ## Third Party Attached 25 | 26 | SpectrA (https://spectralib.org/) 27 | 28 | ## License 29 | See the [LICENSE](LICENSE) file for license rights and limitations. 30 | 31 | ## Contact 32 | Please reach out to me ([Yue Li](yueli.cg@gmail.com)) if you spot any bugs or having quesitons or suggestions! -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | mkdir build 2 | cd build 3 | cmake -DCMAKE_BUILD_TYPE=Release .. 4 | make -j8 ProgrammableDigitalWeaves 5 | -------------------------------------------------------------------------------- /img/teaser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liyuesolo/ProgrammableDigitalWeaves/20d9431e67f9b9262adc478c7d99833e01eafea7/img/teaser.png --------------------------------------------------------------------------------