6void validate_commercial_license() {
14 std::size_t num_threads,
15 std::size_t chunk_size)
16 : jt_(jt), num_threads_(num_threads),
17 chunk_size_(std::max<std::size_t>(1, chunk_size)) {
25 std::size_t batch_size,
26 std::size_t num_vars,
double *output_data,
27 std::size_t query_var) {
28 const std::size_t num_states =
30 const std::size_t query_vars[1] = {query_var};
31 const std::size_t offsets[2] = {0, num_states};
32 evaluate_multi(evidence_data, batch_size, num_vars, query_vars, 1, offsets,
37 std::size_t batch_size,
39 const std::size_t *query_vars,
40 std::size_t num_queries,
41 const std::size_t *output_offsets,
42 double *output_data) {
43 validate_commercial_license();
44 if (!query_vars || !output_offsets || !output_data) {
45 throw std::invalid_argument(
46 "BatchExecutionEngine::evaluate_multi received null query/output buffers.");
48 if (num_queries == 0 || batch_size == 0) {
51 const std::size_t total_states = output_offsets[num_queries];
52 if (total_states == 0) {
61 std::vector<bncore::NodeId> qvars_nodeid(num_queries);
62 for (std::size_t i = 0; i < num_queries; ++i)
66 std::size_t current_batch_start) {
67 double *out_row = output_data + current_batch_start * total_states;
69 output_offsets, out_row);
75 if (num_chunks == 1) {
76 const std::size_t current_chunk_size = batch_size;
80 std::make_unique<BatchWorkspace>(
jt_, current_chunk_size);
87 const int *chunk_evidence = evidence_data;
95 write_query_outputs(workspace, 0);
99 auto worker = [&](std::size_t start_chunk, std::size_t end_chunk) {
102 for (std::size_t c = start_chunk; c < end_chunk; ++c) {
104 std::size_t current_chunk_size =
105 std::min(
chunk_size_, batch_size - current_batch_start);
107 workspace.
reset(current_chunk_size, current_batch_start);
109 const int *chunk_evidence =
110 evidence_data ? (evidence_data + current_batch_start * num_vars)
117 write_query_outputs(workspace, current_batch_start);
121 const std::size_t effective_threads =
122 std::max<std::size_t>(1, std::min(
num_threads_, num_chunks));
123 if (effective_threads == 1 || num_chunks <= 1) {
124 worker(0, num_chunks);
128 std::vector<std::future<void>> futures;
129 std::size_t chunks_per_thread =
130 (num_chunks + effective_threads - 1) / effective_threads;
132 for (std::size_t t = 0; t < effective_threads; ++t) {
133 std::size_t start_chunk = t * chunks_per_thread;
134 std::size_t end_chunk =
135 std::min(start_chunk + chunks_per_thread, num_chunks);
136 if (start_chunk < end_chunk) {
138 std::async(std::launch::async, worker, start_chunk, end_chunk));
142 for (
auto &f : futures) {
148 std::size_t batch_size,
149 std::size_t num_vars,
150 int *output_states) {
151 validate_commercial_license();
152 if (!output_states) {
153 throw std::invalid_argument(
154 "BatchExecutionEngine::evaluate_map received null output buffer.");
156 if (batch_size == 0) {
161 if (evidence_data && num_vars < model_vars) {
162 throw std::invalid_argument(
163 "BatchExecutionEngine::evaluate_map evidence width is smaller than "
164 "the model variable count.");
170 if (num_chunks == 1 && batch_size == 1) {
187 workspace.
query_map(output_states, model_vars);
191 auto worker = [&](std::size_t start_chunk, std::size_t end_chunk) {
194 for (std::size_t c = start_chunk; c < end_chunk; ++c) {
196 const std::size_t chunk_end =
199 for (std::size_t row = chunk_start; row < chunk_end; ++row) {
200 workspace.
reset(1, row);
201 const int *row_evidence =
202 evidence_data ? (evidence_data + row * num_vars) :
nullptr;
211 workspace.
query_map(output_states + row * model_vars, model_vars);
216 const std::size_t effective_threads =
217 std::max<std::size_t>(1, std::min(
num_threads_, num_chunks));
218 if (effective_threads == 1 || num_chunks <= 1) {
219 worker(0, num_chunks);
223 std::vector<std::future<void>> futures;
224 const std::size_t chunks_per_thread =
225 (num_chunks + effective_threads - 1) / effective_threads;
227 for (std::size_t t = 0; t < effective_threads; ++t) {
228 const std::size_t start_chunk = t * chunks_per_thread;
229 const std::size_t end_chunk =
230 std::min(start_chunk + chunks_per_thread, num_chunks);
231 if (start_chunk < end_chunk) {
233 std::async(std::launch::async, worker, start_chunk, end_chunk));
237 for (
auto &f : futures) {
263 std::size_t current_batch_start,
264 std::size_t current_chunk_size)
const {
273 const std::size_t total_elements = l_mat.size();
277 if (l_mat.size() < (current_batch_start + current_chunk_size) * n_states_var) {
278 throw std::invalid_argument(
"Soft evidence matrix size is too small for the batch dimensions.");
void apply_soft_evidence_to_workspace(BatchWorkspace &workspace, std::size_t current_batch_start, std::size_t current_chunk_size) const
void evaluate_multi(const int *evidence_data, std::size_t batch_size, std::size_t num_vars, const std::size_t *query_vars, std::size_t num_queries, const std::size_t *output_offsets, double *output_data)
Posterior marginals of several variables for every batch row, from one sum-product calibration per ch...
void evaluate_map(const int *evidence_data, std::size_t batch_size, std::size_t num_vars, int *output_states)
Most probable state of every variable for every batch row, from max-product propagation.
std::unique_ptr< BatchWorkspace > single_workspace_cache_
reused when a batch is a single chunk
std::size_t single_workspace_batch_size_
batch size the cache was built for
void invalidate_workspace_cache()
Discards the workspace kept for single-chunk evaluations.
void clear_soft_evidence()
Removes all stored soft evidence, scalar and matrix.
std::unordered_map< bncore::NodeId, std::vector< double > > soft_evidence_scalar_
void set_soft_evidence_matrix(bncore::NodeId var, const double *likelihoods_matrix, std::size_t total_n_states)
Sets a separate likelihood vector per batch row for a variable.
std::unordered_map< bncore::NodeId, std::vector< double > > soft_evidence_matrix_
BatchExecutionEngine(const JunctionTree &jt, std::size_t num_threads, std::size_t chunk_size)
void set_soft_evidence(bncore::NodeId var, const double *likelihoods, std::size_t n_states)
Sets one likelihood vector for a variable, used for every batch row.
void evaluate(const int *evidence_data, std::size_t batch_size, std::size_t num_vars, double *output_data, std::size_t query_var)
Posterior marginal of one variable for every batch row.
Message passing state for one junction tree and one batch size.
void clear_soft_evidence()
Removes all soft evidence, scalar and matrix.
void set_soft_evidence_matrix(NodeId var, const double *likelihoods_matrix, std::size_t n_states)
Sets a separate likelihood vector per batch row for a variable.
void calibrate()
Runs sum-product message passing with the current evidence.
void clear_evidence()
Forgets the hard evidence matrix. Soft evidence is not affected.
void max_calibrate()
Runs max-product propagation with the current evidence, for query_map().
void set_soft_evidence(NodeId var, const double *likelihoods, std::size_t n_states)
Sets one likelihood vector for a variable, shared by all batch rows.
void set_evidence_matrix(const int *evidence_matrix, std::size_t num_vars)
Sets the hard evidence for the next calibration.
void query_marginals_multi(const NodeId *query_vars, std::size_t num_queries, const std::size_t *offsets, double *out) const
Marginals of several variables from the last calibrate(), with one scan of each clique involved.
void reset(std::size_t new_batch_size, std::size_t cpt_batch_offset=0)
Prepares the workspace for a new evidence case.
void query_map(int *out_states, std::size_t n_vars) const
Decodes a state for each variable from the max-product beliefs.
std::size_t num_variables() const
Number of variables added so far.
const VariableMetadata & get_variable(NodeId id) const
Metadata of a variable.
Cliques and separators compiled from one Graph.
const Graph * graph() const
The graph given at construction.
bncore::BatchExecutionEngine, the query interface of BNCore: exact posterior marginals and MAP states...
std::uint32_t NodeId
Handle of a variable within one Graph.