diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e0f2961 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,109 @@ +name: CI + +on: + push: + pull_request: + +permissions: + contents: read + +jobs: + build-and-test: + name: ${{ matrix.name }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - name: MSVC + os: windows-latest + - name: GCC + os: ubuntu-latest + cc: gcc + cxx: g++ + - name: Clang + os: ubuntu-latest + cc: clang + cxx: clang++ + + steps: + - uses: actions/checkout@v4 + + - name: Install Linux dependencies + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y libeigen3-dev ninja-build + + - name: Configure (MSVC) + if: runner.os == 'Windows' + run: >- + cmake -S . -B build-ci + -DVMD_USE_VENDORED_EIGEN=ON + -DVMD_BUILD_DOCS=OFF + -DVMD_ENABLE_WARNINGS=ON + + - name: Configure (GCC or Clang) + if: runner.os == 'Linux' + env: + CC: ${{ matrix.cc }} + CXX: ${{ matrix.cxx }} + run: >- + cmake -S . -B build-ci -G Ninja + -DCMAKE_BUILD_TYPE=Release + -DVMD_BUILD_DOCS=OFF + -DVMD_ENABLE_WARNINGS=ON + + - name: Build + run: cmake --build build-ci --config Release --parallel 2 + + - name: Test + run: ctest --test-dir build-ci -C Release --output-on-failure + + install-package: + name: Installed package consumer + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y libeigen3-dev ninja-build + - name: Build and install VMD_cpp + run: | + cmake -S . -B build-install -G Ninja -DCMAKE_BUILD_TYPE=Release -DVMD_BUILD_DOCS=OFF + cmake --build build-install --parallel 2 + cmake --install build-install --prefix "$PWD/install" + - name: Build downstream consumer + run: | + cmake -S tests/package_consumer -B build-consumer -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$PWD/install" + cmake --build build-consumer --parallel 2 + ./build-consumer/vmd_package_consumer + + sanitizers: + name: Clang ASan and UBSan + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y clang libeigen3-dev ninja-build + - name: Configure + env: + CC: clang + CXX: clang++ + run: >- + cmake -S . -B build-sanitizers -G Ninja + -DCMAKE_BUILD_TYPE=Debug + -DVMD_BUILD_DOCS=OFF + -DVMD_BUILD_BENCHMARKS=OFF + -DVMD_ENABLE_SANITIZERS=ON + -DVMD_ENABLE_WARNINGS=ON + - name: Build + run: cmake --build build-sanitizers --parallel 2 + - name: Test + env: + ASAN_OPTIONS: detect_leaks=1:halt_on_error=1 + UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1 + run: ctest --test-dir build-sanitizers --output-on-failure diff --git a/.github/workflows/msbuild.yml b/.github/workflows/msbuild.yml deleted file mode 100644 index 4ff5521..0000000 --- a/.github/workflows/msbuild.yml +++ /dev/null @@ -1,44 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -name: MSBuild - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - -env: - # Path to the solution file relative to the root of the project. - SOLUTION_FILE_PATH: . - - # Configuration type to build. - # You can convert this to a build matrix if you need coverage of multiple configuration types. - # https://docs.github.com/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix - BUILD_CONFIGURATION: Release - -permissions: - contents: read - -jobs: - build: - runs-on: windows-latest - - steps: - - uses: actions/checkout@v3 - - - name: Add MSBuild to PATH - uses: microsoft/setup-msbuild@v1.0.2 - - - name: Restore NuGet packages - working-directory: ${{env.GITHUB_WORKSPACE}} - run: nuget restore ${{env.SOLUTION_FILE_PATH}} - - - name: Build - working-directory: ${{env.GITHUB_WORKSPACE}} - # Add additional options to the MSBuild command line here (like platform or verbosity level). - # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference - run: msbuild /m /p:Configuration=${{env.BUILD_CONFIGURATION}} ${{env.SOLUTION_FILE_PATH}} diff --git a/.gitignore b/.gitignore index 0650a26..deb89fe 100644 --- a/.gitignore +++ b/.gitignore @@ -1974,3 +1974,7 @@ eigen-3.4.0/unsupported/test/special_functions.cpp eigen-3.4.0/unsupported/test/special_packetmath.cpp eigen-3.4.0/unsupported/test/splines.cpp eigen-3.4.0/ci/build.gitlab-ci.yml +# Local CMake and Codex build trees +/.codex-build/ +/build-*/ +/vmd-output/ diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index da29477..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "eigen"] - path = eigen - url = https://gitlab.com/libeigen/eigen.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 63bf245..f5de668 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,15 +1,192 @@ -cmake_minimum_required (VERSION 3.8) +cmake_minimum_required(VERSION 3.20) -project ("VMD_CPP") +project(VMD_cpp VERSION 2.0.0 LANGUAGES CXX) -find_package(Eigen3) +include(CMakePackageConfigHelpers) +include(CTest) +include(FetchContent) +include(GNUInstallDirs) -include_directories($SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/Eigen) -include_directories($SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/Eigen/unsupported ) -add_executable (VMD_CPP "VMD.cpp" "VMD.h" "VMD_Utils.cpp") +option(VMD_BUILD_CLI "Build the vmd_cli executable" ON) +option(VMD_BUILD_EXAMPLES "Build examples" ON) +option(VMD_BUILD_TESTS "Build tests" ${BUILD_TESTING}) +option(VMD_BUILD_BENCHMARKS "Build benchmarks" ON) +option(VMD_BUILD_DOCS "Add the optional Doxygen docs target" ON) +option(VMD_ENABLE_WARNINGS "Enable high compiler warnings" ON) +option(VMD_ENABLE_SANITIZERS "Enable AddressSanitizer and UndefinedBehaviorSanitizer" OFF) +option(VMD_USE_VENDORED_EIGEN "Use the repository's Eigen checkout before downloading Eigen" OFF) -add_custom_target(build_all_configs - COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target ALL_BUILD --config Debug - COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target ALL_BUILD --config RelWithDebInfo - COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target ALL_BUILD --config Release -) \ No newline at end of file +set(VMD_FFT_BACKEND "Eigen" CACHE STRING "FFT backend: Eigen, FFTW, or MKL") +set_property(CACHE VMD_FFT_BACKEND PROPERTY STRINGS Eigen FFTW MKL) + +find_package(Eigen3 3.3 CONFIG QUIET) +if(NOT TARGET Eigen3::Eigen AND VMD_USE_VENDORED_EIGEN AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Eigen/Eigen/Core") + add_library(Eigen3::Eigen INTERFACE IMPORTED) + set_target_properties(Eigen3::Eigen PROPERTIES + INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/Eigen" + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/Eigen") +endif() + +if(NOT TARGET Eigen3::Eigen) + FetchContent_Declare( + eigen + URL https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.tar.gz + URL_HASH SHA256=8586084f71f9bde545ee7fa6d00288b264a2b7ac3607b974e54d13e7162c1c72 + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + ) + if(POLICY CMP0169) + cmake_policy(SET CMP0169 OLD) + endif() + FetchContent_GetProperties(eigen) + if(NOT eigen_POPULATED) + FetchContent_Populate(eigen) + endif() + add_library(Eigen3::Eigen INTERFACE IMPORTED) + set_target_properties(Eigen3::Eigen PROPERTIES + INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${eigen_SOURCE_DIR}" + INTERFACE_INCLUDE_DIRECTORIES "${eigen_SOURCE_DIR}") +endif() + +add_library(vmd + src/fft_backend.cpp + src/vmd.cpp + VMD_Utils.cpp +) +add_library(VMD_cpp::vmd ALIAS vmd) + +target_compile_features(vmd PUBLIC cxx_std_17) +target_include_directories(vmd + PUBLIC + $ + $ + $ + PRIVATE src +) +target_link_libraries(vmd PUBLIC + $ + $ +) + +string(TOUPPER "${VMD_FFT_BACKEND}" VMD_FFT_BACKEND_UPPER) +if(VMD_FFT_BACKEND_UPPER STREQUAL "EIGEN") + target_compile_definitions(vmd PRIVATE VMD_FFT_BACKEND_EIGEN=1) +elseif(VMD_FFT_BACKEND_UPPER STREQUAL "FFTW") + find_path(FFTW3_INCLUDE_DIR fftw3.h REQUIRED) + find_library(FFTW3_LIBRARY NAMES fftw3 libfftw3-3 REQUIRED) + if(NOT TARGET FFTW3::fftw3) + add_library(FFTW3::fftw3 UNKNOWN IMPORTED) + set_target_properties(FFTW3::fftw3 PROPERTIES + IMPORTED_LOCATION "${FFTW3_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${FFTW3_INCLUDE_DIR}" + ) + endif() + target_link_libraries(vmd PRIVATE FFTW3::fftw3) + target_compile_definitions(vmd PRIVATE VMD_FFT_BACKEND_FFTW=1) +elseif(VMD_FFT_BACKEND_UPPER STREQUAL "MKL") + find_package(MKL CONFIG REQUIRED) + target_link_libraries(vmd PRIVATE MKL::MKL) + target_compile_definitions(vmd PRIVATE VMD_FFT_BACKEND_MKL=1) +else() + message(FATAL_ERROR "VMD_FFT_BACKEND must be Eigen, FFTW, or MKL") +endif() + +function(vmd_enable_warnings target) + if(MSVC) + target_compile_options(${target} PRIVATE /utf-8) + endif() + if(NOT VMD_ENABLE_WARNINGS) + return() + endif() + if(MSVC) + target_compile_options(${target} PRIVATE /W4 /permissive-) + else() + target_compile_options(${target} PRIVATE -Wall -Wextra -Wpedantic -Wconversion -Wshadow) + endif() +endfunction() +vmd_enable_warnings(vmd) + +if(VMD_ENABLE_SANITIZERS AND NOT MSVC) + target_compile_options(vmd PUBLIC -fsanitize=address,undefined -fno-omit-frame-pointer) + target_link_options(vmd PUBLIC -fsanitize=address,undefined) +endif() + +if(VMD_BUILD_CLI) + add_library(vmd_cli_support STATIC app/cli_io.cpp app/report.cpp) + target_compile_features(vmd_cli_support PUBLIC cxx_std_17) + target_include_directories(vmd_cli_support PUBLIC app) + target_link_libraries(vmd_cli_support PUBLIC vmd) + vmd_enable_warnings(vmd_cli_support) + + add_executable(vmd_cli app/vmd_cli.cpp) + target_link_libraries(vmd_cli PRIVATE vmd_cli_support) + vmd_enable_warnings(vmd_cli) +endif() + +if(VMD_BUILD_EXAMPLES) + add_executable(vmd_example examples/vmd_example.cpp) + target_link_libraries(vmd_example PRIVATE vmd) + vmd_enable_warnings(vmd_example) +endif() + +if(VMD_BUILD_TESTS AND BUILD_TESTING) + add_executable(vmd_tests tests/vmd_tests.cpp) + target_link_libraries(vmd_tests PRIVATE vmd) + vmd_enable_warnings(vmd_tests) + add_test(NAME vmd.unit COMMAND vmd_tests) + + if(VMD_BUILD_CLI) + add_test( + NAME vmd.cli.e2e + COMMAND ${CMAKE_COMMAND} + -DCLI=$ + -DOUTPUT_DIR=${CMAKE_CURRENT_BINARY_DIR}/cli-e2e-output + -P ${CMAKE_CURRENT_SOURCE_DIR}/tests/cli_e2e.cmake + ) + endif() +endif() + +if(VMD_BUILD_BENCHMARKS) + add_executable(vmd_benchmark benchmarks/vmd_benchmark.cpp) + target_link_libraries(vmd_benchmark PRIVATE vmd) + vmd_enable_warnings(vmd_benchmark) +endif() + +if(VMD_BUILD_DOCS) + find_package(Doxygen QUIET) + if(Doxygen_FOUND) + set(DOXYGEN_USE_MDFILE_AS_MAINPAGE README.md) + doxygen_add_docs(docs README.md include/vmd docs COMMENT "Generate VMD_cpp API documentation") + else() + message(STATUS "Doxygen not found; the optional docs target will not be generated") + endif() +endif() + +install(TARGETS vmd EXPORT VMD_cppTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) +install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +install(FILES VMD.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + +configure_package_config_file( + cmake/VMD_cppConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/VMD_cppConfig.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/VMD_cpp +) +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/VMD_cppConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion +) +install(EXPORT VMD_cppTargets + FILE VMD_cppTargets.cmake + NAMESPACE VMD_cpp:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/VMD_cpp +) +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/VMD_cppConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/VMD_cppConfigVersion.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/VMD_cpp +) diff --git a/Eigen/.gitignore b/Eigen/.gitignore index f6ab76f..c8d8c12 100644 --- a/Eigen/.gitignore +++ b/Eigen/.gitignore @@ -8,8 +8,8 @@ save *.old *.gmo *.qm -core -core.* +/core +/core.* *.bak *~ *build* diff --git a/Eigen/Eigen/src/Core/BooleanRedux.h b/Eigen/Eigen/src/Core/BooleanRedux.h new file mode 100644 index 0000000..e916c36 --- /dev/null +++ b/Eigen/Eigen/src/Core/BooleanRedux.h @@ -0,0 +1,162 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_ALLANDANY_H +#define EIGEN_ALLANDANY_H + +namespace Eigen { + +namespace internal { + +template +struct all_unroller +{ + enum { + col = (UnrollCount-1) / Rows, + row = (UnrollCount-1) % Rows + }; + + EIGEN_DEVICE_FUNC static inline bool run(const Derived &mat) + { + return all_unroller::run(mat) && mat.coeff(row, col); + } +}; + +template +struct all_unroller +{ + EIGEN_DEVICE_FUNC static inline bool run(const Derived &/*mat*/) { return true; } +}; + +template +struct all_unroller +{ + EIGEN_DEVICE_FUNC static inline bool run(const Derived &) { return false; } +}; + +template +struct any_unroller +{ + enum { + col = (UnrollCount-1) / Rows, + row = (UnrollCount-1) % Rows + }; + + EIGEN_DEVICE_FUNC static inline bool run(const Derived &mat) + { + return any_unroller::run(mat) || mat.coeff(row, col); + } +}; + +template +struct any_unroller +{ + EIGEN_DEVICE_FUNC static inline bool run(const Derived & /*mat*/) { return false; } +}; + +template +struct any_unroller +{ + EIGEN_DEVICE_FUNC static inline bool run(const Derived &) { return false; } +}; + +} // end namespace internal + +/** \returns true if all coefficients are true + * + * Example: \include MatrixBase_all.cpp + * Output: \verbinclude MatrixBase_all.out + * + * \sa any(), Cwise::operator<() + */ +template +EIGEN_DEVICE_FUNC inline bool DenseBase::all() const +{ + typedef internal::evaluator Evaluator; + enum { + unroll = SizeAtCompileTime != Dynamic + && SizeAtCompileTime * (int(Evaluator::CoeffReadCost) + int(NumTraits::AddCost)) <= EIGEN_UNROLLING_LIMIT + }; + Evaluator evaluator(derived()); + if(unroll) + return internal::all_unroller::RowsAtCompileTime>::run(evaluator); + else + { + for(Index j = 0; j < cols(); ++j) + for(Index i = 0; i < rows(); ++i) + if (!evaluator.coeff(i, j)) return false; + return true; + } +} + +/** \returns true if at least one coefficient is true + * + * \sa all() + */ +template +EIGEN_DEVICE_FUNC inline bool DenseBase::any() const +{ + typedef internal::evaluator Evaluator; + enum { + unroll = SizeAtCompileTime != Dynamic + && SizeAtCompileTime * (int(Evaluator::CoeffReadCost) + int(NumTraits::AddCost)) <= EIGEN_UNROLLING_LIMIT + }; + Evaluator evaluator(derived()); + if(unroll) + return internal::any_unroller::RowsAtCompileTime>::run(evaluator); + else + { + for(Index j = 0; j < cols(); ++j) + for(Index i = 0; i < rows(); ++i) + if (evaluator.coeff(i, j)) return true; + return false; + } +} + +/** \returns the number of coefficients which evaluate to true + * + * \sa all(), any() + */ +template +EIGEN_DEVICE_FUNC inline Eigen::Index DenseBase::count() const +{ + return derived().template cast().template cast().sum(); +} + +/** \returns true is \c *this contains at least one Not A Number (NaN). + * + * \sa allFinite() + */ +template +inline bool DenseBase::hasNaN() const +{ +#if EIGEN_COMP_MSVC || (defined __FAST_MATH__) + return derived().array().isNaN().any(); +#else + return !((derived().array()==derived().array()).all()); +#endif +} + +/** \returns true if \c *this contains only finite numbers, i.e., no NaN and no +/-INF values. + * + * \sa hasNaN() + */ +template +inline bool DenseBase::allFinite() const +{ +#if EIGEN_COMP_MSVC || (defined __FAST_MATH__) + return derived().array().isFinite().all(); +#else + return !((derived()-derived()).hasNaN()); +#endif +} + +} // end namespace Eigen + +#endif // EIGEN_ALLANDANY_H diff --git a/Eigen/Eigen/src/Core/arch/CUDA/Complex.h b/Eigen/Eigen/src/Core/arch/CUDA/Complex.h new file mode 100644 index 0000000..e19e1fb --- /dev/null +++ b/Eigen/Eigen/src/Core/arch/CUDA/Complex.h @@ -0,0 +1,258 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2014 Benoit Steiner +// Copyright (C) 2021 C. Antonio Sanchez +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_COMPLEX_CUDA_H +#define EIGEN_COMPLEX_CUDA_H + +// clang-format off +// Many std::complex methods such as operator+, operator-, operator* and +// operator/ are not constexpr. Due to this, GCC and older versions of clang do +// not treat them as device functions and thus Eigen functors making use of +// these operators fail to compile. Here, we manually specialize these +// operators and functors for complex types when building for CUDA to enable +// their use on-device. + +#if defined(EIGEN_CUDACC) && defined(EIGEN_GPU_COMPILE_PHASE) + +// ICC already specializes std::complex and std::complex +// operators, preventing us from making them device functions here. +// This will lead to silent runtime errors if the operators are used on device. +// +// To allow std::complex operator use on device, define _OVERRIDE_COMPLEX_SPECIALIZATION_ +// prior to first inclusion of . This prevents ICC from adding +// its own specializations, so our custom ones below can be used instead. +#if !(defined(EIGEN_COMP_ICC) && defined(_USE_COMPLEX_SPECIALIZATION_)) + +// Import Eigen's internal operator specializations. +#define EIGEN_USING_STD_COMPLEX_OPERATORS \ + using Eigen::complex_operator_detail::operator+; \ + using Eigen::complex_operator_detail::operator-; \ + using Eigen::complex_operator_detail::operator*; \ + using Eigen::complex_operator_detail::operator/; \ + using Eigen::complex_operator_detail::operator+=; \ + using Eigen::complex_operator_detail::operator-=; \ + using Eigen::complex_operator_detail::operator*=; \ + using Eigen::complex_operator_detail::operator/=; \ + using Eigen::complex_operator_detail::operator==; \ + using Eigen::complex_operator_detail::operator!=; + +namespace Eigen { + +// Specialized std::complex overloads. +namespace complex_operator_detail { + +template +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE +std::complex complex_multiply(const std::complex& a, const std::complex& b) { + const T a_real = numext::real(a); + const T a_imag = numext::imag(a); + const T b_real = numext::real(b); + const T b_imag = numext::imag(b); + return std::complex( + a_real * b_real - a_imag * b_imag, + a_imag * b_real + a_real * b_imag); +} + +template +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE +std::complex complex_divide_fast(const std::complex& a, const std::complex& b) { + const T a_real = numext::real(a); + const T a_imag = numext::imag(a); + const T b_real = numext::real(b); + const T b_imag = numext::imag(b); + const T norm = (b_real * b_real + b_imag * b_imag); + return std::complex((a_real * b_real + a_imag * b_imag) / norm, + (a_imag * b_real - a_real * b_imag) / norm); +} + +template +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE +std::complex complex_divide_stable(const std::complex& a, const std::complex& b) { + const T a_real = numext::real(a); + const T a_imag = numext::imag(a); + const T b_real = numext::real(b); + const T b_imag = numext::imag(b); + // Smith's complex division (https://arxiv.org/pdf/1210.4539.pdf), + // guards against over/under-flow. + const bool scale_imag = numext::abs(b_imag) <= numext::abs(b_real); + const T rscale = scale_imag ? T(1) : b_real / b_imag; + const T iscale = scale_imag ? b_imag / b_real : T(1); + const T denominator = b_real * rscale + b_imag * iscale; + return std::complex((a_real * rscale + a_imag * iscale) / denominator, + (a_imag * rscale - a_real * iscale) / denominator); +} + +template +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE +std::complex complex_divide(const std::complex& a, const std::complex& b) { +#if EIGEN_FAST_MATH + return complex_divide_fast(a, b); +#else + return complex_divide_stable(a, b); +#endif +} + +// NOTE: We cannot specialize compound assignment operators with Scalar T, +// (i.e. operator@=(const T&), for @=+,-,*,/) +// since they are already specialized for float/double/long double within +// the standard header. We also do not specialize the stream +// operators. +#define EIGEN_CREATE_STD_COMPLEX_OPERATOR_SPECIALIZATIONS(T) \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +std::complex operator+(const std::complex& a) { return a; } \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +std::complex operator-(const std::complex& a) { \ + return std::complex(-numext::real(a), -numext::imag(a)); \ +} \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +std::complex operator+(const std::complex& a, const std::complex& b) { \ + return std::complex(numext::real(a) + numext::real(b), numext::imag(a) + numext::imag(b)); \ +} \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +std::complex operator+(const std::complex& a, const T& b) { \ + return std::complex(numext::real(a) + b, numext::imag(a)); \ +} \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +std::complex operator+(const T& a, const std::complex& b) { \ + return std::complex(a + numext::real(b), numext::imag(b)); \ +} \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +std::complex operator-(const std::complex& a, const std::complex& b) { \ + return std::complex(numext::real(a) - numext::real(b), numext::imag(a) - numext::imag(b)); \ +} \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +std::complex operator-(const std::complex& a, const T& b) { \ + return std::complex(numext::real(a) - b, numext::imag(a)); \ +} \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +std::complex operator-(const T& a, const std::complex& b) { \ + return std::complex(a - numext::real(b), -numext::imag(b)); \ +} \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +std::complex operator*(const std::complex& a, const std::complex& b) { \ + return complex_multiply(a, b); \ +} \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +std::complex operator*(const std::complex& a, const T& b) { \ + return std::complex(numext::real(a) * b, numext::imag(a) * b); \ +} \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +std::complex operator*(const T& a, const std::complex& b) { \ + return std::complex(a * numext::real(b), a * numext::imag(b)); \ +} \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +std::complex operator/(const std::complex& a, const std::complex& b) { \ + return complex_divide(a, b); \ +} \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +std::complex operator/(const std::complex& a, const T& b) { \ + return std::complex(numext::real(a) / b, numext::imag(a) / b); \ +} \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +std::complex operator/(const T& a, const std::complex& b) { \ + return complex_divide(std::complex(a, 0), b); \ +} \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +std::complex& operator+=(std::complex& a, const std::complex& b) { \ + numext::real_ref(a) += numext::real(b); \ + numext::imag_ref(a) += numext::imag(b); \ + return a; \ +} \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +std::complex& operator-=(std::complex& a, const std::complex& b) { \ + numext::real_ref(a) -= numext::real(b); \ + numext::imag_ref(a) -= numext::imag(b); \ + return a; \ +} \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +std::complex& operator*=(std::complex& a, const std::complex& b) { \ + a = complex_multiply(a, b); \ + return a; \ +} \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +std::complex& operator/=(std::complex& a, const std::complex& b) { \ + a = complex_divide(a, b); \ + return a; \ +} \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +bool operator==(const std::complex& a, const std::complex& b) { \ + return numext::real(a) == numext::real(b) && numext::imag(a) == numext::imag(b); \ +} \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +bool operator==(const std::complex& a, const T& b) { \ + return numext::real(a) == b && numext::imag(a) == 0; \ +} \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +bool operator==(const T& a, const std::complex& b) { \ + return a == numext::real(b) && 0 == numext::imag(b); \ +} \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +bool operator!=(const std::complex& a, const std::complex& b) { \ + return !(a == b); \ +} \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +bool operator!=(const std::complex& a, const T& b) { \ + return !(a == b); \ +} \ + \ +EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE \ +bool operator!=(const T& a, const std::complex& b) { \ + return !(a == b); \ +} + +// Do not specialize for long double, since that reduces to double on device. +EIGEN_CREATE_STD_COMPLEX_OPERATOR_SPECIALIZATIONS(float) +EIGEN_CREATE_STD_COMPLEX_OPERATOR_SPECIALIZATIONS(double) + +#undef EIGEN_CREATE_STD_COMPLEX_OPERATOR_SPECIALIZATIONS + + +} // namespace complex_operator_detail + +EIGEN_USING_STD_COMPLEX_OPERATORS + +namespace numext { +EIGEN_USING_STD_COMPLEX_OPERATORS +} // namespace numext + +namespace internal { +EIGEN_USING_STD_COMPLEX_OPERATORS + +} // namespace internal +} // namespace Eigen + +#endif // !(EIGEN_COMP_ICC && _USE_COMPLEX_SPECIALIZATION_) + +#endif // EIGEN_CUDACC && EIGEN_GPU_COMPILE_PHASE + +#endif // EIGEN_COMPLEX_CUDA_H diff --git a/Eigen/Eigen/src/Core/arch/Default/TypeCasting.h b/Eigen/Eigen/src/Core/arch/Default/TypeCasting.h new file mode 100644 index 0000000..fb8183b --- /dev/null +++ b/Eigen/Eigen/src/Core/arch/Default/TypeCasting.h @@ -0,0 +1,120 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2016 Benoit Steiner +// Copyright (C) 2019 Rasmus Munk Larsen +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_GENERIC_TYPE_CASTING_H +#define EIGEN_GENERIC_TYPE_CASTING_H + +namespace Eigen { + +namespace internal { + +template<> +struct scalar_cast_op { + EIGEN_EMPTY_STRUCT_CTOR(scalar_cast_op) + typedef Eigen::half result_type; + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Eigen::half operator() (const float& a) const { + #if (defined(EIGEN_HAS_CUDA_FP16) && defined(EIGEN_CUDA_ARCH) && EIGEN_CUDA_ARCH >= 300) || \ + (defined(EIGEN_HAS_HIP_FP16) && defined(EIGEN_HIP_DEVICE_COMPILE)) + return __float2half(a); + #else + return Eigen::half(a); + #endif + } +}; + +template<> +struct functor_traits > +{ enum { Cost = NumTraits::AddCost, PacketAccess = false }; }; + + +template<> +struct scalar_cast_op { + EIGEN_EMPTY_STRUCT_CTOR(scalar_cast_op) + typedef Eigen::half result_type; + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Eigen::half operator() (const int& a) const { + #if (defined(EIGEN_HAS_CUDA_FP16) && defined(EIGEN_CUDA_ARCH) && EIGEN_CUDA_ARCH >= 300) || \ + (defined(EIGEN_HAS_HIP_FP16) && defined(EIGEN_HIP_DEVICE_COMPILE)) + return __float2half(static_cast(a)); + #else + return Eigen::half(static_cast(a)); + #endif + } +}; + +template<> +struct functor_traits > +{ enum { Cost = NumTraits::AddCost, PacketAccess = false }; }; + + +template<> +struct scalar_cast_op { + EIGEN_EMPTY_STRUCT_CTOR(scalar_cast_op) + typedef float result_type; + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE float operator() (const Eigen::half& a) const { + #if (defined(EIGEN_HAS_CUDA_FP16) && defined(EIGEN_CUDA_ARCH) && EIGEN_CUDA_ARCH >= 300) || \ + (defined(EIGEN_HAS_HIP_FP16) && defined(EIGEN_HIP_DEVICE_COMPILE)) + return __half2float(a); + #else + return static_cast(a); + #endif + } +}; + +template<> +struct functor_traits > +{ enum { Cost = NumTraits::AddCost, PacketAccess = false }; }; + + +template<> +struct scalar_cast_op { + EIGEN_EMPTY_STRUCT_CTOR(scalar_cast_op) + typedef Eigen::bfloat16 result_type; + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Eigen::bfloat16 operator() (const float& a) const { + return Eigen::bfloat16(a); + } +}; + +template<> +struct functor_traits > +{ enum { Cost = NumTraits::AddCost, PacketAccess = false }; }; + + +template<> +struct scalar_cast_op { + EIGEN_EMPTY_STRUCT_CTOR(scalar_cast_op) + typedef Eigen::bfloat16 result_type; + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Eigen::bfloat16 operator() (const int& a) const { + return Eigen::bfloat16(static_cast(a)); + } +}; + +template<> +struct functor_traits > +{ enum { Cost = NumTraits::AddCost, PacketAccess = false }; }; + + +template<> +struct scalar_cast_op { + EIGEN_EMPTY_STRUCT_CTOR(scalar_cast_op) + typedef float result_type; + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE float operator() (const Eigen::bfloat16& a) const { + return static_cast(a); + } +}; + +template<> +struct functor_traits > +{ enum { Cost = NumTraits::AddCost, PacketAccess = false }; }; + + +} +} + +#endif // EIGEN_GENERIC_TYPE_CASTING_H diff --git a/Eigen/Eigen/src/Core/arch/SYCL/SyclMemoryModel.h b/Eigen/Eigen/src/Core/arch/SYCL/SyclMemoryModel.h new file mode 100644 index 0000000..f81e59d --- /dev/null +++ b/Eigen/Eigen/src/Core/arch/SYCL/SyclMemoryModel.h @@ -0,0 +1,694 @@ +/*************************************************************************** + * Copyright (C) 2017 Codeplay Software Limited + * This Source Code Form is subject to the terms of the Mozilla + * Public License v. 2.0. If a copy of the MPL was not distributed + * with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * + * SyclMemoryModel.h + * + * Description: + * Interface for SYCL buffers to behave as a non-dereferenceable pointer + * Interface for Placeholder accessor to behave as a pointer on both host + * and device + * + * Authors: + * + * Ruyman Reyes Codeplay Software Ltd. + * Mehdi Goli Codeplay Software Ltd. + * Vanya Yaneva Codeplay Software Ltd. + * + **************************************************************************/ + +#if defined(EIGEN_USE_SYCL) && \ + !defined(EIGEN_CXX11_TENSOR_TENSOR_SYCL_STORAGE_MEMORY_H) +#define EIGEN_CXX11_TENSOR_TENSOR_SYCL_STORAGE_MEMORY_H + +#include +#ifdef EIGEN_EXCEPTIONS +#include +#endif +#include +#include +#include +#include + +namespace Eigen { +namespace TensorSycl { +namespace internal { + +using sycl_acc_target = cl::sycl::access::target; +using sycl_acc_mode = cl::sycl::access::mode; + +/** + * Default values for template arguments + */ +using buffer_data_type_t = uint8_t; +const sycl_acc_target default_acc_target = sycl_acc_target::global_buffer; +const sycl_acc_mode default_acc_mode = sycl_acc_mode::read_write; + +/** + * PointerMapper + * Associates fake pointers with buffers. + * + */ +class PointerMapper { + public: + using base_ptr_t = std::intptr_t; + + /* Structure of a virtual pointer + * + * |================================================| + * | POINTER ADDRESS | + * |================================================| + */ + struct virtual_pointer_t { + /* Type for the pointers + */ + base_ptr_t m_contents; + + /** Conversions from virtual_pointer_t to + * void * should just reinterpret_cast the integer number + */ + operator void *() const { return reinterpret_cast(m_contents); } + + /** + * Convert back to the integer number. + */ + operator base_ptr_t() const { return m_contents; } + + /** + * Add a certain value to the pointer to create a + * new pointer to that offset + */ + virtual_pointer_t operator+(size_t off) { return m_contents + off; } + + /* Numerical order for sorting pointers in containers. */ + bool operator<(virtual_pointer_t rhs) const { + return (static_cast(m_contents) < + static_cast(rhs.m_contents)); + } + + bool operator>(virtual_pointer_t rhs) const { + return (static_cast(m_contents) > + static_cast(rhs.m_contents)); + } + + /** + * Numerical order for sorting pointers in containers + */ + bool operator==(virtual_pointer_t rhs) const { + return (static_cast(m_contents) == + static_cast(rhs.m_contents)); + } + + /** + * Simple forward to the equality overload. + */ + bool operator!=(virtual_pointer_t rhs) const { + return !(this->operator==(rhs)); + } + + /** + * Converts a void * into a virtual pointer structure. + * Note that this will only work if the void * was + * already a virtual_pointer_t, but we have no way of + * checking + */ + virtual_pointer_t(const void *ptr) + : m_contents(reinterpret_cast(ptr)){}; + + /** + * Creates a virtual_pointer_t from the given integer + * number + */ + virtual_pointer_t(base_ptr_t u) : m_contents(u){}; + }; + + /* Definition of a null pointer + */ + const virtual_pointer_t null_virtual_ptr = nullptr; + + /** + * Whether if a pointer is null or not. + * A pointer is nullptr if the value is of null_virtual_ptr + */ + static inline bool is_nullptr(virtual_pointer_t ptr) { + return (static_cast(ptr) == nullptr); + } + + /* basic type for all buffers + */ + using buffer_t = cl::sycl::buffer_mem; + + /** + * Node that stores information about a device allocation. + * Nodes are sorted by size to organise a free list of nodes + * that can be recovered. + */ + struct pMapNode_t { + buffer_t m_buffer; + size_t m_size; + bool m_free; + + pMapNode_t(buffer_t b, size_t size, bool f) + : m_buffer{b}, m_size{size}, m_free{f} { + m_buffer.set_final_data(nullptr); + } + + bool operator<=(const pMapNode_t &rhs) { return (m_size <= rhs.m_size); } + }; + + /** Storage of the pointer / buffer tree + */ + using pointerMap_t = std::map; + + /** + * Obtain the insertion point in the pointer map for + * a pointer of the given size. + * \param requiredSize Size attemted to reclaim + */ + typename pointerMap_t::iterator get_insertion_point(size_t requiredSize) { + typename pointerMap_t::iterator retVal; + bool reuse = false; + if (!m_freeList.empty()) { + // try to re-use an existing block + for (auto freeElem : m_freeList) { + if (freeElem->second.m_size >= requiredSize) { + retVal = freeElem; + reuse = true; + // Element is not going to be free anymore + m_freeList.erase(freeElem); + break; + } + } + } + if (!reuse) { + retVal = std::prev(m_pointerMap.end()); + } + return retVal; + } + + /** + * Returns an iterator to the node that stores the information + * of the given virtual pointer from the given pointer map structure. + * If pointer is not found, throws std::out_of_range. + * If the pointer map structure is empty, throws std::out_of_range + * + * \param pMap the pointerMap_t structure storing all the pointers + * \param virtual_pointer_ptr The virtual pointer to obtain the node of + * \throws std::out:of_range if the pointer is not found or pMap is empty + */ + typename pointerMap_t::iterator get_node(const virtual_pointer_t ptr) { + if (this->count() == 0) { + m_pointerMap.clear(); + EIGEN_THROW_X(std::out_of_range("There are no pointers allocated\n")); + + } + if (is_nullptr(ptr)) { + m_pointerMap.clear(); + EIGEN_THROW_X(std::out_of_range("Cannot access null pointer\n")); + } + // The previous element to the lower bound is the node that + // holds this memory address + auto node = m_pointerMap.lower_bound(ptr); + // If the value of the pointer is not the one of the node + // then we return the previous one + if (node == std::end(m_pointerMap)) { + --node; + } else if (node->first != ptr) { + if (node == std::begin(m_pointerMap)) { + m_pointerMap.clear(); + EIGEN_THROW_X( + std::out_of_range("The pointer is not registered in the map\n")); + + } + --node; + } + + return node; + } + + /* get_buffer. + * Returns a buffer from the map using the pointer address + */ + template + cl::sycl::buffer get_buffer( + const virtual_pointer_t ptr) { + using sycl_buffer_t = cl::sycl::buffer; + + // get_node() returns a `buffer_mem`, so we need to cast it to a `buffer<>`. + // We can do this without the `buffer_mem` being a pointer, as we + // only declare member variables in the base class (`buffer_mem`) and not in + // the child class (`buffer<>). + auto node = get_node(ptr); + eigen_assert(node->first == ptr || node->first < ptr); + eigen_assert(ptr < static_cast(node->second.m_size + + node->first)); + return *(static_cast(&node->second.m_buffer)); + } + + /** + * @brief Returns an accessor to the buffer of the given virtual pointer + * @param accessMode + * @param accessTarget + * @param ptr The virtual pointer + */ + template + cl::sycl::accessor + get_access(const virtual_pointer_t ptr) { + auto buf = get_buffer(ptr); + return buf.template get_access(); + } + + /** + * @brief Returns an accessor to the buffer of the given virtual pointer + * in the given command group scope + * @param accessMode + * @param accessTarget + * @param ptr The virtual pointer + * @param cgh Reference to the command group scope + */ + template + cl::sycl::accessor + get_access(const virtual_pointer_t ptr, cl::sycl::handler &cgh) { + auto buf = get_buffer(ptr); + return buf.template get_access(cgh); + } + + /* + * Returns the offset from the base address of this pointer. + */ + inline std::ptrdiff_t get_offset(const virtual_pointer_t ptr) { + // The previous element to the lower bound is the node that + // holds this memory address + auto node = get_node(ptr); + auto start = node->first; + eigen_assert(start == ptr || start < ptr); + eigen_assert(ptr < start + node->second.m_size); + return (ptr - start); + } + + /* + * Returns the number of elements by which the given pointer is offset from + * the base address. + */ + template + inline size_t get_element_offset(const virtual_pointer_t ptr) { + return get_offset(ptr) / sizeof(buffer_data_type); + } + + /** + * Constructs the PointerMapper structure. + */ + PointerMapper(base_ptr_t baseAddress = 4096) + : m_pointerMap{}, m_freeList{}, m_baseAddress{baseAddress} { + if (m_baseAddress == 0) { + EIGEN_THROW_X(std::invalid_argument("Base address cannot be zero\n")); + } + }; + + /** + * PointerMapper cannot be copied or moved + */ + PointerMapper(const PointerMapper &) = delete; + + /** + * Empty the pointer list + */ + inline void clear() { + m_freeList.clear(); + m_pointerMap.clear(); + } + + /* add_pointer. + * Adds an existing pointer to the map and returns the virtual pointer id. + */ + inline virtual_pointer_t add_pointer(const buffer_t &b) { + return add_pointer_impl(b); + } + + /* add_pointer. + * Adds a pointer to the map and returns the virtual pointer id. + */ + inline virtual_pointer_t add_pointer(buffer_t &&b) { + return add_pointer_impl(b); + } + + /** + * @brief Fuses the given node with the previous nodes in the + * pointer map if they are free + * + * @param node A reference to the free node to be fused + */ + void fuse_forward(typename pointerMap_t::iterator &node) { + while (node != std::prev(m_pointerMap.end())) { + // if following node is free + // remove it and extend the current node with its size + auto fwd_node = std::next(node); + if (!fwd_node->second.m_free) { + break; + } + auto fwd_size = fwd_node->second.m_size; + m_freeList.erase(fwd_node); + m_pointerMap.erase(fwd_node); + + node->second.m_size += fwd_size; + } + } + + /** + * @brief Fuses the given node with the following nodes in the + * pointer map if they are free + * + * @param node A reference to the free node to be fused + */ + void fuse_backward(typename pointerMap_t::iterator &node) { + while (node != m_pointerMap.begin()) { + // if previous node is free, extend it + // with the size of the current one + auto prev_node = std::prev(node); + if (!prev_node->second.m_free) { + break; + } + prev_node->second.m_size += node->second.m_size; + + // remove the current node + m_freeList.erase(node); + m_pointerMap.erase(node); + + // point to the previous node + node = prev_node; + } + } + + /* remove_pointer. + * Removes the given pointer from the map. + * The pointer is allowed to be reused only if ReUse if true. + */ + template + void remove_pointer(const virtual_pointer_t ptr) { + if (is_nullptr(ptr)) { + return; + } + auto node = this->get_node(ptr); + + node->second.m_free = true; + m_freeList.emplace(node); + + // Fuse the node + // with free nodes before and after it + fuse_forward(node); + fuse_backward(node); + + // If after fusing the node is the last one + // simply remove it (since it is free) + if (node == std::prev(m_pointerMap.end())) { + m_freeList.erase(node); + m_pointerMap.erase(node); + } + } + + /* count. + * Return the number of active pointers (i.e, pointers that + * have been malloc but not freed). + */ + size_t count() const { return (m_pointerMap.size() - m_freeList.size()); } + + private: + /* add_pointer_impl. + * Adds a pointer to the map and returns the virtual pointer id. + * BufferT is either a const buffer_t& or a buffer_t&&. + */ + template + virtual_pointer_t add_pointer_impl(BufferT b) { + virtual_pointer_t retVal = nullptr; + size_t bufSize = b.get_count(); + pMapNode_t p{b, bufSize, false}; + // If this is the first pointer: + if (m_pointerMap.empty()) { + virtual_pointer_t initialVal{m_baseAddress}; + m_pointerMap.emplace(initialVal, p); + return initialVal; + } + + auto lastElemIter = get_insertion_point(bufSize); + // We are recovering an existing free node + if (lastElemIter->second.m_free) { + lastElemIter->second.m_buffer = b; + lastElemIter->second.m_free = false; + + // If the recovered node is bigger than the inserted one + // add a new free node with the remaining space + if (lastElemIter->second.m_size > bufSize) { + // create a new node with the remaining space + auto remainingSize = lastElemIter->second.m_size - bufSize; + pMapNode_t p2{b, remainingSize, true}; + + // update size of the current node + lastElemIter->second.m_size = bufSize; + + // add the new free node + auto newFreePtr = lastElemIter->first + bufSize; + auto freeNode = m_pointerMap.emplace(newFreePtr, p2).first; + m_freeList.emplace(freeNode); + } + + retVal = lastElemIter->first; + } else { + size_t lastSize = lastElemIter->second.m_size; + retVal = lastElemIter->first + lastSize; + m_pointerMap.emplace(retVal, p); + } + return retVal; + } + + /** + * Compare two iterators to pointer map entries according to + * the size of the allocation on the device. + */ + struct SortBySize { + bool operator()(typename pointerMap_t::iterator a, + typename pointerMap_t::iterator b) const { + return ((a->first < b->first) && (a->second <= b->second)) || + ((a->first < b->first) && (b->second <= a->second)); + } + }; + + /* Maps the pointer addresses to buffer and size pairs. + */ + pointerMap_t m_pointerMap; + + /* List of free nodes available for re-using + */ + std::set m_freeList; + + /* Base address used when issuing the first virtual pointer, allows users + * to specify alignment. Cannot be zero. */ + std::intptr_t m_baseAddress; +}; + +/* remove_pointer. + * Removes the given pointer from the map. + * The pointer is allowed to be reused only if ReUse if true. + */ +template <> +inline void PointerMapper::remove_pointer(const virtual_pointer_t ptr) { + if (is_nullptr(ptr)) { + return; + } + m_pointerMap.erase(this->get_node(ptr)); +} + +/** + * Malloc-like interface to the pointer-mapper. + * Given a size, creates a byte-typed buffer and returns a + * fake pointer to keep track of it. + * \param size Size in bytes of the desired allocation + * \throw cl::sycl::exception if error while creating the buffer + */ +inline void *SYCLmalloc(size_t size, PointerMapper &pMap) { + if (size == 0) { + return nullptr; + } + // Create a generic buffer of the given size + using buffer_t = cl::sycl::buffer; + auto thePointer = pMap.add_pointer(buffer_t(cl::sycl::range<1>{size})); + // Store the buffer on the global list + return static_cast(thePointer); +} + +/** + * Free-like interface to the pointer mapper. + * Given a fake-pointer created with the virtual-pointer malloc, + * destroys the buffer and remove it from the list. + * If ReUse is false, the pointer is not added to the freeList, + * it should be false only for sub-buffers. + */ +template +inline void SYCLfree(void *ptr, PointerMapper &pMap) { + pMap.template remove_pointer(ptr); +} + +/** + * Clear all the memory allocated by SYCL. + */ +template +inline void SYCLfreeAll(PointerMapper &pMap) { + pMap.clear(); +} + +template +struct RangeAccess { + static const auto global_access = cl::sycl::access::target::global_buffer; + static const auto is_place_holder = cl::sycl::access::placeholder::true_t; + typedef T scalar_t; + typedef scalar_t &ref_t; + typedef typename cl::sycl::global_ptr::pointer_t ptr_t; + + // the accessor type does not necessarily the same as T + typedef cl::sycl::accessor + accessor; + + typedef RangeAccess self_t; + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE RangeAccess(accessor access, + size_t offset, + std::intptr_t virtual_ptr) + : access_(access), offset_(offset), virtual_ptr_(virtual_ptr) {} + + RangeAccess(cl::sycl::buffer buff = + cl::sycl::buffer(cl::sycl::range<1>(1))) + : access_{accessor{buff}}, offset_(0), virtual_ptr_(-1) {} + + // This should be only used for null constructor on the host side + RangeAccess(std::nullptr_t) : RangeAccess() {} + // This template parameter must be removed and scalar_t should be replaced + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE ptr_t get_pointer() const { + return (access_.get_pointer().get() + offset_); + } + template + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE self_t &operator+=(Index offset) { + offset_ += (offset); + return *this; + } + template + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE self_t operator+(Index offset) const { + return self_t(access_, offset_ + offset, virtual_ptr_); + } + template + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE self_t operator-(Index offset) const { + return self_t(access_, offset_ - offset, virtual_ptr_); + } + template + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE self_t &operator-=(Index offset) { + offset_ -= offset; + return *this; + } + + // THIS IS FOR NULL COMPARISON ONLY + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE friend bool operator==( + const RangeAccess &lhs, std::nullptr_t) { + return ((lhs.virtual_ptr_ == -1)); + } + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE friend bool operator!=( + const RangeAccess &lhs, std::nullptr_t i) { + return !(lhs == i); + } + + // THIS IS FOR NULL COMPARISON ONLY + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE friend bool operator==( + std::nullptr_t, const RangeAccess &rhs) { + return ((rhs.virtual_ptr_ == -1)); + } + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE friend bool operator!=( + std::nullptr_t i, const RangeAccess &rhs) { + return !(i == rhs); + } + // Prefix operator (Increment and return value) + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE self_t &operator++() { + offset_++; + return (*this); + } + + // Postfix operator (Return value and increment) + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE self_t operator++(int i) { + EIGEN_UNUSED_VARIABLE(i); + self_t temp_iterator(*this); + offset_++; + return temp_iterator; + } + + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::ptrdiff_t get_size() const { + return (access_.get_count() - offset_); + } + + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::ptrdiff_t get_offset() const { + return offset_; + } + + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void set_offset(std::ptrdiff_t offset) { + offset_ = offset; + } + + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE ref_t operator*() const { + return *get_pointer(); + } + + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE ref_t operator*() { + return *get_pointer(); + } + + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE ptr_t operator->() = delete; + + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE ref_t operator[](int x) { + return *(get_pointer() + x); + } + + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE ref_t operator[](int x) const { + return *(get_pointer() + x); + } + + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE scalar_t *get_virtual_pointer() const { + return reinterpret_cast(virtual_ptr_ + + (offset_ * sizeof(scalar_t))); + } + + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE explicit operator bool() const { + return (virtual_ptr_ != -1); + } + + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE operator RangeAccess() { + return RangeAccess(access_, offset_, virtual_ptr_); + } + + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE + operator RangeAccess() const { + return RangeAccess(access_, offset_, virtual_ptr_); + } + // binding placeholder accessors to a command group handler for SYCL + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void bind( + cl::sycl::handler &cgh) const { + cgh.require(access_); + } + + private: + accessor access_; + size_t offset_; + std::intptr_t virtual_ptr_; // the location of the buffer in the map +}; + +template +struct RangeAccess : RangeAccess { + typedef RangeAccess Base; + using Base::Base; +}; + +} // namespace internal +} // namespace TensorSycl +} // namespace Eigen + +#endif // EIGEN_CXX11_TENSOR_TENSOR_SYCL_STORAGE_MEMORY_H diff --git a/Eigen/Eigen/src/Core/util/NonMPL2.h b/Eigen/Eigen/src/Core/util/NonMPL2.h new file mode 100644 index 0000000..1af67cf --- /dev/null +++ b/Eigen/Eigen/src/Core/util/NonMPL2.h @@ -0,0 +1,3 @@ +#ifdef EIGEN_MPL2_ONLY +#error Including non-MPL2 code in EIGEN_MPL2_ONLY mode +#endif diff --git a/README.md b/README.md index df2129d..10966e8 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,147 @@ # VMD_cpp -# C++ implementation of Variational Mode Decomposition using Eigen3 -Written by: Dodge(Lang HE) asdsay@gmail.com -Updated date: 2024-12-05 +VMD_cpp is a C++17 implementation of Variational Mode Decomposition (VMD). The +project provides a reusable Eigen-based library, a compatibility wrapper for the +original API, a command-line tool, structured CSV/JSON output, and a fully +self-contained HTML report. -VMD, aka Variational Mode Decomposition, is a signal processing tool that decompse the input signal into different band-limited IMFs. -Project **VMD_cpp** is an imitation of [that in MATLAB](https://ww2.mathworks.cn/help/wavelet/ref/vmd.html). In this project, I used eigen3 to refactor VMD in C++, so that we can use it without MATLAB. -Detail input and output please check out function **VMD** in file [VMD_Utils.cpp](https://github.com/DodgeHo/VMD_cpp/blob/master/VMD_Utils.cpp). +## Highlights -This sample code was written in MSBuild. You can both run in Visual Studio 2022 or MSVC or CMAKE/GCC, you can use either the sln project file, or the CMakeList.txt, they both work. +- Corrected `N x K` mode spectra: each column is `fftshift(fft(IMF))`. +- Stable handling of zero, constant, short, odd-length, and even-length signals. +- Per-mode `alpha`, reproducible random initialization, warm starts, and optional adaptive `tau`. +- Double-buffered iterations with `O(K * N)` working memory instead of storing every iteration. +- CSV/TSV CLI input and CSV, JSON, and offline HTML/SVG output. +- Installable CMake package exported as `VMD_cpp::vmd`. +- Eigen FFT by default, with optional FFTW and MKL backends. -If you are looking for document to describe Variational mode decomposition, please turn to the original paper [Variational Mode Decomposition](https://ieeexplore.ieee.org/document/6655981). You can also find the MATLAB codes here. +## Requirements -This VMD runs too slow. I tried to use Multiple Threads in Eigen, and I did everything I can. Hopefully this is good enough for someone using it. +- CMake 3.20 or newer +- A C++17 compiler +- Eigen 3.3 or newer +CMake first searches for `Eigen3::Eigen`. If it is unavailable, Eigen 3.4.0 is +fetched automatically. The bundled historical `Eigen/` tree is ignored unless +`VMD_USE_VENDORED_EIGEN=ON` is explicitly selected. -# VMD(变分模态分解)的C++实现,使用了Eigen3 +## Build -作者:Dodge asdsay@gmail.com -更新日期:2024-12-05 +```sh +cmake -S . -B build -DCMAKE_BUILD_TYPE=Release +cmake --build build --config Release --parallel +ctest --test-dir build -C Release --output-on-failure +``` -VMD(变分模态分解)是一种信号处理算法,可以将输入信号分解为不同带限的内禀模态函数(IMFs)。 -本项目**VMD_cpp** 是参考于[其在MATLAB中的实现](https://ww2.mathworks.cn/help/wavelet/ref/vmd.html)。在项目中,借助eigen3来实现C++中的VMD,从而无须MATLAB的计算。详细的输入输出,可以查看[VMD_Utils.cpp](https://github.com/DodgeHo/VMD_cpp/blob/master/VMD_Utils.cpp)文件中的 **VMD**函数。 +Useful options: -本项目用MSBuild编写,也可在Visual Studio 2022、MSVC或CMAKE/GCC中运行,sln项目文件或CMakeList.txt都能用。 +| Option | Default | Purpose | +|---|---:|---| +| `VMD_BUILD_CLI` | `ON` | Build `vmd_cli`. | +| `VMD_BUILD_EXAMPLES` | `ON` | Build `vmd_example`. | +| `VMD_BUILD_TESTS` | `BUILD_TESTING` | Build unit and CLI tests. | +| `VMD_BUILD_BENCHMARKS` | `ON` | Build `vmd_benchmark`. | +| `VMD_BUILD_DOCS` | `ON` | Add a `docs` target when Doxygen exists. | +| `VMD_FFT_BACKEND` | `Eigen` | Select `Eigen`, `FFTW`, or `MKL`. | +| `VMD_ENABLE_SANITIZERS` | `OFF` | Enable ASan and UBSan on supported compilers. | -如果需要描述变分模态分解的文档,可参阅原始论文[Variational Mode Decomposition](https://ieeexplore.ieee.org/document/6655981)。 +## Library API -这个项目稍慢。我尝试加入了Eigen自带的多线程计算,并尽可能在原代码上修改,尽可能让Eigen加快,希望效果足够好吧。 +```cpp +#include + +Eigen::VectorXd signal = /* finite samples */; + +vmd::Options options; +options.mode_count = 6; +options.alpha = {2000.0}; // broadcast to all modes +options.sample_rate_hz = 1000.0; +options.initialization = vmd::Initialization::Uniform; + +vmd::Result result = vmd::decompose(signal, options); + +// result.modes: K x N +// result.spectra: N x K, fftshift(fft(mode)) +// result.omega_normalized: iterations x K, cycles/sample +// result.omega_hz: iterations x K, hertz +``` + +Invalid arguments throw `std::invalid_argument`. Every returned matrix owns its +storage. Calls do not share decomposition state, so independent calls may run in +parallel. + +See [API documentation](docs/api.md) and [algorithm notes](docs/algorithm.md). + +## CLI Quick Start + +```sh +vmd_cli --input signal.csv --column value --sample-rate 1000 \ + --modes 6 --alpha 2000 --output-dir results --report +``` + +Read TSV automatically, select a zero-based column index, or read from standard +input with `--input -`. The built-in example is explicit: + +```sh +vmd_cli --demo --sample-rate 1200 --modes 8 --alpha 50 \ + --output-dir results --report +``` + +With no input, the CLI prints help and exits with code 2. A nonconverged run exits +with code 4 but still writes its result files. + +## Output Files + +- `modes.csv`: index, time, input, every IMF, reconstruction, and residual. +- `spectra.csv`: shifted frequency and real, imaginary, and magnitude values. +- `omega.csv`: center-frequency trajectory in hertz. +- `summary.json`: input metadata, options, diagnostics, timings, and file names. +- `report.html`: optional offline report with inline CSS, JavaScript, data, and SVG. + +The report includes input/reconstruction, residual, IMF, spectrum, and frequency +convergence views. Plot data is min/max-envelope downsampled; CSV output retains +all samples. + +See [CLI and file formats](docs/cli-and-formats.md). + +## Legacy Compatibility + +Existing code may continue to include `VMD.h` and call the global `VMD(...)` +function. The wrapper maps old arguments to `vmd::decompose` while preserving: + +- `u` as `K x N`. +- `u_hat` as `N x K` (now corrected to the documented spectrum). +- `omega` as normalized cycles per sample. +- Initialization values `0 = zero`, `1 = uniform`, and `2 = random`. + +New integrations should use the namespaced API. + +## Install and Consume + +```sh +cmake --install build --prefix install +``` + +Downstream CMake: + +```cmake +find_package(VMD_cpp 2 CONFIG REQUIRED) +add_executable(my_app main.cpp) +target_link_libraries(my_app PRIVATE VMD_cpp::vmd) +``` + +Ensure Eigen is discoverable by the downstream project. Packages built with FFTW +or MKL also require the selected backend at link and runtime. + +## Validation and Performance + +`vmd_tests` covers reference, single-frequency, multifrequency, noisy, chirp, +DC, zero, constant, short, odd/even length, random-seed, warm-start, and legacy +paths. `vmd_benchmark` reports stage timings and an estimated core workspace. + +See [validation and performance](docs/validation-and-performance.md). + +## License + +This project is distributed under the Mozilla Public License 2.0. See +[LICENSE.txt](LICENSE.txt). diff --git a/VMD.cpp b/VMD.cpp deleted file mode 100644 index 6da6cd4..0000000 --- a/VMD.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "VMD.h" -#include -#include -#include -using namespace Eigen; -using namespace std; -/* - -Copyright (C) <2019> -Mozilla Public License v. 2.0. -*/ - -void printMatrix(const MatrixXd& u) { - std::ostringstream out; // use ostringstream to accumulate output - for (int i = 0; i < u.rows(); i++) { - for (int j = 0; j < u.cols(); j++) - out << u(i, j) << ' '; - out << "\n\n"; - } - std::cout << out.str(); // output once -} - -int main() { - - // create a signal to simulation the procedure. - double f_1 = 2.0, f_2 = 24.0, f_3 = 288.0; - int T = 1200; - vectord t(T), v_1(T), v_2(T), v_3(T),signal(T); - for (int i = 0; i < T; i++) { - t[i] = double(i + 1) / T; - v_1[i] = cos(2 * pI * f_1 * t[i]); - v_2[i] = cos(2 * pI * f_2 * t[i]) / 10.0; - v_3[i] = cos(2 * pI * f_3 * t[i]) / 200.0; - signal[i] = v_1[i] + v_2[i] + v_3[i]; - } - - // initial some input parameters - const double alpha = 50.0, tau = 0, tol = 1e-7, eps = 2.2204e-16; - const int K = 8, DC = 0, init = 1; - const static double CSVFormat(4); - Eigen::setNbThreads(std::thread::hardware_concurrency()); // Set the numbers of threads that Eigen uses - - // Example 1: If you want to get the full results as a 2D matrix of VMD. - MatrixXd u, omega; - MatrixXcd u_hat; - VMD(u, u_hat, omega, signal, alpha, tau, K, DC, init, tol, eps); - - //Example 2: If you only wants to get sum result of the first n mode of signals. - const double hp_cut_off = 1, lp_cut_off = 15; // Hz - const double Fs = 50; - /* Same as Example 1 - MatrixXd u, omega; - MatrixXcd u_hat; - VMD(u, u_hat, omega, signal, alpha, tau, K, DC, init, tol, eps);*/ - double center_freq; - MatrixXd p_data = u.row(0); p_data.fill(0); - for (int k = 0; k < K; k++) { - center_freq = Fs * omega(omega.rows() - 1, k); - if (hp_cut_off < center_freq && center_freq < lp_cut_off) - p_data = p_data + u.row(k); - } - - for (int j = 0; j < T; j++) { - if (p_data(0, j) < 0) { - p_data = p_data.array() - p_data.minCoeff(); - break; - } - } - - - // Output results - cout << "VMD Decomposition Results:" << endl; - cout << "Number of modes: " << K << endl; - cout << "Matrix dimensions: " << u.rows() << " x " << u.cols() << endl; - - // Save decomposition results to CSV - const string output_filename = "vmd_decomposition_results.csv"; - ofstream output_file(output_filename); - if (!output_file) { - cerr << "Error: Could not open file " << output_filename << endl; - return 1; - } - output_file << u.format(CSVFormat); - output_file.close(); - cout << "Results saved to: " << output_filename << endl; - - return 0; -}; \ No newline at end of file diff --git a/VMD.exe b/VMD.exe deleted file mode 100644 index f6e2ff5..0000000 Binary files a/VMD.exe and /dev/null differ diff --git a/VMD.h b/VMD.h index 63ab8f8..18d4125 100644 --- a/VMD.h +++ b/VMD.h @@ -1,28 +1,31 @@ -#pragma once -#include -#include -#include -//#include "eigen/Eigen/Eigen" -//#include "eigen/unsupported/Eigen/FFT" -#include <../eigen/Eigen/Core> -#include <../eigen/unsupported/Eigen/FFT> - -#define pI acos(-1) -using namespace Eigen; - -#ifdef EIGEN_HAS_OPENMP -Eigen::setNbThreads(omp_get_max_threads()); -#endif - -typedef std::vector vectord; -typedef std::vector > vectorcd; -typedef std::vector Matrix3DXd; - -void VMD(MatrixXd& u, MatrixXcd& u_hat, MatrixXd& omega, - const vectord& signal, const double alpha, const double tau, - const int K, const int DC, const int init, const double tol, const double eps); - -vectorcd circshift(vectorcd& data, int offset); -vectord omega_init_method2(int K, const double fs); -vectorcd ExtractColFromMatrixXcd(MatrixXcd& Input, const int k, const int T); -vectorcd ExtractRowFromMatrixXd(MatrixXd& Input, const int k, const int T); \ No newline at end of file +#pragma once + +#include + +#include +#include + +using vectord = std::vector; +using vectorcd = std::vector>; + +/** + * Compatibility wrapper for the original VMD_cpp API. + * + * `u` is K x N, `u_hat` is N x K, and `omega` contains normalized + * center frequencies in cycles per sample. New code should prefer + * `vmd::decompose()`. + */ +void VMD( + Eigen::MatrixXd& u, + Eigen::MatrixXcd& u_hat, + Eigen::MatrixXd& omega, + const vectord& signal, + double alpha, + double tau, + int K, + int DC, + int init, + double tol, + double eps); + +vectorcd circshift(const vectorcd& data, int offset); diff --git a/VMD_Utils.cpp b/VMD_Utils.cpp index 58b346f..ddfea67 100644 --- a/VMD_Utils.cpp +++ b/VMD_Utils.cpp @@ -1,262 +1,52 @@ #include "VMD.h" -using namespace Eigen; -using namespace std; -void VMD -(MatrixXd& u, MatrixXcd& u_hat, MatrixXd& omega, - const vectord& signal, const double alpha, const double tau, - const int K, const int DC, const int init, const double tol, const double eps) { - /* --------------------- - - Output: - ------- - u - the collection of decomposed modes (2D double Matrix in Eigen -MatrixXd) - u_hat - spectra of the modes (2D complex Matrix in Eigen -MatrixXd) - omega - estimated mode center - frequencies (2D double Matrix in Eigen -MatrixXd) - ------- - Input: - ------- - signal - the time domain signal(1D vector) to be decomposed - alpha - the balancing parameter of the data - fidelity constraint - tau - time - step of the dual ascent(pick 0 for noise - slack) - K - the number of modes to be recovered - DC - true if the first mode is putand kept at DC(0 - freq) - init - 0 = all omegas start at 0 - 1 = all omegas start uniformly distributed - 2 = all omegas initialized randomly - tol - tolerance of convergence criterion; typically around 1e-6 - - */ - - // ----------Preparations - // Periodand sampling frequency of input signal - int T = int(signal.size()); - int saveT = T; - double fs = 1.0 / T; - - //Extend the signal by mirroring - vectord f(2 * T, 0.0); - copy(signal.begin(), signal.end(), f.begin() + T / 2); - for (int i = 0; i < T / 2; i++) - f[i] = signal[T / 2 - 1 - i]; - for (int i = 3 * T / 2; i < 2 * T; i++) - f[i] = signal[T + 3 * T / 2 - 1 - i]; - - // Time Domain 0 to T (of mirrored signal) - // Spectral Domain discretization - T = int(f.size()); - vectorcd freqs(T, 0.0); - vectord timevec(T, 0.0); - for (int i = 0; i < T; i++) { - timevec[i] = double(i + 1.0) / T; - freqs[i] = (timevec[i] - 0.5) - (1.0 / T); - } - - // Maximum number of iterations(if not converged yet, then it won't anyway) - int N = 500; - - // Construct and center f_hat - vectorcd freqvec(T, 0.0); - FFT fft; fft.fwd(freqvec, f); - vectorcd f_hat = circshift(freqvec, T / 2); - vectorcd f_hat_plus(f_hat.size(), 0.0); - copy(f_hat.begin() + T / 2, f_hat.end(), f_hat_plus.begin() + T / 2); - - // Calculate Matrix-Column in advance - MatrixXcd f_hat_plus_Xcd = Eigen::Map(f_hat_plus.data(), 1, int(f_hat_plus.size())); - MatrixXcd freqs_Xcd = Eigen::Map(freqs.data(), 1, int(freqs.size())); - - // Matrix keeping track of every iterant // could be discarded for mem - Matrix3DXd u_hat_plus(N, MatrixXcd::Zero(K, T)); - - // Initialization of omega_k - MatrixXcd omega_plus = MatrixXcd::Zero(N, K); - vectord tmp; - switch (init) { - case 1: - for (int i = 0; i < K; i++) { - omega_plus(0, i) = double(0.5 / K) * (i); - for (int j = 1; j < N; j++) - omega_plus(j, i) = 0.0; - } - break; - case 2: - tmp = omega_init_method2(K, fs); - for (int i = 0; i < K; i++) { - omega_plus(0, i) = tmp[i]; - for (int j = 1; j < N; j++) - omega_plus(j, i) = 0.0; - } - break; - default: - break; - } - - // If DC mode imposed, set its omega to 0 - if (DC) - omega_plus(0, 0) = 0; - - // Start with empty dual variables - MatrixXcd lambda_hat = MatrixXcd::Zero(N, T); - - // Other inits - double uDiff = tol + eps;//% update step - int n = 1;// loop counter - MatrixXcd sum_uk = MatrixXcd::Zero(1, T); - // Accumulator - int k; - - // ----------- Main loop for iterative updates - while (uDiff > tol && n < N) { - - //update first mode accumulator - k = 0; - sum_uk = u_hat_plus[n - 1].row(K - 1) + sum_uk - u_hat_plus[n - 1].row(0); - - //update spectrum of first mode through Wiener filter of residuals - MatrixXcd Dividend_vec = f_hat_plus_Xcd - sum_uk - (lambda_hat.row(n - 1) / 2.0); - MatrixXcd Divisor_vec = (1 + alpha * - ((freqs_Xcd.array() - omega_plus(n - 1, k))).array().square()); - u_hat_plus[n].row(k).noalias() = Dividend_vec.cwiseQuotient(Divisor_vec); - - //update first omega if not held at 0 - if (!DC) { - std::complex Dividend{ 0,0 }, Divisor{ 0, 0 }, Addend{ 0, 0 }, Addend_sqrt{ 0, 0 }; - for (int i = 0; i < T - T / 2; i++) { - Addend_sqrt = abs(u_hat_plus[n](k, T / 2 + i)); - Addend = Addend_sqrt * Addend_sqrt; - Divisor += Addend; - Dividend += freqs[T / 2 + i] * Addend; - } - omega_plus(n, k) = Dividend / Divisor; - - } - // Dual ascent - - auto lambda_hat_lastrow_half = lambda_hat.row(n - 1) / 2.0; - for (k = 1; k < K; k++) { - //accumulator - sum_uk.noalias() += u_hat_plus[n].row(k - 1) - u_hat_plus[n - 1].row(k); - - //mode spectrum - MatrixXcd Dividend_vec = f_hat_plus_Xcd; - Dividend_vec.noalias() -= sum_uk; // in-place calculate - Dividend_vec.noalias() -= lambda_hat_lastrow_half; // in-place calculate - MatrixXcd Divisor_vec = (1 + alpha * - ((freqs_Xcd.array() - omega_plus(n - 1, k))).array().square()); - - u_hat_plus[n].row(k).noalias() = Dividend_vec.cwiseQuotient(Divisor_vec); - - //center frequencies - std::complex Dividend{ 0,0 }, Divisor{ 0, 0 }, Addend{ 0, 0 }, Addend_sqrt{ 0, 0 }; - for (int i = 0; i < T - T / 2; i++) { - Addend_sqrt = abs(u_hat_plus[n](k, T / 2 + i)); - Addend = Addend_sqrt * Addend_sqrt; - Divisor += Addend; - Dividend += freqs[T / 2 + i] * Addend; - } - omega_plus(n, k) = Dividend / Divisor; - } - - lambda_hat.row(n) = lambda_hat.row(n - 1) + tau * (u_hat_plus[n].colwise().sum() - f_hat_plus_Xcd); - //lambda_hat.row(n).noalias() = lambda_hat.row(n - 1) + tau * (u_hat_plus[n].rowwise().sum() - f_hat_plus_Xcd); - n++; - - std::complex acc{ eps, 0 }; - for (int i = 0; i < K; i++) { - MatrixXcd tmp = u_hat_plus[n - 1].row(i) - u_hat_plus[n - 2].row(i); - tmp = (tmp * (tmp.adjoint())); - acc = acc + tmp(0, 0) / double(T); - - } - uDiff = abs(acc); - - } - - //Postprocessing and cleanup - //Discard empty space if converged early - N = std::min(N, n); - omega = omega_plus.topRows(N).real(); - - //Signal reconstruction - u_hat = MatrixXcd::Zero(T, K); - for (int i = T / 2; i < T; i++) - for (int k = 0; k < K; k++) - u_hat(i, k) = u_hat_plus[N - 1](k, i); - - for (int i = T / 2; i >= 0; i--) - for (int k = 0; k < K; k++) - u_hat(i, k) = conj(u_hat_plus[N - 1](k, T - i - 1)); - - u_hat.row(0) = u_hat.row(T - 1).transpose().adjoint(); - u.resize(K, saveT); - vectord result_col; - for (int k = 0; k < K; k++) { - vectorcd u_hat_col = ExtractColFromMatrixXcd(u_hat, k, T); - u_hat_col = circshift(u_hat_col, int(floor(T / 2))); - fft.inv(result_col, u_hat_col); - for (int t = 0; t < saveT; t++) - u(k, t) = result_col[t + T / 4]; - } - - vectord result_timevec(saveT, 0); - for (int i = 0; i < saveT; i += 1) { - result_timevec[i] = double(i + 1) / saveT; - } - - for (int k = 0; k < K; k++) { - vectorcd u_row = ExtractRowFromMatrixXd(u, k, saveT); - fft.inv(result_timevec, u_row); - u_row = circshift(u_row, saveT / 2); - for (int t = 0; t < saveT; t++) - u_hat(t, k) = u_row[t].real(); - } - - return; -} - -#pragma region Ancillary Functions - -vectorcd circshift(vectorcd& data, int offset) { - const int n = int(data.size()); - if (offset == 0) { - vectorcd out_data(data); - return out_data; - } - else { - offset = (offset > 0) ? n - offset : -offset; // move to right by offset positions - vectorcd out_data(data.begin() + offset, data.end()); - out_data.insert(out_data.end(), data.begin(), data.begin() + offset); - return out_data; - } -} - -vectord omega_init_method2(int K, const double fs) { - vectord res(K, 0); - int N = INT_MAX / 2; - srand(int(time(NULL))); - for (int i = 0; i < K; i++) { - res[i] = exp(log(fs) + (log(0.5) - log(fs)) * - (rand() % (N + 1) / (double)(N + 1)) - ); - } - sort(res.begin(), res.end()); - return res; -} - -vectorcd ExtractColFromMatrixXcd(MatrixXcd& Input, const int ColIdx, const int RowNum) { - vectorcd Output(RowNum, 0); - for (int i = 0; i < RowNum; ++i) - Output[i] = Input(i, ColIdx); - return Output; +#include + +#include +#include +#include +#include + +void VMD( + Eigen::MatrixXd& u, + Eigen::MatrixXcd& u_hat, + Eigen::MatrixXd& omega, + const vectord& signal, + double alpha, + double tau, + int K, + int DC, + int init, + double tol, + double eps) { + if (K <= 0) throw std::invalid_argument("K must be positive"); + if (init < 0 || init > 2) throw std::invalid_argument("init must be 0, 1, or 2"); + vmd::Options options; + options.mode_count = static_cast(K); + options.alpha = {alpha}; + options.tau = tau; + options.dc_mode = DC != 0; + options.tolerance = tol; + options.max_iterations = 500; + options.keep_omega_history = true; + options.initialization = init == 1 ? vmd::Initialization::Uniform + : init == 2 ? vmd::Initialization::Random + : vmd::Initialization::Zero; + (void)eps; + + const Eigen::Map mapped(signal.data(), static_cast(signal.size())); + vmd::Result result = vmd::decompose(mapped, options); + u = std::move(result.modes); + u_hat = std::move(result.spectra); + omega = std::move(result.omega_normalized); } -vectorcd ExtractRowFromMatrixXd(MatrixXd& Input, const int RowIdx, const int ColNum) { - vectorcd Output(ColNum, 0); - for (int i = 0; i < ColNum; ++i) - Output[i] = Input(RowIdx, i); - return Output; +vectorcd circshift(const vectorcd& data, int offset) { + if (data.empty()) return {}; + const int size = static_cast(data.size()); + int shift = offset % size; + if (shift < 0) shift += size; + vectorcd result(data.size()); + for (int i = 0; i < size; ++i) result[static_cast((i + shift) % size)] = data[static_cast(i)]; + return result; } - -#pragma endregion - diff --git a/VMD_cpp.sln b/VMD_cpp.sln deleted file mode 100644 index bb41f8a..0000000 --- a/VMD_cpp.sln +++ /dev/null @@ -1,31 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.31912.275 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VMD_cpp", "VMD_cpp.vcxproj", "{807D1970-A512-4977-82B3-5BFFE74511B8}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {807D1970-A512-4977-82B3-5BFFE74511B8}.Debug|x64.ActiveCfg = Debug|x64 - {807D1970-A512-4977-82B3-5BFFE74511B8}.Debug|x64.Build.0 = Debug|x64 - {807D1970-A512-4977-82B3-5BFFE74511B8}.Debug|x86.ActiveCfg = Debug|Win32 - {807D1970-A512-4977-82B3-5BFFE74511B8}.Debug|x86.Build.0 = Debug|Win32 - {807D1970-A512-4977-82B3-5BFFE74511B8}.Release|x64.ActiveCfg = Release|x64 - {807D1970-A512-4977-82B3-5BFFE74511B8}.Release|x64.Build.0 = Release|x64 - {807D1970-A512-4977-82B3-5BFFE74511B8}.Release|x86.ActiveCfg = Release|Win32 - {807D1970-A512-4977-82B3-5BFFE74511B8}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {25AD96C3-BE21-4B48-9556-CEE702BCDC15} - EndGlobalSection -EndGlobal diff --git a/VMD_cpp.vcxproj b/VMD_cpp.vcxproj deleted file mode 100644 index 9b0a945..0000000 --- a/VMD_cpp.vcxproj +++ /dev/null @@ -1,158 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 16.0 - Win32Proj - {807d1970-a512-4977-82b3-5bffe74511b8} - VMD_cpp - 10.0 - - - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - - - false - - - true - ..\eigen\Eigen\unsupported;..\eigen\Eigen;$(VC_IncludePath);$(WindowsSDK_IncludePath); - - - false - ..\eigen\Eigen\unsupported;..\eigen\Eigen;$(VC_IncludePath);$(WindowsSDK_IncludePath); - - - - Level3 - true - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - .\Eigen;.\Eigen\unsupported;include;%(AdditionalIncludeDirectories) - - - Console - true - - - - - Level3 - true - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - .\Eigen;.\Eigen\unsupported;include;%(AdditionalIncludeDirectories) - - - Console - true - true - true - - - - - Level3 - true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpp17 - ..\eigen\Eigen;..\eigen\Eigen\unsupported;include;%(AdditionalIncludeDirectories) - - - Console - true - - - - - Level3 - true - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - ..\eigen\Eigen;..\eigen\Eigen\unsupported;include;%(AdditionalIncludeDirectories) - - - Console - true - true - true - - - - - - - - - - - - - \ No newline at end of file diff --git a/VMD_cpp.vcxproj.filters b/VMD_cpp.vcxproj.filters deleted file mode 100644 index 849a975..0000000 --- a/VMD_cpp.vcxproj.filters +++ /dev/null @@ -1,26 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd - - - - - src - - - src - - - - - include - - - \ No newline at end of file diff --git a/app/cli_io.cpp b/app/cli_io.cpp new file mode 100644 index 0000000..53ae069 --- /dev/null +++ b/app/cli_io.cpp @@ -0,0 +1,375 @@ +#include "cli_io.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace vmd::cli { +namespace { + +std::string trim(std::string value) { + const auto not_space = [](unsigned char character) { return std::isspace(character) == 0; }; + value.erase(value.begin(), std::find_if(value.begin(), value.end(), not_space)); + value.erase(std::find_if(value.rbegin(), value.rend(), not_space).base(), value.end()); + return value; +} + +std::vector parse_row(const std::string& line, char delimiter, const std::string& source, std::size_t line_number) { + std::vector fields; + std::string field; + bool quoted = false; + for (std::size_t i = 0; i < line.size(); ++i) { + const char character = line[i]; + if (character == '"') { + if (quoted && i + 1 < line.size() && line[i + 1] == '"') { + field.push_back('"'); + ++i; + } else { + quoted = !quoted; + } + } else if (character == delimiter && !quoted) { + fields.push_back(trim(field)); + field.clear(); + } else { + field.push_back(character); + } + } + if (quoted) { + throw IoError(source + ":" + std::to_string(line_number) + ": unterminated quoted field"); + } + fields.push_back(trim(field)); + return fields; +} + +std::size_t delimiter_count(const std::string& line, char delimiter) { + bool quoted = false; + std::size_t count = 0; + for (std::size_t i = 0; i < line.size(); ++i) { + if (line[i] == '"') { + if (quoted && i + 1 < line.size() && line[i + 1] == '"') ++i; + else quoted = !quoted; + } else if (!quoted && line[i] == delimiter) { + ++count; + } + } + return count; +} + +std::optional number(const std::string& field) { + if (field.empty()) return std::nullopt; + std::size_t consumed = 0; + try { + const double value = std::stod(field, &consumed); + if (consumed != field.size() || !std::isfinite(value)) return std::nullopt; + return value; + } catch (const std::exception&) { + return std::nullopt; + } +} + +bool is_index(const std::string& value) { + return !value.empty() && std::all_of(value.begin(), value.end(), [](unsigned char character) { + return std::isdigit(character) != 0; + }); +} + +std::string json_escape(const std::string& value) { + std::ostringstream output; + for (unsigned char character : value) { + switch (character) { + case '"': output << "\\\""; break; + case '\\': output << "\\\\"; break; + case '\b': output << "\\b"; break; + case '\f': output << "\\f"; break; + case '\n': output << "\\n"; break; + case '\r': output << "\\r"; break; + case '\t': output << "\\t"; break; + default: + if (character < 0x20) { + output << "\\u" << std::hex << std::setw(4) << std::setfill('0') << static_cast(character); + } else { + output << static_cast(character); + } + } + } + return output.str(); +} + +std::ofstream open_output(const std::filesystem::path& path) { + std::ofstream output(path, std::ios::binary); + if (!output) throw IoError("cannot open output file: " + path.string()); + output << std::setprecision(17); + return output; +} + +std::string output_name(const std::filesystem::path& path) { + return path.filename().string(); +} + +} // namespace + +InputSignal read_signal(const std::string& path, const std::string& column) { + std::ifstream file; + std::istream* stream = nullptr; + const std::string source = path == "-" ? "" : path; + if (path == "-") { + stream = &std::cin; + } else { + file.open(path, std::ios::binary); + if (!file) throw IoError("cannot open input file: " + path); + stream = &file; + } + + std::vector> lines; + std::string line; + std::size_t line_number = 0; + while (std::getline(*stream, line)) { + ++line_number; + if (!line.empty() && line.back() == '\r') line.pop_back(); + if (trim(line).empty()) continue; + lines.emplace_back(line_number, line); + } + if (lines.empty()) throw IoError(source + ": no data rows found"); + if (lines.front().second.size() >= 3 && + static_cast(lines.front().second[0]) == 0xEF && + static_cast(lines.front().second[1]) == 0xBB && + static_cast(lines.front().second[2]) == 0xBF) { + lines.front().second.erase(0, 3); + } + + const std::size_t commas = delimiter_count(lines.front().second, ','); + const std::size_t tabs = delimiter_count(lines.front().second, '\t'); + const char delimiter = tabs > commas ? '\t' : ','; + auto first = parse_row(lines.front().second, delimiter, source, lines.front().first); + if (first.empty()) throw IoError(source + ": first row is empty"); + + std::size_t selected = 0; + std::string selected_name; + bool has_header = false; + if (!column.empty() && is_index(column)) { + try { + selected = static_cast(std::stoull(column)); + } catch (const std::exception&) { + throw IoError("column index is out of range: " + column); + } + if (selected >= first.size()) { + throw IoError(source + ": selected column " + std::to_string(selected) + + " exceeds row width " + std::to_string(first.size())); + } + has_header = !number(first[selected]).has_value(); + } else if (!column.empty()) { + has_header = true; + const auto found = std::find(first.begin(), first.end(), column); + if (found == first.end()) throw IoError(source + ": header has no column named '" + column + "'"); + selected = static_cast(std::distance(first.begin(), found)); + if (std::find(found + 1, first.end(), column) != first.end()) { + throw IoError(source + ": duplicate header column '" + column + "'"); + } + } else { + has_header = !number(first.front()).has_value(); + } + + selected_name = has_header ? first[selected] : std::to_string(selected); + + const std::size_t data_begin = has_header ? 1 : 0; + std::vector values; + values.reserve(lines.size() - data_begin); + const std::size_t expected_width = first.size(); + for (std::size_t row_index = data_begin; row_index < lines.size(); ++row_index) { + const auto& [physical_line, text] = lines[row_index]; + const auto fields = parse_row(text, delimiter, source, physical_line); + if (fields.size() != expected_width) { + throw IoError(source + ":" + std::to_string(physical_line) + ": mixed column width; expected " + + std::to_string(expected_width) + ", found " + std::to_string(fields.size())); + } + const auto parsed = number(fields[selected]); + if (!parsed.has_value()) { + throw IoError(source + ":" + std::to_string(physical_line) + ":" + + std::to_string(selected + 1) + ": missing, non-finite, or invalid number '" + fields[selected] + "'"); + } + values.push_back(*parsed); + } + if (values.empty()) throw IoError(source + ": no numeric samples found"); + + InputSignal result; + result.values = Eigen::Map(values.data(), static_cast(values.size())); + result.source = source; + result.column_name = selected_name; + result.delimiter = delimiter; + result.has_header = has_header; + return result; +} + +InputSignal demo_signal(double sample_rate_hz, std::size_t sample_count) { + constexpr double pi = 3.141592653589793238462643383279502884; + InputSignal input; + input.values.resize(static_cast(sample_count)); + for (std::size_t i = 0; i < sample_count; ++i) { + const double time = static_cast(i) / sample_rate_hz; + input.values(static_cast(i)) = + std::cos(2.0 * pi * 2.0 * time) + + 0.1 * std::cos(2.0 * pi * 24.0 * time) + + 0.005 * std::cos(2.0 * pi * 288.0 * time); + } + input.source = "demo"; + input.column_name = "value"; + input.has_header = true; + return input; +} + +std::vector parse_alpha_list(const std::string& text) { + std::vector values; + std::stringstream stream(text); + std::string field; + while (std::getline(stream, field, ',')) { + const auto parsed = number(trim(field)); + if (!parsed.has_value() || *parsed <= 0.0) throw std::invalid_argument("--alpha expects positive comma-separated numbers"); + values.push_back(*parsed); + } + if (values.empty()) throw std::invalid_argument("--alpha must not be empty"); + return values; +} + +std::string initialization_name(Initialization initialization) { + switch (initialization) { + case Initialization::Zero: return "zero"; + case Initialization::Uniform: return "uniform"; + case Initialization::Random: return "random"; + } + return "unknown"; +} + +OutputPaths write_csv_outputs( + const std::filesystem::path& output_directory, + const InputSignal& input, + const Result& result, + double sample_rate_hz) { + std::error_code error; + std::filesystem::create_directories(output_directory, error); + if (error) throw IoError("cannot create output directory '" + output_directory.string() + "': " + error.message()); + + OutputPaths paths; + paths.modes = output_directory / "modes.csv"; + paths.spectra = output_directory / "spectra.csv"; + paths.omega = output_directory / "omega.csv"; + paths.summary = output_directory / "summary.json"; + paths.report = output_directory / "report.html"; + + { + auto output = open_output(paths.modes); + output << "index,time_s,signal"; + for (Eigen::Index k = 0; k < result.modes.rows(); ++k) output << ",imf_" << (k + 1); + output << ",reconstruction,residual\n"; + for (Eigen::Index i = 0; i < input.values.size(); ++i) { + const double reconstruction = result.modes.col(i).sum(); + output << i << ',' << static_cast(i) / sample_rate_hz << ',' << input.values(i); + for (Eigen::Index k = 0; k < result.modes.rows(); ++k) output << ',' << result.modes(k, i); + output << ',' << reconstruction << ',' << input.values(i) - reconstruction << '\n'; + } + } + + { + auto output = open_output(paths.spectra); + output << "index,frequency_hz"; + for (Eigen::Index k = 0; k < result.spectra.cols(); ++k) { + output << ",imf_" << (k + 1) << "_real,imf_" << (k + 1) << "_imag,imf_" << (k + 1) << "_magnitude"; + } + output << '\n'; + const Eigen::Index n = result.spectra.rows(); + const Eigen::Index center = n / 2; + for (Eigen::Index i = 0; i < n; ++i) { + const double frequency = static_cast(i - center) * sample_rate_hz / static_cast(n); + output << i << ',' << frequency; + for (Eigen::Index k = 0; k < result.spectra.cols(); ++k) { + const auto value = result.spectra(i, k); + output << ',' << value.real() << ',' << value.imag() << ',' << std::abs(value); + } + output << '\n'; + } + } + + { + auto output = open_output(paths.omega); + output << "iteration"; + for (Eigen::Index k = 0; k < result.omega_hz.cols(); ++k) output << ",imf_" << (k + 1) << "_hz"; + output << '\n'; + for (Eigen::Index i = 0; i < result.omega_hz.rows(); ++i) { + output << i; + for (Eigen::Index k = 0; k < result.omega_hz.cols(); ++k) output << ',' << result.omega_hz(i, k); + output << '\n'; + } + } + return paths; +} + +void write_summary_json( + const std::filesystem::path& path, + const InputSignal& input, + const Options& options, + const Result& result, + const CliTimings& timings, + const OutputPaths& outputs, + bool report_generated) { + auto output = open_output(path); + output << "{\n" + << " \"schema_version\": 1,\n" + << " \"input\": {\n" + << " \"source\": \"" << json_escape(input.source) << "\",\n" + << " \"column\": \"" << json_escape(input.column_name) << "\",\n" + << " \"samples\": " << input.values.size() << ",\n" + << " \"sample_rate_hz\": " << options.sample_rate_hz << ",\n" + << " \"delimiter\": \"" << (input.delimiter == '\t' ? "tab" : "comma") << "\",\n" + << " \"has_header\": " << (input.has_header ? "true" : "false") << "\n" + << " },\n" + << " \"options\": {\n" + << " \"mode_count\": " << options.mode_count << ",\n" + << " \"alpha\": ["; + for (std::size_t i = 0; i < options.alpha.size(); ++i) { + if (i != 0) output << ", "; + output << options.alpha[i]; + } + output << "],\n" + << " \"tau\": " << options.tau << ",\n" + << " \"dc_mode\": " << (options.dc_mode ? "true" : "false") << ",\n" + << " \"initialization\": \"" << initialization_name(options.initialization) << "\",\n" + << " \"tolerance\": " << options.tolerance << ",\n" + << " \"max_iterations\": " << options.max_iterations << ",\n" + << " \"random_seed\": " << options.random_seed << ",\n" + << " \"keep_omega_history\": " << (options.keep_omega_history ? "true" : "false") << ",\n" + << " \"adaptive_tau\": " << (options.adaptive_tau ? "true" : "false") << "\n" + << " },\n" + << " \"diagnostics\": {\n" + << " \"converged\": " << (result.diagnostics.converged ? "true" : "false") << ",\n" + << " \"iterations\": " << result.diagnostics.iterations << ",\n" + << " \"final_mode_delta\": " << result.diagnostics.final_mode_delta << ",\n" + << " \"reconstruction_error\": " << result.diagnostics.reconstruction_error << ",\n" + << " \"primal_residual\": " << result.diagnostics.primal_residual << ",\n" + << " \"dual_residual\": " << result.diagnostics.dual_residual << ",\n" + << " \"final_tau\": " << result.diagnostics.final_tau << "\n" + << " },\n" + << " \"timings_ms\": {\n" + << " \"input\": " << timings.input_ms << ",\n" + << " \"mirror_fft\": " << result.timings.mirror_and_fft_ms << ",\n" + << " \"iteration\": " << result.timings.iteration_ms << ",\n" + << " \"spectrum_reconstruction\": " << result.timings.reconstruction_ms << ",\n" + << " \"serialization\": " << timings.serialization_ms << ",\n" + << " \"report\": " << timings.report_ms << "\n" + << " },\n" + << " \"outputs\": {\n" + << " \"modes\": \"" << json_escape(output_name(outputs.modes)) << "\",\n" + << " \"spectra\": \"" << json_escape(output_name(outputs.spectra)) << "\",\n" + << " \"omega\": \"" << json_escape(output_name(outputs.omega)) << "\",\n" + << " \"summary\": \"" << json_escape(output_name(outputs.summary)) << "\",\n" + << " \"report\": " << (report_generated ? "\"report.html\"" : "null") << "\n" + << " }\n" + << "}\n"; +} + +} // namespace vmd::cli diff --git a/app/cli_io.hpp b/app/cli_io.hpp new file mode 100644 index 0000000..c14c502 --- /dev/null +++ b/app/cli_io.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include + +#include + +#include +#include +#include +#include + +namespace vmd::cli { + +class IoError : public std::runtime_error { +public: + using std::runtime_error::runtime_error; +}; + +struct InputSignal { + Eigen::VectorXd values; + std::string source; + std::string column_name; + char delimiter = ','; + bool has_header = false; +}; + +struct OutputPaths { + std::filesystem::path modes; + std::filesystem::path spectra; + std::filesystem::path omega; + std::filesystem::path summary; + std::filesystem::path report; +}; + +struct CliTimings { + double input_ms = 0.0; + double serialization_ms = 0.0; + double report_ms = 0.0; +}; + +InputSignal read_signal(const std::string& path, const std::string& column); +InputSignal demo_signal(double sample_rate_hz, std::size_t sample_count = 1200); +std::vector parse_alpha_list(const std::string& text); +std::string initialization_name(Initialization initialization); + +OutputPaths write_csv_outputs( + const std::filesystem::path& output_directory, + const InputSignal& input, + const Result& result, + double sample_rate_hz); + +void write_summary_json( + const std::filesystem::path& path, + const InputSignal& input, + const Options& options, + const Result& result, + const CliTimings& timings, + const OutputPaths& outputs, + bool report_generated); + +} // namespace vmd::cli diff --git a/app/report.cpp b/app/report.cpp new file mode 100644 index 0000000..76182c2 --- /dev/null +++ b/app/report.cpp @@ -0,0 +1,151 @@ +#include "report.hpp" + +#include +#include +#include +#include +#include +#include + +namespace vmd::cli { +namespace { + +struct Point { double x; double y; }; + +std::string html_escape(const std::string& value) { + std::string result; + for (char character : value) { + switch (character) { + case '&': result += "&"; break; + case '<': result += "<"; break; + case '>': result += ">"; break; + case '"': result += """; break; + case '\'': result += "'"; break; + default: result += character; + } + } + return result; +} + +std::vector envelope(const std::vector& input, std::size_t max_intervals = 2000) { + if (input.size() <= max_intervals) return input; + const std::size_t buckets = std::max(1, max_intervals / 2); + const double width = static_cast(input.size()) / static_cast(buckets); + std::vector output; + output.reserve(buckets * 2); + for (std::size_t bucket = 0; bucket < buckets; ++bucket) { + const std::size_t begin = static_cast(std::floor(static_cast(bucket) * width)); + const std::size_t end = std::min(input.size(), static_cast(std::ceil(static_cast(bucket + 1) * width))); + if (begin >= end) continue; + std::size_t minimum = begin; + std::size_t maximum = begin; + for (std::size_t i = begin + 1; i < end; ++i) { + if (input[i].y < input[minimum].y) minimum = i; + if (input[i].y > input[maximum].y) maximum = i; + } + if (minimum <= maximum) { + output.push_back(input[minimum]); + if (maximum != minimum) output.push_back(input[maximum]); + } else { + output.push_back(input[maximum]); + output.push_back(input[minimum]); + } + } + return output; +} + +void write_points(std::ostream& output, const std::vector& points) { + output << '['; + for (std::size_t i = 0; i < points.size(); ++i) { + if (i != 0) output << ','; + output << '[' << points[i].x << ',' << points[i].y << ']'; + } + output << ']'; +} + +std::vector vector_series(const Eigen::VectorXd& values, double sample_rate_hz) { + std::vector points(static_cast(values.size())); + for (Eigen::Index i = 0; i < values.size(); ++i) { + points[static_cast(i)] = {static_cast(i) / sample_rate_hz, values(i)}; + } + return envelope(points); +} + +std::vector mode_series(const Result& result, Eigen::Index mode, double sample_rate_hz) { + std::vector points(static_cast(result.modes.cols())); + for (Eigen::Index i = 0; i < result.modes.cols(); ++i) { + points[static_cast(i)] = {static_cast(i) / sample_rate_hz, result.modes(mode, i)}; + } + return envelope(points); +} + +std::vector spectrum_series(const Result& result, Eigen::Index mode, double sample_rate_hz) { + const Eigen::Index n = result.spectra.rows(); + const Eigen::Index center = n / 2; + std::vector points; + points.reserve(static_cast(n - center)); + for (Eigen::Index i = center; i < n; ++i) { + points.push_back({static_cast(i - center) * sample_rate_hz / static_cast(n), + std::abs(result.spectra(i, mode))}); + } + return envelope(points); +} + +std::vector omega_series(const Result& result, Eigen::Index mode) { + std::vector points(static_cast(result.omega_hz.rows())); + for (Eigen::Index i = 0; i < result.omega_hz.rows(); ++i) { + points[static_cast(i)] = {static_cast(i), result.omega_hz(i, mode)}; + } + return envelope(points); +} + +const char* color(Eigen::Index index) { + static constexpr const char* palette[] = { + "#1769aa", "#c43c39", "#27845f", "#8a4fa3", "#d17b0f", + "#007f86", "#b13c75", "#5d6d2c", "#6b5fca", "#8b5a2b" + }; + return palette[static_cast(index) % (sizeof(palette) / sizeof(palette[0]))]; +} + +} // namespace + +void write_html_report( + const std::filesystem::path& path, + const InputSignal& input, + const Options& options, + const Result& result) { + std::ofstream output(path, std::ios::binary); + if (!output) throw IoError("cannot open report file: " + path.string()); + output << std::setprecision(12); + const Eigen::VectorXd reconstruction = result.modes.colwise().sum().transpose(); + const Eigen::VectorXd residual = input.values - reconstruction; + + output << R"HTML(VMD decomposition report +

VMD decomposition report

)HTML"; + output << "" + << (result.diagnostics.converged ? "Converged" : "Not converged") << "

