Skip to content

Commit 7c3af8d

Browse files
author
Stefan Haan
committed
plot true irf vs estimated irf, wrap lp_solve C API
1 parent e657f1e commit 7c3af8d

File tree

4 files changed

+130
-48
lines changed

4 files changed

+130
-48
lines changed

R/RcppExports.R

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ irf_cpp <- function(coefficients, factor_loadings, U_vecs, logvar_t, shocks, ahe
3535
.Call(`_bayesianVARs_irf_cpp`, coefficients, factor_loadings, U_vecs, logvar_t, shocks, ahead, rotation_)
3636
}
3737

38+
irf_from_true_parameters <- function(true_structural_matrix, true_reduced_coeff, ahead) {
39+
.Call(`_bayesianVARs_irf_from_true_parameters`, true_structural_matrix, true_reduced_coeff, ahead)
40+
}
41+
3842
sample_PHI_cholesky <- function(PHI, PHI_prior, Y, X, U, d_sqrt, V_prior) {
3943
.Call(`_bayesianVARs_sample_PHI_cholesky`, PHI, PHI_prior, Y, X, U, d_sqrt, V_prior)
4044
}

R/plots.R

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -625,7 +625,13 @@ plot.bayesianVARs_predict <- function(x, dates = NULL, vars = "all", ahead = NUL
625625
}
626626

