forked from GeomScale/volesti
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathuserDefinedFunction_manual_gradient.cpp
155 lines (127 loc) · 4.96 KB
/
userDefinedFunction_manual_gradient.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// Use manual gradient function in existing sampling method
// sampling from log-density equal to f(x) = x^4 -2 * x^2;
// VolEsti (volume computation and sampling library)
// Copyright (c) 2012-2020 Vissarion Fisikopoulos
// Copyright (c) 2018-2020 Apostolos Chalkis
// Copyright (c) 2020-2020 Marios Papachristou
// Copyright (c) 2022-2022 Zhang zhuyan
// Licensed under GNU LGPL.3, see LICENCE file
#include <iostream>
#include <cmath>
#include <functional>
#include <vector>
#include <unistd.h>
#include <string>
#include <typeinfo>
#include "Eigen/Eigen"
#include "ode_solvers/ode_solvers.hpp"
#include "ode_solvers/oracle_autodiff_functors.hpp"
#include "random.hpp"
#include "random/uniform_int.hpp"
#include "random/normal_distribution.hpp"
#include "random/uniform_real_distribution.hpp"
#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 "generators/known_polytope_generators.h"
#include "diagnostics/diagnostics.hpp"
struct CustomFunctor {
template <
typename NT>
struct parameters {
unsigned int order;
NT L; // Lipschitz constant for gradient
NT m; // Strong convexity constant
NT kappa; // Condition number
parameters() : order(2), L(4), m(4), kappa(1){};
};
template <
typename Point>
struct GradientFunctor {
typedef typename Point::FT NT;
typedef std::vector<Point> pts;
parameters<NT> ¶ms;
GradientFunctor(parameters<NT> ¶ms_) : params(params_){};
// The index i represents the state vector index
Point operator()(unsigned int const &i, pts const &xs, NT const &t) const {
if (i == params.order - 1) {
auto temp = xs[0].getCoefficients().array();
auto result = -4 * temp.pow(3) + 4 * temp;
Point y(result);
return y;
}
else {
return xs[i + 1]; // returns derivative
}
}
};
template <
typename Point>
struct FunctionFunctor {
typedef typename Point::FT NT;
parameters<NT> ¶ms;
FunctionFunctor(parameters<NT> ¶ms_) : params(params_){};
// The index i represents the state vector index
NT operator()(Point const &x) const {
auto temp = x.getCoefficients().array();
NT y = temp.pow(4).sum() - 2 * temp.pow(2).sum() + 2 * 0.795;
return y;
}
};
};
template <typename NT>
void run_main() {
typedef Cartesian<NT> Kernel;
typedef typename Kernel::Point Point;
typedef std::vector<Point> pts;
typedef HPolytope<Point> Hpolytope;
typedef BoostRandomNumberGenerator<boost::mt19937, NT> RandomNumberGenerator;
typedef CustomFunctor::GradientFunctor<Point> NegativeGradientFunctor;
typedef CustomFunctor::FunctionFunctor<Point> NegativeLogprobFunctor;
typedef LeapfrogODESolver<Point, NT, Hpolytope, NegativeGradientFunctor> Solver;
typedef typename Hpolytope::MT MT;
typedef typename Hpolytope::VT VT;
CustomFunctor::parameters<NT> params;
NegativeGradientFunctor F(params);
NegativeLogprobFunctor f(params);
RandomNumberGenerator rng(1);
unsigned int dim = 2;
HamiltonianMonteCarloWalk::parameters<NT, NegativeGradientFunctor> hmc_params(F, dim);
Hpolytope P = generate_cube<Hpolytope>(dim, false);
Point x0 = -0.25 * Point::all_ones(dim);
// In the first argument put in the address of an H-Polytope
// for truncated sampling and NULL for untruncated
HamiltonianMonteCarloWalk::Walk<Point, Hpolytope, RandomNumberGenerator, NegativeGradientFunctor, NegativeLogprobFunctor, Solver>
hmc(&P, x0, F, f, hmc_params);
std::chrono::time_point<std::chrono::high_resolution_clock> start, stop;
int n_samples = 50000; // Half will be burned
int max_actual_draws = n_samples / 2;
unsigned int min_ess = 0;
MT samples;
samples.resize(dim, max_actual_draws);
for (int i = 0; i < n_samples - max_actual_draws; i++) {
hmc.apply(rng, 3);
}
start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < max_actual_draws; i++) {
std::cout << hmc.x.getCoefficients().transpose() << std::endl;
hmc.apply(rng, 3);
samples.col(i) = hmc.x.getCoefficients();
}
stop = std::chrono::high_resolution_clock::now();
long ETA = (long)std::chrono::duration_cast<std::chrono::microseconds>(stop - start).count();
std::cerr << "total time taken " << ETA << std::endl;
std::cerr << std::endl;
print_diagnostics<NT, VT, MT>(samples, min_ess, std::cerr);
std::cerr << "min ess " << min_ess << "us" << std::endl;
std::cerr << "Average time per sample: " << ETA / max_actual_draws << "us" << std::endl;
std::cerr << "Average time per independent sample: " << ETA / min_ess << "us" << std::endl;
std::cerr << "Average number of reflections: " << (1.0 * hmc.solver->num_reflections) / hmc.solver->num_steps << std::endl;
std::cerr << "Step size (final): " << hmc.solver->eta << std::endl;
std::cerr << "PSRF: " << multivariate_psrf<NT, VT, MT>(samples) << std::endl;
}
int main() {
run_main<double>();
return 0;
}