13static std::vector<std::size_t>
make_strides(
const std::vector<std::size_t> &shape) {
14 std::vector<std::size_t> st(shape.size());
17 for (
int i = (
int)shape.size() - 2; i >= 0; --i)
18 st[i] = st[i + 1] * shape[i + 1];
24 const std::vector<NodeId> &b,
25 std::vector<int> &b_in_c) {
26 std::vector<NodeId> c = a;
27 b_in_c.resize(b.size(), -1);
28 for (std::size_t j = 0; j < b.size(); ++j) {
29 auto it = std::find(c.begin(), c.end(), b[j]);
30 if (it == c.end()) { b_in_c[j] = (int)c.size(); c.push_back(b[j]); }
31 else b_in_c[j] = (int)(it - c.begin());
39 const std::vector<std::size_t> &prod_shape,
40 const std::vector<NodeId> &src_scope,
41 const std::vector<std::size_t> &src_shape,
42 std::vector<uint32_t> &pot_map) {
43 std::size_t prod_states = 1;
44 for (
auto s : prod_shape) prod_states *= s;
47 pot_map.resize(prod_states);
49 for (std::size_t pi = 0; pi < prod_states; ++pi) {
50 std::size_t si = 0, rem = pi;
51 for (std::size_t d = 0; d < prod_scope.size(); ++d) {
52 std::size_t coord = rem / prod_st[d];
53 rem -= coord * prod_st[d];
54 for (std::size_t sd = 0; sd < src_scope.size(); ++sd)
55 if (src_scope[sd] == prod_scope[d]) { si += coord * src_st[sd];
break; }
57 pot_map[pi] = (uint32_t)si;
64 const std::vector<std::size_t> &src_shape,
65 const std::vector<NodeId> &dst_scope,
66 const std::vector<std::size_t> &dst_shape,
67 std::vector<uint32_t> &marg_map) {
68 std::size_t src_st_tot = 1;
69 for (
auto s : src_shape) src_st_tot *= s;
72 marg_map.resize(src_st_tot);
74 for (std::size_t si = 0; si < src_st_tot; ++si) {
75 std::size_t di = 0, rem = si;
76 for (std::size_t d = 0; d < src_scope.size(); ++d) {
77 std::size_t coord = rem / src_st[d];
78 rem -= coord * src_st[d];
79 for (std::size_t dd = 0; dd < dst_scope.size(); ++dd)
80 if (dst_scope[dd] == src_scope[d]) { di += coord * dst_st[dd];
break; }
82 marg_map[si] = (uint32_t)di;
90 : jt_(jt), batch_size_(batch_size) {
98 std::vector<bool> visited(n,
false);
99 std::vector<std::size_t> stack;
100 for (std::size_t start = 0; start < n; ++start) {
101 if (visited[start])
continue;
103 stack.push_back(start);
104 visited[start] =
true;
105 while (!stack.empty()) {
106 std::size_t u = stack.back(); stack.pop_back();
108 for (std::size_t v :
jt_.
cliques()[u].neighbor_cliques)
145 std::vector<std::vector<std::size_t>> cshapes(n);
147 for (std::size_t i = 0; i < n; ++i) {
156 auto sepset_scope_shape = [&](std::size_t u, std::size_t v)
157 -> std::pair<std::vector<NodeId>, std::vector<std::size_t>> {
159 std::vector<NodeId> sep;
160 std::set_intersection(us.begin(), us.end(), vs.begin(), vs.end(), std::back_inserter(sep));
161 std::vector<std::size_t> sh;
166 for (std::size_t u = 0; u < n; ++u) {
167 if (
parent_of_[u] == ~std::size_t(0))
continue;
168 auto [sep, sh] = sepset_scope_shape(u,
parent_of_[u]);
187 if (
parent_of_[u] == ~std::size_t(0))
continue;
191 std::vector<NodeId> prod_scope =
jt_.
cliques()[u].scope;
192 std::vector<std::size_t> prod_shape = cshapes[u];
195 for (std::size_t ci = 0; ci <
children_of_[u].size(); ++ci) {
196 auto [sep, sh] = sepset_scope_shape(
children_of_[u][ci], u);
197 std::vector<int> b_in_c;
199 std::vector<std::size_t> new_shape;
200 for (
auto nd : new_scope) {
201 auto it = std::find(prod_scope.begin(), prod_scope.end(), nd);
202 new_shape.push_back(it != prod_scope.end()
203 ? prod_shape[it - prod_scope.begin()]
206 prod_scope = new_scope; prod_shape = new_shape;
212 for (std::size_t ci = 0; ci <
children_of_[u].size(); ++ci) {
213 auto [sep, sh] = sepset_scope_shape(
children_of_[u][ci], u);
216 auto [sep_scope, sep_shape] = sepset_scope_shape(u,
parent_of_[u]);
225 if (
parent_of_[u] == ~std::size_t(0))
continue;
230 std::vector<NodeId> prod_scope =
jt_.
cliques()[p].scope;
231 std::vector<std::size_t> prod_shape = cshapes[p];
238 auto [sep, sh] = sepset_scope_shape(s, p);
239 std::vector<int> b_in_c;
241 std::vector<std::size_t> new_shape;
242 for (
auto nd : new_scope) {
243 auto it = std::find(prod_scope.begin(), prod_scope.end(), nd);
244 new_shape.push_back(it != prod_scope.end()
245 ? prod_shape[it - prod_scope.begin()]
248 prod_scope = new_scope; prod_shape = new_shape;
255 auto [sep, sh] = sepset_scope_shape(p,
parent_of_[p]);
263 auto [sep_scope, sep_shape] = sepset_scope_shape(u, p);
271 for (std::size_t i = 0; i < n; ++i) {
278 auto [sep, sh] = sepset_scope_shape(i,
parent_of_[i]);
282 for (std::size_t ci = 0; ci <
children_of_[i].size(); ++ci) {
283 auto [sep, sh] = sepset_scope_shape(
children_of_[i][ci], i);
295 for (std::size_t ci = 0; ci < n; ++ci) {
298 std::size_t K = scope.size();
304 std::size_t rem = flat;
305 for (std::size_t d = 0; d < K; ++d) {
306 uint8_t coord = (uint8_t)(rem / strides[d]);
307 rem -= coord * strides[d];
323 const std::size_t effective_batch_size = std::max<std::size_t>(1, target_batch_size);
326 for (std::size_t u = 0; u < n; ++u) {
337 const std::size_t msg_size =
sepset_size_[u] * effective_batch_size;
346 for (std::size_t i = 0; i < n; ++i) {
347 const std::size_t pot_size =
clique_states_[i] * effective_batch_size;
364 Factor owned(pot.scope(), pot.tensor().shape());
365 std::copy(pot.tensor().data(), pot.tensor().data() + pot.tensor().size(),
372 if (target_batch_size == 0) {
373 throw std::invalid_argument(
"BatchWorkspace batch_size must be > 0.");
383 bool uses_batch_offset =
false;
386 const Factor &unbatched = clique.base_potential;
387 std::vector<std::size_t> new_sizes;
388 for (std::size_t i = 0; i < unbatched.
scope().
size(); ++i)
389 new_sizes.push_back(unbatched.
tensor().
shape()[i]);
390 new_sizes.push_back(target_batch_size);
395 for (
NodeId n_id : clique.assigned_cpts) {
396 std::vector<NodeId> family;
398 family.push_back(n_id);
399 std::vector<std::size_t> fam_sizes;
401 std::size_t fam_states = 1;
for (
auto s : fam_sizes) fam_states *= s;
403 std::vector<std::size_t> new_fam = fam_sizes;
404 new_fam.push_back(target_batch_size);
405 Factor cpt(family, new_fam);
409 throw std::invalid_argument(
"CPT for variable '" +
413 if (probs.size() % fam_states != 0) {
414 throw std::invalid_argument(
415 "CPT geometry mismatch for variable '" +
417 std::to_string(fam_states) +
" or batched size " +
418 std::to_string(fam_states) +
" * B, but got " +
419 std::to_string(probs.size()) +
".");
421 const std::size_t cpt_batch_size = probs.size() / fam_states;
422 if (cpt_batch_size == 0) {
423 throw std::invalid_argument(
"CPT for variable '" +
425 "' has invalid batch size 0.");
427 if (cpt_batch_size != 1 &&
429 !(cpt_batch_size > target_batch_size &&
431 throw std::invalid_argument(
432 "Batched CPT batch size mismatch for variable '" +
434 std::to_string(cpt_batch_size) +
", workspace batch size is " +
435 std::to_string(target_batch_size) +
", batch offset is " +
440 if (cpt_batch_size == 1) {
441 for (std::size_t s = 0; s < fam_states; ++s)
442 for (std::size_t b = 0; b < target_batch_size; ++b)
443 d[s * target_batch_size + b] = probs[s];
444 }
else if (cpt_batch_size == target_batch_size) {
445 std::copy(probs.begin(), probs.end(), d);
447 uses_batch_offset =
true;
448 for (std::size_t s = 0; s < fam_states; ++s) {
450 double *dst = d + s * target_batch_size;
451 std::copy(src, src + target_batch_size, dst);
476 std::size_t cpt_batch_offset) {
477 if (new_batch_size == 0) {
478 throw std::invalid_argument(
"BatchWorkspace batch_size must be > 0.");
494 Factor(bp.scope(), bp.tensor().shape(),
495 const_cast<double*
>(bp.tensor().data())));
515 std::size_t n_states) {
518 if (n_states != card)
519 throw std::invalid_argument(
520 "set_soft_evidence: n_states (" + std::to_string(n_states) +
521 ") does not match variable cardinality (" + std::to_string(card) +
").");
522 for (std::size_t s = 0; s < n_states; ++s)
523 if (likelihoods[s] < 0.0)
524 throw std::invalid_argument(
525 "set_soft_evidence: likelihood[" + std::to_string(s) +
526 "] is negative (" + std::to_string(likelihoods[s]) +
").");
533 const double *likelihoods_matrix,
534 std::size_t n_states) {
536 if (n_states != card)
537 throw std::invalid_argument(
538 "set_soft_evidence_matrix: n_states (" + std::to_string(n_states) +
539 ") does not match variable cardinality (" + std::to_string(card) +
").");
541 for (std::size_t i = 0; i < B * n_states; ++i)
542 if (likelihoods_matrix[i] < 0.0)
543 throw std::invalid_argument(
544 "set_soft_evidence_matrix: negative likelihood at flat index " +
545 std::to_string(i) +
".");
547 likelihoods_matrix + B * n_states);
575 const auto &scope = pot.
scope();
577 const std::size_t K = scope.size();
587 std::vector<EvidenceDim> ev_dims;
590 for (std::size_t d = 0; d < K; ++d) {
596 if (!has_hard && !has_scalar_soft && !has_matrix_soft)
continue;
600 if (has_scalar_soft || has_matrix_soft) {
601 ev_dims.push_back({d, nid, has_scalar_soft});
604 bool any_obs =
false;
605 for (std::size_t b = 0; b < B && !any_obs; ++b)
607 if (any_obs) ev_dims.push_back({d, nid,
true});
610 if (ev_dims.empty())
return false;
615 scratch.resize(total * B);
617 std::copy(src, src + total * B, scratch.data());
622 for (
const EvidenceDim &ed : ev_dims) {
623 const std::size_t d = ed.pos;
624 const NodeId nid = ed.nid;
625 const std::size_t card = shape[d];
630 for (std::size_t flat = 0; flat < total; ++flat) {
632 double *dst = scratch.data() + flat * B;
634 if (has_scalar_soft) {
637 const double factor = lam[state];
638 if (factor == 1.0)
continue;
640 for (std::size_t b = 0; b < B; ++b) dst[b] *= factor;
642 }
else if (has_matrix_soft) {
645 for (std::size_t b = 0; b < B; ++b) {
646 dst[b] *= lam[b * card + state];
651 for (std::size_t b = 0; b < B; ++b) {
653 if (obs < 0)
continue;
654 if (
static_cast<std::size_t
>(obs) >= card)
655 throw std::invalid_argument(
656 "Hard evidence state index out of range for variable '" +
658 if (
static_cast<std::size_t
>(obs) != state) dst[b] = 0.0;
667 for (std::size_t f = 0; f < total; ++f) sum += scratch[f];
669 throw std::invalid_argument(
"Inconsistent evidence: zero-sum potential in clique.");
698 for (std::size_t ci = 0; ci < n; ++ci) {
709 if (
parent_of_[u] == ~std::size_t(0))
continue;
719 for (std::size_t pi = 0; pi < PS; ++pi) scratch[pi] = pot[op.
pot_map[pi]];
720 for (std::size_t ci = 0; ci < op.
child_cliques.size(); ++ci) {
722 const uint32_t *cmap = op.
child_maps[ci].data();
723 for (std::size_t pi = 0; pi < PS; ++pi) scratch[pi] *= cm[cmap[pi]];
727 std::fill(umsg, umsg + US, 0.0);
728 const uint32_t *mm = op.
marg_map.data();
729 for (std::size_t pi = 0; pi < PS; ++pi) umsg[mm[pi]] += scratch[pi];
750 for (std::size_t pi = 0; pi < PS; ++pi) scratch[pi] = ppot[op.
pot_map[pi]];
753 const uint32_t *dm = op.
down_map.data();
754 for (std::size_t pi = 0; pi < PS; ++pi) scratch[pi] *= pm[dm[pi]];
759 for (std::size_t pi = 0; pi < PS; ++pi) scratch[pi] *= sm[smap[pi]];
763 std::fill(dmsg, dmsg + DS, 0.0);
764 const uint32_t *mm = op.
marg_map.data();
765 for (std::size_t pi = 0; pi < PS; ++pi) dmsg[mm[pi]] += scratch[pi];
773 for (std::size_t i = 0; i < n; ++i) {
782 std::copy(pot, pot + NS, out);
785 const uint32_t *dmap = op.
down_map.data();
786 for (std::size_t pi = 0; pi < NS; ++pi) out[pi] *= dm[dmap[pi]];
788 for (std::size_t ci = 0; ci < op.
child_cliques.size(); ++ci) {
790 const uint32_t *cmap = op.
child_maps[ci].data();
791 for (std::size_t pi = 0; pi < NS; ++pi) out[pi] *= cm[cmap[pi]];
797 if (
parent_of_[u] == ~std::size_t(0))
continue;
809 for (std::size_t pi = 0; pi < PS; ++pi) {
810 double *dst = scratch + pi * B;
811 const double *src = pot +
static_cast<std::size_t
>(op.
pot_map[pi]) * B;
813 for (std::size_t b = 0; b < B; ++b) {
818 for (std::size_t ci = 0; ci < op.
child_cliques.size(); ++ci) {
820 const uint32_t *cmap = op.
child_maps[ci].data();
821 for (std::size_t pi = 0; pi < PS; ++pi) {
822 double *dst = scratch + pi * B;
823 const double *src = cm +
static_cast<std::size_t
>(cmap[pi]) * B;
825 for (std::size_t b = 0; b < B; ++b) {
833 const uint32_t *mm = op.
marg_map.data();
834 for (std::size_t pi = 0; pi < PS; ++pi) {
835 double *dst = umsg +
static_cast<std::size_t
>(mm[pi]) * B;
836 const double *src = scratch + pi * B;
838 for (std::size_t b = 0; b < B; ++b) {
864 for (std::size_t pi = 0; pi < PS; ++pi) {
865 double *dst = scratch + pi * B;
867 ppot +
static_cast<std::size_t
>(op.
pot_map[pi]) * B;
869 for (std::size_t b = 0; b < B; ++b) {
876 const uint32_t *dm = op.
down_map.data();
877 for (std::size_t pi = 0; pi < PS; ++pi) {
878 double *dst = scratch + pi * B;
879 const double *src = pm +
static_cast<std::size_t
>(dm[pi]) * B;
881 for (std::size_t b = 0; b < B; ++b) {
890 for (std::size_t pi = 0; pi < PS; ++pi) {
891 double *dst = scratch + pi * B;
892 const double *src = sm +
static_cast<std::size_t
>(smap[pi]) * B;
894 for (std::size_t b = 0; b < B; ++b) {
902 const uint32_t *mm = op.
marg_map.data();
903 for (std::size_t pi = 0; pi < PS; ++pi) {
904 double *dst = dmsg +
static_cast<std::size_t
>(mm[pi]) * B;
905 const double *src = scratch + pi * B;
907 for (std::size_t b = 0; b < B; ++b) {
919 for (std::size_t i = 0; i < n; ++i) {
929 std::copy(pot, pot + (NS * B), out);
933 const uint32_t *dmap = op.
down_map.data();
934 for (std::size_t pi = 0; pi < NS; ++pi) {
935 double *dst = out + pi * B;
936 const double *src = dm +
static_cast<std::size_t
>(dmap[pi]) * B;
938 for (std::size_t b = 0; b < B; ++b) {
944 for (std::size_t ci = 0; ci < op.
child_cliques.size(); ++ci) {
946 const uint32_t *cmap = op.
child_maps[ci].data();
947 for (std::size_t pi = 0; pi < NS; ++pi) {
948 double *dst = out + pi * B;
949 const double *src = cm +
static_cast<std::size_t
>(cmap[pi]) * B;
951 for (std::size_t b = 0; b < B; ++b) {
963 if (total <= 0.0)
throw std::invalid_argument(
"Inconsistent evidence");
968 for (std::size_t u = 0; u < n; ++u) {
969 if (
parent_of_[u] == ~std::size_t(0))
continue;
988 std::size_t num_queries,
989 const std::size_t *offsets,
991 if (num_queries == 0) {
997 std::size_t node_dim;
998 std::size_t n_states;
1001 constexpr std::size_t kStackQueryLimit = 64;
1002 constexpr std::size_t kStackCliqueLimit = 200;
1004 std::size_t clique_of_q_stack[kStackQueryLimit];
1005 QEntry entries_stack[kStackQueryLimit];
1006 uint8_t visited_stack[kStackCliqueLimit];
1008 std::vector<std::size_t> clique_of_q_heap;
1009 std::vector<QEntry> entries_heap;
1011 std::size_t *clique_of_q =
nullptr;
1012 QEntry *entries =
nullptr;
1013 uint8_t *visited =
nullptr;
1015 if (num_queries <= kStackQueryLimit) {
1016 clique_of_q = clique_of_q_stack;
1017 entries = entries_stack;
1019 clique_of_q_heap.resize(num_queries);
1020 entries_heap.resize(num_queries);
1021 clique_of_q = clique_of_q_heap.data();
1022 entries = entries_heap.data();
1025 const std::size_t n_cliques =
jt_.
cliques().size();
1026 if (n_cliques <= kStackCliqueLimit) {
1027 std::memset(visited_stack, 0, n_cliques);
1028 visited = visited_stack;
1038 const std::size_t total_states = offsets[num_queries];
1041 for (std::size_t qi = 0; qi < num_queries; ++qi) {
1042 NodeId nid = query_vars[qi];
1045 throw std::invalid_argument(
"query node not in JT");
1046 std::size_t ci = it->second;
1047 clique_of_q[qi] = ci;
1049 std::size_t node_dim = 0;
1050 for (std::size_t d = 0; d < scope.size(); ++d) {
1051 if (scope[d] == nid) {
1060 double *qout = out + offsets[qi];
1061 std::fill(qout, qout + entries[qi].n_states, 0.0);
1063 for (std::size_t b = 0; b < B; ++b) {
1064 double *qout = out + b * total_states + offsets[qi];
1065 std::fill(qout, qout + entries[qi].n_states, 0.0);
1071 for (std::size_t qi = 0; qi < num_queries; ++qi) {
1072 std::size_t ci = clique_of_q[qi];
1073 if (visited[ci])
continue;
1081 for (std::size_t flat = 0; flat < total; ++flat) {
1082 const double v = cal[flat];
1083 for (std::size_t qj = qi; qj < num_queries; ++qj) {
1084 if (clique_of_q[qj] != ci)
continue;
1085 const QEntry &
e = entries[qj];
1090 for (std::size_t qj = qi; qj < num_queries; ++qj) {
1091 if (clique_of_q[qj] != ci)
continue;
1092 const QEntry &
e = entries[qj];
1093 double *qout = out + offsets[
e.qi];
1095 for (std::size_t s = 0; s <
e.n_states; ++s) sum += qout[s];
1097 for (std::size_t s = 0; s <
e.n_states; ++s) qout[s] /= sum;
1101 for (std::size_t flat = 0; flat < total; ++flat) {
1102 const double *cal_row = cal + flat * B;
1103 for (std::size_t qj = qi; qj < num_queries; ++qj) {
1104 if (clique_of_q[qj] != ci)
continue;
1105 const QEntry &
e = entries[qj];
1106 const std::size_t state = mi.
state_of_dim[
e.node_dim][flat];
1107 for (std::size_t b = 0; b < B; ++b) {
1108 out[b * total_states + offsets[
e.qi] + state] += cal_row[b];
1113 for (std::size_t qj = qi; qj < num_queries; ++qj) {
1114 if (clique_of_q[qj] != ci)
continue;
1115 const QEntry &
e = entries[qj];
1116 for (std::size_t b = 0; b < B; ++b) {
1117 double *qout = out + b * total_states + offsets[
e.qi];
1119 for (std::size_t s = 0; s <
e.n_states; ++s) sum += qout[s];
1121 for (std::size_t s = 0; s <
e.n_states; ++s) qout[s] /= sum;
1123 std::fill(qout, qout +
e.n_states, 0.0);
1137 std::vector<std::size_t> out_shape = {
batch_size_, n_states};
1141 std::size_t offsets[2] = {0, n_states};
1163 throw std::invalid_argument(
1164 "BatchWorkspace::max_calibrate currently supports batch_size == 1. "
1165 "Use BatchExecutionEngine::evaluate_map for batched MAP.");
1173 for (std::size_t u = 0; u < n; ++u) {
1174 if (
parent_of_[u] == ~std::size_t(0))
continue;
1178 for (std::size_t i = 0; i < n; ++i)
1187 Factor(bp.scope(), bp.tensor().shape(),
1188 const_cast<double *
>(bp.tensor().data())));
1196 for (std::size_t ci = 0; ci < n; ++ci)
1204 if (
parent_of_[u] == ~std::size_t(0))
continue;
1210 for (std::size_t pi = 0; pi < PS; ++pi)
1211 scratch[pi] = pot[op.
pot_map[pi]];
1214 for (std::size_t ci = 0; ci < op.
child_cliques.size(); ++ci) {
1216 const uint32_t *cmap = op.
child_maps[ci].data();
1217 for (std::size_t pi = 0; pi < PS; ++pi) scratch[pi] *= cm[cmap[pi]];
1223 std::fill(umsg, umsg + US, 0.0);
1224 const uint32_t *mm = op.
marg_map.data();
1225 for (std::size_t pi = 0; pi < PS; ++pi) {
1226 if (scratch[pi] > umsg[mm[pi]]) umsg[mm[pi]] = scratch[pi];
1233 for (std::size_t i = 0; i < n; ++i) {
1238 std::copy(pot, pot + NS, out);
1240 for (std::size_t ci = 0; ci < op.
child_cliques.size(); ++ci) {
1242 const uint32_t *cmap = op.
child_maps[ci].data();
1243 for (std::size_t pi = 0; pi < NS; ++pi) out[pi] *= cm[cmap[pi]];
1259 std::fill(out_states, out_states + n_vars, -1);
1266 if (nid >=
static_cast<NodeId>(n_vars))
continue;
1274 std::size_t node_dim = 0;
1275 for (std::size_t d = 0; d < scope.size(); ++d)
1276 if (scope[d] == nid) { node_dim = d;
break; }
1281 double best_val = -1.0;
1283 for (std::size_t s = 0; s < n_states; ++s) {
1285 for (std::size_t flat = 0; flat < tot; ++flat)
1286 if (mi.
state_of_dim[node_dim][flat] ==
static_cast<uint8_t
>(s))
1287 v = std::max(v, cal[flat]);
1288 if (v > best_val) { best_val = v; best_s =
static_cast<int>(s); }
1290 out_states[nid] = best_s;
std::vector< std::size_t > down_msg_size_
std::unordered_map< NodeId, std::vector< double > > soft_evidence_matrix_
void build_message_schedule()
Fills collect_ops_, distribute_ops_, assemble_ops_ and clique_marg_info_.
std::vector< std::size_t > collect_order_
std::vector< std::size_t > sepset_size_
std::vector< std::vector< uint32_t > > map_traceback_
std::vector< std::size_t > cal_pot_size_
std::vector< uint8_t > cal_changed_
std::unordered_map< NodeId, std::vector< double > > soft_evidence_scalar_
const int * evidence_matrix_
void clear_soft_evidence()
Removes all soft evidence, scalar and matrix.
std::vector< std::vector< double > > map_cal_pot_buf_
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.
DenseTensor query_marginal(NodeId node) const
Marginal of one variable from the last calibrate().
void calibrate()
Runs sum-product message passing with the current evidence.
std::unordered_map< NodeId, std::size_t > node_to_clique_
std::vector< std::vector< double > > base_up_buf_
std::vector< std::vector< double > > ev_scratch_
std::vector< uint8_t > query_visited_scratch_
std::vector< Factor > clique_potentials_
bool apply_soft_evidence_to_clique(std::size_t ci)
void clear_evidence()
Forgets the hard evidence matrix. Soft evidence is not affected.
std::vector< std::vector< double > > map_up_msg_buf_
std::vector< std::vector< double > > up_msg_buf_
std::size_t base_batch_size_
std::vector< bool > ev_scratch_active_
std::vector< std::vector< double > > base_cal_buf_
std::vector< std::vector< double > > cal_pot_buf_
std::vector< CollectOp > collect_ops_
std::unordered_map< NodeId, std::vector< std::size_t > > node_in_cliques_
BatchWorkspace(const JunctionTree &jt, std::size_t batch_size=1)
Builds the workspace and calibrates it without evidence.
std::vector< std::vector< double > > base_down_buf_
void resize_runtime_buffers(std::size_t target_batch_size)
Sizes message, belief and scratch buffers for a batch size.
std::vector< std::size_t > distribute_order_
void build_node_to_clique_map()
std::size_t evidence_num_vars_
std::vector< AssembleOp > assemble_ops_
std::vector< std::vector< std::size_t > > children_of_
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.
std::vector< std::size_t > clique_states_
bool base_depends_on_offset_
std::vector< DistributeOp > distribute_ops_
bool is_first_calibration_
void snapshot_base_potentials()
Copies clique_potentials_ into owning base_clique_potentials_.
std::vector< uint8_t > up_changed_
std::size_t base_batch_offset_
void set_evidence_matrix(const int *evidence_matrix, std::size_t num_vars)
Sets the hard evidence for the next calibration.
std::vector< double > scratch_buf_
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.
std::vector< std::vector< double > > down_msg_buf_
std::vector< CliqueMargInfo > clique_marg_info_
std::size_t max_product_states_
std::vector< std::size_t > up_msg_size_
std::vector< std::size_t > parent_of_
std::vector< uint8_t > ev_clique_
void reset(std::size_t new_batch_size, std::size_t cpt_batch_offset=0)
Prepares the workspace for a new evidence case.
std::size_t cpt_batch_offset_
std::vector< Factor > base_clique_potentials_
std::vector< uint8_t > down_changed_
void rebuild_clique_potentials(std::size_t target_batch_size)
void query_map(int *out_states, std::size_t n_vars) const
Decodes a state for each variable from the max-product beliefs.
Dense row-major array of doubles with a fixed shape.
double * data()
Pointer to the first element: the borrowed buffer when one is bound, otherwise the owned storage.
const std::vector< std::size_t > & shape() const
Extent of each dimension, as given at construction.
void fill(double value)
Sets every element to value.
std::size_t size() const
Total number of elements, the product of the shape.
A potential over an ordered scope of variables.
Factor multiply(const Factor &other, class BumpAllocator *allocator=nullptr) const
Product of two factors.
const std::vector< NodeId > & scope() const
Variables of the factor, in tensor dimension order.
DenseTensor & tensor()
The value table.
const std::vector< NodeId > & get_parents(NodeId id) const
Parents of a variable, in the order their edges were added.
const VariableMetadata & get_variable(NodeId id) const
Metadata of a variable.
Cliques and separators compiled from one Graph.
const std::vector< Clique > & cliques() const
All cliques, indexed by Clique::id.
const Graph * graph() const
The graph given at construction.
int size
Total number of MPI processes.
static std::vector< NodeId > combine_scopes(const std::vector< NodeId > &a, const std::vector< NodeId > &b, std::vector< int > &b_in_c)
static void build_marg_map_u32(const std::vector< NodeId > &src_scope, const std::vector< std::size_t > &src_shape, const std::vector< NodeId > &dst_scope, const std::vector< std::size_t > &dst_shape, std::vector< uint32_t > &marg_map)
static std::size_t build_pot_map_u32(const std::vector< NodeId > &prod_scope, const std::vector< std::size_t > &prod_shape, const std::vector< NodeId > &src_scope, const std::vector< std::size_t > &src_shape, std::vector< uint32_t > &pot_map)
static std::vector< std::size_t > make_strides(const std::vector< std::size_t > &shape)
std::uint32_t NodeId
Handle of a variable within one Graph.
Index tables that assemble the calibrated belief of one clique from its potential,...
std::vector< uint32_t > down_map
[clique_idx] to index in the clique's down message; empty unless has_down
std::size_t product_states
number of states of the clique
std::vector< std::vector< uint32_t > > child_maps
[child_i][clique_idx] to index in that child's up message
std::vector< std::size_t > child_cliques
clique indices of the children, parallel to child_maps
bool has_down
true when the clique has a parent
Per-clique lookup from a flat clique index to the state of each scope variable.
std::vector< std::vector< uint8_t > > state_of_dim
std::size_t n_states_total
number of joint states of the clique
Index tables for the upward (collect) message of one non-root clique u to its parent.
std::vector< std::vector< uint32_t > > child_maps
[child_i][prod_idx] to index in that child's up message
std::vector< uint32_t > pot_map
[prod_idx] to flat index in clique u's potential
std::size_t product_states
number of states of the product space
std::vector< uint32_t > marg_map
[prod_idx] to index in u's up message (its separator states)
std::vector< std::size_t > child_cliques
clique indices of the children, parallel to child_maps
Index tables for the downward (distribute) message from a parent clique p to one child clique u.
std::size_t product_states
number of states of the product space
std::vector< uint32_t > marg_map
[prod_idx] to index in u's down message
std::size_t parent_clique
clique index of p
bool has_parent_down
true when p has a parent of its own
std::vector< std::size_t > sibling_cliques
other children of p, parallel to sibling_maps
std::vector< uint32_t > down_map
[prod_idx] to index in p's down message; empty unless has_parent_down
std::vector< std::vector< uint32_t > > sibling_maps
[sibling_i][prod_idx] to index in that sibling's up message
std::vector< uint32_t > pot_map
[prod_idx] to flat index in parent clique p's potential
bncore::BatchWorkspace, the message passing state of junction tree inference, and the precomputed ind...