diff --git a/.gitignore b/.gitignore index 2e1dbc718..529ff1e41 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ - +test/build +examples/build +.vscode test/Testing/Temporary/CTestCostData.txt *.log .Rproj.user diff --git a/include/integration/simple_MC_integration.hpp b/include/integration/simple_MC_integration.hpp new file mode 100644 index 000000000..f6cb1739c --- /dev/null +++ b/include/integration/simple_MC_integration.hpp @@ -0,0 +1,234 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2021 Vissarion Fisikopoulos +// Copyright (c) 2018-2021 Apostolos Chalkis + +// Contributed and/or modified by Suraj Choubey, as part of Google Summer of Code 2021 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// Monte Carlo Integration algorithm used here : https://en.wikipedia.org/wiki/Monte_Carlo_integration#Overview + +#ifndef SIMPLE_MC_INTEGRATION_HPP +#define SIMPLE_MC_INTEGRATION_HPP + +#include +#include +#include +#include +#include "convex_bodies/hpolytope.h" +#include "Eigen/Eigen" +#include "generators/known_polytope_generators.h" +#include "boost_random_number_generator.hpp" +#include "cartesian_geom/cartesian_kernel.h" +#include "random_walks/random_walks.hpp" +#include "volume/volume_sequence_of_balls.hpp" +#include "volume/volume_cooling_gaussians.hpp" +#include "volume/volume_cooling_balls.hpp" +#include "misc.h" + +typedef double NT; +typedef Cartesian Kernel; +typedef typename Kernel::Point Point; +typedef HPolytope Hpolytope; +typedef boost::mt19937 RNGType; +typedef BoostRandomNumberGenerator RandomNumberGenerator; +typedef typename HPolytope::MT MT; +typedef typename HPolytope::VT VT; + +enum volumetype {CB ,CG ,SOB}; // Volume type for polytope +typedef typename std::vector Limit; // Standard way for user to use limits +// E.g. Limits LL{0.5, 1.5, 2.5} ; Limits UL{1.2, 1.8 , 2.8 } ; + +const Limit lt{0}; // To initialize non-initialized limits +const Point pt{0}; // To initialize non-initialized points + +// To check if two n-dimensional points ensure valid limits in integration +template +< + typename Point = Point, + typename NT = NT +> +bool valid_limits(Point LL, Point UL) { + if (UL.dimension() == LL.dimension()) { + for (int i = 0; i < LL.dimension(); i++) { + if (LL[i] > UL[i]) { + std::cerr << "Invalid integration limits\n"; + return false; + } + } + return true; + } else { + std::cerr << "Invalid integration limits\n"; + return false; + } +} + +// Initialize Limit Point +template +< + typename Point = Point, + typename NT = NT +> +Point init_limit(Limit L, int dim) { + Point pt(dim); + for (int i = 0; i < dim; i++) { + pt.set_coord(i, L[i]); + } + return pt; +} + +//Initialize to [-1,1]^n +template +< + typename Point = Point, + typename NT = NT +> +void initiate_unit_limits(Point& LL, Point& UL, int dim) { + LL.set_dimension(dim); + UL.set_dimension(dim); + LL.set_to_origin(); + UL.set_to_origin(); + + for (int i = 0 ; i < dim ; i++) { + LL.set_coord(i, -1); + UL.set_coord(i, 1); + } +} + +// Simple MC Integration Over Polytopes +template +< + typename WalkType = BallWalk, + typename Polytope = Hpolytope, + typename VolumeWalkType = BallWalk, + typename RNG = RandomNumberGenerator, + typename NT = NT, + typename Functor +> +NT simple_mc_polytope_integrate(Functor Fx, + Polytope &P, + int N = 10000, + volumetype voltype = SOB, + int walk_length = 1, + NT e = 0.1, + Point Origin = pt) +{ + + int dim = P.dimension(); + // P.print(); + + // Check if ShiftPoint is shifted with accurate dimensions + if (Origin.dimension() == 0 && dim > 0) { + Origin.set_dimension(dim); + Origin.set_to_origin(); + } else if (Origin.dimension() != dim && dim > 0) { + std::cerr << "Polytope Dimension != Shiftpoint Dimension" << std::endl; + return -1; + } + + // std::cout << "Origin.dimension() = " << Origin.dimension() << std::endl; + // std::cout << "P.dimension() = " << P.dimension() << std::endl; + + // Volume calculation for HPolytope + NT volume; + + switch (voltype) { + case CB: + volume = volume_cooling_balls (P, e, walk_length).second; + break; + case CG: + volume = volume_cooling_gaussians (P, e, walk_length); + break; + case SOB: + volume = volume_sequence_of_balls (P, e, walk_length); + break; + default: + std::cerr << "Error in volume type: CB / SOB / CG" << std::endl; + return -1; + } + + // std::cout << "Volume of the convex body = " << volume << std::endl; + + // For implementing Uniform Walks + RNG rng(1); + std::pair inner_ball = P.ComputeInnerBall(); + Point x0 = inner_ball.first; + typename WalkType::template Walk walk(P, x0, rng); + + NT sum = 0; + + // Applying and walking through Uniform Walks + Storing Points in Vector + for (int i = 0; i < N; i++) { + walk.apply(P, x0, walk_length, rng); + sum += Fx(x0 + Origin); + + // (x0 + Origin).print(); + } + + // Integration Value + NT integration_value = volume * sum / N ; + return integration_value; +} + +// Simple MC Integration over Hyper-Rectangles +template +< + typename WalkType = BallWalk, + typename RNG = RandomNumberGenerator, + typename NT = NT, + typename Functor +> +NT simple_mc_integrate (Functor Fx, + int dim, + int N = 10000, + volumetype voltype = SOB, + Limit LowLimit = lt, + Limit UpLimit = lt, + int walk_length = 10, + NT e = 0.1) +{ + + // Setting up integration limits + Point LL, UL; + if (LowLimit.size() == 1 && UpLimit.size() == 1 && LowLimit[0] == 0 && UpLimit[0] == 0) { + initiate_unit_limits(LL, UL, dim); + } else if (LowLimit.size() == UpLimit.size() && LowLimit.size() == dim) { + LL = init_limit (LowLimit, dim); + UL = init_limit (UpLimit, dim); + } else { + std::cerr << "Invalid limits entered"; + return -1; + } + + NT sum = 0; + + if (valid_limits(LL, UL)) { + + // Creating an MT & VT for HPolytope(Hyper-Rectangle) for integration limits using LL & UL + MT mt(dim*2, dim); + mt.setZero(); + VT vt(dim*2); + vt.setZero(); + + for (int i=0; i (Fx, P, N, voltype, walk_length, e); + return integration_value; + + } else { + std::cerr << "Invalid integration limits" << std::endl; + return -1; + } +} + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f4b5a0b22..2d5afb9e4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,7 +1,7 @@ # VolEsti (volume computation and sampling library) # Copyright (c) 2012-2020 Vissarion Fisikopoulos # Copyright (c) 2018-2020 Apostolos Chalkis -# Copyright (c) 2021- Vaibhav Thakkar +# Copyright (c) 2021 Vaibhav Thakkar # Contributed and/or modified by Vaibhav Thakkar # Licensed under GNU LGPL.3, see LICENCE file @@ -144,6 +144,7 @@ else () include_directories (BEFORE ../include/generators) include_directories (BEFORE ../include/volume) include_directories (BEFORE ../include) + include_directories (BEFORE ../include/integration) include_directories (BEFORE ../include/convex_bodies) include_directories (BEFORE ../include/convex_bodies/spectrahedra) include_directories (BEFORE ../include/annealing) @@ -264,13 +265,26 @@ else () add_test(NAME logconcave_sampling_test_uld COMMAND logconcave_sampling_test -tc=uld) + add_executable (simple_mc_integration simple_mc_integration.cpp $) + add_test(NAME simple_mc_integration_over_limits + COMMAND simple_mc_integration -tc=rectangle) + add_test(NAME simple_mc_integration_over_cubes + COMMAND simple_mc_integration -tc=cube) + add_test(NAME simple_mc_integration_over_simplices + COMMAND simple_mc_integration -tc=simplex) + add_test(NAME simple_mc_integration_over_product_simplices + COMMAND simple_mc_integration -tc=prod_simplex) + add_test(NAME simple_mc_integration_over_cross_polytopes + COMMAND simple_mc_integration -tc=cross) + add_test(NAME simple_mc_integration_over_birkhoff_polytopes + COMMAND simple_mc_integration -tc=birkhoff) + add_executable (order_polytope order_polytope.cpp $) add_test(NAME order_polytope_basics COMMAND order_polytope -tc=basics) add_test(NAME order_polytope_line_intersect COMMAND order_polytope -tc=line_intersect) add_test(NAME order_polytope_reflection COMMAND order_polytope -tc=reflection) add_test(NAME order_polytope_vec_mult COMMAND order_polytope -tc=vec_mult) - TARGET_LINK_LIBRARIES(new_volume_example ${LP_SOLVE}) TARGET_LINK_LIBRARIES(volume_sob_hpolytope ${LP_SOLVE}) TARGET_LINK_LIBRARIES(volume_sob_vpolytope ${LP_SOLVE}) @@ -285,6 +299,7 @@ else () TARGET_LINK_LIBRARIES(mcmc_diagnostics_test ${LP_SOLVE}) TARGET_LINK_LIBRARIES(benchmarks_sob ${LP_SOLVE}) TARGET_LINK_LIBRARIES(benchmarks_cg ${LP_SOLVE}) + TARGET_LINK_LIBRARIES(simple_mc_integration ${LP_SOLVE}) TARGET_LINK_LIBRARIES(benchmarks_cb ${LP_SOLVE}) TARGET_LINK_LIBRARIES(ode_solvers_test ${LP_SOLVE} ${IFOPT} ${IFOPT_IPOPT} ${PTHREAD} ${GMP} ${MPSOLVE} ${FFTW3}) TARGET_LINK_LIBRARIES(boundary_oracles_test ${LP_SOLVE} ${IFOPT} ${IFOPT_IPOPT} ${PTHREAD} ${GMP} ${MPSOLVE} ${FFTW3}) diff --git a/test/simple_mc_integration.cpp b/test/simple_mc_integration.cpp new file mode 100644 index 000000000..ef3e60ddd --- /dev/null +++ b/test/simple_mc_integration.cpp @@ -0,0 +1,312 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2021 Vissarion Fisikopoulos +// Copyright (c) 2018-2021 Apostolos Chalkis + +// Contributed and/or modified by Suraj Choubey, as part of Google Summer of Code 2021 program. +// Licensed under GNU LGPL.3, see LICENCE file + +// Testing of Integral over Polytope has been done using Latte-Integrale Software (latte-integrale-1.7.3b.tar.gz bundle) +// Link to the Latte-Integrale Software: https://www.math.ucdavis.edu/~latte/software.php +// Link to the tests: https://github.com/surajchoubey/latte-integrale-checks + +#include "doctest.h" +#include "simple_MC_integration.hpp" +#include "Eigen/Eigen" +#include +#include "cartesian_geom/cartesian_kernel.h" +#include "convex_bodies/hpolytope.h" +#include "generators/known_polytope_generators.h" +#include "ode_solvers/oracle_functors.hpp" +#include "random_walks/random_walks.hpp" +#include +#include +#include "misc.h" + +template +NT exp_normsq(Point X) { + return exp(-X.squared_length()) ; +} + +template +NT simple_polynomial_1D(Point X) { + return (X[0] - 1) * (X[0] - 2) * (X[0] - 3); +} + +template +NT logx_natural_1D(Point X) { + return log(X[0]); +} + +template +NT rooted_squaresum(Point X) { + return sqrt(X.squared_length()); +} + +template +NT one_sqsum(Point X) { + return 1 - X.squared_length(); +} + +template +void test_values (NT computed, NT expected, NT exact) { + std::cout << "Computed integration value = " << computed << std::endl; + std::cout << "Expected integration value = " << expected << std::endl; + std::cout << "Exact integration value = " << exact << std::endl; + std::cout << "Relative error (expected) = " << std::abs((computed - expected)/expected) << std::endl; + std::cout << "Relative error (exact) = " << std::abs((computed - exact)/exact) << std::endl ; + CHECK(((std::abs((computed - expected)/expected) < 0.00001) || (std::abs((computed - exact)/exact) < 0.2))); +} + +template +void call_test_simple_mc_integration_over_rectangles() { + + typedef Cartesian Kernel; + typedef typename Kernel::Point Point; + typedef HPolytope HPOLYTOPE; + typedef VPolytope VPOLYTOPE; + typedef boost::mt19937 RNGType; + typedef BoostRandomNumberGenerator RandomNumberGenerator; + + NT integration_value; + std::cout << "\nTESTS FOR SIMPLE MC INTEGRATION OVER RECTANGLES USING UNIFORM RANDOM WALKS\n"; + + Limit LL{-1}; + Limit UL{6}; + integration_value = simple_mc_integrate (simple_polynomial_1D, 1, 100000, CB, LL, UL); + test_values(integration_value, 39.7, 40.25); + + Limit LL1{0.5}; + Limit UL1{10}; + integration_value = simple_mc_integrate (logx_natural_1D, 1, 1000, CB, LL1, UL1); + test_values(integration_value, 13.65, 13.872); + + Limit LL2{-1, -1}; + Limit UL2{1, 1}; + integration_value = simple_mc_integrate (rooted_squaresum, 2, 1000, SOB, LL2, UL2); + test_values(integration_value, 2.99, 3.0607); + + integration_value = simple_mc_integrate (exp_normsq, 5, 1000, SOB); + test_values(integration_value, 7.49, 7.46); + + integration_value = simple_mc_integrate (exp_normsq, 8, 1000, SOB); + test_values(integration_value, 24.8, 24.76); + + integration_value = simple_mc_integrate (exp_normsq, 10, 10000, SOB); + test_values(integration_value, 54.8, 55.25); + +} + +template +void call_test_simple_mc_integration_over_cubes() { + + typedef Cartesian Kernel; + typedef typename Kernel::Point Point; + typedef HPolytope HPOLYTOPE; + typedef VPolytope VPOLYTOPE; + typedef boost::mt19937 RNGType; + typedef BoostRandomNumberGenerator RandomNumberGenerator; + + std::cout << "\nTESTS FOR SIMPLE MC INTEGRATION OVER CUBES USING UNIFORM RANDOM WALKS\n"; + + NT integration_value; + HPOLYTOPE HP; + + HP = generate_cube (2, false); + integration_value = simple_mc_polytope_integrate (exp_normsq, HP, 1000, SOB, 10, 0.01); + test_values(integration_value, 2.20, 2.230); + + // For 2D Polytope shifted to (1,1) from origin + std::vector Origin{1, 1}; + Point newOrigin(2, Origin); + integration_value = simple_mc_polytope_integrate (exp_normsq, HP, 1000, SOB, 1, 0.01, newOrigin); + test_values(integration_value, 0.78, 0.777); + + HP = generate_cube (10, false); + integration_value = simple_mc_polytope_integrate (exp_normsq, HP, 10000, SOB); + test_values(integration_value, 54.7, 55.25); + + HP = generate_cube (15, false); + integration_value = simple_mc_polytope_integrate (exp_normsq, HP, 10000, SOB); + test_values(integration_value, 405.9, 410.690); + + HP = generate_cube (20, false); + integration_value = simple_mc_polytope_integrate (exp_normsq, HP, 10000, SOB); + test_values(integration_value, 3050.0, 3052.71); + + // Reading a H-Polytope from *.ine file for 20 Dimensions + // std::string fileName("cube10.ine"); + // std::ifstream inp; + // std::vector> Pin; + // inp.open(fileName, std::ifstream::in); + // read_pointset(inp,Pin); + // HPOLYTOPE HP(Pin); + // integration_value = simple_mc_polytope_integrate (exp_normsq, 2, HP, 1000, SOB); + // std::cout << "Integration value: " << integration_value << std::endl; + // test_values(integration_value, expected, exact); + // inp.close(); +} + +template +void call_test_simple_mc_integration_over_simplices() { + + typedef Cartesian Kernel; + typedef typename Kernel::Point Point; + typedef HPolytope HPOLYTOPE; + typedef VPolytope VPOLYTOPE; + typedef boost::mt19937 RNGType; + typedef BoostRandomNumberGenerator RandomNumberGenerator; + + std::cout << "\nTESTS FOR SIMPLE MC INTEGRATION OVER SIMPLICES USING UNIFORM RANDOM WALKS\n"; + + NT integration_value; + HPOLYTOPE HP; + + HP = generate_simplex (1, false); + integration_value = simple_mc_polytope_integrate (one_sqsum, HP, 1000, CB, 10, 0.01); + test_values(integration_value, 0.67, 0.666); + + HP = generate_simplex (2, false); + integration_value = simple_mc_polytope_integrate (one_sqsum, HP, 1000, SOB, 10, 0.01); + test_values(integration_value, 0.34, 0.333); + + HP = generate_simplex (3, false); + integration_value = simple_mc_polytope_integrate (one_sqsum, HP, 1000, SOB); + test_values(integration_value, 0.116, 0.1166); + + HP = generate_simplex (5, false); + integration_value = simple_mc_polytope_integrate (one_sqsum, HP, 1000, SOB); + test_values(integration_value, 0.00656, 0.0063492); + + HP = generate_simplex (7, false); + integration_value = simple_mc_polytope_integrate (one_sqsum, HP, 10000, SOB); + test_values(integration_value, 0.000159, 0.000159832); + +} + +template +void call_test_simple_mc_integration_over_product_simplices() { + + typedef Cartesian Kernel; + typedef typename Kernel::Point Point; + typedef HPolytope HPOLYTOPE; + typedef VPolytope VPOLYTOPE; + typedef boost::mt19937 RNGType; + typedef BoostRandomNumberGenerator RandomNumberGenerator; + + std::cout << "\nTESTS FOR SIMPLE MC INTEGRATION OVER PRODUCT SIMPLICES USING UNIFORM RANDOM WALKS\n"; + + NT integration_value; + HPOLYTOPE HP; + + HP = generate_prod_simplex (1, false); + integration_value = simple_mc_polytope_integrate (one_sqsum, HP, 1000, CB, 10, 0.01); + test_values(integration_value, 0.334, 0.333); + + HP = generate_prod_simplex (2, false); + integration_value = simple_mc_polytope_integrate (one_sqsum, HP, 1000, SOB); + test_values(integration_value, 0.0834, 0.0833); + + HP = generate_prod_simplex (3, false); + integration_value = simple_mc_polytope_integrate (one_sqsum, HP, 1000, SOB); + test_values(integration_value, 0.0110, 0.01111); + + HP = generate_prod_simplex (5, false); + integration_value = simple_mc_polytope_integrate (one_sqsum, HP, 10000, SOB); + test_values(integration_value, 0.36e-4, 0.36375e-4); + + HP = generate_prod_simplex (7, false); + integration_value = simple_mc_polytope_integrate (one_sqsum, HP, 10000, SOB); + test_values(integration_value, 0.235e-7, 0.24079e-7); + +} + +template +void call_test_simple_mc_integration_over_cross_polytopes() { + + typedef Cartesian Kernel; + typedef typename Kernel::Point Point; + typedef HPolytope HPOLYTOPE; + typedef VPolytope VPOLYTOPE; + typedef boost::mt19937 RNGType; + typedef BoostRandomNumberGenerator RandomNumberGenerator; + + std::cout << "\nTESTS FOR SIMPLE MC INTEGRATION OVER CROSS POLYTOPES USING UNIFORM RANDOM WALKS\n"; + + NT integration_value; + HPOLYTOPE HP; + + HP = generate_cross (1, false); + integration_value = simple_mc_polytope_integrate (one_sqsum, HP, 1000, CB, 10, 0.01); + test_values(integration_value, 1.334, 1.333333); + + HP = generate_cross (2, false); + integration_value = simple_mc_polytope_integrate (one_sqsum, HP, 1000, SOB, 10, 0.01); + test_values(integration_value, 1.334, 1.33333); + + HP = generate_cross (3, false); + integration_value = simple_mc_polytope_integrate (one_sqsum, HP, 1000, SOB); + test_values(integration_value, 0.935000, 0.933333); + + HP = generate_cross (5, false); + integration_value = simple_mc_polytope_integrate (one_sqsum, HP, 1000, SOB); + test_values(integration_value, 0.200000, 0.203174); + + HP = generate_cross (7, false); + integration_value = simple_mc_polytope_integrate (one_sqsum, HP, 10000, SOB); + test_values(integration_value, 0.020000, 0.020458); + +} + +template +void call_test_simple_mc_integration_over_birkhoff_polytopes() { + + typedef Cartesian Kernel; + typedef typename Kernel::Point Point; + typedef HPolytope HPOLYTOPE; + typedef VPolytope VPOLYTOPE; + typedef boost::mt19937 RNGType; + typedef BoostRandomNumberGenerator RandomNumberGenerator; + + std::cout << "\nTESTS FOR SIMPLE MC INTEGRATION OVER BIRKHOFF POLYTOPES USING UNIFORM RANDOM WALKS\n"; + + NT integration_value; + HPOLYTOPE HP; + + HP = generate_birkhoff (2); + integration_value = simple_mc_polytope_integrate (one_sqsum, HP, 1000, CB, 10, 0.01); + test_values(integration_value, 0.67, 0.6666); + + HP = generate_birkhoff (3); + integration_value = simple_mc_polytope_integrate (one_sqsum, HP, 1000, SOB); + test_values(integration_value, 0.0470, 0.04722); + + HP = generate_birkhoff (4); + integration_value = simple_mc_polytope_integrate (one_sqsum, HP, 10000, SOB); + test_values(integration_value, 0.000150, 0.000164); + +} + +TEST_CASE("rectangle") { + call_test_simple_mc_integration_over_rectangles(); +} + +TEST_CASE("cube") { + call_test_simple_mc_integration_over_cubes(); +} + +TEST_CASE("simplex") { + call_test_simple_mc_integration_over_simplices(); +} + +TEST_CASE("prod_simplex") { + call_test_simple_mc_integration_over_product_simplices(); +} + +TEST_CASE("cross") { + call_test_simple_mc_integration_over_cross_polytopes(); +} + +TEST_CASE("birkhoff") { + call_test_simple_mc_integration_over_birkhoff_polytopes(); +}