@@ -136,28 +136,78 @@ mat construct_sign_restriction (const NumericMatrix::ConstColumn& spec) {
136
136
}
137
137
138
138
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
+ };
141
205
142
206
// [[Rcpp::export]]
143
207
arma::cube find_rotation_cpp (
144
208
const arma::field<arma::cube>& parameter_transformations, // each field element: rows: transformation size, cols: variables, slices: draws
145
209
const arma::field<Rcpp::NumericMatrix>& restriction_specs // each field element: rows: transformation size, cols: variables
146
210
) {
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
-
161
211
// algorithm from RUBIO-RAMÍREZ ET AL. (doi: 10.1111/j.1467-937X.2009.00578.x)
162
212
if (restriction_specs.n_elem != parameter_transformations.n_elem ) {
163
213
throw std::logic_error (" Number of restrictions does not match number of parameter transformations." );
@@ -184,7 +234,6 @@ arma::cube find_rotation_cpp(
184
234
}
185
235
186
236
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
188
237
189
238
for (uword r = 0 ; r < n_posterior_draws; r++) {
190
239
for (uword j_index = 0 ; j_index < n_variables; j_index++) {
@@ -193,30 +242,19 @@ arma::cube find_rotation_cpp(
193
242
// we start with the column with the most restrictions
194
243
const uword j = col_order[j_index];
195
244
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);
208
246
209
247
// the column j of the rotation matrix must be orthogonal to columns that came before
210
248
for (uword j_index_other = 0 ; j_index_other < j_index; j_index_other++) {
211
249
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 );
213
251
}
214
252
215
253
// add zero restrictions
216
254
for (uword i = 0 ; i < parameter_transformations.n_elem ; i++) {
217
255
mat zero_constraints = zero_restrictions (i, j) * parameter_transformations (i).slice (r);
218
256
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 );
220
258
});
221
259
}
222
260
@@ -225,29 +263,21 @@ arma::cube find_rotation_cpp(
225
263
for (uword i = 0 ; i < parameter_transformations.n_elem ; i++) {
226
264
mat sign_constraints = sign_restrictions (i, j) * parameter_transformations (i).slice (r);
227
265
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 );
229
267
});
230
268
}
231
269
}
232
270
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 ();
241
272
if (p_j.is_zero (1e-6 )) {
273
+ // zero was the optimal solution
242
274
Rcerr << " Cannot satisfy restrictions for posterior sample: #" << r
243
275
<< " , column:" << j+1
244
276
<< " (" << j_index+1 << " -th in order of rank)" << endl;
245
- (*print_lp)(lp );
277
+ solver. print ( );
246
278
throw std::logic_error (" Could not satisfy restrictions" );
247
279
}
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);
251
281
}
252
282
}
253
283
return rotation;
@@ -317,3 +347,25 @@ arma::field<arma::cube> irf_cpp(
317
347
318
348
return ret;
319
349
}
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