627627
#' @export
628-
plot.bayesianVARs_irf <- function(x, vars = "all", quantiles = c(0.05,0.25,0.5,0.75,0.95), ...){
628+
plot.bayesianVARs_irf <- function(
629+
x,
630+
vars = "all",
631+
quantiles = c(0.05,0.25,0.5,0.75,0.95),
632+
true_irf = NULL,
633+
...
634+
) {
629635
n_ahead <- dim(x)[3]
630636
n_shocks <- ncol(x)
631637
var_names <- rownames(x)
@@ -658,11 +664,14 @@ plot.bayesianVARs_irf <- function(x, vars = "all", quantiles = c(0.05,0.25,0.5,0
658664

659665
oldpar <- par(no.readonly = TRUE)
660666
on.exit(par(oldpar), add = TRUE)
661-
par(mfrow=c(length(vars), n_shocks), mar = c(2,2,2,1), mgp = c(2,.5,0))
667+
par(mfrow=c(length(vars), n_shocks), mar=c(2,2,2,1), mgp=c(2,.5,0))
662668
for(j in seq_along(vars)){
663669
for(i in seq_len(n_shocks)) {
664-
plot(t, rep(0, n_ahead), type = "n", xlab="", ylab = "",
665-
xaxt="n", ylim = range(pred_quants[,vars[j],i,]))
670+
ylim <- range(pred_quants[,vars[j],i,])
671+
if (!is.null(true_irf)) {
672+
ylim <- range(ylim, true_irf[j,i,])
673+
}
674+
plot(t, rep(0, n_ahead), type="n", xlab="", ylab="", xaxt="n", ylim=ylim)
666675
abline(h=0, lty=2)
667676
axis(side=1, at = t, labels = dates[t])
668677
mtext(var_names[j], side = 3)
@@ -692,6 +701,9 @@ plot.bayesianVARs_irf <- function(x, vars = "all", quantiles = c(0.05,0.25,0.5,0
692701
col = "red", lwd = 2)
693702
}
694703
}
704+
if (!is.null(true_irf)) {
705+
lines(t, true_irf[j,i,], col="black", lwd=2, lty=6)
706+
}
695707
}
696708
}
697709

src/RcppExports.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,19 @@ BEGIN_RCPP
104104
return rcpp_result_gen;
105105
END_RCPP
106106
}
107+
// irf_from_true_parameters
108+
arma::cube irf_from_true_parameters(arma::mat true_structural_matrix, arma::mat true_reduced_coeff, arma::uword ahead);
109+
RcppExport SEXP _bayesianVARs_irf_from_true_parameters(SEXP true_structural_matrixSEXP, SEXP true_reduced_coeffSEXP, SEXP aheadSEXP) {
110+
BEGIN_RCPP
111+
Rcpp::RObject rcpp_result_gen;
112+
Rcpp::RNGScope rcpp_rngScope_gen;
113+
Rcpp::traits::input_parameter< arma::mat >::type true_structural_matrix(true_structural_matrixSEXP);
114+
Rcpp::traits::input_parameter< arma::mat >::type true_reduced_coeff(true_reduced_coeffSEXP);
115+
Rcpp::traits::input_parameter< arma::uword >::type ahead(aheadSEXP);
116+
rcpp_result_gen = Rcpp::wrap(irf_from_true_parameters(true_structural_matrix, true_reduced_coeff, ahead));
117+
return rcpp_result_gen;
118+
END_RCPP
119+
}
107120
// sample_PHI_cholesky
108121
arma::mat sample_PHI_cholesky(const arma::mat PHI, const arma::mat& PHI_prior, const arma::mat& Y, const arma::mat& X, const arma::mat& U, const arma::mat& d_sqrt, const arma::mat& V_prior);
109122
RcppExport SEXP _bayesianVARs_sample_PHI_cholesky(SEXP PHISEXP, SEXP PHI_priorSEXP, SEXP YSEXP, SEXP XSEXP, SEXP USEXP, SEXP d_sqrtSEXP, SEXP V_priorSEXP) {
@@ -218,6 +231,7 @@ static const R_CallMethodDef CallEntries[] = {
218231
{"_bayesianVARs_compute_parameter_transformations", (DL_FUNC) &_bayesianVARs_compute_parameter_transformations, 9},
219232
{"_bayesianVARs_find_rotation_cpp", (DL_FUNC) &_bayesianVARs_find_rotation_cpp, 2},
220233
{"_bayesianVARs_irf_cpp", (DL_FUNC) &_bayesianVARs_irf_cpp, 7},
234+
{"_bayesianVARs_irf_from_true_parameters", (DL_FUNC) &_bayesianVARs_irf_from_true_parameters, 3},
221235
{"_bayesianVARs_sample_PHI_cholesky", (DL_FUNC) &_bayesianVARs_sample_PHI_cholesky, 7},
222236
{"_bayesianVARs_dmvnrm_arma_fast", (DL_FUNC) &_bayesianVARs_dmvnrm_arma_fast, 4},
223237
{"_bayesianVARs_predh", (DL_FUNC) &_bayesianVARs_predh, 6},

src/irf.cpp

Lines changed: 96 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -136,28 +136,78 @@ mat construct_sign_restriction (const NumericMatrix::ConstColumn& spec) {
136136
}
137137

138138

139-
using make_lp_func_ptr= lprec*(*)(int, int);
140-
template<typename R, typename... T> using lp_func_ptr = R(*)(lprec*, T...);
139+
class Solver {
140+
using make_lp_func_ptr= lprec*(*)(int, int);
141+
template<typename R, typename... T> using lp_func_ptr = R(*)(lprec*, T...);
142+
143+
private:
144+
//import routines from the R package "lpSolveAPI"
145+
make_lp_func_ptr make_lp = (make_lp_func_ptr)R_GetCCallable("lpSolveAPI", "make_lp");
146+
lp_func_ptr<void, int> set_verbose = (lp_func_ptr<void, int>)R_GetCCallable("lpSolveAPI", "set_verbose");
147+
lp_func_ptr<void> set_maxim = (lp_func_ptr<void>)R_GetCCallable("lpSolveAPI", "set_maxim");
148+
lp_func_ptr<bool, int, double> set_obj = (lp_func_ptr<bool, int, double>)R_GetCCallable("lpSolveAPI", "set_obj");
149+
lp_func_ptr<bool, int, double, double> set_bounds = (lp_func_ptr<bool, int, double, double>)R_GetCCallable("lpSolveAPI", "set_bounds");
150+
lp_func_ptr<bool, bool> set_add_rowmode = (lp_func_ptr<bool, bool>)R_GetCCallable("lpSolveAPI", "set_add_rowmode");
151+
lp_func_ptr<bool, int, double*, int*, int, double> add_constraintex = (lp_func_ptr<bool, int, double*, int*, int, double>)R_GetCCallable("lpSolveAPI", "add_constraintex");
152+
lp_func_ptr<int> lp_solve = (lp_func_ptr<int>)R_GetCCallable("lpSolveAPI", "solve");
153+
lp_func_ptr<bool, double*> get_variables = (lp_func_ptr<bool, double*>)R_GetCCallable("lpSolveAPI", "get_variables");
154+
lp_func_ptr<char*, int> get_statustext = (lp_func_ptr<char*, int>)R_GetCCallable("lpSolveAPI", "get_statustext");
155+
lp_func_ptr<void> print_lp = (lp_func_ptr<void>)R_GetCCallable("lpSolveAPI", "print_lp");
156+
lp_func_ptr<void> delete_lp = (lp_func_ptr<void>)R_GetCCallable("lpSolveAPI", "delete_lp");
157+
158+
lprec *lp;
159+
uword n_variables;
160+
ivec x_cols; // the array 1...n_variables
161+
162+
public:
163+
Solver(const uword n_variables_) : n_variables(n_variables_) {
164+
lp = (*make_lp)(0, n_variables);
165+
if (lp == NULL) throw std::bad_alloc();
166+
x_cols = regspace<ivec>(1, n_variables);
167+
(*set_verbose)(lp, IMPORTANT);
168+
(*set_maxim)(lp);
169+
(*set_add_rowmode)(lp, TRUE);
170+
// try to avoid having the zero vector as the optimum
171+
for (uword jj = 0; jj < n_variables; jj++) {
172+
(*set_obj)(lp, jj+1, 1.0);
173+
(*set_bounds)(lp, jj+1, -1, 1);
174+
}
175+
}
176+
177+
void add_constraint(double* x_coeff, int constr_type, double rhs) {
178+
const bool success = (*add_constraintex)(lp, n_variables, x_coeff, x_cols.memptr(), constr_type, rhs);
179+
if (success == FALSE) {
180+
throw std::runtime_error("Could not add constraint");
181+
}
182+
}
183+
184+
vec solve() {
185+
(*set_add_rowmode)(lp, FALSE);
186+
int ret = (*lp_solve)(lp);
187+
if(ret != 0) {
188+
throw std::logic_error((*get_statustext)(lp, ret));
189+
};
190+
vec optimal_x(n_variables);
191+
(*get_variables)(lp, optimal_x.memptr());
192+
return optimal_x;
193+
}
194+
195+
void print() {
196+
(*set_add_rowmode)(lp, FALSE);
197+
(*print_lp)(lp);
198+
}
199+
200+
~Solver() {
201+
(*delete_lp)(lp);
202+
}
203+
204+
};
141205

142206
// [[Rcpp::export]]
143207
arma::cube find_rotation_cpp(
144208
const arma::field<arma::cube>& parameter_transformations, //each field element: rows: transformation size, cols: variables, slices: draws
145209
const arma::field<Rcpp::NumericMatrix>& restriction_specs //each field element: rows: transformation size, cols: variables
146210
) {
147-
//import routines from the R package "lpSolveAPI"
148-
auto make_lp = (make_lp_func_ptr)R_GetCCallable("lpSolveAPI", "make_lp");
149-
auto set_verbose = (lp_func_ptr<void, int>)R_GetCCallable("lpSolveAPI", "set_verbose");
150-
auto set_maxim = (lp_func_ptr<void>)R_GetCCallable("lpSolveAPI", "set_maxim");
151-
auto set_obj = (lp_func_ptr<bool, int, double>)R_GetCCallable("lpSolveAPI", "set_obj");
152-
auto set_bounds = (lp_func_ptr<bool, int, double, double>)R_GetCCallable("lpSolveAPI", "set_bounds");
153-
auto set_add_rowmode = (lp_func_ptr<bool, bool>)R_GetCCallable("lpSolveAPI", "set_add_rowmode");
154-
auto add_constraintex = (lp_func_ptr<bool, int, double*, int*, int, double>)R_GetCCallable("lpSolveAPI", "add_constraintex");
155-
auto solve = (lp_func_ptr<int>)R_GetCCallable("lpSolveAPI", "solve");
156-
auto get_variables = (lp_func_ptr<bool, double*>)R_GetCCallable("lpSolveAPI", "get_variables");
157-
auto get_statustext = (lp_func_ptr<char*, int>)R_GetCCallable("lpSolveAPI", "get_statustext");
158-
auto print_lp = (lp_func_ptr<void>)R_GetCCallable("lpSolveAPI", "print_lp");
159-
auto delete_lp = (lp_func_ptr<void>)R_GetCCallable("lpSolveAPI", "delete_lp");
160-
161211
//algorithm from RUBIO-RAMÍREZ ET AL. (doi: 10.1111/j.1467-937X.2009.00578.x)
162212
if (restriction_specs.n_elem != parameter_transformations.n_elem) {
163213
throw std::logic_error("Number of restrictions does not match number of parameter transformations.");
@@ -184,7 +234,6 @@ arma::cube find_rotation_cpp(
184234
}
185235

186236
uvec col_order = sort_index(2 * n_zero_restrictions + n_sign_restrictions, "descend");
187-
ivec p_cols = regspace<ivec>(1, n_variables); // the array 1...n_variables
188237

189238
for (uword r = 0; r < n_posterior_draws; r++) {
190239
for (uword j_index = 0; j_index < n_variables; j_index++) {
@@ -193,30 +242,19 @@ arma::cube find_rotation_cpp(
193242
//we start with the column with the most restrictions
194243
const uword j = col_order[j_index];
195244

196-
lprec *lp = (*make_lp)(0, n_variables);
197-
if (lp == NULL) throw std::bad_alloc();
198-
199-
(*set_verbose)(lp, IMPORTANT);
200-
set_maxim(lp);
201-
202-
// try to avoid having the zero vector as the optimum
203-
for (uword jj = 0; jj < n_variables; jj++) {
204-
(*set_obj)(lp, jj+1, 1.0);
205-
(*set_bounds)(lp, jj+1, -1, 1);
206-
}
207-
(*set_add_rowmode)(lp, TRUE);
245+
Solver solver(n_variables);
208246

209247
// the column j of the rotation matrix must be orthogonal to columns that came before
210248
for (uword j_index_other = 0; j_index_other < j_index; j_index_other++) {
211249
const uword j_other = col_order[j_index_other];
212-
(*add_constraintex)(lp, n_variables, rotation.slice(r).colptr(j_other), p_cols.memptr(), EQ, 0);
250+
solver.add_constraint(rotation.slice(r).colptr(j_other), EQ, 0);
213251
}
214252

215253
// add zero restrictions
216254
for (uword i = 0; i < parameter_transformations.n_elem; i++) {
217255
mat zero_constraints = zero_restrictions(i, j) * parameter_transformations(i).slice(r);
218256
zero_constraints.each_row([&](rowvec& row) {
219-
(*add_constraintex)(lp, n_variables, row.memptr(), p_cols.memptr(), EQ, 0);
257+
solver.add_constraint(row.memptr(), EQ, 0);
220258
});
221259
}
222260

@@ -225,29 +263,21 @@ arma::cube find_rotation_cpp(
225263
for (uword i = 0; i < parameter_transformations.n_elem; i++) {
226264
mat sign_constraints = sign_restrictions(i, j) * parameter_transformations(i).slice(r);
227265
sign_constraints.each_row([&](rowvec& row){
228-
(*add_constraintex)(lp, n_variables, row.memptr(), p_cols.memptr(), GE, 0);
266+
solver.add_constraint(row.memptr(), GE, 0);
229267
});
230268
}
231269
}
232270

233-
(*set_add_rowmode)(lp, FALSE);
234-
int ret = (*solve)(lp);
235-
if(ret != 0) {
236-
(*delete_lp)(lp);
237-
throw std::logic_error((*get_statustext)(lp, ret));
238-
};
239-
vec p_j(n_variables);
240-
(*get_variables)(lp, p_j.memptr());
271+
const vec p_j = solver.solve();
241272
if (p_j.is_zero(1e-6)) {
273+
//zero was the optimal solution
242274
Rcerr << "Cannot satisfy restrictions for posterior sample: #" << r
243275
<< ", column:" << j+1
244276
<< " (" << j_index+1 << "-th in order of rank)" << endl;
245-
(*print_lp)(lp);
277+
solver.print();
246278
throw std::logic_error("Could not satisfy restrictions");
247279
}
248-
p_j = normalise(p_j);
249-
rotation.slice(r).col(j) = p_j;
250-
(*delete_lp)(lp); //TODO: maybe we can recycle lp efficiently
280+
rotation.slice(r).col(j) = normalise(p_j);
251281
}
252282
}
253283
return rotation;
@@ -317,3 +347,25 @@ arma::field<arma::cube> irf_cpp(
317347

318348
return ret;
319349
}
350+
351+
// [[Rcpp::export]]
352+
arma::cube irf_from_true_parameters(
353+
arma::mat true_structural_matrix,
354+
arma::mat true_reduced_coeff,
355+
arma::uword ahead
356+
) {
357+
const uword n_variables = true_structural_matrix.n_rows;
358+
const uword n_shocks = true_structural_matrix.n_cols;
359+
360+
cube irf(n_variables, n_shocks, ahead+1);
361+
irf.slice(0) = inv(true_structural_matrix).t();
362+
363+
mat current_predictors(n_shocks, true_reduced_coeff.n_rows, fill::zeros);
364+
current_predictors.head_cols(n_variables) = irf.slice(0).t();
365+
for (uword t = 1; t <= ahead; t++) {
366+
mat new_predictiors = current_predictors * true_reduced_coeff;
367+
irf.slice(t) = new_predictiors.t();
368+
shift_and_insert(current_predictors, new_predictiors);
369+
}
370+
return irf;
371+
}

0 commit comments

Comments
 (0)