new interface, new timing

This commit is contained in:
David Holzmüller 2019-04-07 20:42:24 +02:00
parent cc842d6d30
commit 999de0cbf7
5 changed files with 275 additions and 259 deletions

View File

@ -16,21 +16,14 @@ file(GLOB INC_TEST "test/*.hpp")
option(BUILD_TEST ON) option(BUILD_TEST ON)
## Build application ## Build application
add_library(fast_sparse_interpolation ${SRC}) # add_library(fast_sparse_interpolation ${SRC})
include_directories("${PROJECT_SOURCE_DIR}/src") include_directories("${PROJECT_SOURCE_DIR}/src")
if(BUILD_TEST) file(GLOB SRC_TEST "test/*.cpp")
if(BUILD_TEST STREQUAL "ON") add_executable(fsi-test ${SRC_TEST})
file(GLOB SRC_TEST "test/*.cpp") include_directories("${PROJECT_SOURCE_DIR}/src")
add_executable(fsi-test ${SRC_TEST})
include_directories("${PROJECT_SOURCE_DIR}/src")
target_link_libraries(fast_sparse_interpolation)
endif()
endif()
# install(TARGETS fast_sparse_interpolation DESTINATION lib)
install(TARGETS fast_sparse_interpolation DESTINATION lib)
install(FILES ${INC} DESTINATION include/sfcpp) install(FILES ${INC} DESTINATION include/sfcpp)

View File

@ -1,4 +1,4 @@
/* Copyright 2017 The sfcpp Authors. All Rights Reserved. /* Copyright 2019 The fast_sparse_interpolation Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@ -1,8 +0,0 @@
// Copyright (C) 2008-today The SG++ project
// This file is part of the SG++ project. For conditions of distribution and
// use, please see the copyright notice provided with SG++ or at
// sgpp.sparsegrids.org
#include "Interpolation.hpp"
namespace fsi {} /* namespace fsi */

View File

