Conditional simulation

Drawing from the conditional distribution of some variables given others is a question of the sampling order: a vine can be sampled in sequence along its order, so the variables you want to condition on must sit at the tail of that order. Vinecop therefore offers two ways to arrange for that — asking the fit for it, or relabeling an existing model.

Fitting with a conditioning set

FitControlsVinecop::set_conditioning_set() constrains structure selection so that the given variables end up last in the order. Indices are 1-based, matching the vine order.

  size_t d = 5;
  auto data = tools_stats::simulate_uniform(500, d, false, { 1 });

  // Ask the selection to end up with variables 3 and 4 last in the order, so
  // that they can be conditioned on afterwards. Indices are 1-based, matching
  // the vine order.
  FitControlsVinecop controls;
  controls.set_conditioning_set({ 3, 4 });
  Vinecop model(data, RVineStructure(), {}, controls);

  const auto order = model.get_order();
  std::cout << "order: ";
  for (auto v : order)
    std::cout << v << " ";
  std::cout << "(the conditioning set is the tail)" << std::endl;

Simulating conditionally

simulate_conditional(u_cond, ...) draws the remaining variables given the conditioning values. By default, the conditioning variables are the last k entries of the vine order. The overload simulate_conditional(u_cond, conditioning_set, ...) accepts any conditioning set that is admissible as a sampling-order tail and evaluates the model in that orientation without modifying it. Indices are 1-based, and the first k columns of u_cond correspond to conditioning_set in the supplied order. u_cond has one row per output sample; to draw many samples at a single conditioning point, repeat that point over the rows.

For all-continuous conditioning variables, u_cond is an $ n \times k $ matrix. If any are discrete, the expanded $ n \times 2k $ layout contains their values followed by all left-limits, as described in the discrete-data documentation. With an explicit conditioning_set, the compact $ n \times (k + k_d) $ layout is also accepted, where k_d is the number of discrete conditioning variables. The overload without conditioning_set infers k from the column count and uses the compact layout.

The result is an $ n \times d $ matrix in which the conditioning columns reproduce the first k columns of u_cond and the remaining ones are draws from the conditional distribution. Discrete conditioning variables are supported and need one additional left-limit column each — see Discrete variables, and the method's own documentation for the exact column layout.

  Vinecop model(DVineStructure({ 1, 2, 3, 4 }));
  std::vector<size_t> conditioning_set{ 1, 2 };

  // One conditioning point per output row; columns follow conditioning_set.
  size_t n = 100;
  Eigen::MatrixXd u_cond(n, 2);
  u_cond.col(0).setConstant(0.3);
  u_cond.col(1).setConstant(0.8);

  // n x d: the conditioning columns reproduce u_cond, the rest are draws from
  // the conditional distribution.
  Eigen::MatrixXd sim =
    model.simulate_conditional(u_cond, conditioning_set, false, 1, { 7 });
  std::cout << "conditional draws: " << sim.rows() << " x " << sim.cols()
            << std::endl;

Re-orienting an existing model

reorient(conditioning_set) relabels an already-fitted vine to an equivalent one whose order ends in conditioning_set. It is value-preserving: the density and the log-likelihood are invariant, only the sampling-order representation moves.

Not every set can be reached this way — only those admissible as a sampling-order tail of the current structure — and reorient() throws when the set is not. If you know in advance which variables you want to condition on, fit with set_conditioning_set() instead, which guarantees one exists.

Truncated models are relabeled too: only the trees the model stores take part, and the truncation level is preserved. A model truncated at zero is independence, so any order represents it and the conditioning variables are simply moved to the tail.

  auto data = tools_stats::simulate_uniform(500, 5, false, { 3 });
  Vinecop model(data);

  const double loglik_before = model.loglik(data);

  // Relabel an already-fitted vine so that a given pair comes last and can be
  // conditioned on. Only sets admissible as a sampling-order tail of this vine
  // can be reached; swapping the two variables already at the tail always is.
  // To guarantee a particular set, fit with
  // FitControlsVinecop::set_conditioning_set() instead.
  const auto order = model.get_order();
  const size_t d = order.size();
  model.reorient({ order[d - 1], order[d - 2] });

  // The model itself is unchanged: only its sampling-order representation
  // moves, so the density and the log-likelihood are invariant.
  std::cout << "loglik before: " << loglik_before
            << ", after: " << model.loglik(data) << std::endl;

  // An arbitrary set generally is not admissible.
  try {
    model.reorient({ 1, 2 });
  } catch (const std::runtime_error& e) {
    std::cout << "not admissible: " << e.what() << std::endl;
  }