" + << html_escape(input.source) << " · column " << html_escape(input.column_name) << " · " + << input.values.size() << " samples at " << options.sample_rate_hz << " Hz

"; + output << R"HTML(
)HTML"; + output << "
" << result.modes.rows() << "modes
" + << "
" << result.diagnostics.iterations << "iterations
" + << "
" << result.diagnostics.reconstruction_error << "relative reconstruction error
" + << "
" << result.timings.iteration_ms << " msmain iteration
"; + output << R"HTML(

Input and reconstruction

Time in seconds. Hover for sample values.

Reconstruction residual

Input minus the sum of all modes.

Intrinsic mode functions

Mode controls also update spectrum and convergence views.

Mode spectra

One-sided magnitude spectrum in hertz.

Center-frequency convergence

Estimated center frequency by iteration, in hertz.

Generated by VMD_cpp. Plot data uses a min/max envelope; CSV files retain every sample.
)HTML"; +} + +} // namespace vmd::cli diff --git a/app/report.hpp b/app/report.hpp new file mode 100644 index 0000000..eeb842c --- /dev/null +++ b/app/report.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include "cli_io.hpp" + +#include + +namespace vmd::cli { + +void write_html_report( + const std::filesystem::path& path, + const InputSignal& input, + const Options& options, + const Result& result); + +} // namespace vmd::cli diff --git a/app/vmd_cli.cpp b/app/vmd_cli.cpp new file mode 100644 index 0000000..6f0229c --- /dev/null +++ b/app/vmd_cli.cpp @@ -0,0 +1,181 @@ +#include "cli_io.hpp" +#include "report.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +using Clock = std::chrono::steady_clock; + +struct Arguments { + std::string input; + std::string column; + std::filesystem::path output_directory = "vmd-output"; + vmd::Options options; + bool sample_rate_set = false; + bool demo = false; + bool report = false; + bool help = false; +}; + +void print_help(std::ostream& output) { + output << R"HELP(VMD_cpp command-line interface + +Usage: + vmd_cli --input signal.csv --column value --sample-rate 1000 \ + --modes 6 --alpha 2000 --output-dir results --report + vmd_cli --demo --sample-rate 1200 --modes 8 --alpha 50 --report + +Input: + --input PATH CSV/TSV path, or - for standard input + --column NAME|INDEX Column name or zero-based column index (default: 0) + --demo Use the built-in deterministic example signal + +Algorithm: + --sample-rate HZ Sampling rate in hertz (default: 1; demo: 1200) + --modes K Number of modes (default: 3) + --alpha A[,A...] One alpha or one value per mode (default: 2000) + --tau VALUE Dual ascent step, non-negative (default: 0) + --dc Keep the first mode at zero frequency + --init zero|uniform|random + --tol VALUE Convergence tolerance (default: 1e-7) + --max-iterations N Maximum iterations (default: 500) + --seed N Random initialization seed (default: 0) + --adaptive-tau Enable residual-balanced tau adaptation + --no-omega-history Keep only final center frequencies + +Output: + --output-dir PATH Output directory (default: vmd-output) + --report Generate self-contained report.html + --help Show this help + +Exit codes: 0 success, 2 argument error, 3 I/O error, 4 nonconvergence. +)HELP"; +} + +template +Integer integer_value(const std::string& text, const std::string& option) { + std::size_t consumed = 0; + try { + if (text.empty() || text.front() == '-') throw std::invalid_argument(""); + const unsigned long long value = std::stoull(text, &consumed); + if (consumed != text.size() || value > static_cast(std::numeric_limits::max())) { + throw std::invalid_argument(""); + } + return static_cast(value); + } catch (const std::exception&) { + throw std::invalid_argument(option + " expects a non-negative integer"); + } +} + +double real_value(const std::string& text, const std::string& option) { + std::size_t consumed = 0; + try { + const double value = std::stod(text, &consumed); + if (consumed != text.size() || !std::isfinite(value)) throw std::invalid_argument(""); + return value; + } catch (const std::exception&) { + throw std::invalid_argument(option + " expects a finite number"); + } +} + +Arguments parse_arguments(int argc, char** argv) { + Arguments arguments; + for (int i = 1; i < argc; ++i) { + const std::string option = argv[i]; + const auto value = [&]() -> std::string { + if (i + 1 >= argc) throw std::invalid_argument(option + " requires a value"); + return argv[++i]; + }; + + if (option == "--help" || option == "-h") arguments.help = true; + else if (option == "--input") arguments.input = value(); + else if (option == "--column") arguments.column = value(); + else if (option == "--sample-rate") { arguments.options.sample_rate_hz = real_value(value(), option); arguments.sample_rate_set = true; } + else if (option == "--modes") arguments.options.mode_count = integer_value(value(), option); + else if (option == "--alpha") arguments.options.alpha = vmd::cli::parse_alpha_list(value()); + else if (option == "--tau") arguments.options.tau = real_value(value(), option); + else if (option == "--dc") arguments.options.dc_mode = true; + else if (option == "--init") { + const std::string init = value(); + if (init == "zero") arguments.options.initialization = vmd::Initialization::Zero; + else if (init == "uniform") arguments.options.initialization = vmd::Initialization::Uniform; + else if (init == "random") arguments.options.initialization = vmd::Initialization::Random; + else throw std::invalid_argument("--init expects zero, uniform, or random"); + } else if (option == "--tol") arguments.options.tolerance = real_value(value(), option); + else if (option == "--max-iterations") arguments.options.max_iterations = integer_value(value(), option); + else if (option == "--seed") arguments.options.random_seed = integer_value(value(), option); + else if (option == "--adaptive-tau") arguments.options.adaptive_tau = true; + else if (option == "--no-omega-history") arguments.options.keep_omega_history = false; + else if (option == "--output-dir") arguments.output_directory = value(); + else if (option == "--report") arguments.report = true; + else if (option == "--demo") arguments.demo = true; + else throw std::invalid_argument("unknown argument: " + option); + } + if (arguments.demo && !arguments.input.empty()) throw std::invalid_argument("--demo and --input cannot be used together"); + if (arguments.demo && !arguments.sample_rate_set) arguments.options.sample_rate_hz = 1200.0; + return arguments; +} + +} // namespace + +int main(int argc, char** argv) { + try { + Arguments arguments = parse_arguments(argc, argv); + if (arguments.help) { + print_help(std::cout); + return 0; + } + if (!arguments.demo && arguments.input.empty()) { + print_help(std::cerr); + return 2; + } + + vmd::cli::CliTimings timings; + const auto input_start = Clock::now(); + vmd::cli::InputSignal input = arguments.demo + ? vmd::cli::demo_signal(arguments.options.sample_rate_hz) + : vmd::cli::read_signal(arguments.input, arguments.column); + timings.input_ms = std::chrono::duration(Clock::now() - input_start).count(); + + const vmd::Result result = vmd::decompose(input.values, arguments.options); + + const auto serialization_start = Clock::now(); + vmd::cli::OutputPaths outputs = vmd::cli::write_csv_outputs( + arguments.output_directory, input, result, arguments.options.sample_rate_hz); + timings.serialization_ms = std::chrono::duration(Clock::now() - serialization_start).count(); + + if (arguments.report) { + const auto report_start = Clock::now(); + vmd::cli::write_html_report(outputs.report, input, arguments.options, result); + timings.report_ms = std::chrono::duration(Clock::now() - report_start).count(); + } + vmd::cli::write_summary_json( + outputs.summary, input, arguments.options, result, timings, outputs, arguments.report); + + std::cout << std::setprecision(8) + << "VMD " << (result.diagnostics.converged ? "converged" : "did not converge") + << " after " << result.diagnostics.iterations << " iterations\n" + << "reconstruction error: " << result.diagnostics.reconstruction_error << '\n' + << "outputs: " << std::filesystem::absolute(arguments.output_directory).string() << '\n'; + return result.diagnostics.converged ? 0 : 4; + } catch (const std::invalid_argument& error) { + std::cerr << "argument error: " << error.what() << '\n'; + return 2; + } catch (const vmd::cli::IoError& error) { + std::cerr << "I/O error: " << error.what() << '\n'; + return 3; + } catch (const std::exception& error) { + std::cerr << "error: " << error.what() << '\n'; + return 3; + } +} diff --git a/benchmarks/vmd_benchmark.cpp b/benchmarks/vmd_benchmark.cpp new file mode 100644 index 0000000..6254060 --- /dev/null +++ b/benchmarks/vmd_benchmark.cpp @@ -0,0 +1,82 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +template +Integer positive_integer(const char* text, const char* name) { + const std::string value(text); + std::size_t consumed = 0; + if (value.empty() || value.front() == '-') { + throw std::invalid_argument(std::string(name) + " must be a positive integer"); + } + const unsigned long long parsed = std::stoull(value, &consumed); + if (consumed != value.size() || parsed == 0) { + throw std::invalid_argument(std::string(name) + " must be a positive integer"); + } + return static_cast(parsed); +} + +} // namespace + +int main(int argc, char** argv) { + try { + const Eigen::Index n = argc > 1 ? positive_integer(argv[1], "N") : 1200; + const std::size_t k = argc > 2 ? positive_integer(argv[2], "K") : 8; + const std::size_t repetitions = argc > 3 ? positive_integer(argv[3], "repetitions") : 1; + constexpr double pi = 3.14159265358979323846; + Eigen::VectorXd signal(n); + for (Eigen::Index i = 0; i < n; ++i) { + const double t = static_cast(i) / static_cast(n); + signal(i) = std::cos(2.0 * pi * 3.0 * t) + 0.2 * std::cos(2.0 * pi * 71.0 * t); + } + + vmd::Options options; + options.mode_count = k; + options.alpha = {2000.0}; + options.max_iterations = 500; + options.keep_omega_history = true; + options.sample_rate_hz = static_cast(n); + + vmd::Result result; + double mirror_fft_total = 0.0; + double iteration_total = 0.0; + double reconstruction_total = 0.0; + const auto start = std::chrono::steady_clock::now(); + for (std::size_t repeat = 0; repeat < repetitions; ++repeat) { + result = vmd::decompose(signal, options); + mirror_fft_total += result.timings.mirror_and_fft_ms; + iteration_total += result.timings.iteration_ms; + reconstruction_total += result.timings.reconstruction_ms; + } + const double total = std::chrono::duration( + std::chrono::steady_clock::now() - start).count(); + + const double working_set_estimate = static_cast( + 2 * k * static_cast(n) * sizeof(std::complex) + + 7 * static_cast(n) * sizeof(std::complex) + + 5 * static_cast(n) * sizeof(double)); + const double divisor = static_cast(repetitions); + + std::cout << std::fixed << std::setprecision(3) + << "N=" << n << " K=" << k << " repetitions=" << repetitions + << " iterations=" << result.diagnostics.iterations << '\n' + << "mirror_fft_mean_ms=" << mirror_fft_total / divisor << '\n' + << "iteration_mean_ms=" << iteration_total / divisor << '\n' + << "reconstruction_mean_ms=" << reconstruction_total / divisor << '\n' + << "total_mean_ms=" << total / divisor << '\n' + << "estimated_core_workspace_mib=" << working_set_estimate / (1024.0 * 1024.0) << '\n'; + return 0; + } catch (const std::exception& error) { + std::cerr << "benchmark error: " << error.what() << '\n'; + return 2; + } +} diff --git a/cmake/VMD_cppConfig.cmake.in b/cmake/VMD_cppConfig.cmake.in new file mode 100644 index 0000000..1a193bc --- /dev/null +++ b/cmake/VMD_cppConfig.cmake.in @@ -0,0 +1,20 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) +find_dependency(Eigen3 3.3 CONFIG) + +set(VMD_cpp_FFT_BACKEND "@VMD_FFT_BACKEND_UPPER@") +if(VMD_cpp_FFT_BACKEND STREQUAL "FFTW" AND NOT TARGET FFTW3::fftw3) + find_path(FFTW3_INCLUDE_DIR fftw3.h REQUIRED) + find_library(FFTW3_LIBRARY NAMES fftw3 libfftw3-3 REQUIRED) + add_library(FFTW3::fftw3 UNKNOWN IMPORTED) + set_target_properties(FFTW3::fftw3 PROPERTIES + IMPORTED_LOCATION "${FFTW3_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${FFTW3_INCLUDE_DIR}" + ) +elseif(VMD_cpp_FFT_BACKEND STREQUAL "MKL") + find_dependency(MKL CONFIG) +endif() + +include("${CMAKE_CURRENT_LIST_DIR}/VMD_cppTargets.cmake") +check_required_components(VMD_cpp) diff --git a/docs/algorithm.md b/docs/algorithm.md new file mode 100644 index 0000000..bf2b710 --- /dev/null +++ b/docs/algorithm.md @@ -0,0 +1,118 @@ +# Algorithm and Numerical Behavior + +## Objective + +VMD represents a real signal `f(t)` as a sum of band-limited modes `u_k(t)`. +For center frequencies `omega_k`, the constrained objective minimizes the summed +bandwidth of each demodulated analytic mode while enforcing reconstruction. The +implementation follows the standard augmented-Lagrangian/ADMM update in the +frequency domain: + +```text +u_k <- (f_plus - sum_{i != k} u_i - lambda / 2) + / (1 + alpha_k * (frequency - omega_k)^2) + +omega_k <- dot(frequency, abs(u_k)^2) / sum(abs(u_k)^2) + +lambda <- lambda + tau * (sum_k u_k - f_plus) +``` + +`alpha_k` may differ by mode. A single `Options::alpha` value is broadcast to all +modes. + +## Frequency Layout + +The mirrored signal has length `2N`. A real-to-complex FFT produces its +`N + 1` non-negative bins; the solver stores only bins `j = 0 ... N-1` because +the Nyquist bin is outside the analytic update interval. Normalized frequencies +are `j / (2N)`, in cycles per sample, and therefore lie in `[0, 0.5)`. This is +equivalent to the positive half of the shifted spectrum used by the reference +formulation, but avoids allocating or transforming a redundant negative half. + +Public spectra have a different, explicit contract: after every time-domain IMF +is cropped back to `N` samples, the implementation performs a real-to-complex +FFT, reconstructs the Hermitian half, and applies `fftshift`. `Result::spectra` +is always `N x K`. It never exposes the mirrored-domain iterate. + +For an output row `i`, the corresponding shifted frequency in hertz is: + +```text +(i - floor(N / 2)) * sample_rate_hz / N +``` + +## Mirror Boundary + +For input length `N`, the extension contains: + +```text +reverse(first floor(N/2) samples) +original N samples +reverse(last ceil(N/2) samples) +``` + +The total mirrored length is exactly `2N` for both odd and even inputs. Cropping +starts at `floor(N/2)` and therefore returns exactly the original sample count. + +## Initialization + +- `Zero`: all initial center frequencies are zero. +- `Uniform`: mode `k` starts at `0.5 * k / K`. +- `Random`: log-uniform values are drawn in the resolvable positive frequency + interval with `std::mt19937_64`, sorted, and controlled by `random_seed`. + +If `dc_mode` is enabled, mode zero is fixed at frequency zero. Optional warm-start +modes are mirrored and transformed into the internal positive-frequency layout. +Optional warm-start frequencies are normalized cycles per sample in `[0, 0.5]`. + +## Degenerate Inputs + +The center-frequency denominator can be zero for a zero signal or an empty mode. +When mode energy is below a scale-aware floor, the previous frequency is retained +instead of dividing by zero. Length-one FFTs are handled as identity transforms +because some FFT backends do not support that plan size safely. Inputs and all +public results are checked for finite values. + +## Stopping and Diagnostics + +The compatibility stopping metric is retained: + +```text +mode_delta = epsilon + squaredNorm(current - previous) / mirrored_length +``` + +The solver stops when `mode_delta <= tolerance` or `max_iterations` is reached. +It also reports: + +- `reconstruction_error`: time-domain relative norm of `sum(modes) - signal`. +- `primal_residual`: relative spectral norm of `sum(mode_iterates) - input_plus`. +- `dual_residual`: `tau` times the relative iterate-change norm. + +For a zero input, reconstruction error is normalized by 1 rather than zero. + +## Adaptive Tau + +Adaptive `tau` is disabled by default. When enabled, residual balancing increases +or decreases `tau` if one residual exceeds the other by +`adaptive_tau_balance`, while clamping to `[tau_min, tau_max]`. This is an +advanced convergence heuristic and may change the decomposition path; use a fixed +`tau` when reproducing historical results. + +## Memory Model + +The main mode iterate uses two `K x N` complex buffers. The dual variable is +updated in place. Residual, numerator, denominator, frequency, and energy vectors +are preallocated. The core workspace is therefore `O(K * N)` and does not grow +with `max_iterations`. + +Center-frequency history is the only optional iteration-sized output. Set +`keep_omega_history = false` to retain only the final `1 x K` row. + +## FFT Backends + +- `Eigen`: default and dependency-free beyond Eigen. +- `FFTW`: selected with `-DVMD_FFT_BACKEND=FFTW`. +- `MKL`: selected with `-DVMD_FFT_BACKEND=MKL`. + +Each decomposition owns one backend object and reuses its plans and buffers across +mirror, warm-start, reconstruction, and public-spectrum transforms. Real inputs +use the backend's dedicated real-to-complex half-spectrum path. diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..fbad666 --- /dev/null +++ b/docs/api.md @@ -0,0 +1,115 @@ +# C++ API + +Include the public header: + +```cpp +#include +``` + +All new API types are in namespace `vmd`. The library requires C++17 and exposes +Eigen matrix types in its public interface. + +## `Initialization` + +| Value | Behavior | +|---|---| +| `Zero` | All initial center frequencies are zero. | +| `Uniform` | Frequencies are distributed from zero toward Nyquist. | +| `Random` | Sorted log-uniform frequencies generated from `random_seed`. | + +## `Options` + +| Field | Default | Meaning | +|---|---:|---| +| `mode_count` | `3` | Number of modes `K`; must be positive. | +| `alpha` | `{2000}` | One positive value or exactly `K` positive values. | +| `tau` | `0` | Non-negative dual ascent step. | +| `dc_mode` | `false` | Fix the first center frequency at zero. | +| `initialization` | `Uniform` | Initial frequency strategy. | +| `tolerance` | `1e-7` | Positive stopping threshold for mode delta. | +| `max_iterations` | `500` | Positive iteration limit. | +| `random_seed` | `0` | Seed used by random initialization. | +| `sample_rate_hz` | `1` | Positive sample rate used to derive `omega_hz`. | +| `keep_omega_history` | `true` | Keep every frequency row instead of only the final row. | +| `warm_start_modes` | empty | Optional `K x N` finite time-domain modes. | +| `warm_start_omega_normalized` | empty | Optional finite `K`-vector in `[0, 0.5]`. | +| `adaptive_tau` | `false` | Enable residual-balanced step adjustment. | +| `adaptive_tau_balance` | `10` | Residual imbalance threshold, greater than one. | +| `adaptive_tau_increase` | `2` | Multiplicative increase, greater than one. | +| `adaptive_tau_decrease` | `2` | Multiplicative decrease divisor, greater than one. | +| `tau_min` | `1e-8` | Finite non-negative lower bound. | +| `tau_max` | `1e3` | Finite upper bound, not below `tau_min`. | + +## `Result` + +| Field | Shape/unit | Meaning | +|---|---|---| +| `modes` | `K x N` | Time-domain intrinsic mode functions. | +| `spectra` | `N x K` | `fftshift(fft(mode))`, complex and unnormalized. | +| `omega_normalized` | `I x K` | Center frequencies in cycles per sample. | +| `omega_hz` | `I x K` | Center frequencies in hertz. | +| `diagnostics` | scalar fields | Convergence and residual metrics. | +| `timings` | milliseconds | Mirror/FFT, iteration, and reconstruction durations. | + +`I` is `iterations + 1` when history is enabled because row zero contains the +initial frequencies. It is one when history is disabled. + +## Diagnostics + +- `converged`: the final mode delta met `tolerance`. +- `iterations`: completed update passes. +- `final_mode_delta`: compatibility stopping metric. +- `reconstruction_error`: relative time-domain reconstruction norm. +- `primal_residual`: relative frequency-domain constraint residual. +- `dual_residual`: scaled relative iterate change. +- `final_tau`: final dual ascent step after optional adaptation. + +## Function + +```cpp +vmd::Result vmd::decompose( + Eigen::Ref signal, + const vmd::Options& options); +``` + +The signal must be non-empty and finite. Invalid arguments throw +`std::invalid_argument`. A non-finite internal result throws `std::runtime_error`. +Nonconvergence is not an exception; inspect `Result::diagnostics.converged`. + +Inputs are borrowed only for the duration of the call. Results and work buffers +own their storage. The implementation has no mutable decomposition globals, so +independent calls are reentrant. Backend library initialization rules still apply +when an application selects FFTW or MKL. + +## Warm Start Example + +```cpp +vmd::Result first = vmd::decompose(signal, options); + +vmd::Options next = options; +next.warm_start_modes = first.modes; +next.warm_start_omega_normalized = + first.omega_normalized.bottomRows(1).transpose(); + +vmd::Result refined = vmd::decompose(signal, next); +``` + +Warm starts must use the same sample count and mode count. + +## Legacy API + +`VMD.h` retains the original global wrapper: + +```cpp +void VMD(Eigen::MatrixXd& u, + Eigen::MatrixXcd& u_hat, + Eigen::MatrixXd& omega, + const std::vector& signal, + double alpha, double tau, int K, int DC, int init, + double tol, double eps); +``` + +The wrapper uses at most 500 iterations and maps `init` values 0, 1, and 2 to +zero, uniform, and random initialization. `eps` is accepted for source +compatibility. Prefer `vmd::decompose` for explicit seeds, sample rates, per-mode +alpha, warm starts, adaptive tau, and diagnostics. diff --git a/docs/cli-and-formats.md b/docs/cli-and-formats.md new file mode 100644 index 0000000..2af0b0a --- /dev/null +++ b/docs/cli-and-formats.md @@ -0,0 +1,122 @@ +# CLI and File Formats + +## Invocation + +```text +vmd_cli --input signal.csv --column value --sample-rate 1000 \ + --modes 6 --alpha 2000 --output-dir results --report +``` + +Use `--input -` for standard input. Use `--demo` for the deterministic built-in +1,200-sample signal. `--demo` and `--input` are mutually exclusive. + +## Options + +| Option | Value | Default | +|---|---|---| +| `--input` | path or `-` | none | +| `--column` | name or zero-based index | `0` | +| `--demo` | flag | off | +| `--sample-rate` | positive hertz | `1`; demo uses `1200` | +| `--modes` | positive integer | `3` | +| `--alpha` | one value or comma-separated `K` values | `2000` | +| `--tau` | non-negative real | `0` | +| `--dc` | flag | off | +| `--init` | `zero`, `uniform`, or `random` | `uniform` | +| `--tol` | positive real | `1e-7` | +| `--max-iterations` | positive integer | `500` | +| `--seed` | unsigned integer | `0` | +| `--adaptive-tau` | flag | off | +| `--no-omega-history` | flag | off | +| `--output-dir` | path | `vmd-output` | +| `--report` | flag | off | +| `--help`, `-h` | flag | off | + +## Input Rules + +- Comma and tab delimiters are detected from the first non-empty row. +- UTF-8 BOM, CRLF, and blank physical lines are accepted. +- The selected column may be a header name or a zero-based index. +- Header detection for an indexed column uses that selected field, allowing text + metadata in unselected columns. +- Quoted fields and doubled quotes are supported. +- Every non-empty row must have the same number of fields. +- The selected sample must parse completely as a finite `double`. +- Missing values, `NaN`, infinity, malformed numbers, and unterminated quotes are + rejected. Errors include source, physical line, and one-based column where + applicable. + +Input does not carry time stamps into the solver. Output time is reconstructed as +`index / sample_rate_hz`. + +## `modes.csv` + +One row per input sample: + +```text +index,time_s,signal,imf_1,...,imf_K,reconstruction,residual +``` + +`reconstruction` is the sum of all IMFs. `residual` is +`signal - reconstruction`. + +## `spectra.csv` + +One row per shifted FFT bin: + +```text +index,frequency_hz, +imf_1_real,imf_1_imag,imf_1_magnitude,... +``` + +Frequency is `(index - floor(N/2)) * sample_rate_hz / N`. Complex values are the +unnormalized forward FFT of each cropped IMF after `fftshift`. + +## `omega.csv` + +```text +iteration,imf_1_hz,...,imf_K_hz +``` + +Row zero is initialization when history is enabled. With +`--no-omega-history`, the file contains a header and one final-frequency row. + +## `summary.json` + +The summary contains: + +- `schema_version` +- `input`: source, selected column, sample count, rate, delimiter, and header flag +- `options`: effective algorithm settings +- `diagnostics`: convergence and residual values +- `timings_ms`: input, core stages, serialization, and report generation +- `outputs`: generated file names + +The machine-readable schema is [summary.schema.json](summary.schema.json). Paths +in the summary are file names relative to the output directory. `outputs.report` +is `null` when `--report` is not requested. + +## `report.html` + +The report is one offline file. CSS, JavaScript, plot data, and SVG generation are +inline; there are no CDN or runtime dependencies. It provides: + +- Input and reconstruction comparison +- Reconstruction residual +- Per-mode time plots and visibility controls +- One-sided magnitude spectra +- Center-frequency convergence +- Hover readouts and SVG download buttons +- Links to the CSV and JSON files + +Plots use a min/max envelope capped at 2,000 plotted points per series. The CSV +files are never downsampled. + +## Exit Codes + +| Code | Meaning | +|---:|---| +| `0` | Successful convergence or help output. | +| `2` | CLI argument or algorithm option error. | +| `3` | Input, output, parsing, or runtime error. | +| `4` | Maximum iterations reached before convergence. Outputs are retained. | diff --git a/docs/summary.schema.json b/docs/summary.schema.json new file mode 100644 index 0000000..170fc5c --- /dev/null +++ b/docs/summary.schema.json @@ -0,0 +1,230 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://example.invalid/VMD_cpp/summary.schema.json", + "title": "VMD_cpp CLI summary", + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "input", + "options", + "diagnostics", + "timings_ms", + "outputs" + ], + "properties": { + "schema_version": { + "const": 1 + }, + "input": { + "type": "object", + "required": [ + "source", + "column", + "samples", + "sample_rate_hz", + "delimiter", + "has_header" + ], + "properties": { + "source": { + "type": "string" + }, + "column": { + "type": "string" + }, + "samples": { + "type": "integer", + "minimum": 1 + }, + "sample_rate_hz": { + "type": "number", + "exclusiveMinimum": 0 + }, + "delimiter": { + "enum": [ + "comma", + "tab" + ] + }, + "has_header": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "options": { + "type": "object", + "required": [ + "mode_count", + "alpha", + "tau", + "dc_mode", + "initialization", + "tolerance", + "max_iterations", + "random_seed", + "keep_omega_history", + "adaptive_tau" + ], + "properties": { + "mode_count": { + "type": "integer", + "minimum": 1 + }, + "alpha": { + "type": "array", + "minItems": 1, + "items": { + "type": "number", + "exclusiveMinimum": 0 + } + }, + "tau": { + "type": "number", + "minimum": 0 + }, + "dc_mode": { + "type": "boolean" + }, + "initialization": { + "enum": [ + "zero", + "uniform", + "random" + ] + }, + "tolerance": { + "type": "number", + "exclusiveMinimum": 0 + }, + "max_iterations": { + "type": "integer", + "minimum": 1 + }, + "random_seed": { + "type": "integer", + "minimum": 0 + }, + "keep_omega_history": { + "type": "boolean" + }, + "adaptive_tau": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "diagnostics": { + "type": "object", + "required": [ + "converged", + "iterations", + "final_mode_delta", + "reconstruction_error", + "primal_residual", + "dual_residual", + "final_tau" + ], + "properties": { + "converged": { + "type": "boolean" + }, + "iterations": { + "type": "integer", + "minimum": 1 + }, + "final_mode_delta": { + "type": "number", + "minimum": 0 + }, + "reconstruction_error": { + "type": "number", + "minimum": 0 + }, + "primal_residual": { + "type": "number", + "minimum": 0 + }, + "dual_residual": { + "type": "number", + "minimum": 0 + }, + "final_tau": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": false + }, + "timings_ms": { + "type": "object", + "required": [ + "input", + "mirror_fft", + "iteration", + "spectrum_reconstruction", + "serialization", + "report" + ], + "additionalProperties": false, + "properties": { + "input": { + "type": "number", + "minimum": 0 + }, + "mirror_fft": { + "type": "number", + "minimum": 0 + }, + "iteration": { + "type": "number", + "minimum": 0 + }, + "spectrum_reconstruction": { + "type": "number", + "minimum": 0 + }, + "serialization": { + "type": "number", + "minimum": 0 + }, + "report": { + "type": "number", + "minimum": 0 + } + } + }, + "outputs": { + "type": "object", + "required": [ + "modes", + "spectra", + "omega", + "summary", + "report" + ], + "properties": { + "modes": { + "type": "string" + }, + "spectra": { + "type": "string" + }, + "omega": { + "type": "string" + }, + "summary": { + "type": "string" + }, + "report": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + } +} diff --git a/docs/validation-and-performance.md b/docs/validation-and-performance.md new file mode 100644 index 0000000..48cb2f0 --- /dev/null +++ b/docs/validation-and-performance.md @@ -0,0 +1,110 @@ +# Validation and Performance + +## Correctness Tests + +The `vmd_tests` executable covers: + +- The historical 1,200-sample multifrequency example +- Single-frequency center estimation +- Deterministic noisy input and a chirp +- DC-constrained decomposition +- Zero and constant inputs +- Length 1, length 2, odd, and even signals +- Reproducible random initialization +- Per-mode alpha and warm starts +- Optional frequency-history suppression and adaptive tau bounds +- Invalid and non-finite arguments +- The legacy `VMD(...)` wrapper + +The spectrum contract is checked independently with Eigen FFT. The relative norm +between every returned spectrum and `fftshift(fft(mode))` must be below `1e-12`. +The historical example must have relative reconstruction error no greater than +`5e-5`, and all public numeric matrices must remain finite. + +Run: + +```sh +ctest --test-dir build -C Release --output-on-failure +``` + +## CLI End-to-End Test + +`vmd.cli.e2e` executes the demo and verifies that all CSV, JSON, and HTML files +exist and contain their expected structural markers. It also verifies: + +- Indexed input does not lose its first row when another column contains text. +- Invalid numeric input returns code 3 with line and column information. +- A deliberately nonconverged run returns code 4 and preserves output files. + +## Benchmark + +Build and run: + +```sh +cmake --build build --config Release --target vmd_benchmark +./build/vmd_benchmark 1200 8 20 +``` + +The optional third argument repeats the same deterministic decomposition and the +reported stage and wall times are per-run means. Repetitions make short runs easier +to profile without changing their workspace shape. The benchmark reports +mirror/FFT, main iteration, output reconstruction, total wall time, and an +estimated core workspace. CLI `summary.json` additionally +reports input parsing, CSV serialization, and HTML report generation time. + +Performance numbers should always record: + +- CPU model and logical core count +- Operating system and compiler version +- CMake build type and FFT backend +- Signal length, mode count, tolerance, and completed iterations + +### Local Release Snapshot + +The final local regression on July 31, 2026 used Windows 11 64-bit, +an Intel Core i5-13500HX (20 logical processors), MinGW GCC 15.2.0, Release +mode, and the Eigen real-to-complex FFT backend. For `N = 1200`, `K = 8`, +500 repeated decompositions, and 452 iterations per decomposition, it measured: + +| Stage | Mean time | +| --- | ---: | +| Mirror and input FFT | 0.154 ms | +| Main iteration | 32.029 ms | +| Mode and spectrum reconstruction | 0.480 ms | +| Total | 32.847 ms | + +The estimated core workspace was 0.467 MiB and the sampled process working-set +peak was 6.023 MiB. These values are a reproducibility snapshot, not a portable +performance guarantee. + +## Complexity + +The iteration keeps two complex `K x N` mode buffers and a fixed number of +length-`N` work vectors. Core working memory is `O(K * N)` and does not scale with +`max_iterations`. Full omega history is `O(iterations * K)` and may be disabled. + +For `N = 1200` and `K = 8`, the benchmark estimate is far below the 32 MiB core +workspace target. Process peak memory is higher because it also includes the C++ +runtime, Eigen/FFT backend, executable image, and serialized result matrices. + +## Sanitizers and Warnings + +High warnings are enabled by default. On GCC or Clang: + +```sh +cmake -S . -B build-asan -DCMAKE_BUILD_TYPE=Debug \ + -DVMD_ENABLE_SANITIZERS=ON -DVMD_BUILD_BENCHMARKS=OFF +cmake --build build-asan --parallel +ctest --test-dir build-asan --output-on-failure +``` + +The CI workflow covers MSVC, GCC, and Clang plus an ASan/UBSan Linux job. + +## Reference Comparison + +The original implementation and standard VMD formulation use a shifted +positive-frequency signal and the same sequential Wiener-filter update. This +implementation stores the equivalent unshifted positive half to reduce memory. +Behavioral comparison should use time-domain modes, final normalized center +frequencies, and reconstruction residual rather than the historical `u_hat`, +which mixed domains and did not satisfy its documented shape/content contract. diff --git a/examples/vmd_example.cpp b/examples/vmd_example.cpp new file mode 100644 index 0000000..3b24c14 --- /dev/null +++ b/examples/vmd_example.cpp @@ -0,0 +1,29 @@ +#include + +#include +#include + +int main() { + constexpr double pi = 3.14159265358979323846; + constexpr Eigen::Index n = 1200; + Eigen::VectorXd signal(n); + for (Eigen::Index i = 0; i < n; ++i) { + const double t = static_cast(i + 1) / static_cast(n); + signal(i) = std::cos(2.0 * pi * 2.0 * t) + + std::cos(2.0 * pi * 24.0 * t) / 10.0 + + std::cos(2.0 * pi * 288.0 * t) / 200.0; + } + + vmd::Options options; + options.mode_count = 8; + options.alpha = {50.0}; + options.sample_rate_hz = 1200.0; + const vmd::Result result = vmd::decompose(signal, options); + + std::cout << "modes: " << result.modes.rows() << " x " << result.modes.cols() << '\n' + << "iterations: " << result.diagnostics.iterations << '\n' + << "converged: " << (result.diagnostics.converged ? "yes" : "no") << '\n' + << "reconstruction error: " << result.diagnostics.reconstruction_error << '\n' + << "center frequencies (Hz): " << result.omega_hz.bottomRows(1) << '\n'; + return result.diagnostics.converged ? 0 : 4; +} diff --git a/include/vmd/vmd.hpp b/include/vmd/vmd.hpp new file mode 100644 index 0000000..a3fdf2b --- /dev/null +++ b/include/vmd/vmd.hpp @@ -0,0 +1,95 @@ +#pragma once + +#include + +#include +#include +#include + +namespace vmd { + +/** Center-frequency initialization strategy. */ +enum class Initialization { + Zero, ///< Initialize every mode at zero cycles per sample. + Uniform, ///< Distribute modes uniformly from DC toward Nyquist. + Random ///< Use sorted, seeded log-uniform center frequencies. +}; + +/** Timings for the core numerical stages, in milliseconds. */ +struct Timings { + double mirror_and_fft_ms = 0.0; ///< Mirror extension and input FFT. + double iteration_ms = 0.0; ///< Main alternating-update loop. + double reconstruction_ms = 0.0; ///< IMF reconstruction and public spectra. +}; + +/** Configuration for variational mode decomposition. */ +struct Options { + std::size_t mode_count = 3; ///< Number of modes K; must be positive. + + /** One positive bandwidth penalty or exactly K positive penalties. */ + std::vector alpha{2000.0}; + + double tau = 0.0; ///< Non-negative dual-ascent step. + bool dc_mode = false; ///< Keep the first mode centered at zero frequency. + Initialization initialization = Initialization::Uniform; ///< Frequency initializer. + double tolerance = 1e-7; ///< Positive stopping threshold for mode delta. + std::size_t max_iterations = 500; ///< Positive iteration limit. + std::uint64_t random_seed = 0; ///< Seed for Random initialization. + double sample_rate_hz = 1.0; ///< Positive sample rate used for omega_hz. + bool keep_omega_history = true; ///< Keep all center-frequency iterates. + + /** Optional finite K x N time-domain modes used as the initial iterate. */ + Eigen::MatrixXd warm_start_modes; + + /** Optional finite K-vector of initial frequencies in cycles per sample. */ + Eigen::VectorXd warm_start_omega_normalized; + + /** Enable residual-balanced tau adaptation. Disabled by default. */ + bool adaptive_tau = false; + double adaptive_tau_balance = 10.0; ///< Residual imbalance threshold (> 1). + double adaptive_tau_increase = 2.0; ///< Multiplicative increase factor (> 1). + double adaptive_tau_decrease = 2.0; ///< Multiplicative decrease divisor (> 1). + double tau_min = 1e-8; ///< Finite non-negative adaptive lower bound. + double tau_max = 1e3; ///< Finite adaptive upper bound, at least tau_min. +}; + +/** Numerical convergence and reconstruction information. */ +struct Diagnostics { + bool converged = false; ///< True when final_mode_delta <= tolerance. + std::size_t iterations = 0; ///< Number of completed mode-update passes. + double final_mode_delta = 0.0; ///< Compatibility squared-iterate metric. + double reconstruction_error = 0.0; ///< Relative time-domain residual norm. + double primal_residual = 0.0; ///< Relative spectral constraint residual. + double dual_residual = 0.0; ///< Tau-scaled relative iterate change. + double final_tau = 0.0; ///< Final dual-ascent step after optional adaptation. +}; + +/** VMD outputs. Matrix orientations and units are part of the API contract. */ +struct Result { + Eigen::MatrixXd modes; ///< K x N time-domain IMFs. + Eigen::MatrixXcd spectra; ///< N x K unnormalized fftshift(fft(IMF)). + Eigen::MatrixXd omega_normalized; ///< I x K center frequencies, cycles/sample. + Eigen::MatrixXd omega_hz; ///< I x K center frequencies, hertz. + Diagnostics diagnostics; ///< Convergence and residual metrics. + Timings timings; ///< Core-stage durations in milliseconds. +}; + +/** + * Decompose a finite, non-empty real signal into band-limited modes. + * + * @param signal Borrowed N-sample input vector. The function does not retain it. + * @param options Decomposition settings. A one-element alpha vector broadcasts + * to all modes; otherwise alpha must contain exactly mode_count values. + * @return Owning mode, spectrum, frequency, diagnostic, and timing data. + * + * @throws std::invalid_argument for invalid options, non-finite samples, or + * incompatible warm-start dimensions. + * @throws std::runtime_error if the numerical backend produces non-finite output. + * + * When frequency history is enabled, I equals iterations + 1 and row zero is + * the initialization. Otherwise I is one and contains only the final values. + * Calls own all temporary state and are reentrant across independent threads. + */ +Result decompose(Eigen::Ref signal, const Options& options); + +} // namespace vmd diff --git a/src/fft_backend.cpp b/src/fft_backend.cpp new file mode 100644 index 0000000..5ff5bf8 --- /dev/null +++ b/src/fft_backend.cpp @@ -0,0 +1,228 @@ +#include "fft_backend.hpp" + +#include +#include + +#if defined(VMD_FFT_BACKEND_FFTW) +#include +#elif defined(VMD_FFT_BACKEND_MKL) +#include +#else +#include +#endif + +namespace vmd::detail { + +struct FFTBackend::Impl { +#if defined(VMD_FFT_BACKEND_FFTW) + std::size_t complex_size = 0; + fftw_complex* complex_input = nullptr; + fftw_complex* complex_output = nullptr; + fftw_plan forward_plan = nullptr; + fftw_plan inverse_plan = nullptr; + + std::size_t real_size = 0; + double* real_input = nullptr; + fftw_complex* real_output = nullptr; + fftw_plan real_forward_plan = nullptr; + + ~Impl() { + reset_complex(); + reset_real(); + } + + void reset_complex() { + if (forward_plan != nullptr) fftw_destroy_plan(forward_plan); + if (inverse_plan != nullptr) fftw_destroy_plan(inverse_plan); + if (complex_input != nullptr) fftw_free(complex_input); + if (complex_output != nullptr) fftw_free(complex_output); + complex_size = 0; + complex_input = nullptr; + complex_output = nullptr; + forward_plan = nullptr; + inverse_plan = nullptr; + } + + void reset_real() { + if (real_forward_plan != nullptr) fftw_destroy_plan(real_forward_plan); + if (real_input != nullptr) fftw_free(real_input); + if (real_output != nullptr) fftw_free(real_output); + real_size = 0; + real_input = nullptr; + real_output = nullptr; + real_forward_plan = nullptr; + } + + void ensure_complex(std::size_t requested) { + if (requested == complex_size) return; + reset_complex(); + complex_size = requested; + complex_input = static_cast(fftw_malloc(sizeof(fftw_complex) * complex_size)); + complex_output = static_cast(fftw_malloc(sizeof(fftw_complex) * complex_size)); + if (complex_input == nullptr || complex_output == nullptr) throw std::bad_alloc(); + forward_plan = fftw_plan_dft_1d( + static_cast(complex_size), complex_input, complex_output, FFTW_FORWARD, FFTW_ESTIMATE); + inverse_plan = fftw_plan_dft_1d( + static_cast(complex_size), complex_input, complex_output, FFTW_BACKWARD, FFTW_ESTIMATE); + if (forward_plan == nullptr || inverse_plan == nullptr) { + throw std::runtime_error("failed to create FFTW complex plan"); + } + } + + void ensure_real(std::size_t requested) { + if (requested == real_size) return; + reset_real(); + real_size = requested; + real_input = static_cast(fftw_malloc(sizeof(double) * real_size)); + real_output = static_cast( + fftw_malloc(sizeof(fftw_complex) * (real_size / 2 + 1))); + if (real_input == nullptr || real_output == nullptr) throw std::bad_alloc(); + real_forward_plan = fftw_plan_dft_r2c_1d( + static_cast(real_size), real_input, real_output, FFTW_ESTIMATE); + if (real_forward_plan == nullptr) { + throw std::runtime_error("failed to create FFTW real plan"); + } + } +#elif defined(VMD_FFT_BACKEND_MKL) + std::size_t complex_size = 0; + DFTI_DESCRIPTOR_HANDLE complex_descriptor = nullptr; + std::size_t real_size = 0; + DFTI_DESCRIPTOR_HANDLE real_descriptor = nullptr; + + ~Impl() { + if (complex_descriptor != nullptr) DftiFreeDescriptor(&complex_descriptor); + if (real_descriptor != nullptr) DftiFreeDescriptor(&real_descriptor); + } + + void ensure_complex(std::size_t requested) { + if (requested == complex_size) return; + if (complex_descriptor != nullptr) DftiFreeDescriptor(&complex_descriptor); + complex_size = requested; + if (DftiCreateDescriptor( + &complex_descriptor, DFTI_DOUBLE, DFTI_COMPLEX, 1, + static_cast(complex_size)) != 0 || + DftiSetValue(complex_descriptor, DFTI_PLACEMENT, DFTI_NOT_INPLACE) != 0 || + DftiSetValue( + complex_descriptor, DFTI_BACKWARD_SCALE, + 1.0 / static_cast(complex_size)) != 0 || + DftiCommitDescriptor(complex_descriptor) != 0) { + throw std::runtime_error("failed to create MKL complex DFT descriptor"); + } + } + + void ensure_real(std::size_t requested) { + if (requested == real_size) return; + if (real_descriptor != nullptr) DftiFreeDescriptor(&real_descriptor); + real_size = requested; + if (DftiCreateDescriptor( + &real_descriptor, DFTI_DOUBLE, DFTI_REAL, 1, + static_cast(real_size)) != 0 || + DftiSetValue(real_descriptor, DFTI_PLACEMENT, DFTI_NOT_INPLACE) != 0 || + DftiSetValue(real_descriptor, DFTI_CONJUGATE_EVEN_STORAGE, DFTI_COMPLEX_COMPLEX) != 0 || + DftiCommitDescriptor(real_descriptor) != 0) { + throw std::runtime_error("failed to create MKL real DFT descriptor"); + } + } +#else + Eigen::FFT fft; + + Impl() { fft.SetFlag(Eigen::FFT::HalfSpectrum); } +#endif +}; + +FFTBackend::FFTBackend() : impl_(std::make_unique()) {} +FFTBackend::~FFTBackend() = default; +FFTBackend::FFTBackend(FFTBackend&&) noexcept = default; +FFTBackend& FFTBackend::operator=(FFTBackend&&) noexcept = default; + +void FFTBackend::forward_real( + const std::vector& input, + std::vector>& output) { + if (input.size() <= 1) { + output.assign(input.size(), input.empty() ? std::complex{} : std::complex{input.front(), 0.0}); + return; + } +#if defined(VMD_FFT_BACKEND_FFTW) + impl_->ensure_real(input.size()); + output.resize(input.size() / 2 + 1); + for (std::size_t i = 0; i < input.size(); ++i) impl_->real_input[i] = input[i]; + fftw_execute(impl_->real_forward_plan); + for (std::size_t i = 0; i < output.size(); ++i) { + output[i] = {impl_->real_output[i][0], impl_->real_output[i][1]}; + } +#elif defined(VMD_FFT_BACKEND_MKL) + impl_->ensure_real(input.size()); + output.resize(input.size() / 2 + 1); + if (DftiComputeForward( + impl_->real_descriptor, const_cast(input.data()), output.data()) != 0) { + throw std::runtime_error("MKL real forward FFT failed"); + } +#else + impl_->fft.fwd(output, input); +#endif +} + +void FFTBackend::forward( + const std::vector>& input, + std::vector>& output) { + if (input.size() <= 1) { + output = input; + return; + } +#if defined(VMD_FFT_BACKEND_FFTW) + impl_->ensure_complex(input.size()); + output.resize(input.size()); + for (std::size_t i = 0; i < input.size(); ++i) { + impl_->complex_input[i][0] = input[i].real(); + impl_->complex_input[i][1] = input[i].imag(); + } + fftw_execute(impl_->forward_plan); + for (std::size_t i = 0; i < output.size(); ++i) { + output[i] = {impl_->complex_output[i][0], impl_->complex_output[i][1]}; + } +#elif defined(VMD_FFT_BACKEND_MKL) + impl_->ensure_complex(input.size()); + output.resize(input.size()); + if (DftiComputeForward( + impl_->complex_descriptor, const_cast*>(input.data()), output.data()) != 0) { + throw std::runtime_error("MKL forward FFT failed"); + } +#else + impl_->fft.fwd(output, input); +#endif +} + +void FFTBackend::inverse( + const std::vector>& input, + std::vector>& output) { + if (input.size() <= 1) { + output = input; + return; + } +#if defined(VMD_FFT_BACKEND_FFTW) + impl_->ensure_complex(input.size()); + output.resize(input.size()); + for (std::size_t i = 0; i < input.size(); ++i) { + impl_->complex_input[i][0] = input[i].real(); + impl_->complex_input[i][1] = input[i].imag(); + } + fftw_execute(impl_->inverse_plan); + const double scale = 1.0 / static_cast(input.size()); + for (std::size_t i = 0; i < output.size(); ++i) { + output[i] = { + impl_->complex_output[i][0] * scale, + impl_->complex_output[i][1] * scale}; + } +#elif defined(VMD_FFT_BACKEND_MKL) + impl_->ensure_complex(input.size()); + output.resize(input.size()); + if (DftiComputeBackward( + impl_->complex_descriptor, const_cast*>(input.data()), output.data()) != 0) { + throw std::runtime_error("MKL inverse FFT failed"); + } +#else + impl_->fft.inv(output, input); +#endif +} + +} // namespace vmd::detail diff --git a/src/fft_backend.hpp b/src/fft_backend.hpp new file mode 100644 index 0000000..3bc3034 --- /dev/null +++ b/src/fft_backend.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +namespace vmd::detail { + +class FFTBackend { +public: + FFTBackend(); + ~FFTBackend(); + FFTBackend(FFTBackend&&) noexcept; + FFTBackend& operator=(FFTBackend&&) noexcept; + FFTBackend(const FFTBackend&) = delete; + FFTBackend& operator=(const FFTBackend&) = delete; + + void forward_real( + const std::vector& input, + std::vector>& output); + void forward( + const std::vector>& input, + std::vector>& output); + void inverse( + const std::vector>& input, + std::vector>& output); + +private: + struct Impl; + std::unique_ptr impl_; +}; + +} // namespace vmd::detail diff --git a/src/vmd.cpp b/src/vmd.cpp new file mode 100644 index 0000000..1713bb7 --- /dev/null +++ b/src/vmd.cpp @@ -0,0 +1,332 @@ +#include + +#include "fft_backend.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace vmd { +namespace { + +using Clock = std::chrono::steady_clock; +using Complex = std::complex; +using RowMatrixXcd = Eigen::Matrix; + +constexpr double kEnergyFloor = 64.0 * std::numeric_limits::epsilon(); + +void require(bool condition, const char* message) { + if (!condition) throw std::invalid_argument(message); +} + +std::vector expanded_alpha(const Options& options) { + require(options.alpha.size() == 1 || options.alpha.size() == options.mode_count, + "alpha must contain one value or exactly mode_count values"); + std::vector values(options.mode_count, options.alpha.front()); + if (options.alpha.size() == options.mode_count) values = options.alpha; + for (double value : values) { + require(std::isfinite(value) && value > 0.0, "alpha values must be finite and positive"); + } + return values; +} + +void validate(Eigen::Ref signal, const Options& options) { + require(signal.size() > 0, "signal must not be empty"); + require(static_cast(signal.size()) <= + static_cast(std::numeric_limits::max() / 2), + "signal is too large"); + require(signal.array().isFinite().all(), "signal must contain only finite values"); + require(options.mode_count > 0, "mode_count must be positive"); + require(options.mode_count <= static_cast(std::numeric_limits::max()), + "mode_count is too large"); + (void)expanded_alpha(options); + require(std::isfinite(options.tau) && options.tau >= 0.0, "tau must be finite and non-negative"); + require(std::isfinite(options.tolerance) && options.tolerance > 0.0, + "tolerance must be finite and positive"); + require(options.max_iterations > 0, "max_iterations must be positive"); + require(std::isfinite(options.sample_rate_hz) && options.sample_rate_hz > 0.0, + "sample_rate_hz must be finite and positive"); + switch (options.initialization) { + case Initialization::Zero: + case Initialization::Uniform: + case Initialization::Random: + break; + default: + throw std::invalid_argument("initialization is not a valid enum value"); + } + require(std::isfinite(options.tau_min) && std::isfinite(options.tau_max) && + options.tau_min >= 0.0 && options.tau_max >= options.tau_min, + "adaptive tau bounds must be finite, non-negative, and ordered"); + require(std::isfinite(options.adaptive_tau_balance) && + std::isfinite(options.adaptive_tau_increase) && + std::isfinite(options.adaptive_tau_decrease) && + options.adaptive_tau_balance > 1.0 && options.adaptive_tau_increase > 1.0 && + options.adaptive_tau_decrease > 1.0, + "adaptive tau factors must be finite and greater than one"); + if (options.adaptive_tau) require(options.tau > 0.0, "adaptive_tau requires tau > 0"); + + const Eigen::Index k = static_cast(options.mode_count); + if (options.warm_start_modes.size() != 0) { + require(options.warm_start_modes.rows() == k && options.warm_start_modes.cols() == signal.size(), + "warm_start_modes must be K x N"); + require(options.warm_start_modes.array().isFinite().all(), + "warm_start_modes must contain only finite values"); + } + if (options.warm_start_omega_normalized.size() != 0) { + require(options.warm_start_omega_normalized.size() == k, + "warm_start_omega_normalized must contain K values"); + require(options.warm_start_omega_normalized.array().isFinite().all(), + "warm_start_omega_normalized must contain only finite values"); + require((options.warm_start_omega_normalized.array() >= 0.0).all() && + (options.warm_start_omega_normalized.array() <= 0.5).all(), + "warm-start frequencies must lie in [0, 0.5]"); + } +} + +std::vector mirror_signal(Eigen::Ref signal) { + const Eigen::Index n = signal.size(); + const Eigen::Index left = n / 2; + const Eigen::Index right = n - left; + std::vector mirrored(static_cast(2 * n)); + for (Eigen::Index i = 0; i < left; ++i) mirrored[static_cast(i)] = signal(left - 1 - i); + for (Eigen::Index i = 0; i < n; ++i) mirrored[static_cast(left + i)] = signal(i); + for (Eigen::Index i = 0; i < right; ++i) { + mirrored[static_cast(left + n + i)] = signal(n - 1 - i); + } + return mirrored; +} + +Eigen::RowVectorXd initialize_omega(const Options& options, Eigen::Index signal_size) { + const Eigen::Index k_count = static_cast(options.mode_count); + Eigen::RowVectorXd omega = Eigen::RowVectorXd::Zero(k_count); + if (options.warm_start_omega_normalized.size() != 0) { + omega = options.warm_start_omega_normalized.transpose(); + } else if (options.initialization == Initialization::Uniform) { + for (Eigen::Index k = 0; k < k_count; ++k) { + omega(k) = 0.5 * static_cast(k) / static_cast(k_count); + } + } else if (options.initialization == Initialization::Random && signal_size > 2) { + const double low = std::min(0.5, 1.0 / static_cast(signal_size)); + std::mt19937_64 generator(options.random_seed); + std::uniform_real_distribution distribution(std::log(low), std::log(0.5)); + for (Eigen::Index k = 0; k < k_count; ++k) omega(k) = std::exp(distribution(generator)); + std::sort(omega.data(), omega.data() + omega.size()); + } + if (options.dc_mode) omega(0) = 0.0; + return omega; +} + +void initialize_modes_from_warm_start( + const Options& options, + detail::FFTBackend& fft, + RowMatrixXcd& modes) { + if (options.warm_start_modes.size() == 0) return; + const Eigen::Index n = options.warm_start_modes.cols(); + const Eigen::Index half = n; + for (Eigen::Index k = 0; k < options.warm_start_modes.rows(); ++k) { + Eigen::VectorXd mode = options.warm_start_modes.row(k).transpose(); + auto mirrored = mirror_signal(mode); + std::vector spectrum; + fft.forward_real(mirrored, spectrum); + for (Eigen::Index j = 0; j < half; ++j) modes(k, j) = spectrum[static_cast(j)]; + } +} + +Eigen::MatrixXd build_history( + const std::vector& history, + const Eigen::RowVectorXd& final_omega, + bool keep_history) { + if (!keep_history) { + Eigen::MatrixXd result(1, final_omega.size()); + result.row(0) = final_omega; + return result; + } + Eigen::MatrixXd result(static_cast(history.size()), final_omega.size()); + for (std::size_t i = 0; i < history.size(); ++i) { + result.row(static_cast(i)) = history[i]; + } + return result; +} + +void verify_finite(const Result& result) { + if (!result.modes.array().isFinite().all() || !result.spectra.real().array().isFinite().all() || + !result.spectra.imag().array().isFinite().all() || + !result.omega_normalized.array().isFinite().all() || !result.omega_hz.array().isFinite().all()) { + throw std::runtime_error("VMD produced a non-finite result"); + } +} + +} // namespace + +Result decompose(Eigen::Ref signal, const Options& options) { + validate(signal, options); + const auto alphas = expanded_alpha(options); + const Eigen::Index n = signal.size(); + const Eigen::Index mirrored_size = 2 * n; + const Eigen::Index half = n; + const Eigen::Index k_count = static_cast(options.mode_count); + + Result result; + detail::FFTBackend fft; + + const auto preparation_start = Clock::now(); + Eigen::RowVectorXcd input_positive(half); + Eigen::RowVectorXd frequencies(half); + { + const auto mirrored = mirror_signal(signal); + std::vector mirrored_spectrum; + fft.forward_real(mirrored, mirrored_spectrum); + for (Eigen::Index j = 0; j < half; ++j) { + input_positive(j) = mirrored_spectrum[static_cast(j)]; + frequencies(j) = static_cast(j) / static_cast(mirrored_size); + } + } + + RowMatrixXcd previous = RowMatrixXcd::Zero(k_count, half); + RowMatrixXcd current = RowMatrixXcd::Zero(k_count, half); + initialize_modes_from_warm_start(options, fft, previous); + + Eigen::RowVectorXd omega_previous = initialize_omega(options, n); + Eigen::RowVectorXd omega_current = omega_previous; + std::vector omega_history; + if (options.keep_omega_history) { + omega_history.reserve(options.max_iterations + 1); + omega_history.push_back(omega_previous); + } + result.timings.mirror_and_fft_ms = + std::chrono::duration(Clock::now() - preparation_start).count(); + + const auto iteration_start = Clock::now(); + Eigen::RowVectorXcd lambda = Eigen::RowVectorXcd::Zero(half); + Eigen::RowVectorXcd remaining_previous(half); + Eigen::RowVectorXcd current_sum(half); + Eigen::RowVectorXcd numerator(half); + Eigen::RowVectorXcd spectral_residual(half); + Eigen::RowVectorXd denominator(half); + Eigen::RowVectorXd frequency_delta(half); + Eigen::RowVectorXd energy(half); + + const double input_scale = std::max(input_positive.norm(), kEnergyFloor); + const double energy_floor = kEnergyFloor * std::max(1.0, input_positive.squaredNorm()); + double tau = options.tau; + double final_delta = std::numeric_limits::infinity(); + double primal_residual = std::numeric_limits::infinity(); + double dual_residual = std::numeric_limits::infinity(); + bool converged = false; + std::size_t iterations = 0; + + for (std::size_t iteration = 0; iteration < options.max_iterations; ++iteration) { + remaining_previous = previous.colwise().sum(); + current_sum.setZero(); + + for (Eigen::Index k = 0; k < k_count; ++k) { + remaining_previous -= previous.row(k); + numerator = input_positive - current_sum - remaining_previous - 0.5 * lambda; + frequency_delta = frequencies.array() - omega_previous(k); + denominator = 1.0 + alphas[static_cast(k)] * frequency_delta.array().square(); + current.row(k) = numerator.array() / denominator.array(); + + if (options.dc_mode && k == 0) { + omega_current(k) = 0.0; + } else { + energy = current.row(k).cwiseAbs2(); + const double mode_energy = energy.sum(); + omega_current(k) = mode_energy > energy_floor + ? frequencies.dot(energy) / mode_energy + : omega_previous(k); + } + current_sum += current.row(k); + } + + spectral_residual = current_sum - input_positive; + primal_residual = spectral_residual.norm() / input_scale; + const double iterate_norm = (current - previous).norm(); + dual_residual = tau * iterate_norm / input_scale; + lambda += tau * spectral_residual; + final_delta = std::numeric_limits::epsilon() + + (current - previous).squaredNorm() / static_cast(mirrored_size); + iterations = iteration + 1; + + if (options.keep_omega_history) omega_history.push_back(omega_current); + + if (options.adaptive_tau) { + if (primal_residual > options.adaptive_tau_balance * std::max(dual_residual, kEnergyFloor)) { + tau = std::min(options.tau_max, tau * options.adaptive_tau_increase); + } else if (dual_residual > options.adaptive_tau_balance * std::max(primal_residual, kEnergyFloor)) { + tau = std::max(options.tau_min, tau / options.adaptive_tau_decrease); + } + } + + previous.swap(current); + omega_previous.swap(omega_current); + if (final_delta <= options.tolerance) { + converged = true; + break; + } + } + + result.timings.iteration_ms = + std::chrono::duration(Clock::now() - iteration_start).count(); + + const auto reconstruction_start = Clock::now(); + result.modes.resize(k_count, n); + result.spectra.resize(n, k_count); + std::vector full_spectrum(static_cast(mirrored_size)); + std::vector mirrored_mode; + std::vector cropped_mode(static_cast(n)); + std::vector cropped_half_spectrum; + const Eigen::Index crop_start = n / 2; + + for (Eigen::Index k = 0; k < k_count; ++k) { + std::fill(full_spectrum.begin(), full_spectrum.end(), Complex{}); + for (Eigen::Index j = 0; j < half; ++j) { + full_spectrum[static_cast(j)] = previous(k, j); + } + for (Eigen::Index j = 1; j < half; ++j) { + full_spectrum[static_cast(mirrored_size - j)] = std::conj(previous(k, j)); + } + fft.inverse(full_spectrum, mirrored_mode); + for (Eigen::Index i = 0; i < n; ++i) { + const double value = mirrored_mode[static_cast(crop_start + i)].real(); + result.modes(k, i) = value; + cropped_mode[static_cast(i)] = value; + } + + fft.forward_real(cropped_mode, cropped_half_spectrum); + const Eigen::Index shift = (n + 1) / 2; + for (Eigen::Index i = 0; i < n; ++i) { + const Eigen::Index source = (i + shift) % n; + result.spectra(i, k) = source <= n / 2 + ? cropped_half_spectrum[static_cast(source)] + : std::conj(cropped_half_spectrum[static_cast(n - source)]); + } + } + + result.omega_normalized = build_history(omega_history, omega_previous, options.keep_omega_history); + result.omega_hz = result.omega_normalized * options.sample_rate_hz; + result.diagnostics.converged = converged; + result.diagnostics.iterations = iterations; + result.diagnostics.final_mode_delta = final_delta; + const Eigen::VectorXd reconstruction = result.modes.colwise().sum().transpose(); + const double signal_norm = signal.norm(); + result.diagnostics.reconstruction_error = (reconstruction - signal).norm() / + (signal_norm > kEnergyFloor ? signal_norm : 1.0); + result.diagnostics.primal_residual = primal_residual; + result.diagnostics.dual_residual = dual_residual; + result.diagnostics.final_tau = tau; + result.timings.reconstruction_ms = + std::chrono::duration(Clock::now() - reconstruction_start).count(); + + verify_finite(result); + return result; +} + +} // namespace vmd diff --git a/tests/cli_e2e.cmake b/tests/cli_e2e.cmake new file mode 100644 index 0000000..4ac3de8 --- /dev/null +++ b/tests/cli_e2e.cmake @@ -0,0 +1,118 @@ +if(NOT DEFINED CLI OR NOT DEFINED OUTPUT_DIR) + message(FATAL_ERROR "CLI and OUTPUT_DIR are required") +endif() + +file(REMOVE_RECURSE "${OUTPUT_DIR}") +execute_process( + COMMAND "${CLI}" --demo --sample-rate 1200 --modes 8 --alpha 50 + --output-dir "${OUTPUT_DIR}" --report + RESULT_VARIABLE result + OUTPUT_VARIABLE stdout + ERROR_VARIABLE stderr +) +if(NOT result EQUAL 0) + message(FATAL_ERROR "vmd_cli failed with ${result}\nstdout:\n${stdout}\nstderr:\n${stderr}") +endif() + +foreach(name modes.csv spectra.csv omega.csv summary.json report.html) + set(path "${OUTPUT_DIR}/${name}") + if(NOT EXISTS "${path}") + message(FATAL_ERROR "missing CLI output: ${path}") + endif() + file(SIZE "${path}" size) + if(size EQUAL 0) + message(FATAL_ERROR "empty CLI output: ${path}") + endif() +endforeach() + +file(READ "${OUTPUT_DIR}/modes.csv" modes LIMIT 256) +string(FIND "${modes}" "index,time_s,signal,imf_1" modes_header) +if(modes_header LESS 0) + message(FATAL_ERROR "modes.csv header is invalid") +endif() + +file(READ "${OUTPUT_DIR}/spectra.csv" spectra LIMIT 256) +string(FIND "${spectra}" "frequency_hz,imf_1_real,imf_1_imag,imf_1_magnitude" spectra_header) +if(spectra_header LESS 0) + message(FATAL_ERROR "spectra.csv header is invalid") +endif() + +file(READ "${OUTPUT_DIR}/omega.csv" omega LIMIT 256) +string(FIND "${omega}" "iteration,imf_1_hz" omega_header) +if(omega_header LESS 0) + message(FATAL_ERROR "omega.csv header is invalid") +endif() + +file(READ "${OUTPUT_DIR}/summary.json" summary) +foreach(fragment "\"schema_version\"" "\"converged\": true" "\"reconstruction_error\"" "\"report\": \"report.html\"") + string(FIND "${summary}" "${fragment}" found) + if(found LESS 0) + message(FATAL_ERROR "summary.json is missing ${fragment}") + endif() +endforeach() + + +set(index_input "${OUTPUT_DIR}/index-input.csv") +file(WRITE "${index_input}" "label,value\na,1\nb,2\n") +execute_process( + COMMAND "${CLI}" --input "${index_input}" --column 1 --sample-rate 2 --modes 1 + --alpha 50 --dc --output-dir "${OUTPUT_DIR}/index-output" + RESULT_VARIABLE index_result + ERROR_VARIABLE index_stderr +) +if(NOT index_result EQUAL 0) + message(FATAL_ERROR "index-selected mixed text/numeric input failed: ${index_stderr}") +endif() +file(STRINGS "${OUTPUT_DIR}/index-output/modes.csv" index_rows) +list(LENGTH index_rows index_row_count) +if(NOT index_row_count EQUAL 3) + message(FATAL_ERROR "index-selected input lost a data row; modes.csv has ${index_row_count} rows") +endif() + +set(invalid_input "${OUTPUT_DIR}/invalid.csv") +file(WRITE "${invalid_input}" "value\n1\nNaN\n") +execute_process( + COMMAND "${CLI}" --input "${invalid_input}" --column value + --output-dir "${OUTPUT_DIR}/invalid-output" + RESULT_VARIABLE invalid_result + ERROR_VARIABLE invalid_stderr +) +if(NOT invalid_result EQUAL 3) + message(FATAL_ERROR "invalid numeric input returned ${invalid_result}, expected exit code 3") +endif() +string(FIND "${invalid_stderr}" "invalid.csv:3:1" invalid_location) +if(invalid_location LESS 0) + message(FATAL_ERROR "invalid input error did not include line and column: ${invalid_stderr}") +endif() + +execute_process( + COMMAND "${CLI}" --demo --seed -1 --output-dir "${OUTPUT_DIR}/negative-seed" + RESULT_VARIABLE negative_seed_result + ERROR_VARIABLE negative_seed_stderr +) +if(NOT negative_seed_result EQUAL 2) + message(FATAL_ERROR "negative seed returned ${negative_seed_result}, expected exit code 2") +endif() +string(FIND "${negative_seed_stderr}" "--seed expects a non-negative integer" negative_seed_message) +if(negative_seed_message LESS 0) + message(FATAL_ERROR "negative seed error was unclear: ${negative_seed_stderr}") +endif() + +execute_process( + COMMAND "${CLI}" --demo --max-iterations 1 --output-dir "${OUTPUT_DIR}/nonconverged" + RESULT_VARIABLE nonconverged_result +) +if(NOT nonconverged_result EQUAL 4) + message(FATAL_ERROR "nonconverged run returned ${nonconverged_result}, expected exit code 4") +endif() +if(NOT EXISTS "${OUTPUT_DIR}/nonconverged/summary.json") + message(FATAL_ERROR "nonconverged run did not preserve output files") +endif() + +file(READ "${OUTPUT_DIR}/report.html" report) +foreach(fragment "" "createElementNS" "VMD decomposition report" "XMLSerializer" "mode-toggle") + string(FIND "${report}" "${fragment}" found) + if(found LESS 0) + message(FATAL_ERROR "report.html is missing ${fragment}") + endif() +endforeach() diff --git a/tests/package_consumer/CMakeLists.txt b/tests/package_consumer/CMakeLists.txt new file mode 100644 index 0000000..bfd406d --- /dev/null +++ b/tests/package_consumer/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.20) +project(VMD_cpp_package_consumer LANGUAGES CXX) + +find_package(VMD_cpp 2 CONFIG REQUIRED) + +add_executable(vmd_package_consumer main.cpp) +target_link_libraries(vmd_package_consumer PRIVATE VMD_cpp::vmd) diff --git a/tests/package_consumer/main.cpp b/tests/package_consumer/main.cpp new file mode 100644 index 0000000..9c74792 --- /dev/null +++ b/tests/package_consumer/main.cpp @@ -0,0 +1,24 @@ +#include + +#include + +#include + +int main() { + Eigen::VectorXd signal(8); + signal << 0.0, 1.0, 0.0, -1.0, 0.0, 1.0, 0.0, -1.0; + + vmd::Options options; + options.mode_count = 1; + options.alpha = {100.0}; + options.max_iterations = 50; + + const vmd::Result result = vmd::decompose(signal, options); + if (result.modes.rows() != 1 || result.modes.cols() != signal.size() || + result.spectra.rows() != signal.size() || result.spectra.cols() != 1) { + std::cerr << "unexpected installed-package result shape\n"; + return 1; + } + std::cout << "installed package consumer passed\n"; + return 0; +} diff --git a/tests/vmd_tests.cpp b/tests/vmd_tests.cpp new file mode 100644 index 0000000..06552e3 --- /dev/null +++ b/tests/vmd_tests.cpp @@ -0,0 +1,249 @@ +#include +#include "VMD.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +constexpr double kPi = 3.141592653589793238462643383279502884; +int failures = 0; + +void check(bool condition, const std::string& message) { + if (!condition) { + ++failures; + std::cerr << "FAIL: " << message << '\n'; + } +} + +Eigen::VectorXd make_signal(std::size_t n) { + Eigen::VectorXd value(static_cast(n)); + for (std::size_t i = 0; i < n; ++i) { + const double t = static_cast(i + 1) / static_cast(n); + value(static_cast(i)) = std::cos(2.0 * kPi * 2.0 * t) + + 0.1 * std::cos(2.0 * kPi * 24.0 * t) + + 0.005 * std::cos(2.0 * kPi * 288.0 * t); + } + return value; +} + +vmd::Options make_options(std::size_t k = 8) { + vmd::Options value; + value.mode_count = k; + value.alpha = {50.0}; + value.tau = 0.0; + value.initialization = vmd::Initialization::Uniform; + value.tolerance = 1e-7; + value.max_iterations = 500; + value.sample_rate_hz = 1200.0; + return value; +} + +void test_reference_signal() { + const Eigen::VectorXd input = make_signal(1200); + const vmd::Result result = vmd::decompose(input, make_options()); + check(result.modes.rows() == 8 && result.modes.cols() == 1200, "mode dimensions are K x N"); + check(result.spectra.rows() == 1200 && result.spectra.cols() == 8, "spectrum dimensions are N x K"); + check(result.diagnostics.reconstruction_error <= 5e-5, "reference reconstruction error <= 5e-5"); + check(result.modes.array().isFinite().all(), "reference modes are finite"); + check(result.spectra.real().array().isFinite().all(), "reference spectra are finite"); +} + +void test_spectrum_contract() { + const Eigen::VectorXd input = make_signal(257); + const vmd::Result result = vmd::decompose(input, make_options(5)); + Eigen::FFT fft; + double squared_error = 0.0; + double squared_reference = 0.0; + for (Eigen::Index k = 0; k < result.modes.rows(); ++k) { + std::vector mode(static_cast(input.size())); + for (Eigen::Index i = 0; i < input.size(); ++i) mode[static_cast(i)] = result.modes(k, i); + std::vector> raw; + fft.fwd(raw, mode); + const Eigen::Index shift = (input.size() + 1) / 2; + for (Eigen::Index i = 0; i < input.size(); ++i) { + const auto expected = raw[static_cast((i + shift) % input.size())]; + squared_error += std::norm(result.spectra(i, k) - expected); + squared_reference += std::norm(expected); + } + } + const double relative = std::sqrt(squared_error / std::max(squared_reference, 1e-300)); + check(relative < 1e-12, "spectrum equals fftshift(fft(mode))"); +} + +void test_degenerate_and_lengths() { + for (Eigen::Index n : {Eigen::Index{1}, Eigen::Index{2}, Eigen::Index{31}, Eigen::Index{32}}) { + Eigen::VectorXd zero = Eigen::VectorXd::Zero(n); + vmd::Options value = make_options(3); + value.dc_mode = true; + const vmd::Result zero_result = vmd::decompose(zero, value); + check(zero_result.modes.cols() == n, "zero signal preserves length"); + check(zero_result.omega_normalized.array().isFinite().all(), "zero frequencies are finite"); + + Eigen::VectorXd constant = Eigen::VectorXd::Constant(n, 3.5); + const vmd::Result constant_result = vmd::decompose(constant, value); + check(constant_result.modes.cols() == n, "constant signal preserves odd/even length"); + check(constant_result.modes.array().isFinite().all(), "constant modes are finite"); + } +} + +void test_random_seed() { + const Eigen::VectorXd input = make_signal(129); + vmd::Options value = make_options(4); + value.initialization = vmd::Initialization::Random; + value.random_seed = 42; + const vmd::Result a = vmd::decompose(input, value); + const vmd::Result b = vmd::decompose(input, value); + check((a.modes - b.modes).norm() == 0.0, "fixed random seed is reproducible"); + check((a.omega_normalized - b.omega_normalized).norm() == 0.0, "frequency history is reproducible"); +} + +void test_per_mode_alpha_and_warm_start() { + const Eigen::VectorXd input = make_signal(128); + vmd::Options value = make_options(3); + value.alpha = {40.0, 50.0, 60.0}; + const vmd::Result first = vmd::decompose(input, value); + value.warm_start_modes = first.modes; + value.warm_start_omega_normalized = first.omega_normalized.bottomRows(1).transpose(); + const vmd::Result warm = vmd::decompose(input, value); + check(warm.modes.array().isFinite().all(), "warm start produces finite modes"); +} + + +void test_single_frequency() { + constexpr Eigen::Index n = 256; + constexpr double frequency_hz = 16.0; + Eigen::VectorXd input(n); + for (Eigen::Index i = 0; i < n; ++i) { + input(i) = std::cos(2.0 * kPi * frequency_hz * static_cast(i) / static_cast(n)); + } + vmd::Options value = make_options(1); + value.alpha = {2000.0}; + value.sample_rate_hz = static_cast(n); + const vmd::Result result = vmd::decompose(input, value); + check(result.diagnostics.reconstruction_error < 5e-2, "single-mode sinusoid reconstruction stays within boundary tolerance"); + check(std::abs(result.omega_hz(result.omega_hz.rows() - 1, 0) - frequency_hz) < 0.25, + "single-mode center frequency matches the sinusoid"); +} + +void test_noisy_and_chirp_signals() { + constexpr Eigen::Index n = 257; + std::mt19937_64 generator(1234); + std::normal_distribution noise(0.0, 0.03); + Eigen::VectorXd noisy(n); + Eigen::VectorXd chirp(n); + for (Eigen::Index i = 0; i < n; ++i) { + const double t = static_cast(i) / static_cast(n); + noisy(i) = std::cos(2.0 * kPi * 13.0 * t) + 0.2 * std::cos(2.0 * kPi * 47.0 * t) + noise(generator); + chirp(i) = std::cos(2.0 * kPi * (5.0 * t + 35.0 * t * t / 2.0)); + } + vmd::Options value = make_options(4); + value.alpha = {500.0}; + value.sample_rate_hz = static_cast(n); + const vmd::Result noisy_result = vmd::decompose(noisy, value); + const vmd::Result chirp_result = vmd::decompose(chirp, value); + check(noisy_result.modes.array().isFinite().all() && noisy_result.spectra.real().array().isFinite().all(), + "noisy signal produces finite outputs"); + check(chirp_result.modes.array().isFinite().all() && chirp_result.omega_hz.array().isFinite().all(), + "chirp signal produces finite outputs"); +} + +void test_dc_and_advanced_options() { + constexpr Eigen::Index n = 192; + Eigen::VectorXd input(n); + for (Eigen::Index i = 0; i < n; ++i) { + const double t = static_cast(i) / static_cast(n); + input(i) = 2.0 + 0.25 * std::cos(2.0 * kPi * 18.0 * t); + } + vmd::Options value = make_options(2); + value.alpha = {1000.0}; + value.dc_mode = true; + value.keep_omega_history = false; + value.sample_rate_hz = static_cast(n); + const vmd::Result dc_result = vmd::decompose(input, value); + check(dc_result.omega_normalized.rows() == 1, "omega history can be disabled"); + check(dc_result.omega_normalized(0, 0) == 0.0, "DC mode remains fixed at zero frequency"); + + value.keep_omega_history = true; + value.adaptive_tau = true; + value.tau = 0.1; + value.max_iterations = 30; + const vmd::Result adaptive = vmd::decompose(input, value); + check(std::isfinite(adaptive.diagnostics.final_tau) && adaptive.diagnostics.final_tau >= value.tau_min && + adaptive.diagnostics.final_tau <= value.tau_max, + "adaptive tau remains finite and bounded"); +} + +void test_validation() { + bool threw = false; + try { + Eigen::VectorXd empty; + (void)vmd::decompose(empty, make_options()); + } catch (const std::invalid_argument&) { + threw = true; + } + check(threw, "empty signal is rejected"); + + threw = false; + try { + Eigen::VectorXd invalid(2); + invalid << 0.0, std::numeric_limits::quiet_NaN(); + (void)vmd::decompose(invalid, make_options()); + } catch (const std::invalid_argument&) { + threw = true; + } + check(threw, "non-finite signal is rejected"); + + threw = false; + try { + Eigen::VectorXd input = Eigen::VectorXd::Zero(4); + vmd::Options invalid = make_options(); + invalid.initialization = static_cast(99); + (void)vmd::decompose(input, invalid); + } catch (const std::invalid_argument&) { + threw = true; + } + check(threw, "invalid initialization enum is rejected"); +} + +void test_legacy_wrapper() { + const Eigen::VectorXd input = make_signal(96); + vectord values(input.data(), input.data() + input.size()); + Eigen::MatrixXd modes; + Eigen::MatrixXcd spectra; + Eigen::MatrixXd omega; + VMD(modes, spectra, omega, values, 50.0, 0.0, 4, 0, 1, 1e-7, 2.2204e-16); + check(modes.rows() == 4 && modes.cols() == 96, "legacy wrapper preserves mode orientation"); + check(spectra.rows() == 96 && spectra.cols() == 4, "legacy wrapper returns corrected spectra"); +} + +} // namespace + +int main() { + test_reference_signal(); + test_spectrum_contract(); + test_degenerate_and_lengths(); + test_random_seed(); + test_per_mode_alpha_and_warm_start(); + test_single_frequency(); + test_noisy_and_chirp_signals(); + test_dc_and_advanced_options(); + test_validation(); + test_legacy_wrapper(); + if (failures != 0) { + std::cerr << failures << " test(s) failed\n"; + return EXIT_FAILURE; + } + std::cout << "All VMD tests passed\n"; + return EXIT_SUCCESS; +} diff --git a/vmd_decomposition_results.csv b/vmd_decomposition_results.csv deleted file mode 100644 index 7449104..0000000 --- a/vmd_decomposition_results.csv +++ /dev/null @@ -1,8 +0,0 @@ - 0.9976 0.9975 0.9974 0.997 0.9964 0.9958 0.9951 0.9943 0.9934 0.9924 0.9912 0.99 0.9887 0.9872 0.9857 0.984 0.9822 0.9803 0.9784 0.9763 0.9741 0.9718 0.9694 0.9669 0.9643 0.9616 0.9589 0.956 0.953 0.9499 0.9467 0.9434 0.94 0.9366 0.933 0.9293 0.9255 0.9216 0.9176 0.9135 0.9093 0.905 0.9005 0.896 0.8914 0.8866 0.8818 0.8768 0.8718 0.8666 0.8614 0.856 0.8505 0.8449 0.8392 0.8335 0.8276 0.8216 0.8155 0.8093 0.8031 0.7967 0.7903 0.7838 0.7771 0.7705 0.7637 0.7568 0.7499 0.7429 0.7358 0.7286 0.7214 0.7141 0.7067 0.6993 0.6918 0.6842 0.6766 0.6688 0.6611 0.6532 0.6453 0.6373 0.6293 0.6212 0.613 0.6048 0.5964 0.5881 0.5796 0.5711 0.5625 0.5539 0.5452 0.5364 0.5276 0.5186 0.5097 0.5006 0.4915 0.4824 0.4731 0.4639 0.4545 0.4451 0.4357 0.4261 0.4166 0.407 0.3973 0.3876 0.3779 0.3681 0.3583 0.3484 0.3385 0.3286 0.3186 0.3086 0.2986 0.2886 0.2785 0.2684 0.2583 0.2482 0.2381 0.2279 0.2177 0.2075 0.1973 0.1871 0.1768 0.1666 0.1563 0.146 0.1357 0.1254 0.1151 0.1047 0.09439 0.08402 0.07363 0.06323 0.05282 0.0424 0.03196 0.02151 0.01105 0.0005855 -0.009891 -0.02037 -0.03086 -0.04136 -0.05185 -0.06235 -0.07285 -0.08334 -0.09383 -0.1043 -0.1148 -0.1253 -0.1357 -0.1461 -0.1566 -0.167 -0.1773 -0.1877 -0.198 -0.2083 -0.2186 -0.2288 -0.239 -0.2492 -0.2593 -0.2694 -0.2795 -0.2895 -0.2995 -0.3094 -0.3193 -0.3292 -0.339 -0.3488 -0.3585 -0.3682 -0.3778 -0.3874 -0.397 -0.4065 -0.416 -0.4254 -0.4348 -0.4442 -0.4535 -0.4628 -0.472 -0.4812 -0.4903 -0.4994 -0.5085 -0.5175 -0.5264 -0.5353 -0.5442 -0.553 -0.5617 -0.5704 -0.579 -0.5876 -0.5961 -0.6045 -0.6129 -0.6212 -0.6294 -0.6376 -0.6457 -0.6537 -0.6617 -0.6695 -0.6773 -0.685 -0.6926 -0.7002 -0.7076 -0.715 -0.7222 -0.7294 -0.7365 -0.7435 -0.7505 -0.7573 -0.764 -0.7707 -0.7773 -0.7837 -0.7901 -0.7964 -0.8027 -0.8088 -0.8149 -0.8208 -0.8267 -0.8325 -0.8382 -0.8438 -0.8493 -0.8548 -0.8602 -0.8654 -0.8706 -0.8757 -0.8808 -0.8857 -0.8905 -0.8953 -0.8999 -0.9045 -0.909 -0.9133 -0.9176 -0.9218 -0.9259 -0.9298 -0.9337 -0.9375 -0.9411 -0.9447 -0.9481 -0.9514 -0.9547 -0.9578 -0.9608 -0.9636 -0.9664 -0.9691 -0.9716 -0.974 -0.9763 -0.9785 -0.9806 -0.9826 -0.9844 -0.9862 -0.9878 -0.9893 -0.9907 -0.992 -0.9932 -0.9943 -0.9953 -0.9962 -0.9969 -0.9976 -0.9981 -0.9986 -0.999 -0.9992 -0.9994 -0.9994 -0.9994 -0.9992 -0.999 -0.9986 -0.9981 -0.9976 -0.9969 -0.9962 -0.9953 -0.9943 -0.9932 -0.992 -0.9907 -0.9893 -0.9878 -0.9862 -0.9844 -0.9826 -0.9806 -0.9785 -0.9763 -0.974 -0.9716 -0.9691 -0.9664 -0.9637 -0.9608 -0.9578 -0.9547 -0.9514 -0.9481 -0.9447 -0.9411 -0.9375 -0.9337 -0.9298 -0.9259 -0.9218 -0.9176 -0.9133 -0.909 -0.9045 -0.8999 -0.8953 -0.8905 -0.8857 -0.8808 -0.8757 -0.8706 -0.8654 -0.8602 -0.8548 -0.8494 -0.8438 -0.8382 -0.8325 -0.8267 -0.8208 -0.8149 -0.8088 -0.8027 -0.7965 -0.7902 -0.7838 -0.7773 -0.7707 -0.7641 -0.7573 -0.7505 -0.7435 -0.7365 -0.7294 -0.7222 -0.715 -0.7076 -0.7002 -0.6926 -0.685 -0.6773 -0.6695 -0.6617 -0.6537 -0.6457 -0.6376 -0.6295 -0.6212 -0.6129 -0.6045 -0.5961 -0.5876 -0.579 -0.5704 -0.5617 -0.553 -0.5442 -0.5353 -0.5264 -0.5175 -0.5085 -0.4994 -0.4903 -0.4812 -0.472 -0.4628 -0.4535 -0.4442 -0.4349 -0.4255 -0.416 -0.4066 -0.397 -0.3875 -0.3779 -0.3682 -0.3585 -0.3488 -0.339 -0.3292 -0.3193 -0.3094 -0.2995 -0.2895 -0.2795 -0.2694 -0.2593 -0.2492 -0.239 -0.2288 -0.2186 -0.2083 -0.198 -0.1877 -0.1774 -0.167 -0.1566 -0.1462 -0.1357 -0.1253 -0.1148 -0.1044 -0.09387 -0.08338 -0.07289 -0.06239 -0.05189 -0.0414 -0.0309 -0.02042 -0.009932 0.0005433 0.01101 0.02147 0.03192 0.04235 0.05277 0.06319 0.07358 0.08397 0.09434 0.1047 0.115 0.1254 0.1357 0.146 0.1563 0.1665 0.1768 0.187 0.1973 0.2075 0.2177 0.2278 0.238 0.2482 0.2583 0.2684 0.2785 0.2885 0.2986 0.3086 0.3186 0.3285 0.3384 0.3483 0.3582 0.368 0.3778 0.3875 0.3972 0.4069 0.4165 0.4261 0.4356 0.445 0.4544 0.4638 0.473 0.4823 0.4914 0.5005 0.5096 0.5185 0.5274 0.5363 0.5451 0.5538 0.5624 0.571 0.5795 0.5879 0.5963 0.6046 0.6129 0.621 0.6291 0.6372 0.6451 0.6531 0.6609 0.6687 0.6764 0.684 0.6916 0.6991 0.7065 0.7139 0.7212 0.7284 0.7356 0.7427 0.7497 0.7566 0.7635 0.7703 0.777 0.7836 0.7901 0.7965 0.8029 0.8092 0.8153 0.8214 0.8274 0.8333 0.8391 0.8448 0.8504 0.8559 0.8612 0.8665 0.8717 0.8768 0.8818 0.8866 0.8914 0.8961 0.9006 0.9051 0.9094 0.9137 0.9178 0.9219 0.9258 0.9296 0.9334 0.937 0.9406 0.944 0.9473 0.9506 0.9537 0.9568 0.9597 0.9626 0.9654 0.968 0.9706 0.973 0.9754 0.9777 0.9798 0.9819 0.9839 0.9857 0.9875 0.9891 0.9907 0.9921 0.9934 0.9947 0.9958 0.9967 0.9976 0.9984 0.999 0.9996 1 1 1 1.001 1 1 1 0.9996 0.999 0.9984 0.9976 0.9967 0.9958 0.9947 0.9934 0.9921 0.9907 0.9891 0.9875 0.9857 0.9839 0.9819 0.9798 0.9777 0.9754 0.973 0.9706 0.968 0.9654 0.9626 0.9597 0.9568 0.9537 0.9506 0.9473 0.944 0.9406 0.937 0.9334 0.9296 0.9258 0.9219 0.9178 0.9137 0.9094 0.9051 0.9006 0.8961 0.8914 0.8866 0.8818 0.8768 0.8717 0.8665 0.8612 0.8559 0.8504 0.8448 0.8391 0.8333 0.8274 0.8214 0.8153 0.8092 0.8029 0.7965 0.7901 0.7836 0.777 0.7703 0.7635 0.7566 0.7497 0.7427 0.7356 0.7284 0.7212 0.7139 0.7065 0.6991 0.6916 0.684 0.6764 0.6687 0.6609 0.6531 0.6451 0.6372 0.6291 0.621 0.6128 0.6046 0.5963 0.5879 0.5795 0.571 0.5624 0.5538 0.5451 0.5363 0.5274 0.5185 0.5096 0.5005 0.4914 0.4823 0.473 0.4637 0.4544 0.445 0.4356 0.4261 0.4165 0.4069 0.3972 0.3875 0.3778 0.368 0.3582 0.3483 0.3384 0.3285 0.3186 0.3086 0.2986 0.2885 0.2785 0.2684 0.2583 0.2481 0.238 0.2278 0.2177 0.2075 0.1973 0.187 0.1768 0.1665 0.1563 0.146 0.1357 0.1254 0.115 0.1047 0.09433 0.08396 0.07358 0.06318 0.05277 0.04234 0.03191 0.02146 0.011 0.0005344 -0.009941 -0.02042 -0.03091 -0.04141 -0.0519 -0.0624 -0.0729 -0.08339 -0.09388 -0.1044 -0.1148 -0.1253 -0.1357 -0.1462 -0.1566 -0.167 -0.1774 -0.1877 -0.198 -0.2083 -0.2186 -0.2288 -0.2391 -0.2492 -0.2594 -0.2695 -0.2795 -0.2895 -0.2995 -0.3094 -0.3193 -0.3292 -0.339 -0.3488 -0.3585 -0.3682 -0.3779 -0.3875 -0.397 -0.4066 -0.416 -0.4255 -0.4349 -0.4442 -0.4535 -0.4628 -0.472 -0.4812 -0.4904 -0.4995 -0.5085 -0.5175 -0.5264 -0.5353 -0.5442 -0.553 -0.5617 -0.5704 -0.579 -0.5876 -0.5961 -0.6046 -0.6129 -0.6212 -0.6295 -0.6376 -0.6457 -0.6537 -0.6617 -0.6696 -0.6773 -0.685 -0.6926 -0.7002 -0.7076 -0.715 -0.7223 -0.7295 -0.7366 -0.7436 -0.7505 -0.7573 -0.7641 -0.7707 -0.7773 -0.7838 -0.7902 -0.7965 -0.8027 -0.8088 -0.8149 -0.8208 -0.8267 -0.8325 -0.8382 -0.8438 -0.8494 -0.8548 -0.8602 -0.8655 -0.8707 -0.8758 -0.8808 -0.8857 -0.8905 -0.8953 -0.9 -0.9045 -0.909 -0.9134 -0.9176 -0.9218 -0.9259 -0.9299 -0.9337 -0.9375 -0.9411 -0.9447 -0.9481 -0.9515 -0.9547 -0.9578 -0.9608 -0.9637 -0.9664 -0.9691 -0.9716 -0.9741 -0.9764 -0.9786 -0.9806 -0.9826 -0.9845 -0.9862 -0.9878 -0.9894 -0.9908 -0.9921 -0.9932 -0.9943 -0.9953 -0.9962 -0.9969 -0.9976 -0.9982 -0.9986 -0.999 -0.9992 -0.9994 -0.9994 -0.9994 -0.9992 -0.999 -0.9986 -0.9982 -0.9976 -0.997 -0.9962 -0.9953 -0.9943 -0.9933 -0.9921 -0.9908 -0.9894 -0.9878 -0.9862 -0.9845 -0.9826 -0.9806 -0.9786 -0.9764 -0.9741 -0.9716 -0.9691 -0.9664 -0.9637 -0.9608 -0.9578 -0.9547 -0.9515 -0.9481 -0.9447 -0.9412 -0.9375 -0.9337 -0.9299 -0.9259 -0.9218 -0.9176 -0.9134 -0.909 -0.9045 -0.9 -0.8953 -0.8906 -0.8857 -0.8808 -0.8758 -0.8707 -0.8655 -0.8602 -0.8548 -0.8494 -0.8438 -0.8382 -0.8325 -0.8267 -0.8209 -0.8149 -0.8088 -0.8027 -0.7965 -0.7902 -0.7838 -0.7773 -0.7707 -0.7641 -0.7573 -0.7505 -0.7436 -0.7366 -0.7295 -0.7223 -0.715 -0.7076 -0.7002 -0.6927 -0.685 -0.6773 -0.6696 -0.6617 -0.6538 -0.6457 -0.6377 -0.6295 -0.6213 -0.6129 -0.6046 -0.5961 -0.5876 -0.5791 -0.5704 -0.5617 -0.553 -0.5442 -0.5354 -0.5265 -0.5175 -0.5085 -0.4995 -0.4904 -0.4812 -0.4721 -0.4628 -0.4536 -0.4443 -0.4349 -0.4255 -0.4161 -0.4066 -0.3971 -0.3875 -0.3779 -0.3682 -0.3586 -0.3488 -0.339 -0.3292 -0.3194 -0.3095 -0.2995 -0.2896 -0.2795 -0.2695 -0.2594 -0.2493 -0.2391 -0.2289 -0.2186 -0.2084 -0.1981 -0.1878 -0.1774 -0.167 -0.1566 -0.1462 -0.1358 -0.1253 -0.1149 -0.1044 -0.09392 -0.08343 -0.07293 -0.06244 -0.05194 -0.04145 -0.03095 -0.02046 -0.009982 0.0004928 0.01096 0.02142 0.03186 0.0423 0.05272 0.06313 0.07353 0.08391 0.09428 0.1046 0.115 0.1253 0.1356 0.1459 0.1562 0.1665 0.1767 0.187 0.1972 0.2074 0.2176 0.2278 0.2379 0.2481 0.2582 0.2683 0.2784 0.2885 0.2985 0.3085 0.3185 0.3284 0.3384 0.3483 0.3581 0.3679 0.3777 0.3875 0.3972 0.4068 0.4164 0.426 0.4355 0.4449 0.4543 0.4637 0.4729 0.4822 0.4913 0.5004 0.5095 0.5184 0.5273 0.5362 0.5449 0.5536 0.5623 0.5709 0.5794 0.5878 0.5962 0.6045 0.6127 0.6209 0.629 0.637 0.645 0.6529 0.6607 0.6685 0.6762 0.6838 0.6914 0.6989 0.7064 0.7137 0.721 0.7283 0.7354 0.7425 0.7495 0.7564 0.7633 0.7701 0.7768 0.7834 0.7899 0.7963 0.8027 0.809 0.8151 0.8212 0.8272 0.8331 0.8389 0.8446 0.8502 0.8557 0.8611 0.8664 0.8716 0.8767 0.8817 0.8866 0.8914 0.8961 0.9007 0.9052 0.9096 0.9138 0.918 0.9221 0.9261 0.93 0.9338 0.9374 0.941 0.9445 0.9479 0.9512 0.9544 0.9575 0.9606 0.9635 0.9663 0.969 0.9717 0.9742 0.9767 0.979 0.9812 0.9834 0.9854 0.9874 0.9892 0.991 0.9926 0.9941 0.9955 0.9968 0.998 0.9991 1 1.001 1.002 1.002 1.003 1.003 1.003 1.003 - 0.09709 0.09552 0.0925 0.08784 0.08166 0.0742 0.06558 0.05591 0.04535 0.03405 0.02221 0.01002 -0.002327 -0.01464 -0.02671 -0.03835 -0.04937 -0.0596 -0.06887 -0.07703 -0.08395 -0.08952 -0.09366 -0.09628 -0.09736 -0.09688 -0.09484 -0.09128 -0.08625 -0.07984 -0.07215 -0.0633 -0.05343 -0.0427 -0.03129 -0.01938 -0.007156 0.005187 0.01745 0.02944 0.04096 0.05182 0.06187 0.07093 0.07887 0.08555 0.09087 0.09475 0.09712 0.09795 0.09723 0.09497 0.0912 0.08598 0.0794 0.07157 0.0626 0.05264 0.04185 0.03039 0.01846 0.006231 -0.006093 -0.01832 -0.03026 -0.04172 -0.05252 -0.06249 -0.07147 -0.07933 -0.08593 -0.09118 -0.09498 -0.09729 -0.09807 -0.0973 -0.09499 -0.09119 -0.08595 -0.07936 -0.07151 -0.06253 -0.05258 -0.04179 -0.03034 -0.01842 -0.006204 0.006107 0.01832 0.03025 0.04169 0.05248 0.06245 0.07142 0.07927 0.08587 0.09111 0.09492 0.09723 0.098 0.09723 0.09493 0.09112 0.08588 0.07929 0.07144 0.06247 0.05251 0.04172 0.03028 0.01835 0.006137 -0.006174 -0.01839 -0.03031 -0.04176 -0.05255 -0.06251 -0.07148 -0.07933 -0.08592 -0.09116 -0.09497 -0.09727 -0.09805 -0.09727 -0.09497 -0.09116 -0.08592 -0.07932 -0.07147 -0.0625 -0.05254 -0.04175 -0.03031 -0.01838 -0.006168 0.006143 0.01836 0.03028 0.04173 0.05252 0.06247 0.07145 0.0793 0.08589 0.09113 0.09494 0.09725 0.09802 0.09725 0.09494 0.09114 0.08589 0.0793 0.07145 0.06248 0.05252 0.04173 0.03028 0.01836 0.006146 -0.006165 -0.01838 -0.0303 -0.04175 -0.05254 -0.0625 -0.07147 -0.07932 -0.08591 -0.09115 -0.09496 -0.09727 -0.09804 -0.09727 -0.09496 -0.09115 -0.08591 -0.07932 -0.07147 -0.06249 -0.05253 -0.04175 -0.0303 -0.01838 -0.006163 0.006148 0.01836 0.03029 0.04173 0.05252 0.06248 0.07145 0.0793 0.0859 0.09114 0.09494 0.09725 0.09802 0.09725 0.09494 0.09114 0.0859 0.0793 0.07145 0.06248 0.05252 0.04173 0.03029 0.01836 0.006149 -0.006161 -0.01838 -0.0303 -0.04175 -0.05253 -0.06249 -0.07147 -0.07931 -0.08591 -0.09115 -0.09496 -0.09726 -0.09804 -0.09726 -0.09496 -0.09115 -0.08591 -0.07931 -0.07147 -0.06249 -0.05253 -0.04174 -0.0303 -0.01837 -0.006161 0.00615 0.01836 0.03029 0.04173 0.05252 0.06248 0.07146 0.0793 0.0859 0.09114 0.09495 0.09725 0.09803 0.09725 0.09495 0.09114 0.0859 0.0793 0.07146 0.06248 0.05252 0.04173 0.03029 0.01836 0.006151 -0.00616 -0.01837 -0.0303 -0.04174 -0.05253 -0.06249 -0.07147 -0.07931 -0.08591 -0.09115 -0.09495 -0.09726 -0.09803 -0.09726 -0.09495 -0.09115 -0.08591 -0.07931 -0.07146 -0.06249 -0.05253 -0.04174 -0.0303 -0.01837 -0.006159 0.006152 0.01837 0.03029 0.04174 0.05252 0.06248 0.07146 0.0793 0.0859 0.09114 0.09495 0.09725 0.09803 0.09725 0.09495 0.09114 0.0859 0.0793 0.07146 0.06248 0.05252 0.04174 0.03029 0.01837 0.006152 -0.006159 -0.01837 -0.0303 -0.04174 -0.05253 -0.06249 -0.07146 -0.07931 -0.08591 -0.09115 -0.09495 -0.09726 -0.09803 -0.09726 -0.09495 -0.09115 -0.08591 -0.07931 -0.07146 -0.06249 -0.05253 -0.04174 -0.0303 -0.01837 -0.006158 0.006153 0.01837 0.03029 0.04174 0.05252 0.06248 0.07146 0.07931 0.0859 0.09114 0.09495 0.09725 0.09803 0.09725 0.09495 0.09114 0.0859 0.07931 0.07146 0.06248 0.05252 0.04174 0.03029 0.01837 0.006153 -0.006158 -0.01837 -0.0303 -0.04174 -0.05253 -0.06249 -0.07146 -0.07931 -0.08591 -0.09115 -0.09495 -0.09726 -0.09803 -0.09726 -0.09495 -0.09115 -0.08591 -0.07931 -0.07146 -0.06249 -0.05253 -0.04174 -0.03029 -0.01837 -0.006157 0.006153 0.01837 0.03029 0.04174 0.05253 0.06248 0.07146 0.07931 0.0859 0.09114 0.09495 0.09726 0.09803 0.09726 0.09495 0.09114 0.0859 0.07931 0.07146 0.06249 0.05253 0.04174 0.03029 0.01837 0.006154 -0.006157 -0.01837 -0.03029 -0.04174 -0.05253 -0.06249 -0.07146 -0.07931 -0.08591 -0.09115 -0.09495 -0.09726 -0.09803 -0.09726 -0.09495 -0.09115 -0.08591 -0.07931 -0.07146 -0.06249 -0.05253 -0.04174 -0.03029 -0.01837 -0.006157 0.006154 0.01837 0.03029 0.04174 0.05253 0.06249 0.07146 0.07931 0.0859 0.09114 0.09495 0.09726 0.09803 0.09726 0.09495 0.09115 0.0859 0.07931 0.07146 0.06249 0.05253 0.04174 0.03029 0.01837 0.006154 -0.006156 -0.01837 -0.03029 -0.04174 -0.05253 -0.06249 -0.07146 -0.07931 -0.08591 -0.09115 -0.09495 -0.09726 -0.09803 -0.09726 -0.09495 -0.09115 -0.08591 -0.07931 -0.07146 -0.06249 -0.05253 -0.04174 -0.03029 -0.01837 -0.006156 0.006155 0.01837 0.03029 0.04174 0.05253 0.06249 0.07146 0.07931 0.0859 0.09115 0.09495 0.09726 0.09803 0.09726 0.09495 0.09115 0.0859 0.07931 0.07146 0.06249 0.05253 0.04174 0.03029 0.01837 0.006155 -0.006156 -0.01837 -0.03029 -0.04174 -0.05253 -0.06249 -0.07146 -0.07931 -0.0859 -0.09115 -0.09495 -0.09726 -0.09803 -0.09726 -0.09495 -0.09115 -0.0859 -0.07931 -0.07146 -0.06249 -0.05253 -0.04174 -0.03029 -0.01837 -0.006155 0.006155 0.01837 0.03029 0.04174 0.05253 0.06249 0.07146 0.07931 0.0859 0.09115 0.09495 0.09726 0.09803 0.09726 0.09495 0.09115 0.0859 0.07931 0.07146 0.06249 0.05253 0.04174 0.03029 0.01837 0.006156 -0.006155 -0.01837 -0.03029 -0.04174 -0.05253 -0.06249 -0.07146 -0.07931 -0.0859 -0.09115 -0.09495 -0.09726 -0.09803 -0.09726 -0.09495 -0.09115 -0.0859 -0.07931 -0.07146 -0.06249 -0.05253 -0.04174 -0.03029 -0.01837 -0.006155 0.006156 0.01837 0.03029 0.04174 0.05253 0.06249 0.07146 0.07931 0.0859 0.09115 0.09495 0.09726 0.09803 0.09726 0.09495 0.09115 0.0859 0.07931 0.07146 0.06249 0.05253 0.04174 0.03029 0.01837 0.006156 -0.006155 -0.01837 -0.03029 -0.04174 -0.05253 -0.06249 -0.07146 -0.07931 -0.0859 -0.09115 -0.09495 -0.09726 -0.09803 -0.09726 -0.09495 -0.09115 -0.0859 -0.07931 -0.07146 -0.06249 -0.05253 -0.04174 -0.03029 -0.01837 -0.006155 0.006156 0.01837 0.03029 0.04174 0.05253 0.06249 0.07146 0.07931 0.08591 0.09115 0.09495 0.09726 0.09803 0.09726 0.09495 0.09115 0.08591 0.07931 0.07146 0.06249 0.05253 0.04174 0.03029 0.01837 0.006156 -0.006155 -0.01837 -0.03029 -0.04174 -0.05253 -0.06249 -0.07146 -0.07931 -0.0859 -0.09115 -0.09495 -0.09726 -0.09803 -0.09726 -0.09495 -0.09115 -0.0859 -0.07931 -0.07146 -0.06249 -0.05253 -0.04174 -0.03029 -0.01837 -0.006154 0.006156 0.01837 0.03029 0.04174 0.05253 0.06249 0.07146 0.07931 0.08591 0.09115 0.09495 0.09726 0.09803 0.09726 0.09495 0.09115 0.08591 0.07931 0.07146 0.06249 0.05253 0.04174 0.03029 0.01837 0.006156 -0.006154 -0.01837 -0.03029 -0.04174 -0.05253 -0.06249 -0.07146 -0.07931 -0.0859 -0.09115 -0.09495 -0.09726 -0.09803 -0.09726 -0.09495 -0.09115 -0.0859 -0.07931 -0.07146 -0.06249 -0.05253 -0.04174 -0.03029 -0.01837 -0.006154 0.006156 0.01837 0.03029 0.04174 0.05253 0.06249 0.07146 0.07931 0.08591 0.09115 0.09495 0.09726 0.09803 0.09726 0.09495 0.09115 0.08591 0.07931 0.07146 0.06249 0.05253 0.04174 0.03029 0.01837 0.006156 -0.006154 -0.01837 -0.03029 -0.04174 -0.05253 -0.06249 -0.07146 -0.07931 -0.0859 -0.09114 -0.09495 -0.09726 -0.09803 -0.09726 -0.09495 -0.09114 -0.0859 -0.07931 -0.07146 -0.06249 -0.05253 -0.04174 -0.03029 -0.01837 -0.006154 0.006157 0.01837 0.03029 0.04174 0.05253 0.06249 0.07146 0.07931 0.08591 0.09115 0.09495 0.09726 0.09803 0.09726 0.09495 0.09115 0.08591 0.07931 0.07146 0.06249 0.05253 0.04174 0.03029 0.01837 0.006157 -0.006154 -0.01837 -0.03029 -0.04174 -0.05253 -0.06249 -0.07146 -0.07931 -0.0859 -0.09114 -0.09495 -0.09726 -0.09803 -0.09726 -0.09495 -0.09114 -0.0859 -0.07931 -0.07146 -0.06249 -0.05253 -0.04174 -0.03029 -0.01837 -0.006154 0.006157 0.01837 0.03029 0.04174 0.05253 0.06249 0.07146 0.07931 0.08591 0.09115 0.09495 0.09726 0.09803 0.09726 0.09495 0.09115 0.08591 0.07931 0.07146 0.06249 0.05253 0.04174 0.03029 0.01837 0.006157 -0.006154 -0.01837 -0.03029 -0.04174 -0.05253 -0.06248 -0.07146 -0.07931 -0.0859 -0.09114 -0.09495 -0.09726 -0.09803 -0.09726 -0.09495 -0.09114 -0.0859 -0.07931 -0.07146 -0.06248 -0.05253 -0.04174 -0.03029 -0.01837 -0.006153 0.006158 0.01837 0.0303 0.04174 0.05253 0.06249 0.07146 0.07931 0.08591 0.09115 0.09495 0.09726 0.09803 0.09726 0.09495 0.09115 0.08591 0.07931 0.07146 0.06249 0.05253 0.04174 0.0303 0.01837 0.006158 -0.006153 -0.01837 -0.03029 -0.04174 -0.05252 -0.06248 -0.07146 -0.07931 -0.0859 -0.09114 -0.09495 -0.09725 -0.09803 -0.09725 -0.09495 -0.09114 -0.0859 -0.0793 -0.07146 -0.06248 -0.05252 -0.04174 -0.03029 -0.01837 -0.006152 0.006159 0.01837 0.0303 0.04174 0.05253 0.06249 0.07146 0.07931 0.08591 0.09115 0.09495 0.09726 0.09803 0.09726 0.09495 0.09115 0.08591 0.07931 0.07146 0.06249 0.05253 0.04174 0.0303 0.01837 0.00616 -0.006151 -0.01836 -0.03029 -0.04173 -0.05252 -0.06248 -0.07146 -0.0793 -0.0859 -0.09114 -0.09495 -0.09725 -0.09803 -0.09725 -0.09495 -0.09114 -0.0859 -0.0793 -0.07146 -0.06248 -0.05252 -0.04173 -0.03029 -0.01836 -0.00615 0.006161 0.01837 0.0303 0.04174 0.05253 0.06249 0.07147 0.07931 0.08591 0.09115 0.09496 0.09726 0.09804 0.09726 0.09496 0.09115 0.08591 0.07931 0.07147 0.06249 0.05253 0.04175 0.0303 0.01838 0.006162 -0.006148 -0.01836 -0.03029 -0.04173 -0.05252 -0.06248 -0.07145 -0.0793 -0.0859 -0.09114 -0.09494 -0.09725 -0.09802 -0.09725 -0.09494 -0.09114 -0.0859 -0.0793 -0.07145 -0.06248 -0.05252 -0.04173 -0.03028 -0.01836 -0.006146 0.006165 0.01838 0.0303 0.04175 0.05254 0.0625 0.07147 0.07932 0.08591 0.09116 0.09496 0.09727 0.09804 0.09727 0.09496 0.09116 0.08592 0.07932 0.07147 0.0625 0.05254 0.04175 0.03031 0.01838 0.006168 -0.006143 -0.01836 -0.03028 -0.04173 -0.05251 -0.06247 -0.07145 -0.07929 -0.08589 -0.09113 -0.09494 -0.09724 -0.09801 -0.09724 -0.09493 -0.09113 -0.08589 -0.07929 -0.07144 -0.06247 -0.05251 -0.04172 -0.03027 -0.01835 -0.006137 0.006174 0.01839 0.03031 0.04176 0.05255 0.0625 0.07148 0.07933 0.08592 0.09117 0.09497 0.09728 0.09805 0.09728 0.09498 0.09118 0.08594 0.07934 0.0715 0.06253 0.05257 0.04178 0.03034 0.01842 0.006203 -0.006106 -0.01832 -0.03024 -0.04169 -0.05248 -0.06244 -0.07141 -0.07926 -0.08586 -0.0911 -0.0949 -0.09721 -0.09799 -0.09722 -0.09491 -0.09111 -0.08587 -0.07928 -0.07144 -0.06248 -0.05253 -0.04175 -0.03031 -0.0184 -0.006203 0.006094 0.01829 0.03021 0.04164 0.05242 0.06238 0.07135 0.0792 0.08581 0.09107 0.0949 0.09724 0.09806 0.09733 0.09509 0.09135 0.08618 0.07967 0.0719 0.06302 0.05314 0.04244 0.03108 0.01923 0.007076 -0.005186 -0.01737 -0.02928 -0.04074 -0.05156 -0.06158 -0.07065 -0.07862 -0.08537 -0.0908 -0.09482 -0.09737 -0.09841 -0.09792 -0.09592 -0.09244 -0.08753 -0.08126 -0.07374 -0.06508 -0.05541 -0.04489 -0.03368 -0.02195 -0.009886 0.002328 0.0145 0.02645 0.03798 0.04892 0.0591 0.06837 0.07657 0.08355 0.08921 0.0936 0.09669 0.0982 - 0.002027 -0.002625 -0.001584 0.002898 0.001747 -0.002918 -0.002129 0.002731 0.002471 -0.002476 -0.002785 0.002168 0.003054 -0.001831 -0.003289 0.00146 0.003479 -0.001066 -0.003627 0.0006491 0.003726 -0.0002177 -0.003776 -0.0002242 0.003774 0.0006703 -0.003717 -0.001112 0.003609 0.001548 -0.003445 -0.001964 0.003233 0.002363 -0.002966 -0.002727 0.002658 0.003063 -0.0023 -0.003351 0.001911 0.0036 -0.001483 -0.003793 0.001033 0.003936 -0.0005575 -0.004018 7.27e-05 0.004043 0.0004205 -0.004006 -0.0009109 0.003908 0.001392 -0.00375 -0.001856 0.003532 0.002294 -0.003261 -0.002703 0.002935 0.00307 -0.002565 -0.003395 0.002149 0.003666 -0.001701 -0.003885 0.001222 0.004043 -0.0007235 -0.004139 0.0002103 0.004171 0.0003078 -0.004139 -0.0008232 0.004041 0.001327 -0.00388 -0.001813 0.003658 0.00227 -0.003378 -0.002694 0.003044 0.003076 -0.002663 -0.003412 0.002237 0.003693 -0.001777 -0.003918 0.001287 0.004081 -0.0007775 -0.00418 0.0002541 0.004213 0.0002733 -0.004181 -0.0007975 0.004082 0.001309 -0.003919 -0.001801 0.003694 0.002264 -0.003411 -0.002693 0.003073 0.003078 -0.002688 -0.003416 0.002259 0.0037 -0.001795 -0.003926 0.001303 0.00409 -0.0007896 -0.004189 0.0002636 0.004222 0.0002662 -0.004189 -0.0007923 0.00409 0.001306 -0.003926 -0.001799 0.0037 0.002263 -0.003417 -0.002693 0.003078 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007914 -0.004191 0.000265 0.004224 0.0002652 -0.004191 -0.0007916 0.004091 0.001305 -0.003927 -0.001799 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001799 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001799 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001799 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007915 -0.004191 0.0002652 0.004224 0.0002652 -0.004191 -0.0007915 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007914 -0.004191 0.0002653 0.004224 0.0002653 -0.004191 -0.0007914 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007914 -0.004191 0.0002653 0.004224 0.0002653 -0.004191 -0.0007914 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001798 -0.003927 0.001305 0.004091 -0.0007914 -0.004191 0.0002653 0.004224 0.0002653 -0.004191 -0.0007914 0.004091 0.001305 -0.003927 -0.001798 0.003701 0.002263 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003702 -0.001798 -0.003927 0.001305 0.004091 -0.0007913 -0.00419 0.0002652 0.004224 0.0002654 -0.00419 -0.0007916 0.004091 0.001306 -0.003927 -0.001799 0.003701 0.002264 -0.003417 -0.002692 0.003079 0.003079 -0.002692 -0.003417 0.002263 0.003701 -0.001797 -0.003927 0.001304 0.004091 -0.0007901 -0.00419 0.0002639 0.004223 0.0002668 -0.004189 -0.0007929 0.004089 0.001307 -0.003925 -0.0018 0.003698 0.002265 -0.003413 -0.002693 0.003074 0.00308 -0.002686 -0.003417 0.002257 0.0037 -0.001791 -0.003925 0.001297 0.004087 -0.0007818 -0.004185 0.0002551 0.004216 0.000276 -0.00418 -0.0008022 0.004077 0.001316 -0.00391 -0.001808 0.00368 0.002272 -0.003391 -0.002698 0.003049 0.003082 -0.002658 -0.003415 0.002224 0.003694 -0.001755 -0.003913 0.001258 0.004069 -0.0007415 -0.004159 0.0002134 0.004181 0.0003178 -0.004136 -0.0008427 0.004023 0.001354 -0.003844 -0.001841 0.003604 0.002298 -0.003303 -0.002715 0.002951 0.003088 -0.002548 -0.003407 0.002107 0.00367 -0.001629 -0.003869 0.001128 0.004005 -0.0006079 -0.004071 8.032e-05 0.004069 0.0004461 -0.003998 -0.0009642 0.003857 0.00146 -0.003655 -0.001933 0.003386 0.002364 -0.003065 -0.002758 0.002687 0.003095 -0.002271 -0.003383 0.001812 0.003602 -0.00133 -0.003761 0.0008229 0.003848 -0.0003096 -0.00387 -0.000208 0.003819 0.0007154 -0.003701 -0.001206 0.003517 0.00167 -0.003271 -0.002098 0.002969 0.002486 -0.002614 -0.002821 0.002218 0.003105 -0.001781 -0.003324 0.001323 0.003494 -0.0008284 -0.003597 0.0002959 0.003649 0.0004016 -0.003398 -0.0013 0.002462 - 0.0002445 -0.0009974 0.000343 0.001437 -0.0003123 -0.001633 5.172e-05 0.001634 0.0001816 -0.001577 -0.0004008 0.001478 0.0005882 -0.00136 -0.0007584 0.001219 0.0009016 -0.001065 -0.001023 0.0008964 0.001117 -0.0007205 -0.001185 0.0005394 0.001228 -0.0003566 -0.001243 0.0001778 0.001236 -1.876e-06 -0.001203 -0.0001615 0.001153 0.0003187 -0.001078 -0.0004565 0.0009919 0.0005854 -0.0008848 -0.0006907 0.0007727 0.0007848 -0.0006448 -0.000854 0.0005163 0.0009097 -0.0003788 -0.000942 0.0002436 0.0009588 -0.000107 -0.0009559 -2.536e-05 0.0009361 0.0001521 -0.0009011 -0.0002732 0.0008494 0.0003831 -0.0007869 -0.0004855 0.0007096 0.0005735 -0.0006252 -0.0006519 0.0005295 0.0007143 -0.0004297 -0.0007652 0.000323 0.0008002 -0.0002146 -0.0008222 0.0001041 0.0008291 5.536e-06 -0.0008226 -0.0001134 0.0008022 0.0002173 -0.0007692 -0.0003163 0.0007238 0.0004084 -0.0006676 -0.0004931 0.0006007 0.0005682 -0.0005254 -0.000634 0.0004418 0.0006882 -0.0003525 -0.0007316 0.0002576 0.0007623 -0.0001599 -0.0007811 5.975e-05 0.0007869 4.022e-05 -0.0007805 -0.0001395 0.0007613 0.0002356 -0.0007306 -0.0003281 0.0006879 0.0004145 -0.0006349 -0.0004947 0.0005715 0.0005661 -0.0004999 -0.0006291 0.0004199 0.0006813 -0.0003339 -0.0007233 0.0002424 0.0007532 -0.0001476 -0.0007717 5.018e-05 0.0007775 4.75e-05 -0.0007715 -0.0001447 0.0007529 0.0002392 -0.0007228 -0.0003302 0.000681 0.0004156 -0.0006288 -0.0004948 0.0005663 0.0005657 -0.0004954 -0.0006281 0.0004162 0.0006802 -0.0003309 -0.0007218 0.00024 0.0007518 -0.0001457 -0.0007702 4.88e-05 0.0007762 4.855e-05 -0.0007702 -0.0001454 0.0007517 0.0002397 -0.0007218 -0.0003305 0.0006801 0.0004158 -0.000628 -0.0004948 0.0005657 0.0005657 -0.0004948 -0.0006279 0.0004158 0.00068 -0.0003305 -0.0007217 0.0002398 0.0007517 -0.0001455 -0.00077 4.868e-05 0.000776 4.867e-05 -0.00077 -0.0001455 0.0007517 0.0002398 -0.0007216 -0.0003305 0.00068 0.0004158 -0.0006279 -0.0004947 0.0005657 0.0005657 -0.0004948 -0.0006279 0.0004158 0.00068 -0.0003305 -0.0007216 0.0002398 0.0007517 -0.0001455 -0.00077 4.868e-05 0.000776 4.868e-05 -0.00077 -0.0001455 0.0007517 0.0002398 -0.0007216 -0.0003305 0.00068 0.0004158 -0.0006279 -0.0004947 0.0005657 0.0005657 -0.0004947 -0.0006279 0.0004158 0.00068 -0.0003305 -0.0007216 0.0002398 0.0007517 -0.0001455 -0.00077 4.869e-05 0.000776 4.869e-05 -0.00077 -0.0001455 0.0007517 0.0002398 -0.0007216 -0.0003305 0.00068 0.0004158 -0.0006279 -0.0004947 0.0005657 0.0005657 -0.0004947 -0.0006279 0.0004158 0.0006801 -0.0003305 -0.0007216 0.0002398 0.0007517 -0.0001455 -0.00077 4.87e-05 0.0007761 4.87e-05 -0.00077 -0.0001455 0.0007517 0.0002398 -0.0007216 -0.0003305 0.0006801 0.0004158 -0.0006279 -0.0004947 0.0005657 0.0005657 -0.0004947 -0.0006279 0.0004158 0.0006801 -0.0003305 -0.0007216 0.0002398 0.0007517 -0.0001455 -0.00077 4.87e-05 0.0007761 4.87e-05 -0.00077 -0.0001455 0.0007517 0.0002398 -0.0007216 -0.0003305 0.0006801 0.0004158 -0.0006279 -0.0004947 0.0005657 0.0005657 -0.0004947 -0.0006279 0.0004158 0.0006801 -0.0003305 -0.0007216 0.0002398 0.0007517 -0.0001455 -0.00077 4.87e-05 0.0007761 4.871e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003305 0.0006801 0.0004158 -0.0006279 -0.0004947 0.0005657 0.0005657 -0.0004947 -0.0006279 0.0004158 0.0006801 -0.0003305 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.00077 4.871e-05 0.0007761 4.871e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003305 0.0006801 0.0004158 -0.0006279 -0.0004947 0.0005657 0.0005657 -0.0004947 -0.0006279 0.0004158 0.0006801 -0.0003305 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.00077 4.871e-05 0.0007761 4.871e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003305 0.0006801 0.0004158 -0.0006279 -0.0004947 0.0005657 0.0005657 -0.0004947 -0.0006279 0.0004158 0.0006801 -0.0003305 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.00077 4.872e-05 0.0007761 4.872e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003305 0.0006801 0.0004158 -0.0006279 -0.0004947 0.0005657 0.0005657 -0.0004947 -0.0006279 0.0004158 0.0006801 -0.0003305 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.00077 4.872e-05 0.0007761 4.872e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003305 0.0006801 0.0004158 -0.0006279 -0.0004947 0.0005657 0.0005657 -0.0004947 -0.0006279 0.0004158 0.0006801 -0.0003305 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.00077 4.872e-05 0.0007761 4.872e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003304 0.0006801 0.0004158 -0.0006279 -0.0004947 0.0005657 0.0005657 -0.0004947 -0.0006279 0.0004158 0.0006801 -0.0003304 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.00077 4.872e-05 0.0007761 4.872e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003304 0.0006801 0.0004158 -0.0006279 -0.0004947 0.0005657 0.0005657 -0.0004947 -0.0006279 0.0004158 0.0006801 -0.0003304 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.00077 4.873e-05 0.0007761 4.873e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003304 0.0006801 0.0004158 -0.0006279 -0.0004947 0.0005657 0.0005657 -0.0004947 -0.0006279 0.0004158 0.0006801 -0.0003304 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.00077 4.873e-05 0.0007761 4.873e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003304 0.0006801 0.0004158 -0.0006279 -0.0004947 0.0005657 0.0005657 -0.0004947 -0.0006279 0.0004158 0.0006801 -0.0003304 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.00077 4.873e-05 0.0007761 4.873e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003304 0.0006801 0.0004158 -0.0006279 -0.0004947 0.0005657 0.0005657 -0.0004947 -0.0006279 0.0004158 0.0006801 -0.0003304 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.00077 4.873e-05 0.0007761 4.873e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003304 0.0006801 0.0004158 -0.0006279 -0.0004947 0.0005657 0.0005657 -0.0004947 -0.0006279 0.0004158 0.0006801 -0.0003304 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.00077 4.873e-05 0.0007761 4.873e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003304 0.0006801 0.0004159 -0.0006279 -0.0004947 0.0005657 0.0005657 -0.0004947 -0.0006279 0.0004158 0.0006801 -0.0003304 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.00077 4.873e-05 0.0007761 4.873e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003304 0.0006801 0.0004158 -0.0006279 -0.0004947 0.0005657 0.0005657 -0.0004947 -0.0006279 0.0004159 0.0006801 -0.0003304 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.00077 4.874e-05 0.0007761 4.874e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003304 0.0006801 0.0004159 -0.0006279 -0.0004947 0.0005657 0.0005657 -0.0004947 -0.0006279 0.0004159 0.0006801 -0.0003304 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.00077 4.873e-05 0.0007761 4.874e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003304 0.0006801 0.0004159 -0.0006279 -0.0004947 0.0005657 0.0005657 -0.0004947 -0.0006279 0.0004159 0.0006801 -0.0003304 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.00077 4.874e-05 0.0007761 4.874e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003304 0.0006801 0.0004159 -0.0006279 -0.0004947 0.0005657 0.0005657 -0.0004947 -0.0006279 0.0004159 0.0006801 -0.0003304 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.00077 4.874e-05 0.0007761 4.874e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003304 0.0006801 0.0004159 -0.0006279 -0.0004947 0.0005657 0.0005657 -0.0004947 -0.0006279 0.0004159 0.0006801 -0.0003304 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.00077 4.874e-05 0.0007761 4.874e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003304 0.0006801 0.0004159 -0.0006279 -0.0004947 0.0005657 0.0005657 -0.0004947 -0.0006279 0.0004159 0.0006801 -0.0003304 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.00077 4.874e-05 0.0007761 4.874e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003304 0.0006801 0.0004159 -0.0006279 -0.0004947 0.0005658 0.0005657 -0.0004947 -0.0006279 0.0004159 0.0006801 -0.0003304 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.00077 4.874e-05 0.0007761 4.874e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003304 0.0006801 0.0004159 -0.0006279 -0.0004947 0.0005657 0.0005658 -0.0004947 -0.0006279 0.0004159 0.0006801 -0.0003304 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.00077 4.874e-05 0.0007761 4.874e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003304 0.0006801 0.0004159 -0.0006279 -0.0004947 0.0005658 0.0005658 -0.0004947 -0.0006279 0.0004159 0.0006801 -0.0003304 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.00077 4.874e-05 0.0007761 4.874e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003304 0.0006801 0.0004159 -0.0006279 -0.0004947 0.0005658 0.0005658 -0.0004947 -0.0006279 0.0004159 0.0006801 -0.0003304 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.00077 4.874e-05 0.0007761 4.874e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003304 0.0006801 0.0004159 -0.0006279 -0.0004947 0.0005658 0.0005658 -0.0004947 -0.0006279 0.0004159 0.0006801 -0.0003304 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.00077 4.875e-05 0.0007761 4.875e-05 -0.00077 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003304 0.0006801 0.0004159 -0.0006278 -0.0004947 0.0005658 0.0005658 -0.0004947 -0.0006278 0.0004159 0.0006801 -0.0003304 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.0007699 4.875e-05 0.0007761 4.875e-05 -0.0007699 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003304 0.0006801 0.0004159 -0.0006278 -0.0004947 0.0005658 0.0005658 -0.0004947 -0.0006278 0.0004159 0.0006801 -0.0003304 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.0007699 4.875e-05 0.0007761 4.875e-05 -0.0007699 -0.0001454 0.0007517 0.0002398 -0.0007216 -0.0003304 0.0006801 0.0004159 -0.0006278 -0.0004947 0.0005658 0.0005658 -0.0004947 -0.0006278 0.0004159 0.0006801 -0.0003304 -0.0007216 0.0002398 0.0007517 -0.0001454 -0.0007699 4.876e-05 0.0007761 4.876e-05 -0.0007699 -0.0001454 0.0007517 0.0002399 -0.0007216 -0.0003304 0.0006801 0.0004159 -0.0006278 -0.0004947 0.0005658 0.0005658 -0.0004947 -0.0006278 0.0004159 0.0006801 -0.0003304 -0.0007216 0.0002399 0.0007517 -0.0001454 -0.0007699 4.877e-05 0.0007761 4.877e-05 -0.0007699 -0.0001454 0.0007517 0.0002399 -0.0007215 -0.0003304 0.0006801 0.0004159 -0.0006278 -0.0004947 0.0005658 0.0005658 -0.0004947 -0.0006278 0.0004159 0.0006801 -0.0003304 -0.0007215 0.0002399 0.0007517 -0.0001454 -0.0007699 4.877e-05 0.0007761 4.878e-05 -0.0007699 -0.0001454 0.0007518 0.0002399 -0.0007215 -0.0003304 0.0006801 0.0004159 -0.0006278 -0.0004946 0.0005658 0.0005658 -0.0004946 -0.0006278 0.0004159 0.0006801 -0.0003304 -0.0007215 0.0002399 0.0007518 -0.0001454 -0.0007699 4.88e-05 0.0007761 4.878e-05 -0.0007699 -0.0001454 0.0007518 0.0002399 -0.0007215 -0.0003304 0.0006802 0.0004159 -0.0006278 -0.0004946 0.0005659 0.0005658 -0.0004947 -0.0006278 0.000416 0.0006802 -0.0003305 -0.0007215 0.00024 0.0007518 -0.0001455 -0.00077 4.894e-05 0.0007763 4.866e-05 -0.00077 -0.0001452 0.000752 0.0002397 -0.0007218 -0.0003302 0.0006805 0.0004158 -0.0006282 -0.0004945 0.0005664 0.0005658 -0.0004953 -0.0006278 0.0004168 0.0006803 -0.0003313 -0.0007218 0.0002411 0.0007523 -0.0001466 -0.0007707 5.026e-05 0.0007773 4.728e-05 -0.0007714 -0.0001437 0.0007539 0.0002383 -0.0007241 -0.0003287 0.0006836 0.0004146 -0.0006319 -0.0004935 0.0005709 0.0005654 -0.0005005 -0.0006279 0.0004229 0.0006814 -0.0003382 -0.0007239 0.0002488 0.0007559 -0.000155 -0.0007759 5.93e-05 0.0007846 3.796e-05 -0.000781 -0.0001342 0.0007662 0.0002292 -0.0007393 -0.0003204 0.0007021 0.0004077 -0.0006538 -0.0004886 0.0005965 0.0005633 -0.0005296 -0.0006295 0.0004556 0.0006875 -0.0003742 -0.0007357 0.0002875 0.0007742 -0.000196 -0.000802 0.0001013 0.0008193 -4.299e-06 -0.0008253 -9.323e-05 0.0008209 0.0001913 -0.0008047 -0.0002868 0.0007793 0.0003816 -0.0007416 -0.0004707 0.0006963 0.000558 -0.0006386 -0.000637 0.0005749 0.0007129 -0.0004997 -0.0007789 0.0004193 0.0008394 -0.0003298 -0.00089 0.0002347 0.0009318 -0.000134 -0.0009648 2.667e-05 0.000985 8.18e-05 -0.000998 -0.000198 0.0009944 0.0003111 -0.0009848 -0.0004319 0.0009559 0.000546 -0.0009208 -0.0006661 0.0008657 0.0007772 -0.0008031 -0.0008905 0.0007213 0.0009934 -0.0006301 -0.001093 0.000522 0.001181 -0.0004027 -0.001261 0.0002699 0.001327 -0.0001251 -0.001378 -2.91e-05 0.001415 0.0001943 -0.001431 -0.0003641 0.001431 0.0005433 -0.001406 -0.0007218 0.001365 0.0009144 -0.001284 -0.001104 0.001151 0.001304 -0.0008302 -0.001258 0.0002762 0.0005804 - 0.001137 0.001112 0.001149 0.001013 0.0007638 0.0005208 0.0003083 0.0001043 -0.0001029 -0.0003073 -0.0005007 -0.0006787 -0.0008393 -0.0009807 -0.0011 -0.001196 -0.001267 -0.001311 -0.001328 -0.001319 -0.001284 -0.001223 -0.001139 -0.001034 -0.0009086 -0.0007667 -0.0006115 -0.0004459 -0.0002729 -9.574e-05 8.189e-05 0.0002566 0.0004258 0.0005868 0.0007366 0.0008727 0.0009934 0.001098 0.001184 0.001251 0.001298 0.001327 0.001336 0.001327 0.0013 0.001257 0.001199 0.001128 0.001044 0.0009502 0.0008483 0.0007394 0.0006252 0.0005078 0.0003887 0.0002689 0.0001496 3.248e-05 -8.134e-05 -0.0001912 -0.0002965 -0.0003962 -0.0004893 -0.0005755 -0.0006546 -0.0007259 -0.0007885 -0.0008423 -0.0008872 -0.0009227 -0.0009482 -0.0009634 -0.0009684 -0.000963 -0.0009467 -0.0009197 -0.0008821 -0.0008343 -0.0007764 -0.0007087 -0.000632 -0.0005473 -0.0004553 -0.0003568 -0.0002535 -0.0001465 -3.736e-05 7.262e-05 0.0001817 0.0002882 0.0003905 0.0004872 0.0005766 0.0006571 0.0007276 0.000787 0.0008341 0.000868 0.0008883 0.0008945 0.0008865 0.0008644 0.0008283 0.000779 0.0007172 0.0006438 0.0005599 0.0004669 0.0003664 0.0002598 0.0001487 3.513e-05 -7.927e-05 -0.0001927 -0.0003033 -0.0004095 -0.0005094 -0.0006016 -0.0006846 -0.0007571 -0.0008179 -0.0008662 -0.0009011 -0.0009221 -0.0009288 -0.0009212 -0.0008994 -0.0008636 -0.0008145 -0.0007528 -0.0006795 -0.0005957 -0.0005027 -0.0004019 -0.0002951 -0.0001838 -6.974e-05 4.521e-05 0.0001593 0.0002706 0.0003776 0.0004784 0.0005715 0.0006553 0.0007287 0.0007904 0.0008395 0.0008751 0.0008968 0.0009041 0.000897 0.0008756 0.0008402 0.0007914 0.0007299 0.0006568 0.0005731 0.0004802 0.0003796 0.0002729 0.0001617 4.778e-05 -6.704e-05 -0.000181 -0.0002922 -0.0003989 -0.0004995 -0.0005924 -0.0006761 -0.0007493 -0.0008108 -0.0008596 -0.0008951 -0.0009165 -0.0009237 -0.0009164 -0.0008948 -0.0008593 -0.0008103 -0.0007487 -0.0006754 -0.0005916 -0.0004986 -0.0003979 -0.000291 -0.0001797 -6.569e-05 4.924e-05 0.0001633 0.0002746 0.0003814 0.0004822 0.0005751 0.0006589 0.0007322 0.0007938 0.0008427 0.0008783 0.0008998 0.0009071 0.0008999 0.0008784 0.0008429 0.0007941 0.0007325 0.0006593 0.0005756 0.0004827 0.000382 0.0002752 0.000164 5.004e-05 -6.482e-05 -0.0001788 -0.00029 -0.0003968 -0.0004975 -0.0005904 -0.0006741 -0.0007473 -0.0008089 -0.0008578 -0.0008932 -0.0009147 -0.0009219 -0.0009147 -0.0008932 -0.0008576 -0.0008087 -0.0007471 -0.0006739 -0.0005901 -0.0004971 -0.0003964 -0.0002896 -0.0001783 -6.428e-05 5.063e-05 0.0001646 0.0002759 0.0003828 0.0004835 0.0005764 0.0006602 0.0007334 0.000795 0.000844 0.0008795 0.000901 0.0009083 0.0009011 0.0008796 0.0008441 0.0007952 0.0007336 0.0006604 0.0005767 0.0004838 0.0003831 0.0002763 0.000165 5.107e-05 -6.38e-05 -0.0001778 -0.000289 -0.0003958 -0.0004965 -0.0005894 -0.0006732 -0.0007464 -0.0008079 -0.0008568 -0.0008923 -0.0009138 -0.000921 -0.0009138 -0.0008922 -0.0008567 -0.0008078 -0.0007462 -0.0006729 -0.0005892 -0.0004962 -0.0003955 -0.0002887 -0.0001774 -6.336e-05 5.154e-05 0.0001655 0.0002768 0.0003837 0.0004844 0.0005773 0.0006611 0.0007344 0.0007959 0.0008449 0.0008804 0.000902 0.0009092 0.000902 0.0008805 0.000845 0.0007961 0.0007346 0.0006614 0.0005776 0.0004847 0.0003841 0.0002773 0.000166 5.204e-05 -6.283e-05 -0.0001768 -0.000288 -0.0003948 -0.0004955 -0.0005884 -0.0006721 -0.0007454 -0.0008069 -0.0008558 -0.0008913 -0.0009128 -0.00092 -0.0009127 -0.0008912 -0.0008556 -0.0008067 -0.0007451 -0.0006719 -0.0005881 -0.0004951 -0.0003944 -0.0002876 -0.0001762 -6.224e-05 5.267e-05 0.0001667 0.000278 0.0003848 0.0004855 0.0005785 0.0006623 0.0007355 0.0007971 0.0008461 0.0008816 0.0009032 0.0009104 0.0009032 0.0008817 0.0008463 0.0007974 0.0007358 0.0006626 0.0005789 0.000486 0.0003853 0.0002786 0.0001673 5.336e-05 -6.15e-05 -0.0001755 -0.0002867 -0.0003935 -0.0004941 -0.0005871 -0.0006708 -0.000744 -0.0008055 -0.0008544 -0.0008899 -0.0009114 -0.0009185 -0.0009113 -0.0008897 -0.0008542 -0.0008052 -0.0007436 -0.0006704 -0.0005866 -0.0004936 -0.0003929 -0.0002861 -0.0001747 -6.073e-05 5.419e-05 0.0001682 0.0002795 0.0003864 0.0004871 0.0005801 0.0006639 0.0007371 0.0007987 0.0008477 0.0008832 0.0009048 0.000912 0.0009048 0.0008833 0.0008479 0.000799 0.0007375 0.0006643 0.0005806 0.0004876 0.000387 0.0002802 0.000169 5.504e-05 -5.982e-05 -0.0001738 -0.000285 -0.0003918 -0.0004924 -0.0005853 -0.0006691 -0.0007422 -0.0008038 -0.0008527 -0.0008881 -0.0009096 -0.0009168 -0.0009095 -0.000888 -0.0008524 -0.0008035 -0.0007419 -0.0006686 -0.0005848 -0.0004918 -0.0003911 -0.0002843 -0.000173 -5.894e-05 5.599e-05 0.00017 0.0002813 0.0003882 0.0004889 0.0005819 0.0006657 0.0007389 0.0008005 0.0008495 0.000885 0.0009066 0.0009138 0.0009067 0.0008852 0.0008497 0.0008008 0.0007393 0.0006661 0.0005824 0.0004895 0.0003888 0.0002821 0.0001708 5.687e-05 -5.798e-05 -0.0001719 -0.0002832 -0.00039 -0.0004906 -0.0005835 -0.0006672 -0.0007404 -0.0008019 -0.0008508 -0.0008863 -0.0009078 -0.000915 -0.0009077 -0.0008862 -0.0008506 -0.0008017 -0.0007401 -0.0006668 -0.000583 -0.00049 -0.0003893 -0.0002825 -0.0001712 -5.715e-05 5.778e-05 0.0001718 0.0002831 0.00039 0.0004907 0.0005836 0.0006674 0.0007407 0.0008023 0.0008512 0.0008868 0.0009083 0.0009156 0.0009084 0.0008869 0.0008514 0.0008025 0.000741 0.0006678 0.0005841 0.0004912 0.0003905 0.0002837 0.0001725 5.853e-05 -5.633e-05 -0.0001703 -0.0002815 -0.0003883 -0.000489 -0.0005819 -0.0006656 -0.0007388 -0.0008003 -0.0008492 -0.0008847 -0.0009062 -0.0009134 -0.0009062 -0.0008846 -0.0008491 -0.0008001 -0.0007385 -0.0006653 -0.0005815 -0.0004885 -0.0003878 -0.000281 -0.0001697 -5.569e-05 5.922e-05 0.0001732 0.0002845 0.0003914 0.0004921 0.000585 0.0006688 0.0007421 0.0008036 0.0008526 0.0008881 0.0009097 0.0009169 0.0009097 0.0008882 0.0008527 0.0008038 0.0007423 0.0006691 0.0005853 0.0004924 0.0003917 0.0002849 0.0001737 5.971e-05 -5.516e-05 -0.0001691 -0.0002804 -0.0003872 -0.0004879 -0.0005808 -0.0006645 -0.0007377 -0.0007993 -0.0008482 -0.0008837 -0.0009052 -0.0009124 -0.0009052 -0.0008836 -0.0008481 -0.0007992 -0.0007376 -0.0006644 -0.0005806 -0.0004876 -0.0003869 -0.0002801 -0.0001688 -5.484e-05 6.006e-05 0.0001741 0.0002853 0.0003922 0.0004929 0.0005858 0.0006696 0.0007428 0.0008044 0.0008533 0.0008888 0.0009103 0.0009176 0.0009104 0.0008888 0.0008533 0.0008044 0.0007429 0.0006696 0.0005859 0.000493 0.0003923 0.0002855 0.0001742 6.021e-05 -5.468e-05 -0.0001687 -0.0002799 -0.0003868 -0.0004874 -0.0005804 -0.0006641 -0.0007374 -0.0007989 -0.0008478 -0.0008833 -0.0009049 -0.0009121 -0.0009049 -0.0008833 -0.0008478 -0.0007989 -0.0007374 -0.0006641 -0.0005804 -0.0004875 -0.0003868 -0.00028 -0.0001687 -5.471e-05 6.017e-05 0.0001742 0.0002854 0.0003922 0.0004929 0.0005859 0.0006696 0.0007428 0.0008044 0.0008533 0.0008888 0.0009103 0.0009175 0.0009103 0.0008887 0.0008532 0.0008043 0.0007427 0.0006695 0.0005857 0.0004928 0.0003921 0.0002853 0.000174 5.998e-05 -5.492e-05 -0.0001689 -0.0002802 -0.000387 -0.0004877 -0.0005807 -0.0006644 -0.0007377 -0.0007993 -0.0008482 -0.0008837 -0.0009052 -0.0009125 -0.0009053 -0.0008838 -0.0008483 -0.0007994 -0.0007378 -0.0006646 -0.0005809 -0.0004879 -0.0003873 -0.0002805 -0.0001692 -5.524e-05 5.963e-05 0.0001736 0.0002849 0.0003917 0.0004923 0.0005853 0.000669 0.0007422 0.0008037 0.0008526 0.0008881 0.0009096 0.0009168 0.0009096 0.0008881 0.0008525 0.0008036 0.000742 0.0006688 0.000585 0.000492 0.0003913 0.0002845 0.0001732 5.921e-05 -5.57e-05 -0.0001697 -0.000281 -0.0003878 -0.0004885 -0.0005815 -0.0006653 -0.0007385 -0.0008001 -0.000849 -0.0008846 -0.0009061 -0.0009133 -0.0009061 -0.0008846 -0.0008492 -0.0008003 -0.0007387 -0.0006655 -0.0005818 -0.0004889 -0.0003882 -0.0002814 -0.0001701 -5.617e-05 5.869e-05 0.0001727 0.0002839 0.0003907 0.0004914 0.0005843 0.000668 0.0007412 0.0008028 0.0008517 0.0008872 0.0009087 0.0009159 0.0009086 0.0008871 0.0008516 0.0008026 0.000741 0.0006678 0.000584 0.000491 0.0003904 0.0002835 0.0001722 5.821e-05 -5.67e-05 -0.0001707 -0.000282 -0.0003888 -0.0004895 -0.0005825 -0.0006663 -0.0007395 -0.0008011 -0.00085 -0.0008856 -0.0009071 -0.0009143 -0.0009071 -0.0008856 -0.0008501 -0.0008012 -0.0007397 -0.0006665 -0.0005828 -0.0004898 -0.0003892 -0.0002824 -0.0001711 -5.713e-05 5.774e-05 0.0001717 0.000283 0.0003898 0.0004904 0.0005834 0.0006671 0.0007403 0.0008019 0.0008508 0.0008863 0.0009078 0.000915 0.0009077 0.0008862 0.0008507 0.0008017 0.0007402 0.0006669 0.0005832 0.0004902 0.0003895 0.0002827 0.0001714 5.74e-05 -5.75e-05 -0.0001715 -0.0002828 -0.0003896 -0.0004903 -0.0005833 -0.000667 -0.0007403 -0.0008018 -0.0008508 -0.0008863 -0.0009078 -0.000915 -0.0009078 -0.0008863 -0.0008508 -0.0008019 -0.0007403 -0.0006671 -0.0005834 -0.0004904 -0.0003898 -0.0002829 -0.0001717 -5.769e-05 5.72e-05 0.0001712 0.0002824 0.0003893 0.0004899 0.0005829 0.0006666 0.0007399 0.0008014 0.0008503 0.0008858 0.0009074 0.0009146 0.0009074 0.0008858 0.0008503 0.0008014 0.0007399 0.0006666 0.0005829 0.0004899 0.0003893 0.0002824 0.0001712 5.72e-05 -5.769e-05 -0.0001717 -0.0002829 -0.0003898 -0.0004904 -0.0005834 -0.0006671 -0.0007403 -0.0008019 -0.0008508 -0.0008863 -0.0009078 -0.000915 -0.0009078 -0.0008863 -0.0008507 -0.0008018 -0.0007402 -0.000667 -0.0005832 -0.0004903 -0.0003896 -0.0002827 -0.0001715 -5.746e-05 5.744e-05 0.0001714 0.0002827 0.0003896 0.0004903 0.0005832 0.000667 0.0007402 0.0008018 0.0008508 0.0008863 0.0009078 0.0009151 0.0009079 0.0008864 0.0008509 0.000802 0.0007405 0.0006672 0.0005835 0.0004906 0.0003899 0.0002831 0.0001719 5.794e-05 -5.693e-05 -0.0001709 -0.0002821 -0.0003889 -0.0004896 -0.0005825 -0.0006662 -0.0007394 -0.0008009 -0.0008498 -0.0008853 -0.0009068 -0.000914 -0.0009067 -0.0008852 -0.0008496 -0.0008007 -0.0007391 -0.0006658 -0.000582 -0.000489 -0.0003883 -0.0002815 -0.0001702 -5.614e-05 5.879e-05 0.0001728 0.0002841 0.000391 0.0004917 0.0005847 0.0006685 0.0007418 0.0008034 0.0008523 0.0008879 0.0009095 0.0009167 0.0009095 0.0008881 0.0008526 0.0008037 0.0007422 0.000669 0.0005853 0.0004925 0.0003918 0.0002851 0.0001738 5.99e-05 -5.493e-05 -0.0001689 -0.0002801 -0.0003868 -0.0004875 -0.0005804 -0.0006641 -0.0007372 -0.0007987 -0.0008476 -0.000883 -0.0009045 -0.0009116 -0.0009044 -0.0008828 -0.0008472 -0.0007982 -0.0007366 -0.0006633 -0.0005795 -0.0004865 -0.0003857 -0.0002788 -0.0001675 -5.344e-05 6.152e-05 0.0001756 0.0002869 0.0003938 0.0004946 0.0005876 0.0006714 0.0007447 0.0008064 0.0008554 0.0008909 0.0009125 0.0009198 0.0009127 0.0008913 0.0008558 0.000807 0.0007455 0.0006724 0.0005887 0.0004959 0.0003953 0.0002886 0.0001774 6.348e-05 -5.132e-05 -0.0001652 -0.0002764 -0.0003831 -0.0004837 -0.0005765 -0.0006602 -0.0007333 -0.0007948 -0.0008436 -0.000879 -0.0009004 -0.0009075 -0.0009002 -0.0008785 -0.0008429 -0.0007939 -0.0007322 -0.0006589 -0.000575 -0.0004819 -0.0003811 -0.0002742 -0.0001628 -4.868e-05 6.633e-05 0.0001804 0.0002918 0.0003988 0.0004996 0.0005927 0.0006766 0.0007499 0.0008116 0.0008607 0.0008964 0.000918 0.0009254 0.0009184 0.000897 0.0008617 0.000813 0.0007517 0.0006786 0.0005951 0.0005023 0.0004018 0.0002952 0.0001841 7.024e-05 -4.451e-05 -0.0001584 -0.0002695 -0.0003763 -0.0004769 -0.0005697 -0.0006533 -0.0007265 -0.0007879 -0.0008366 -0.0008718 -0.0008931 -0.0008999 -0.0008922 -0.0008702 -0.0008341 -0.0007845 -0.0007222 -0.0006482 -0.0005637 -0.00047 -0.0003685 -0.0002608 -0.0001488 -3.417e-05 8.135e-05 0.0001959 0.0003076 0.0004147 0.0005156 0.0006088 0.0006925 0.0007657 0.0008271 0.0008758 0.000911 0.000932 0.0009387 0.0009307 0.0009083 0.0008715 0.0008211 0.0007578 0.0006824 0.0005959 0.0004999 0.0003957 0.0002849 0.0001692 5.041e-05 -6.932e-05 -0.0001882 -0.0003042 -0.0004153 -0.0005194 -0.0006146 -0.0006992 -0.0007714 -0.0008295 -0.0008722 -0.0008986 -0.0009078 -0.000899 -0.0008719 -0.0008269 -0.0007644 -0.000685 -0.0005896 -0.0004803 -0.0003587 -0.0002269 -8.705e-05 5.782e-05 0.0002047 0.0003509 0.0004936 0.0006294 0.000755 0.0008679 0.0009655 0.001045 0.001105 0.001143 0.001158 0.001149 0.001116 0.001059 0.0009789 0.000877 0.0007546 0.0006139 0.0004579 0.0002896 0.0001119 -7.198e-05 -0.0002579 -0.000442 -0.0006209 -0.0007915 -0.0009499 -0.001093 -0.001217 -0.001322 -0.001404 -0.001461 -0.001494 -0.001502 -0.001484 -0.001441 -0.001374 -0.001286 -0.001177 -0.001051 -0.0009087 -0.0007549 -0.0005923 -0.0004238 -0.0002526 -8.232e-05 8.377e-05 0.0002429 0.0003925 0.0005296 0.0006517 0.0007572 0.0008447 0.0009129 0.0009602 0.0009849 0.000988 0.0009744 0.0009472 0.0008917 0.0007834 0.0006444 0.0005935 0.000687 0.0006914 - 0.0006026 0.0005931 0.0006589 0.0005638 0.0003651 0.0001849 4.757e-05 -7.055e-05 -0.000183 -0.000286 -0.0003733 -0.000443 -0.0004958 -0.0005324 -0.0005534 -0.000559 -0.0005499 -0.0005276 -0.0004939 -0.0004502 -0.0003982 -0.0003398 -0.0002772 -0.0002123 -0.0001465 -8.148e-05 -1.925e-05 3.883e-05 9.208e-05 0.0001397 0.0001806 0.0002142 0.0002409 0.0002609 0.0002742 0.0002809 0.0002822 0.0002792 0.0002723 0.0002622 0.00025 0.000237 0.0002239 0.0002109 0.0001989 0.0001887 0.0001806 0.0001742 0.0001696 0.0001672 0.0001666 0.0001669 0.0001676 0.0001685 0.0001692 0.0001684 0.0001655 0.0001604 0.0001526 0.0001415 0.0001266 0.000108 8.613e-05 6.081e-05 3.212e-05 7.415e-07 -3.244e-05 -6.69e-05 -0.0001022 -0.0001372 -0.000171 -0.0002026 -0.0002316 -0.000257 -0.0002781 -0.0002941 -0.0003048 -0.00031 -0.0003093 -0.0003025 -0.0002901 -0.0002724 -0.0002495 -0.000222 -0.0001906 -0.0001561 -0.0001193 -8.074e-05 -4.143e-05 -2.323e-06 3.587e-05 7.251e-05 0.0001068 0.0001381 0.0001658 0.0001896 0.0002091 0.000224 0.0002341 0.0002394 0.0002401 0.000236 0.0002275 0.0002148 0.0001983 0.0001782 0.0001551 0.0001293 0.0001014 7.179e-05 4.101e-05 9.576e-06 -2.197e-05 -5.317e-05 -8.356e-05 -0.0001127 -0.00014 -0.0001652 -0.0001878 -0.0002076 -0.0002243 -0.0002375 -0.0002471 -0.0002529 -0.000255 -0.0002531 -0.0002474 -0.000238 -0.000225 -0.0002086 -0.000189 -0.0001666 -0.0001418 -0.0001148 -8.624e-05 -5.644e-05 -2.591e-05 4.881e-06 3.543e-05 6.525e-05 9.387e-05 0.0001209 0.0001458 0.0001682 0.0001878 0.0002043 0.0002174 0.000227 0.0002328 0.0002347 0.0002328 0.0002271 0.0002177 0.0002047 0.0001883 0.0001688 0.0001466 0.0001218 9.504e-05 6.662e-05 3.701e-05 6.677e-06 -2.389e-05 -5.422e-05 -8.383e-05 -0.0001123 -0.000139 -0.0001638 -0.0001861 -0.0002055 -0.0002219 -0.0002349 -0.0002444 -0.0002501 -0.000252 -0.0002501 -0.0002443 -0.0002348 -0.0002218 -0.0002054 -0.0001859 -0.0001636 -0.0001388 -0.000112 -8.356e-05 -5.392e-05 -2.356e-05 7.041e-06 3.74e-05 6.704e-05 9.548e-05 0.0001223 0.0001471 0.0001694 0.0001889 0.0002053 0.0002183 0.0002277 0.0002335 0.0002354 0.0002335 0.0002277 0.0002183 0.0002052 0.0001888 0.0001693 0.000147 0.0001223 9.544e-05 6.699e-05 3.734e-05 6.984e-06 -2.362e-05 -5.398e-05 -8.362e-05 -0.0001121 -0.0001389 -0.0001637 -0.000186 -0.0002055 -0.0002219 -0.0002349 -0.0002444 -0.0002501 -0.000252 -0.0002501 -0.0002444 -0.0002349 -0.0002219 -0.0002055 -0.000186 -0.0001637 -0.000139 -0.0001122 -8.374e-05 -5.411e-05 -2.376e-05 6.832e-06 3.718e-05 6.681e-05 9.526e-05 0.0001221 0.0001468 0.0001691 0.0001886 0.000205 0.000218 0.0002275 0.0002332 0.0002351 0.0002332 0.0002275 0.000218 0.000205 0.0001886 0.0001691 0.0001468 0.000122 9.522e-05 6.677e-05 3.714e-05 6.782e-06 -2.381e-05 -5.417e-05 -8.38e-05 -0.0001122 -0.0001391 -0.0001638 -0.0001861 -0.0002056 -0.000222 -0.000235 -0.0002445 -0.0002502 -0.0002521 -0.0002502 -0.0002444 -0.000235 -0.0002219 -0.0002055 -0.000186 -0.0001637 -0.000139 -0.0001121 -8.367e-05 -5.403e-05 -2.366e-05 6.949e-06 3.732e-05 6.696e-05 9.543e-05 0.0001223 0.000147 0.0001693 0.0001889 0.0002053 0.0002183 0.0002278 0.0002335 0.0002355 0.0002336 0.0002279 0.0002184 0.0002054 0.000189 0.0001695 0.0001473 0.0001225 9.574e-05 6.731e-05 3.77e-05 7.369e-06 -2.32e-05 -5.354e-05 -8.315e-05 -0.0001116 -0.0001384 -0.0001631 -0.0001854 -0.0002048 -0.0002212 -0.0002342 -0.0002436 -0.0002493 -0.0002512 -0.0002493 -0.0002435 -0.000234 -0.000221 -0.0002045 -0.000185 -0.0001627 -0.0001379 -0.000111 -8.255e-05 -5.288e-05 -2.249e-05 8.143e-06 3.853e-05 6.821e-05 9.669e-05 0.0001235 0.0001483 0.0001707 0.0001902 0.0002067 0.0002197 0.0002292 0.000235 0.000237 0.0002351 0.0002294 0.00022 0.000207 0.0001906 0.0001712 0.0001489 0.0001242 9.744e-05 6.904e-05 3.945e-05 9.144e-06 -2.141e-05 -5.171e-05 -8.13e-05 -0.0001097 -0.0001365 -0.0001612 -0.0001834 -0.0002029 -0.0002192 -0.0002322 -0.0002416 -0.0002473 -0.0002492 -0.0002472 -0.0002414 -0.0002319 -0.0002188 -0.0002024 -0.0001828 -0.0001604 -0.0001356 -0.0001088 -8.026e-05 -5.056e-05 -2.015e-05 1.05e-05 4.091e-05 7.061e-05 9.911e-05 0.000126 0.0001508 0.0001732 0.0001927 0.0002092 0.0002223 0.0002318 0.0002376 0.0002396 0.0002377 0.000232 0.0002226 0.0002097 0.0001933 0.0001739 0.0001516 0.0001269 0.0001002 7.181e-05 4.224e-05 1.195e-05 -1.859e-05 -4.888e-05 -7.845e-05 -0.0001068 -0.0001336 -0.0001583 -0.0001805 -0.0002 -0.0002163 -0.0002292 -0.0002386 -0.0002443 -0.0002462 -0.0002442 -0.0002384 -0.0002289 -0.0002158 -0.0001993 -0.0001797 -0.0001574 -0.0001325 -0.0001057 -7.715e-05 -4.745e-05 -1.702e-05 1.364e-05 4.406e-05 7.376e-05 0.0001023 0.0001292 0.000154 0.0001763 0.0001959 0.0002124 0.0002255 0.000235 0.0002408 0.0002428 0.0002409 0.0002353 0.0002259 0.0002129 0.0001966 0.0001771 0.0001549 0.0001302 0.0001035 7.509e-05 4.552e-05 1.523e-05 -1.53e-05 -4.559e-05 -7.516e-05 -0.0001035 -0.0001303 -0.000155 -0.0001772 -0.0001967 -0.000213 -0.000226 -0.0002353 -0.000241 -0.0002429 -0.0002409 -0.0002351 -0.0002256 -0.0002125 -0.000196 -0.0001765 -0.0001541 -0.0001293 -0.0001024 -7.389e-05 -4.419e-05 -1.377e-05 1.689e-05 4.73e-05 7.7e-05 0.0001055 0.0001324 0.0001572 0.0001796 0.0001991 0.0002156 0.0002287 0.0002382 0.000244 0.0002459 0.0002441 0.0002384 0.000229 0.000216 0.0001997 0.0001802 0.000158 0.0001333 0.0001065 7.814e-05 4.856e-05 1.826e-05 -1.229e-05 -4.259e-05 -7.217e-05 -0.0001006 -0.0001273 -0.000152 -0.0001743 -0.0001937 -0.0002101 -0.0002231 -0.0002325 -0.0002382 -0.00024 -0.0002381 -0.0002323 -0.0002228 -0.0002097 -0.0001933 -0.0001737 -0.0001514 -0.0001266 -9.971e-05 -7.122e-05 -4.154e-05 -1.115e-05 1.949e-05 4.989e-05 7.957e-05 0.0001081 0.0001349 0.0001597 0.000182 0.0002016 0.000218 0.0002311 0.0002406 0.0002463 0.0002483 0.0002464 0.0002407 0.0002313 0.0002183 0.0002019 0.0001825 0.0001602 0.0001355 0.0001087 8.027e-05 5.066e-05 2.033e-05 -1.024e-05 -4.057e-05 -7.018e-05 -9.86e-05 -0.0001254 -0.0001501 -0.0001724 -0.0001919 -0.0002082 -0.0002212 -0.0002307 -0.0002364 -0.0002383 -0.0002364 -0.0002306 -0.0002211 -0.0002081 -0.0001917 -0.0001721 -0.0001498 -0.0001251 -9.823e-05 -6.977e-05 -4.011e-05 -9.746e-06 2.086e-05 5.123e-05 8.088e-05 0.0001093 0.0001362 0.0001609 0.0001832 0.0002027 0.0002192 0.0002322 0.0002416 0.0002474 0.0002493 0.0002474 0.0002417 0.0002322 0.0002192 0.0002028 0.0001833 0.000161 0.0001362 0.0001094 8.098e-05 5.134e-05 2.099e-05 -9.613e-06 -3.997e-05 -6.961e-05 -9.806e-05 -0.0001249 -0.0001496 -0.000172 -0.0001915 -0.0002079 -0.0002209 -0.0002304 -0.0002361 -0.000238 -0.0002361 -0.0002304 -0.000221 -0.0002079 -0.0001916 -0.0001721 -0.0001498 -0.000125 -9.825e-05 -6.982e-05 -4.02e-05 -9.857e-06 2.072e-05 5.106e-05 8.068e-05 0.0001091 0.0001359 0.0001606 0.0001829 0.0002024 0.0002188 0.0002318 0.0002412 0.0002469 0.0002488 0.0002469 0.0002411 0.0002316 0.0002186 0.0002022 0.0001826 0.0001603 0.0001355 0.0001087 8.019e-05 5.052e-05 2.014e-05 -1.049e-05 -4.088e-05 -7.054e-05 -9.902e-05 -0.0001259 -0.0001507 -0.000173 -0.0001925 -0.000209 -0.000222 -0.0002315 -0.0002373 -0.0002392 -0.0002374 -0.0002317 -0.0002222 -0.0002093 -0.0001929 -0.0001734 -0.0001512 -0.0001265 -9.968e-05 -7.127e-05 -4.168e-05 -1.136e-05 1.919e-05 4.951e-05 7.91e-05 0.0001075 0.0001343 0.000159 0.0001812 0.0002007 0.0002171 0.00023 0.0002395 0.0002451 0.000247 0.000245 0.0002393 0.0002298 0.0002167 0.0002003 0.0001807 0.0001583 0.0001335 0.0001067 7.819e-05 4.851e-05 1.81e-05 -1.254e-05 -4.295e-05 -7.263e-05 -0.0001011 -0.000128 -0.0001528 -0.0001751 -0.0001947 -0.0002111 -0.0002242 -0.0002337 -0.0002395 -0.0002415 -0.0002396 -0.0002339 -0.0002245 -0.0002116 -0.0001952 -0.0001758 -0.0001535 -0.0001288 -0.000102 -7.365e-05 -4.407e-05 -1.376e-05 1.678e-05 4.708e-05 7.667e-05 0.0001051 0.0001318 0.0001565 0.0001788 0.0001982 0.0002146 0.0002275 0.0002369 0.0002426 0.0002445 0.0002425 0.0002367 0.0002272 0.0002142 0.0001977 0.0001781 0.0001558 0.000131 0.0001041 7.562e-05 4.593e-05 1.553e-05 -1.512e-05 -4.553e-05 -7.522e-05 -0.0001037 -0.0001306 -0.0001554 -0.0001777 -0.0001973 -0.0002137 -0.0002268 -0.0002363 -0.0002421 -0.0002441 -0.0002422 -0.0002365 -0.0002271 -0.0002141 -0.0001978 -0.0001783 -0.0001561 -0.0001314 -0.0001046 -7.62e-05 -4.662e-05 -1.631e-05 1.424e-05 4.455e-05 7.414e-05 0.0001025 0.0001293 0.000154 0.0001763 0.0001957 0.0002121 0.0002251 0.0002345 0.0002402 0.0002421 0.0002401 0.0002343 0.0002248 0.0002117 0.0001953 0.0001758 0.0001534 0.0001286 0.0001018 7.329e-05 4.362e-05 1.323e-05 -1.741e-05 -4.78e-05 -7.748e-05 -0.000106 -0.0001328 -0.0001576 -0.0001799 -0.0001995 -0.0002159 -0.000229 -0.0002385 -0.0002442 -0.0002462 -0.0002443 -0.0002386 -0.0002292 -0.0002162 -0.0001998 -0.0001803 -0.000158 -0.0001333 -0.0001065 -7.811e-05 -4.85e-05 -1.817e-05 1.24e-05 4.273e-05 7.235e-05 0.0001008 0.0001276 0.0001523 0.0001746 0.0001941 0.0002104 0.0002234 0.0002329 0.0002386 0.0002405 0.0002386 0.0002328 0.0002233 0.0002103 0.0001939 0.0001744 0.000152 0.0001273 0.0001005 7.199e-05 4.235e-05 1.198e-05 -1.863e-05 -4.899e-05 -7.864e-05 -0.0001071 -0.0001339 -0.0001587 -0.000181 -0.0002005 -0.0002169 -0.0002299 -0.0002394 -0.0002451 -0.000247 -0.0002451 -0.0002394 -0.0002299 -0.0002169 -0.0002005 -0.000181 -0.0001587 -0.0001339 -0.0001071 -7.866e-05 -4.901e-05 -1.865e-05 1.195e-05 4.232e-05 7.196e-05 0.0001004 0.0001272 0.000152 0.0001743 0.0001938 0.0002103 0.0002233 0.0002328 0.0002385 0.0002405 0.0002385 0.0002328 0.0002234 0.0002104 0.000194 0.0001745 0.0001523 0.0001275 0.0001007 7.231e-05 4.27e-05 1.237e-05 -1.82e-05 -4.853e-05 -7.814e-05 -0.0001066 -0.0001333 -0.0001581 -0.0001803 -0.0001998 -0.0002162 -0.0002292 -0.0002386 -0.0002443 -0.0002462 -0.0002442 -0.0002384 -0.0002289 -0.0002159 -0.0001994 -0.0001799 -0.0001575 -0.0001327 -0.0001059 -7.74e-05 -4.772e-05 -1.732e-05 1.333e-05 4.373e-05 7.342e-05 0.0001019 0.0001288 0.0001536 0.0001759 0.0001955 0.0002119 0.000225 0.0002345 0.0002403 0.0002423 0.0002404 0.0002348 0.0002254 0.0002124 0.0001961 0.0001766 0.0001544 0.0001297 0.0001029 7.455e-05 4.498e-05 1.469e-05 -1.584e-05 -4.612e-05 -7.569e-05 -0.0001041 -0.0001308 -0.0001555 -0.0001777 -0.0001972 -0.0002135 -0.0002264 -0.0002358 -0.0002415 -0.0002433 -0.0002413 -0.0002355 -0.000226 -0.0002129 -0.0001964 -0.0001768 -0.0001544 -0.0001296 -0.0001027 -7.414e-05 -4.442e-05 -1.398e-05 1.671e-05 4.715e-05 7.688e-05 0.0001054 0.0001323 0.0001572 0.0001796 0.0001992 0.0002157 0.0002288 0.0002383 0.0002442 0.0002462 0.0002444 0.0002387 0.0002294 0.0002165 0.0002002 0.0001808 0.0001586 0.0001339 0.0001072 7.887e-05 4.935e-05 1.91e-05 -1.138e-05 -4.163e-05 -7.115e-05 -9.948e-05 -0.0001262 -0.0001508 -0.000173 -0.0001924 -0.0002087 -0.0002216 -0.0002309 -0.0002365 -0.0002383 -0.0002363 -0.0002304 -0.0002208 -0.0002077 -0.0001911 -0.0001715 -0.000149 -0.0001242 -9.72e-05 -6.861e-05 -3.883e-05 -8.331e-06 2.241e-05 5.291e-05 8.269e-05 0.0001113 0.0001382 0.0001631 0.0001856 0.0002052 0.0002218 0.0002349 0.0002445 0.0002504 0.0002525 0.0002507 0.0002451 0.0002358 0.000223 0.0002068 0.0001875 0.0001655 0.000141 0.0001144 8.628e-05 5.694e-05 2.689e-05 -3.4e-06 -3.345e-05 -6.279e-05 -9.096e-05 -0.0001175 -0.000142 -0.0001641 -0.0001834 -0.0001996 -0.0002125 -0.0002219 -0.0002275 -0.0002294 -0.0002274 -0.0002217 -0.0002122 -0.0001992 -0.0001829 -0.0001635 -0.0001414 -0.0001169 -9.04e-05 -6.236e-05 -3.326e-05 -3.55e-06 2.632e-05 5.583e-05 8.45e-05 0.0001119 0.0001377 0.0001615 0.0001828 0.0002016 0.0002175 0.0002306 0.0002404 0.0002472 0.0002509 0.0002517 0.0002495 0.0002445 0.000237 0.0002272 0.0002152 0.0002012 0.0001855 0.0001683 0.0001498 0.00013 0.0001092 8.764e-05 6.534e-05 4.229e-05 1.875e-05 -5.025e-06 -2.897e-05 -5.308e-05 -7.705e-05 -0.0001004 -0.000123 -0.0001447 -0.000165 -0.0001833 -0.0001992 -0.0002125 -0.0002227 -0.0002291 -0.0002312 -0.000229 -0.0002222 -0.0002103 -0.0001931 -0.000171 -0.0001442 -0.0001128 -7.713e-05 -3.799e-05 3.52e-06 4.675e-05 9.097e-05 0.0001348 0.0001768 0.0002158 0.000251 0.0002811 0.0003046 0.0003205 0.0003286 0.000328 0.000318 0.0002983 0.0002695 0.000232 0.000186 0.0001322 7.22e-05 7.564e-06 -6.059e-05 -0.0001308 -0.0002009 -0.0002687 -0.0003327 -0.0003913 -0.0004426 -0.0004845 -0.000516 -0.0005364 -0.0005445 -0.0005395 -0.0005213 -0.0004905 -0.0004475 -0.0003929 -0.0003279 -0.0002544 -0.0001743 -8.919e-05 -1.303e-06 8.664e-05 0.0001721 0.0002529 0.0003268 0.0003911 0.0004437 0.0004827 0.000507 0.0005152 0.0005055 0.0004763 0.0004286 0.0003677 0.0002967 0.0002011 5.69e-05 -0.0001124 -0.0001851 -0.000104 -0.0001058 - 0.0004437 0.0004403 0.0005184 0.0004371 0.000254 9.418e-05 -1.721e-05 -0.0001044 -0.000182 -0.0002474 -0.0002955 -0.0003258 -0.0003403 -0.0003412 -0.0003304 -0.0003093 -0.0002797 -0.000244 -0.0002044 -0.0001629 -0.0001209 -8.019e-05 -4.255e-05 -9.039e-06 1.988e-05 4.361e-05 6.143e-05 7.332e-05 7.996e-05 8.19e-05 7.934e-05 7.296e-05 6.409e-05 5.392e-05 4.298e-05 3.198e-05 2.218e-05 1.468e-05 9.756e-06 7.529e-06 8.621e-06 1.357e-05 2.206e-05 3.342e-05 4.745e-05 6.41e-05 8.255e-05 0.0001016 0.0001205 0.000139 0.0001562 0.0001708 0.0001822 0.0001901 0.0001943 0.000194 0.0001888 0.0001791 0.0001653 0.0001472 0.0001252 9.997e-05 7.253e-05 4.333e-05 1.285e-05 -1.787e-05 -4.774e-05 -7.616e-05 -0.0001027 -0.0001267 -0.0001471 -0.0001638 -0.0001766 -0.0001854 -0.0001897 -0.0001897 -0.0001858 -0.0001782 -0.0001671 -0.0001529 -0.0001362 -0.0001176 -9.751e-05 -7.626e-05 -5.456e-05 -3.299e-05 -1.186e-05 8.564e-06 2.783e-05 4.552e-05 6.152e-05 7.577e-05 8.808e-05 9.826e-05 0.0001063 0.0001124 0.0001165 0.0001185 0.0001185 0.0001168 0.0001133 0.0001082 0.0001014 9.323e-05 8.374e-05 7.3e-05 6.11e-05 4.824e-05 3.459e-05 2.025e-05 5.358e-06 -9.858e-06 -2.518e-05 -4.042e-05 -5.541e-05 -6.989e-05 -8.362e-05 -9.64e-05 -0.000108 -0.0001183 -0.000127 -0.000134 -0.0001391 -0.0001422 -0.0001433 -0.0001423 -0.0001392 -0.0001341 -0.0001269 -0.0001178 -0.000107 -9.45e-05 -8.065e-05 -6.56e-05 -4.96e-05 -3.291e-05 -1.58e-05 1.463e-06 1.86e-05 3.534e-05 5.141e-05 6.656e-05 8.054e-05 9.314e-05 0.0001042 0.0001134 0.0001208 0.0001261 0.0001293 0.0001304 0.0001293 0.000126 0.0001207 0.0001133 0.000104 9.294e-05 8.03e-05 6.627e-05 5.108e-05 3.495e-05 1.815e-05 9.364e-07 -1.642e-05 -3.364e-05 -5.045e-05 -6.66e-05 -8.182e-05 -9.588e-05 -0.0001086 -0.0001196 -0.000129 -0.0001364 -0.0001418 -0.0001451 -0.0001462 -0.0001451 -0.0001419 -0.0001366 -0.0001292 -0.00012 -0.0001089 -9.632e-05 -8.232e-05 -6.716e-05 -5.106e-05 -3.43e-05 -1.712e-05 1.866e-07 1.736e-05 3.412e-05 5.022e-05 6.538e-05 7.938e-05 9.198e-05 0.000103 0.0001123 0.0001196 0.0001249 0.0001281 0.0001292 0.0001281 0.0001248 0.0001194 0.000112 0.0001026 9.152e-05 7.884e-05 6.477e-05 4.953e-05 3.336e-05 1.652e-05 -7.275e-07 -1.811e-05 -3.536e-05 -5.22e-05 -6.836e-05 -8.36e-05 -9.767e-05 -0.0001104 -0.0001214 -0.0001308 -0.0001382 -0.0001436 -0.0001469 -0.000148 -0.000147 -0.0001437 -0.0001384 -0.0001311 -0.0001218 -0.0001108 -9.816e-05 -8.415e-05 -6.898e-05 -5.289e-05 -3.612e-05 -1.894e-05 -1.619e-06 1.556e-05 3.233e-05 4.843e-05 6.361e-05 7.762e-05 9.023e-05 0.0001013 0.0001105 0.0001179 0.0001232 0.0001265 0.0001275 0.0001264 0.0001231 0.0001178 0.0001104 0.000101 8.996e-05 7.73e-05 6.325e-05 4.803e-05 3.188e-05 1.507e-05 -2.159e-06 -1.952e-05 -3.674e-05 -5.356e-05 -6.97e-05 -8.491e-05 -9.896e-05 -0.0001116 -0.0001227 -0.000132 -0.0001394 -0.0001447 -0.000148 -0.0001491 -0.000148 -0.0001448 -0.0001394 -0.000132 -0.0001227 -0.0001117 -9.903e-05 -8.5e-05 -6.98e-05 -5.367e-05 -3.686e-05 -1.965e-05 -2.298e-06 1.492e-05 3.172e-05 4.786e-05 6.307e-05 7.711e-05 8.976e-05 0.0001008 0.0001101 0.0001175 0.0001229 0.0001262 0.0001273 0.0001262 0.000123 0.0001176 0.0001103 0.000101 8.994e-05 7.732e-05 6.33e-05 4.812e-05 3.202e-05 1.524e-05 -1.944e-06 -1.927e-05 -3.645e-05 -5.322e-05 -6.932e-05 -8.45e-05 -9.85e-05 -0.0001111 -0.0001221 -0.0001314 -0.0001388 -0.0001441 -0.0001473 -0.0001484 -0.0001472 -0.0001439 -0.0001385 -0.0001311 -0.0001218 -0.0001107 -9.8e-05 -8.392e-05 -6.868e-05 -5.251e-05 -3.566e-05 -1.841e-05 -1.014e-06 1.624e-05 3.309e-05 4.927e-05 6.452e-05 7.86e-05 9.13e-05 0.0001024 0.0001118 0.0001192 0.0001246 0.0001279 0.0001291 0.000128 0.0001248 0.0001195 0.0001122 0.000103 9.197e-05 7.939e-05 6.541e-05 5.028e-05 3.421e-05 1.747e-05 3.268e-07 -1.696e-05 -3.41e-05 -5.083e-05 -6.69e-05 -8.203e-05 -9.599e-05 -0.0001086 -0.0001196 -0.0001288 -0.0001361 -0.0001414 -0.0001446 -0.0001456 -0.0001444 -0.0001411 -0.0001357 -0.0001282 -0.0001188 -0.0001077 -9.497e-05 -8.085e-05 -6.557e-05 -4.937e-05 -3.249e-05 -1.52e-05 2.231e-06 1.952e-05 3.641e-05 5.262e-05 6.79e-05 8.202e-05 9.475e-05 0.0001059 0.0001153 0.0001227 0.0001282 0.0001315 0.0001327 0.0001317 0.0001285 0.0001233 0.000116 0.0001068 9.579e-05 8.323e-05 6.929e-05 5.418e-05 3.814e-05 2.143e-05 4.311e-06 -1.294e-05 -3.006e-05 -4.677e-05 -6.281e-05 -7.792e-05 -9.186e-05 -0.0001044 -0.0001154 -0.0001246 -0.0001319 -0.0001371 -0.0001403 -0.0001413 -0.0001401 -0.0001367 -0.0001313 -0.0001238 -0.0001144 -0.0001033 -9.053e-05 -7.64e-05 -6.11e-05 -4.487e-05 -2.797e-05 -1.067e-05 6.776e-06 2.408e-05 4.098e-05 5.721e-05 7.251e-05 8.664e-05 9.938e-05 0.0001105 0.0001199 0.0001274 0.0001329 0.0001362 0.0001374 0.0001364 0.0001333 0.000128 0.0001207 0.0001115 0.0001005 8.8e-05 7.406e-05 5.896e-05 4.293e-05 2.622e-05 9.111e-06 -8.14e-06 -2.525e-05 -4.196e-05 -5.799e-05 -7.31e-05 -8.703e-05 -9.958e-05 -0.0001105 -0.0001197 -0.000127 -0.0001323 -0.0001355 -0.0001365 -0.0001353 -0.0001319 -0.0001265 -0.000119 -0.0001096 -9.846e-05 -8.572e-05 -7.16e-05 -5.63e-05 -4.008e-05 -2.319e-05 -5.892e-06 1.154e-05 2.884e-05 4.573e-05 6.195e-05 7.724e-05 9.136e-05 0.0001041 0.0001152 0.0001246 0.0001321 0.0001375 0.0001409 0.000142 0.000141 0.0001379 0.0001326 0.0001253 0.0001161 0.0001051 9.254e-05 7.859e-05 6.347e-05 4.742e-05 3.069e-05 1.356e-05 -3.707e-06 -2.084e-05 -3.757e-05 -5.362e-05 -6.874e-05 -8.27e-05 -9.527e-05 -0.0001063 -0.0001155 -0.0001228 -0.0001281 -0.0001313 -0.0001323 -0.0001311 -0.0001278 -0.0001224 -0.0001149 -0.0001056 -9.444e-05 -8.173e-05 -6.763e-05 -5.237e-05 -3.618e-05 -1.931e-05 -2.044e-06 1.536e-05 3.263e-05 4.949e-05 6.568e-05 8.094e-05 9.503e-05 0.0001077 0.0001188 0.0001282 0.0001356 0.000141 0.0001443 0.0001455 0.0001444 0.0001412 0.0001359 0.0001286 0.0001193 0.0001083 9.573e-05 8.174e-05 6.658e-05 5.05e-05 3.374e-05 1.657e-05 -7.406e-07 -1.791e-05 -3.468e-05 -5.077e-05 -6.593e-05 -7.993e-05 -9.254e-05 -0.0001036 -0.0001128 -0.0001202 -0.0001255 -0.0001287 -0.0001298 -0.0001287 -0.0001254 -0.00012 -0.0001126 -0.0001033 -9.221e-05 -7.954e-05 -6.548e-05 -5.026e-05 -3.412e-05 -1.73e-05 -6.839e-08 1.729e-05 3.452e-05 5.134e-05 6.748e-05 8.269e-05 9.674e-05 0.0001094 0.0001205 0.0001298 0.0001372 0.0001425 0.0001458 0.0001469 0.0001458 0.0001425 0.0001372 0.0001298 0.0001205 0.0001094 9.68e-05 8.276e-05 6.756e-05 5.142e-05 3.461e-05 1.74e-05 4.423e-08 -1.717e-05 -3.399e-05 -5.012e-05 -6.534e-05 -7.938e-05 -9.204e-05 -0.0001031 -0.0001124 -0.0001198 -0.0001252 -0.0001285 -0.0001296 -0.0001285 -0.0001253 -0.0001199 -0.0001126 -0.0001033 -9.227e-05 -7.965e-05 -6.565e-05 -5.047e-05 -3.437e-05 -1.76e-05 -4.182e-07 1.69e-05 3.408e-05 5.085e-05 6.694e-05 8.211e-05 9.611e-05 0.0001087 0.0001197 0.000129 0.0001363 0.0001417 0.0001449 0.0001459 0.0001448 0.0001415 0.0001361 0.0001287 0.0001193 0.0001082 9.553e-05 8.145e-05 6.62e-05 5.002e-05 3.317e-05 1.591e-05 -1.488e-06 -1.875e-05 -3.56e-05 -5.179e-05 -6.704e-05 -8.113e-05 -9.383e-05 -0.0001049 -0.0001143 -0.0001217 -0.0001272 -0.0001305 -0.0001316 -0.0001306 -0.0001274 -0.0001221 -0.0001148 -0.0001055 -9.455e-05 -8.197e-05 -6.8e-05 -5.287e-05 -3.68e-05 -2.007e-05 -2.926e-06 1.435e-05 3.149e-05 4.823e-05 6.429e-05 7.942e-05 9.338e-05 0.000106 0.0001169 0.0001262 0.0001335 0.0001388 0.0001419 0.000143 0.0001418 0.0001385 0.000133 0.0001256 0.0001162 0.0001051 9.234e-05 7.823e-05 6.295e-05 4.674e-05 2.986e-05 1.257e-05 -4.852e-06 -2.214e-05 -3.902e-05 -5.523e-05 -7.051e-05 -8.463e-05 -9.736e-05 -0.0001085 -0.0001179 -0.0001253 -0.0001308 -0.0001341 -0.0001353 -0.0001343 -0.0001311 -0.0001258 -0.0001185 -0.0001093 -9.835e-05 -8.579e-05 -7.184e-05 -5.672e-05 -4.068e-05 -2.396e-05 -6.84e-06 1.042e-05 2.755e-05 4.426e-05 6.031e-05 7.542e-05 8.937e-05 0.0001019 0.0001129 0.0001221 0.0001294 0.0001347 0.0001378 0.0001389 0.0001377 0.0001343 0.0001289 0.0001214 0.000112 0.0001009 8.817e-05 7.405e-05 5.877e-05 4.255e-05 2.567e-05 8.373e-06 -9.059e-06 -2.635e-05 -4.324e-05 -5.945e-05 -7.474e-05 -8.885e-05 -0.0001016 -0.0001127 -0.0001221 -0.0001296 -0.000135 -0.0001383 -0.0001395 -0.0001385 -0.0001353 -0.0001301 -0.0001228 -0.0001135 -0.0001026 -9e-05 -7.604e-05 -6.092e-05 -4.487e-05 -2.815e-05 -1.102e-05 6.252e-06 2.338e-05 4.011e-05 5.616e-05 7.129e-05 8.524e-05 9.782e-05 0.0001088 0.000118 0.0001253 0.0001306 0.0001338 0.0001348 0.0001337 0.0001303 0.0001249 0.0001174 0.0001081 9.695e-05 8.424e-05 7.014e-05 5.487e-05 3.868e-05 2.181e-05 4.534e-06 -1.288e-05 -3.015e-05 -4.702e-05 -6.321e-05 -7.847e-05 -9.257e-05 -0.0001053 -0.0001164 -0.0001257 -0.0001332 -0.0001386 -0.0001419 -0.0001431 -0.000142 -0.0001388 -0.0001335 -0.0001262 -0.0001169 -0.0001059 -9.335e-05 -7.937e-05 -6.422e-05 -4.814e-05 -3.138e-05 -1.422e-05 3.08e-06 2.024e-05 3.7e-05 5.309e-05 6.824e-05 8.223e-05 9.484e-05 0.0001059 0.0001151 0.0001225 0.0001278 0.000131 0.000132 0.0001309 0.0001276 0.0001222 0.0001148 0.0001055 9.441e-05 8.174e-05 6.767e-05 5.244e-05 3.629e-05 1.946e-05 2.228e-06 -1.514e-05 -3.238e-05 -4.92e-05 -6.535e-05 -8.057e-05 -9.462e-05 -0.0001073 -0.0001184 -0.0001277 -0.0001351 -0.0001404 -0.0001437 -0.0001448 -0.0001437 -0.0001405 -0.0001351 -0.0001278 -0.0001185 -0.0001074 -9.479e-05 -8.076e-05 -6.556e-05 -4.943e-05 -3.263e-05 -1.542e-05 1.927e-06 1.914e-05 3.594e-05 5.208e-05 6.728e-05 8.132e-05 9.398e-05 0.000105 0.0001143 0.0001217 0.0001271 0.0001304 0.0001315 0.0001304 0.0001272 0.0001218 0.0001145 0.0001052 9.415e-05 8.153e-05 6.752e-05 5.234e-05 3.624e-05 1.946e-05 2.278e-06 -1.504e-05 -3.222e-05 -4.899e-05 -6.509e-05 -8.026e-05 -9.426e-05 -0.0001069 -0.0001179 -0.0001271 -0.0001345 -0.0001398 -0.000143 -0.0001441 -0.0001429 -0.0001397 -0.0001342 -0.0001268 -0.0001175 -0.0001064 -9.367e-05 -7.959e-05 -6.434e-05 -4.816e-05 -3.13e-05 -1.404e-05 3.363e-06 2.063e-05 3.749e-05 5.368e-05 6.894e-05 8.303e-05 9.574e-05 0.0001069 0.0001162 0.0001237 0.0001291 0.0001324 0.0001336 0.0001325 0.0001294 0.0001241 0.0001168 0.0001075 9.656e-05 8.399e-05 7.003e-05 5.491e-05 3.886e-05 2.214e-05 5.01e-06 -1.226e-05 -2.938e-05 -4.61e-05 -6.214e-05 -7.726e-05 -9.121e-05 -0.0001038 -0.0001147 -0.0001239 -0.0001312 -0.0001365 -0.0001397 -0.0001407 -0.0001395 -0.0001361 -0.0001307 -0.0001232 -0.0001138 -0.0001026 -8.988e-05 -7.575e-05 -6.044e-05 -4.421e-05 -2.73e-05 -9.988e-06 7.466e-06 2.478e-05 4.169e-05 5.793e-05 7.325e-05 8.739e-05 0.0001001 0.0001113 0.0001207 0.0001282 0.0001337 0.0001371 0.0001383 0.0001373 0.0001342 0.0001289 0.0001217 0.0001125 0.0001016 8.906e-05 7.516e-05 6.008e-05 4.409e-05 2.741e-05 1.034e-05 -6.879e-06 -2.396e-05 -4.062e-05 -5.662e-05 -7.168e-05 -8.558e-05 -9.808e-05 -0.000109 -0.0001182 -0.0001254 -0.0001306 -0.0001337 -0.0001347 -0.0001334 -0.00013 -0.0001245 -0.000117 -0.0001076 -9.635e-05 -8.356e-05 -6.937e-05 -5.401e-05 -3.772e-05 -2.076e-05 -3.383e-06 1.413e-05 3.152e-05 4.85e-05 6.481e-05 8.02e-05 9.442e-05 0.0001073 0.0001185 0.000128 0.0001356 0.0001411 0.0001445 0.0001458 0.0001449 0.0001418 0.0001366 0.0001294 0.0001202 0.0001093 9.679e-05 8.289e-05 6.783e-05 5.183e-05 3.515e-05 1.805e-05 8.201e-07 -1.629e-05 -3.301e-05 -4.907e-05 -6.422e-05 -7.823e-05 -9.087e-05 -0.0001019 -0.0001113 -0.0001187 -0.0001241 -0.0001273 -0.0001284 -0.0001272 -0.0001238 -0.0001182 -0.0001104 -0.0001005 -8.866e-05 -7.505e-05 -5.983e-05 -4.319e-05 -2.542e-05 -6.808e-06 1.239e-05 3.187e-05 5.128e-05 7.024e-05 8.845e-05 0.0001056 0.0001214 0.0001353 0.0001473 0.0001571 0.0001644 0.0001692 0.0001712 0.0001705 0.0001672 0.0001613 0.0001529 0.0001423 0.0001298 0.0001157 0.0001002 8.389e-05 6.712e-05 5.018e-05 3.34e-05 1.723e-05 2.082e-06 -1.191e-05 -2.464e-05 -3.587e-05 -4.535e-05 -5.319e-05 -5.961e-05 -6.459e-05 -6.811e-05 -7.042e-05 -7.196e-05 -7.288e-05 -7.316e-05 -7.303e-05 -7.291e-05 -7.293e-05 -7.289e-05 -7.274e-05 -7.273e-05 -7.285e-05 -7.261e-05 -7.167e-05 -7.009e-05 -6.777e-05 -6.412e-05 -5.866e-05 -5.143e-05 -4.251e-05 -3.153e-05 -1.814e-05 -2.631e-06 1.447e-05 3.307e-05 5.318e-05 7.414e-05 9.496e-05 0.000115 0.0001341 0.0001514 0.0001656 0.000176 0.0001824 0.0001842 0.0001806 0.000171 0.0001558 0.0001351 0.0001088 7.705e-05 4.089e-05 1.48e-06 -4.051e-05 -8.425e-05 -0.0001281 -0.0001703 -0.0002097 -0.0002452 -0.0002752 -0.000298 -0.0003127 -0.0003188 -0.0003154 -0.0003019 -0.0002781 -0.0002448 -0.0002027 -0.0001522 -9.48e-05 -3.241e-05 3.286e-05 9.911e-05 0.0001641 0.0002251 0.0002791 0.0003239 0.0003576 0.0003777 0.0003815 0.0003663 0.0003323 0.0002839 0.0002234 0.0001357 -3.735e-06 -0.0001701 -0.0002387 -0.000152 -0.000151 - 0.000316 0.000319 0.0004093 0.0003489 0.000194 6.597e-05 -1.268e-05 -6.755e-05 -0.0001146 -0.0001521 -0.0001764 -0.0001876 -0.0001885 -0.0001817 -0.000169 -0.0001518 -0.0001317 -0.0001102 -8.925e-05 -6.968e-05 -5.215e-05 -3.741e-05 -2.626e-05 -1.876e-05 -1.446e-05 -1.311e-05 -1.47e-05 -1.866e-05 -2.387e-05 -2.952e-05 -3.53e-05 -4.06e-05 -4.433e-05 -4.568e-05 -4.461e-05 -4.104e-05 -3.437e-05 -2.426e-05 -1.117e-05 4.271e-06 2.199e-05 4.189e-05 6.31e-05 8.453e-05 0.0001057 0.0001263 0.0001455 0.0001622 0.0001759 0.0001866 0.0001939 0.0001969 0.0001957 0.0001905 0.0001816 0.0001688 0.0001524 0.0001331 0.0001116 8.825e-05 6.335e-05 3.784e-05 1.262e-05 -1.201e-05 -3.576e-05 -5.791e-05 -7.776e-05 -9.512e-05 -0.00011 -0.0001222 -0.0001312 -0.0001372 -0.0001404 -0.000141 -0.0001388 -0.0001341 -0.0001274 -0.0001189 -0.0001087 -9.706e-05 -8.441e-05 -7.109e-05 -5.716e-05 -4.273e-05 -2.816e-05 -1.373e-05 5.32e-07 1.461e-05 2.826e-05 4.124e-05 5.353e-05 6.513e-05 7.586e-05 8.551e-05 9.398e-05 0.0001013 0.0001073 0.0001118 0.0001146 0.0001159 0.0001154 0.0001132 0.0001091 0.0001032 9.551e-05 8.615e-05 7.512e-05 6.264e-05 4.889e-05 3.405e-05 1.829e-05 1.93e-06 -1.474e-05 -3.147e-05 -4.799e-05 -6.399e-05 -7.916e-05 -9.324e-05 -0.000106 -0.0001172 -0.0001267 -0.0001343 -0.0001398 -0.0001431 -0.0001443 -0.0001433 -0.0001401 -0.0001348 -0.0001274 -0.0001181 -0.0001071 -9.456e-05 -8.065e-05 -6.56e-05 -4.967e-05 -3.312e-05 -1.621e-05 8.132e-07 1.766e-05 3.407e-05 4.98e-05 6.459e-05 7.823e-05 9.049e-05 0.0001012 0.0001102 0.0001173 0.0001224 0.0001254 0.0001264 0.0001252 0.000122 0.0001167 0.0001094 0.0001002 8.938e-05 7.698e-05 6.322e-05 4.833e-05 3.254e-05 1.609e-05 -7.577e-07 -1.774e-05 -3.458e-05 -5.103e-05 -6.682e-05 -8.171e-05 -9.547e-05 -0.0001079 -0.0001187 -0.0001279 -0.0001352 -0.0001405 -0.0001437 -0.0001449 -0.0001439 -0.0001409 -0.0001357 -0.0001287 -0.0001197 -0.0001091 -9.687e-05 -8.332e-05 -6.864e-05 -5.305e-05 -3.681e-05 -2.018e-05 -3.408e-06 1.323e-05 2.947e-05 4.505e-05 5.973e-05 7.328e-05 8.548e-05 9.613e-05 0.0001051 0.0001121 0.0001173 0.0001203 0.0001213 0.0001201 0.0001168 0.0001115 0.0001042 9.51e-05 8.424e-05 7.184e-05 5.809e-05 4.32e-05 2.742e-05 1.097e-05 -5.865e-06 -2.284e-05 -3.967e-05 -5.611e-05 -7.19e-05 -8.678e-05 -0.0001005 -0.0001129 -0.0001238 -0.0001329 -0.0001402 -0.0001455 -0.0001487 -0.0001499 -0.0001489 -0.0001458 -0.0001407 -0.0001336 -0.0001246 -0.000114 -0.0001017 -8.817e-05 -7.347e-05 -5.785e-05 -4.159e-05 -2.492e-05 -8.123e-06 8.544e-06 2.482e-05 4.043e-05 5.515e-05 6.873e-05 8.097e-05 9.165e-05 0.0001006 0.0001078 0.0001129 0.000116 0.000117 0.0001159 0.0001127 0.0001074 0.0001002 9.106e-05 8.025e-05 6.79e-05 5.42e-05 3.936e-05 2.362e-05 7.234e-06 -9.551e-06 -2.647e-05 -4.325e-05 -5.963e-05 -7.536e-05 -9.019e-05 -0.0001039 -0.0001162 -0.000127 -0.0001361 -0.0001433 -0.0001485 -0.0001517 -0.0001528 -0.0001518 -0.0001486 -0.0001434 -0.0001363 -0.0001272 -0.0001165 -0.0001042 -9.057e-05 -7.579e-05 -6.011e-05 -4.378e-05 -2.704e-05 -1.017e-05 6.565e-06 2.291e-05 3.86e-05 5.339e-05 6.704e-05 7.935e-05 9.011e-05 9.916e-05 0.0001064 0.0001116 0.0001148 0.0001158 0.0001148 0.0001116 0.0001065 9.929e-05 9.027e-05 7.954e-05 6.727e-05 5.364e-05 3.889e-05 2.323e-05 6.92e-06 -9.786e-06 -2.662e-05 -4.333e-05 -5.963e-05 -7.528e-05 -9.002e-05 -0.0001036 -0.0001159 -0.0001266 -0.0001356 -0.0001427 -0.0001479 -0.000151 -0.000152 -0.0001509 -0.0001476 -0.0001424 -0.0001351 -0.000126 -0.0001152 -0.0001028 -8.911e-05 -7.425e-05 -5.849e-05 -4.207e-05 -2.525e-05 -8.301e-06 8.519e-06 2.494e-05 4.071e-05 5.558e-05 6.932e-05 8.171e-05 9.255e-05 0.0001017 0.000109 0.0001143 0.0001175 0.0001187 0.0001177 0.0001146 0.0001095 0.0001025 9.351e-05 8.286e-05 7.066e-05 5.712e-05 4.244e-05 2.686e-05 1.063e-05 -6e-06 -2.276e-05 -3.939e-05 -5.561e-05 -7.119e-05 -8.585e-05 -9.938e-05 -0.0001116 -0.0001222 -0.0001311 -0.0001382 -0.0001433 -0.0001463 -0.0001472 -0.000146 -0.0001427 -0.0001374 -0.0001301 -0.0001209 -0.00011 -9.757e-05 -8.378e-05 -6.885e-05 -5.303e-05 -3.654e-05 -1.966e-05 -2.638e-06 1.425e-05 3.074e-05 4.657e-05 6.151e-05 7.531e-05 8.776e-05 9.866e-05 0.0001079 0.0001152 0.0001206 0.0001239 0.0001251 0.0001242 0.0001212 0.0001161 0.0001091 0.0001002 8.962e-05 7.748e-05 6.399e-05 4.937e-05 3.384e-05 1.766e-05 1.082e-06 -1.563e-05 -3.22e-05 -4.838e-05 -6.39e-05 -7.852e-05 -9.201e-05 -0.0001041 -0.0001147 -0.0001236 -0.0001306 -0.0001357 -0.0001387 -0.0001395 -0.0001383 -0.000135 -0.0001296 -0.0001222 -0.000113 -0.0001021 -8.961e-05 -7.579e-05 -6.082e-05 -4.496e-05 -2.844e-05 -1.153e-05 5.524e-06 2.244e-05 3.896e-05 5.483e-05 6.979e-05 8.362e-05 9.609e-05 0.000107 0.0001162 0.0001236 0.000129 0.0001323 0.0001336 0.0001327 0.0001297 0.0001247 0.0001177 0.0001088 9.821e-05 8.608e-05 7.261e-05 5.8e-05 4.248e-05 2.631e-05 9.747e-06 -6.953e-06 -2.352e-05 -3.969e-05 -5.52e-05 -6.982e-05 -8.33e-05 -9.542e-05 -0.000106 -0.0001149 -0.0001219 -0.0001269 -0.0001299 -0.0001308 -0.0001296 -0.0001263 -0.0001209 -0.0001135 -0.0001043 -9.339e-05 -8.092e-05 -6.71e-05 -5.215e-05 -3.629e-05 -1.979e-05 -2.882e-06 1.415e-05 3.106e-05 4.756e-05 6.341e-05 7.836e-05 9.217e-05 0.0001046 0.0001155 0.0001247 0.0001321 0.0001374 0.0001408 0.000142 0.0001411 0.0001381 0.000133 0.000126 0.0001171 0.0001065 9.43e-05 8.08e-05 6.616e-05 5.061e-05 3.441e-05 1.781e-05 1.073e-06 -1.553e-05 -3.173e-05 -4.729e-05 -6.194e-05 -7.546e-05 -8.762e-05 -9.825e-05 -0.0001072 -0.0001142 -0.0001193 -0.0001223 -0.0001233 -0.0001221 -0.0001188 -0.0001135 -0.0001062 -9.699e-05 -8.612e-05 -7.37e-05 -5.994e-05 -4.504e-05 -2.923e-05 -1.278e-05 4.073e-06 2.106e-05 3.79e-05 5.435e-05 7.015e-05 8.504e-05 9.879e-05 0.0001112 0.000122 0.0001312 0.0001385 0.0001438 0.000147 0.0001482 0.0001472 0.0001441 0.000139 0.0001319 0.0001229 0.0001123 0.0001 8.647e-05 7.176e-05 5.615e-05 3.988e-05 2.321e-05 6.402e-06 -1.027e-05 -2.655e-05 -4.217e-05 -5.69e-05 -7.049e-05 -8.273e-05 -9.342e-05 -0.0001024 -0.0001095 -0.0001147 -0.0001178 -0.0001188 -0.0001177 -0.0001145 -0.0001092 -0.000102 -9.294e-05 -8.214e-05 -6.98e-05 -5.612e-05 -4.13e-05 -2.557e-05 -9.199e-06 7.57e-06 2.447e-05 4.124e-05 5.761e-05 7.332e-05 8.812e-05 0.0001018 0.0001141 0.0001249 0.0001339 0.0001411 0.0001464 0.0001495 0.0001506 0.0001495 0.0001464 0.0001412 0.000134 0.0001249 0.0001142 0.0001019 8.82e-05 7.34e-05 5.77e-05 4.134e-05 2.459e-05 7.694e-06 -9.066e-06 -2.543e-05 -4.114e-05 -5.595e-05 -6.963e-05 -8.196e-05 -9.275e-05 -0.0001018 -0.000109 -0.0001143 -0.0001175 -0.0001186 -0.0001176 -0.0001144 -0.0001093 -0.0001021 -9.314e-05 -8.243e-05 -7.018e-05 -5.658e-05 -4.185e-05 -2.621e-05 -9.926e-06 6.757e-06 2.357e-05 4.025e-05 5.653e-05 7.215e-05 8.688e-05 0.0001005 0.0001127 0.0001234 0.0001323 0.0001395 0.0001446 0.0001477 0.0001487 0.0001475 0.0001443 0.000139 0.0001317 0.0001226 0.0001117 9.934e-05 8.56e-05 7.072e-05 5.493e-05 3.849e-05 2.166e-05 4.683e-06 -1.216e-05 -2.86e-05 -4.439e-05 -5.929e-05 -7.304e-05 -8.545e-05 -9.631e-05 -0.0001055 -0.0001128 -0.0001181 -0.0001214 -0.0001225 -0.0001216 -0.0001185 -0.0001134 -0.0001064 -9.746e-05 -8.683e-05 -7.465e-05 -6.112e-05 -4.646e-05 -3.09e-05 -1.468e-05 1.932e-06 1.868e-05 3.529e-05 5.15e-05 6.706e-05 8.171e-05 9.523e-05 0.0001074 0.000118 0.0001269 0.000134 0.000139 0.0001421 0.000143 0.0001418 0.0001385 0.0001331 0.0001258 0.0001166 0.0001057 9.324e-05 7.945e-05 6.451e-05 4.867e-05 3.218e-05 1.529e-05 -1.739e-06 -1.863e-05 -3.513e-05 -5.097e-05 -6.591e-05 -7.972e-05 -9.217e-05 -0.0001031 -0.0001123 -0.0001196 -0.000125 -0.0001283 -0.0001295 -0.0001286 -0.0001256 -0.0001206 -0.0001135 -0.0001047 -9.407e-05 -8.193e-05 -6.844e-05 -5.381e-05 -3.829e-05 -2.21e-05 -5.525e-06 1.119e-05 2.777e-05 4.395e-05 5.947e-05 7.41e-05 8.759e-05 9.972e-05 0.0001103 0.0001192 0.0001262 0.0001313 0.0001343 0.0001352 0.0001339 0.0001306 0.0001252 0.0001179 0.0001087 9.776e-05 8.53e-05 7.149e-05 5.654e-05 4.069e-05 2.418e-05 7.277e-06 -9.759e-06 -2.666e-05 -4.317e-05 -5.902e-05 -7.396e-05 -8.777e-05 -0.0001002 -0.0001112 -0.0001204 -0.0001277 -0.0001331 -0.0001364 -0.0001376 -0.0001367 -0.0001337 -0.0001286 -0.0001216 -0.0001127 -0.0001021 -8.997e-05 -7.647e-05 -6.183e-05 -4.63e-05 -3.01e-05 -1.351e-05 3.213e-06 1.98e-05 3.6e-05 5.154e-05 6.618e-05 7.969e-05 9.185e-05 0.0001025 0.0001114 0.0001184 0.0001235 0.0001265 0.0001274 0.0001262 0.0001229 0.0001176 0.0001102 0.0001011 9.018e-05 7.775e-05 6.396e-05 4.905e-05 3.323e-05 1.676e-05 -1.112e-07 -1.711e-05 -3.398e-05 -5.045e-05 -6.626e-05 -8.117e-05 -9.494e-05 -0.0001074 -0.0001182 -0.0001274 -0.0001347 -0.00014 -0.0001433 -0.0001445 -0.0001435 -0.0001405 -0.0001354 -0.0001283 -0.0001193 -0.0001087 -9.648e-05 -8.293e-05 -6.825e-05 -5.265e-05 -3.641e-05 -1.976e-05 -2.978e-06 1.367e-05 2.992e-05 4.552e-05 6.023e-05 7.379e-05 8.601e-05 9.668e-05 0.0001056 0.0001127 0.0001179 0.000121 0.000122 0.0001208 0.0001176 0.0001123 0.0001051 9.594e-05 8.512e-05 7.276e-05 5.904e-05 4.42e-05 2.845e-05 1.205e-05 -4.745e-06 -2.167e-05 -3.846e-05 -5.486e-05 -7.06e-05 -8.543e-05 -9.912e-05 -0.0001115 -0.0001223 -0.0001313 -0.0001386 -0.0001438 -0.000147 -0.0001481 -0.0001471 -0.0001439 -0.0001388 -0.0001316 -0.0001226 -0.0001118 -9.955e-05 -8.592e-05 -7.115e-05 -5.547e-05 -3.914e-05 -2.241e-05 -5.542e-06 1.119e-05 2.753e-05 4.322e-05 5.801e-05 7.166e-05 8.396e-05 9.473e-05 0.0001038 0.000111 0.0001162 0.0001194 0.0001204 0.0001194 0.0001163 0.0001111 0.0001039 9.489e-05 8.416e-05 7.188e-05 5.826e-05 4.351e-05 2.785e-05 1.154e-05 -5.158e-06 -2.199e-05 -3.869e-05 -5.499e-05 -7.064e-05 -8.538e-05 -9.898e-05 -0.0001112 -0.0001219 -0.0001309 -0.000138 -0.0001432 -0.0001463 -0.0001473 -0.0001462 -0.0001429 -0.0001377 -0.0001304 -0.0001213 -0.0001105 -9.809e-05 -8.436e-05 -6.949e-05 -5.372e-05 -3.729e-05 -2.047e-05 -3.505e-06 1.332e-05 2.976e-05 4.554e-05 6.042e-05 7.417e-05 8.657e-05 9.742e-05 0.0001066 0.0001139 0.0001192 0.0001224 0.0001236 0.0001227 0.0001196 0.0001145 0.0001074 9.852e-05 8.789e-05 7.571e-05 6.218e-05 4.752e-05 3.195e-05 1.574e-05 -8.727e-07 -1.762e-05 -3.422e-05 -5.043e-05 -6.598e-05 -8.063e-05 -9.414e-05 -0.0001063 -0.0001169 -0.0001258 -0.0001328 -0.0001379 -0.0001409 -0.0001418 -0.0001406 -0.0001373 -0.0001319 -0.0001246 -0.0001154 -0.0001045 -9.201e-05 -7.819e-05 -6.324e-05 -4.739e-05 -3.087e-05 -1.396e-05 3.086e-06 2e-05 3.652e-05 5.239e-05 6.735e-05 8.118e-05 9.366e-05 0.0001046 0.0001138 0.0001212 0.0001266 0.0001299 0.0001312 0.0001303 0.0001274 0.0001223 0.0001154 0.0001065 9.595e-05 8.385e-05 7.04e-05 5.582e-05 4.033e-05 2.419e-05 7.659e-06 -9.008e-06 -2.554e-05 -4.167e-05 -5.715e-05 -7.172e-05 -8.515e-05 -9.724e-05 -0.0001078 -0.0001166 -0.0001236 -0.0001285 -0.0001315 -0.0001323 -0.000131 -0.0001276 -0.0001222 -0.0001148 -0.0001055 -9.451e-05 -8.198e-05 -6.809e-05 -5.307e-05 -3.715e-05 -2.058e-05 -3.604e-06 1.35e-05 3.048e-05 4.705e-05 6.297e-05 7.799e-05 9.188e-05 0.0001044 0.0001154 0.0001247 0.0001321 0.0001376 0.000141 0.0001423 0.0001414 0.0001385 0.0001335 0.0001266 0.0001178 0.0001073 9.532e-05 8.197e-05 6.75e-05 5.216e-05 3.619e-05 1.985e-05 3.422e-06 -1.284e-05 -2.867e-05 -4.382e-05 -5.803e-05 -7.109e-05 -8.279e-05 -9.296e-05 -0.0001014 -0.000108 -0.0001127 -0.0001155 -0.0001162 -0.0001148 -0.0001114 -0.0001061 -9.889e-05 -8.988e-05 -7.922e-05 -6.708e-05 -5.358e-05 -3.89e-05 -2.328e-05 -6.929e-06 9.953e-06 2.716e-05 4.441e-05 6.145e-05 7.804e-05 9.397e-05 0.000109 0.0001228 0.0001351 0.0001458 0.0001547 0.0001615 0.0001661 0.0001683 0.0001682 0.0001655 0.0001603 0.0001526 0.0001427 0.0001305 0.0001162 0.0001002 8.274e-05 6.414e-05 4.467e-05 2.484e-05 5.14e-06 -1.413e-05 -3.268e-05 -5.003e-05 -6.567e-05 -7.942e-05 -9.118e-05 -0.0001007 -0.0001076 -0.0001119 -0.0001139 -0.0001135 -0.0001108 -0.0001058 -9.916e-05 -9.112e-05 -8.172e-05 -7.121e-05 -6.022e-05 -4.911e-05 -3.785e-05 -2.656e-05 -1.572e-05 -5.625e-06 3.924e-06 1.312e-05 2.172e-05 2.95e-05 3.683e-05 4.413e-05 5.132e-05 5.816e-05 6.495e-05 7.217e-05 7.969e-05 8.709e-05 9.437e-05 0.0001018 0.0001093 0.0001159 0.0001214 0.0001259 0.000129 0.00013 0.0001282 0.0001238 0.0001167 0.0001063 9.214e-05 7.463e-05 5.423e-05 3.086e-05 4.565e-06 -2.377e-05 -5.306e-05 -8.274e-05 -0.0001123 -0.0001406 -0.000166 -0.0001878 -0.0002053 -0.0002176 -0.0002233 -0.0002218 -0.0002132 -0.0001973 -0.0001736 -0.0001426 -0.0001052 -6.252e-05 -1.551e-05 3.442e-05 8.513e-05 0.0001344 0.0001802 0.0002207 0.0002534 0.0002753 0.0002831 0.0002761 0.0002575 0.0002286 0.0001731 6.484e-05 -7.382e-05 -0.0001218 -2.308e-05 -1.591e-05 \ No newline at end of file