@ -1,7 +1,17 @@
// Copyright (C) 2008-today The SG++ project /* Copyright 2019 The fast_sparse_interpolation Authors. All Rights Reserved.
// This file is part of the SG++ project. For conditions of distribution and
// use, please see the copyright notice provided with SG++ or at Licensed under the Apache License, Version 2.0 (the "License");
// sgpp.sparsegrids.org you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#pragma once #pragma once
@ -29,18 +39,6 @@ size_t binom(size_t n, size_t k) {
return prod; return prod;
} }
template <size_t dim>
void iterateRecursive(size_t remaining, std::function<void(size_t)> callback) {
for (size_t val = 0; val <= remaining; ++val) {
iterateRecursive<dim - 1>(remaining - val, callback);
}
}
template <>
void iterateRecursive<0>(size_t remaining, std::function<void(size_t)> callback) {
callback(remaining);
}
template <size_t d> template <size_t d>
class TemplateBoundedSumIterator { class TemplateBoundedSumIterator {
size_t bound; size_t bound;
@ -52,14 +50,6 @@ class TemplateBoundedSumIterator {
TemplateBoundedSumIterator(size_t bound) TemplateBoundedSumIterator(size_t bound)
: bound(bound), index_head(d - 1, 0), index_head_sum(0), is_done(false){}; : bound(bound), index_head(d - 1, 0), index_head_sum(0), is_done(false){};
void iterate(std::function<void(size_t)> callback,
std::function<void(size_t)> outerLoopCallback) {
for (size_t val = 0; val <= bound; ++val) {
outerLoopCallback(val);
iterateRecursive<d - 2>(bound - val, callback);
}
}
/** /**
* At the current multi-index (i_1, ..., i_{d-1}, 0), return how many multi-indices starting with * At the current multi-index (i_1, ..., i_{d-1}, 0), return how many multi-indices starting with
* (i_1, ..., i_{d-1}) are contained in the multi-index set, then advance to the next multi-index * (i_1, ..., i_{d-1}) are contained in the multi-index set, then advance to the next multi-index
@ -106,6 +96,8 @@ class TemplateBoundedSumIterator {
std::vector<size_t> indexBounds() const { return std::vector<size_t>(d, bound + 1); } std::vector<size_t> indexBounds() const { return std::vector<size_t>(d, bound + 1); }
size_t numValues() const { return binom(bound + d, d); }
/** /**
* Returns an iterator where the last index moves to the front. For an index set defined by a sum * Returns an iterator where the last index moves to the front. For an index set defined by a sum
* bound, nothing changes. * bound, nothing changes.
@ -240,12 +232,6 @@ class StandardBoundedSumIterator {
} }
}; };
// class FastSparseInterpolation {
// public:
// FastSparseInterpolation(std::function<double(std::vector<double>)> const &f,
// BoundedSumIterator it, std::vector<std::function<double(size_t)>> phi);
//};
class MultiDimVector { class MultiDimVector {
public: public:
// put the first dimension into an outer vector for processing reasons // put the first dimension into an outer vector for processing reasons
@ -261,28 +247,6 @@ class MultiDimVector {
} }
}; };
inline std::ostream &operator<<(std::ostream &os, boost::numeric::ublas::matrix<double> matrix) {
os << "[";
for (size_t i = 0; i < matrix.size1(); ++i) {
for (size_t j = 0; j < matrix.size2(); ++j) {
os << matrix(i, j) << " ";
}
os << "\n";
}
os << " ]";
return os;
}
template <class T>
inline std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {
os << "[";
for (auto ii = v.begin(); ii != v.end(); ++ii) {
os << " " << *ii;
}
os << " ]";
return os;
}
template <typename It> template <typename It>
void multiply_lower_triangular_inplace(It it, std::vector<boost::numeric::ublas::matrix<double>> L, void multiply_lower_triangular_inplace(It it, std::vector<boost::numeric::ublas::matrix<double>> L,
MultiDimVector &v) { MultiDimVector &v) {
@ -393,6 +357,188 @@ void multiply_upper_triangular_inplace(It it, std::vector<boost::numeric::ublas:
} }
} }
/**
* Represents a sparse linear tensor product operator defined by a matrix for each dimension.
*/
template <typename It>
class SparseTPOperator {
It it;
std::vector<boost::numeric::ublas::matrix<double>> M;
std::vector<boost::numeric::ublas::matrix<double>> LU;
std::vector<boost::numeric::ublas::matrix<double>> L;
std::vector<boost::numeric::ublas::matrix<double>> U;
std::vector<boost::numeric::ublas::matrix<double>> Linv;
std::vector<boost::numeric::ublas::matrix<double>> Uinv;
size_t d;
public:
SparseTPOperator(It it, std::vector<boost::numeric::ublas::matrix<double>> matrices)
: it(it), M(matrices), d(matrices.size()){};
void prepareCommon() {
if (LU.size() > 0) {
return; // already prepared
}
namespace ublas = boost::numeric::ublas;
typedef ublas::matrix<double> Matrix;
for (size_t k = 0; k < d; ++k) {
// std::cout << "Matrix creation loop\n";
Matrix Mk = M[k];
ublas::lu_factorize(Mk);
LU.push_back(Mk);
}
}
void prepareApply() {
if (L.size() > 0) {
return; // already prepared
}
prepareCommon();
namespace ublas = boost::numeric::ublas;
typedef ublas::matrix<double> Matrix;
for (size_t k = 0; k < d; ++k) {
size_t nk = M[k].size1();
Matrix &LUk = LU[k];
Matrix Lk(nk, nk);
Matrix Uk(nk, nk);
for (size_t i = 0; i < nk; ++i) {
for (size_t j = 0; j < i; ++j) {
Lk(i, j) = LUk(i, j);
}
Lk(i, i) = 1.0;
for (size_t j = i; j < nk; ++j) {
Uk(i, j) = LUk(i, j);
}
}
L.push_back(Lk);
U.push_back(Uk);
}
}
void prepareSolve() {
if (Linv.size() > 0) {
return; // already prepared
}
prepareCommon();
namespace ublas = boost::numeric::ublas;
typedef ublas::matrix<double> Matrix;
for (size_t k = 0; k < d; ++k) {
Matrix Lkinv = ublas::identity_matrix<double>(M[k].size1());
Matrix Ukinv = ublas::identity_matrix<double>(M[k].size1());
ublas::inplace_solve(LU[k], Lkinv, ublas::unit_lower_tag());
ublas::inplace_solve(LU[k], Ukinv, ublas::upper_tag());
Linv.push_back(Lkinv);
Uinv.push_back(Ukinv);
}
}
MultiDimVector apply(MultiDimVector input) {
prepareApply();
multiply_upper_triangular_inplace(it, U, input);
multiply_lower_triangular_inplace(it, L, input);
return input;
}
MultiDimVector solve(MultiDimVector rhs) {
prepareSolve();
multiply_lower_triangular_inplace(it, Linv, rhs);
multiply_upper_triangular_inplace(it, Uinv, rhs);
return rhs;
}
};
template <typename It, typename X, typename Phi>
SparseTPOperator<It> createInterpolationOperator(It it, Phi phi, X x) {
auto n = it.indexBounds();
size_t d = it.dim();
namespace ublas = boost::numeric::ublas;
typedef ublas::matrix<double> Matrix;
std::vector<Matrix> matrices;
// create matrices and inverted LU decompositions
for (size_t k = 0; k < d; ++k) {
// std::cout << "Matrix creation loop\n";
Matrix Mk(n[k], n[k]);
for (size_t i = 0; i < n[k]; ++i) {
for (size_t j = 0; j < n[k]; ++j) {
Mk(i, j) = phi[k](j)(x[k](i));
}
}
matrices.push_back(Mk);
}
return SparseTPOperator<It>(it, matrices);
}
template <typename It, typename Func, typename X>
MultiDimVector evaluateFunction(It it, Func f, X x) {
size_t d = it.dim();
auto n = it.indexBounds();
MultiDimVector v(n[0]);
it.reset();
std::vector<double> point(d);
while (not it.done()) {
size_t last_dim_count = it.lastDimensionCount();
for (size_t dim = 0; dim < d - 1; ++dim) {
point[dim] = x[dim](it.indexAt(dim));
}
for (size_t last_dim_idx = 0; last_dim_idx < last_dim_count; ++last_dim_idx) {
point[d - 1] = x[d - 1](last_dim_idx);
double function_value = f(point);
v.data[it.firstIndex()].push_back(function_value);
}
it.next();
}
return v;
}
inline std::ostream &operator<<(std::ostream &os, boost::numeric::ublas::matrix<double> matrix) {
os << "[";
for (size_t i = 0; i < matrix.size1(); ++i) {
for (size_t j = 0; j < matrix.size2(); ++j) {
os << matrix(i, j) << " ";
}
os << "\n";
}
os << " ]";
return os;
}
template <class T>
inline std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {
os << "[";
for (auto ii = v.begin(); ii != v.end(); ++ii) {
os << " " << *ii;
}
os << " ]";
return os;
}
template <typename Func, typename It, typename Phi, typename X> template <typename Func, typename It, typename Phi, typename X>
MultiDimVector interpolate(Func f, It it, Phi phi, X x) { MultiDimVector interpolate(Func f, It it, Phi phi, X x) {
auto n = it.indexBounds(); auto n = it.indexBounds();
@ -486,180 +632,4 @@ MultiDimVector interpolate(Func f, It it, Phi phi, X x) {
return v; return v;
} }
template <typename Func, typename It, typename Phi, typename X>
MultiDimVector interpolate2(Func f, It it, Phi phi, X x) {
auto n = it.indexBounds();
size_t d = it.dim();
namespace ublas = boost::numeric::ublas;
typedef ublas::matrix<double> Matrix;
std::vector<Matrix> Linv, Uinv;
// create matrices and inverted LU decompositions
for (size_t k = 0; k < d; ++k) {
// std::cout << "Matrix creation loop\n";
Matrix Mk(n[k], n[k]);
for (size_t i = 0; i < n[k]; ++i) {
for (size_t j = 0; j < n[k]; ++j) {
Mk(i, j) = phi[k](j)(x[k](i));
}
}
// std::cout << "Matrices:\n";
// std::cout << Mk << "\n";
ublas::lu_factorize(Mk);
// std::cout << Mk << "\n";
Matrix Lkinv = ublas::identity_matrix<double>(n[k]);
Matrix Ukinv = ublas::identity_matrix<double>(n[k]);
ublas::inplace_solve(Mk, Lkinv, ublas::unit_lower_tag());
ublas::inplace_solve(Mk, Ukinv, ublas::upper_tag());
// std::cout << Lkinv << "\n";
// std::cout << Ukinv << "\n";
Linv.push_back(Lkinv);
Uinv.push_back(Ukinv);
}
MultiDimVector v(n[0]);
std::cout << "Compute function values\n";
// compute function values
it.reset();
std::vector<double> point(d);
while (not it.done()) {
size_t last_dim_count = it.lastDimensionCount();
for (size_t dim = 0; dim < d - 1; ++dim) {
point[dim] = x[dim](it.indexAt(dim));
}
for (size_t last_dim_idx = 0; last_dim_idx < last_dim_count; ++last_dim_idx) {
point[d - 1] = x[d - 1](last_dim_idx);
double function_value = f(point);
v.data[it.firstIndex()].push_back(function_value);
}
it.next();
}
size_t number = 0;
for (size_t dim = 0; dim < n[0]; ++dim) {
number += v.data[dim].size();
}
std::cout << "number of points: " << number << "\n";
// for (size_t i = 0; i < v.data.size(); ++i) {
// std::cout << v.data[i] << "\n\n";
// }
size_t num_sum_op = 0;
std::cout << "First matrix multiplication\n";
// multiply by L^{-1}
// the multiplication is based on a cyclic permutation of the indices: the last index of v becomes
// the first index of w
for (int k = d - 1; k >= 0; --k) {
MultiDimVector w(n[k]);
for (size_t idx = 0; idx < n[k]; ++idx) {
// TODO: make compatible with other iterators
w.data[idx].reserve(v.data[idx].size());
}
auto &Lkinv = Linv[k];
it.reset();
size_t second_v_index = 0;
double *data_pointer = &v.data[0][0];
it.iterate(
[&](size_t last_dim_count) {
double *offset_data_pointer = data_pointer + second_v_index;
for (size_t i = 0; i < last_dim_count; ++i) {
double sum = 0.0;
for (size_t j = 0; j <= i; ++j) {
sum += Lkinv(i, j) * (*(offset_data_pointer + j));
++num_sum_op;
}
w.data[i].push_back(sum);
}
second_v_index += last_dim_count;
},
[&](size_t first_dim_index) {
second_v_index = 0;
data_pointer = &v.data[first_dim_index][0];
});
v.swap(w);
it = it.cycle();
// for (size_t i = 0; i < v.data.size(); ++i) {
// std::cout << v.data[i] << "\n\n";
// }
}
std::cout << "Second matrix multiplication\n";
// multiply by U^{-1}
// the multiplication is based on a cyclic permutation of the indices: the last index of v becomes
// the first index of w
for (int k = d - 1; k >= 0; --k) {
MultiDimVector w(n[k]);
for (size_t idx = 0; idx < n[k]; ++idx) {
// TODO: make compatible with other iterators
w.data[idx].reserve(v.data[idx].size());
}
auto &Ukinv = Uinv[k];
it.reset();
size_t second_v_index = 0;
double *data_pointer = &v.data[0][0];
it.iterate(
[&](size_t last_dim_count) {
double *offset_data_pointer = data_pointer + second_v_index;
for (size_t i = 0; i < last_dim_count; ++i) {
double sum = 0.0;
for (size_t j = i; j < last_dim_count; ++j) {
sum += Ukinv(i, j) * (*(offset_data_pointer + j));
++num_sum_op;
}
w.data[i].push_back(sum);
}
second_v_index += last_dim_count;
},
[&](size_t first_dim_index) {
second_v_index = 0;
data_pointer = &v.data[first_dim_index][0];
});
v.swap(w);
it = it.cycle();
// for (size_t i = 0; i < v.data.size(); ++i) {
// std::cout << v.data[i] << "\n\n";
// }
}
std::cout << "Total number of summation operations: " << num_sum_op << "\n";
return v;
}
} /* namespace fsi */ } /* namespace fsi */

View File

@ -1,3 +1,17 @@
/* Copyright 2019 The fast_sparse_interpolation Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include <Interpolation.hpp> #include <Interpolation.hpp>
#include <chrono> #include <chrono>
@ -52,6 +66,14 @@ inline std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {
return os; return os;
} }
std::ostream &operator<<(std::ostream &os, fsi::MultiDimVector const &v) {
for (size_t i = 0; i < v.data.size(); ++i) {
std::cout << v.data[i] << "\n\n";
}
return os;
}
// TODO: refactoring: // TODO: refactoring:
/** /**
* - Interface for MultiDimVector * - Interface for MultiDimVector
@ -66,19 +88,58 @@ inline std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {
* - Computation of derivatives? * - Computation of derivatives?
*/ */
// using namespace fsi; double measure_execution_time(std::function<void()> f) {
int main() {
constexpr size_t d = 5;
size_t bound = 57;
// fsi::TemplateBoundedSumIterator<d> it(bound);
fsi::BoundedSumIterator it(d, bound);
std::vector<MonomialFunctions> phi(d);
std::vector<GoldenPointDistribution> x(d);
auto start = std::chrono::high_resolution_clock::now(); auto start = std::chrono::high_resolution_clock::now();
auto result = fsi::interpolate(f, it, phi, x); f();
auto finish = std::chrono::high_resolution_clock::now(); auto finish = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = finish - start; std::chrono::duration<double> elapsed = finish - start;
std::cout << "Elapsed time: " << elapsed.count() << " s\n"; return elapsed.count();
}
class Timer {
std::chrono::system_clock::time_point start;
public:
Timer() : start(std::chrono::high_resolution_clock::now()){};
void reset() { start = std::chrono::high_resolution_clock::now(); }
double elapsed() {
auto finish = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = finish - start;
return elapsed.count();
}
};
// using namespace fsi;
int main() {
constexpr size_t d = 8;
size_t bound = 24;
fsi::TemplateBoundedSumIterator<d> it(bound);
// fsi::BoundedSumIterator it(d, bound);
std::vector<MonomialFunctions> phi(d);
std::vector<GoldenPointDistribution> x(d);
auto rhs = evaluateFunction(it, f, x);
auto op = createInterpolationOperator(it, phi, x);
// std::cout << rhs << "\n";
Timer timer;
op.prepareSolve();
// auto result = fsi::interpolate(f, it, phi, x);
std::cout << "Time for prepareSolve(): " << timer.elapsed() << " s\n";
timer.reset();
auto c = op.solve(rhs);
// auto c = fsi::interpolate(f, it, phi, x);
std::cout << "Time for solve(): " << timer.elapsed() << " s\n";
// std::cout << c << "\n";
timer.reset();
auto b = op.apply(c);
std::cout << "Time for apply(): " << timer.elapsed() << " s\n";
// std::cout << b << "\n";
std::cout << "Number of points: " << it.numValues() << "\n";
// for (size_t i = 0; i < result.data.size(); ++i) { // for (size_t i = 0; i < result.data.size(); ++i) {
// std::cout << result.data[i] << "\n\n"; // std::cout << result.data[i] << "\n\n";