7 const std::vector<std::size_t> &state_sizes)
8 : scope_(scope), tensor_(state_sizes) {
9 if (
scope.size() != state_sizes.size() &&
10 scope.size() + 1 != state_sizes.size())
11 throw std::invalid_argument(
"state_sizes must either match scope size or "
12 "include a batch dimension.");
16 const std::vector<std::size_t> &state_sizes,
17 double *preallocated_ptr)
18 : scope_(scope), tensor_(state_sizes, preallocated_ptr) {
19 if (
scope.size() != state_sizes.size() &&
20 scope.size() + 1 != state_sizes.size())
21 throw std::invalid_argument(
"state_sizes must either match scope size or "
22 "include a batch dimension.");
28 std::vector<NodeId> new_scope =
scope_;
29 std::vector<std::size_t> new_sizes;
30 for (std::size_t i = 0; i <
scope_.size(); ++i)
33 std::vector<int> b_in_c(other.
scope_.size(), -1);
34 for (std::size_t j = 0; j < other.
scope_.size(); ++j) {
35 auto it = std::find(new_scope.begin(), new_scope.end(), other.
scope_[j]);
36 if (it == new_scope.end()) {
37 new_scope.push_back(other.
scope_[j]);
39 b_in_c[j] = new_scope.size() - 1;
41 b_in_c[j] = std::distance(new_scope.begin(), it);
47 std::size_t batch_size = a_batched
51 if (a_batched || b_batched) {
52 new_sizes.push_back(batch_size);
55 std::size_t num_elements = std::accumulate(new_sizes.begin(), new_sizes.end(),
56 1ULL, std::multiplies<>());
59 :
Factor(new_scope, new_sizes);
61 std::size_t num_states_C = num_elements / batch_size;
64 const std::size_t c_ndim = new_scope.size();
65 const std::size_t a_ndim =
scope_.size();
66 const std::size_t b_ndim = other.
scope_.size();
68 std::vector<std::size_t> c_strides(c_ndim);
70 c_strides[c_ndim - 1] = 1;
71 for (
int i =
static_cast<int>(c_ndim) - 2; i >= 0; --i)
72 c_strides[i] = c_strides[i + 1] * new_sizes[i + 1];
75 std::vector<std::size_t> a_strides(a_ndim);
77 a_strides[a_ndim - 1] = 1;
78 for (
int i =
static_cast<int>(a_ndim) - 2; i >= 0; --i)
79 a_strides[i] = a_strides[i + 1] *
tensor_.
shape()[i + 1];
82 std::vector<std::size_t> b_strides(b_ndim);
84 b_strides[b_ndim - 1] = 1;
85 for (
int i =
static_cast<int>(b_ndim) - 2; i >= 0; --i)
86 b_strides[i] = b_strides[i + 1] * other.
tensor_.
shape()[i + 1];
90 std::vector<std::size_t> map_a(num_states_C);
91 std::vector<std::size_t> map_b(num_states_C);
93 for (std::size_t c_idx = 0; c_idx < num_states_C; ++c_idx) {
95 std::size_t a_idx = 0;
96 std::size_t b_idx = 0;
97 std::size_t remainder = c_idx;
99 for (std::size_t d = 0; d < c_ndim; ++d) {
100 std::size_t coord = remainder / c_strides[d];
101 remainder -= coord * c_strides[d];
105 a_idx += coord * a_strides[d];
109 for (std::size_t j = 0; j < b_ndim; ++j) {
110 if (b_in_c[j] ==
static_cast<int>(d)) {
111 b_idx += coord * b_strides[j];
116 map_a[c_idx] = a_idx;
117 map_b[c_idx] = b_idx;
125 if (batch_size == 1) {
127 for (std::size_t c_idx = 0; c_idx < num_states_C; ++c_idx) {
128 double val_a = a_data[map_a[c_idx]];
129 double val_b = b_data[map_b[c_idx]];
130 c_data[c_idx] = val_a * val_b;
133 for (std::size_t c_idx = 0; c_idx < num_states_C; ++c_idx) {
134 double *c_ptr = c_data + c_idx * batch_size;
135 const double *a_ptr =
136 a_data + map_a[c_idx] * (a_batched ? batch_size : 1);
137 const double *b_ptr =
138 b_data + map_b[c_idx] * (b_batched ? batch_size : 1);
141 for (std::size_t b = 0; b < batch_size; ++b) {
142 double val_a = a_batched ? a_ptr[b] : a_ptr[0];
143 double val_b = b_batched ? b_ptr[b] : b_ptr[0];
144 c_ptr[b] = val_a * val_b;
153 std::vector<NodeId> new_scope;
154 std::vector<std::size_t> new_sizes;
155 std::vector<int> c_in_a;
156 for (std::size_t i = 0; i <
scope_.size(); ++i) {
157 if (std::find(marg_vars.begin(), marg_vars.end(),
scope_[i]) ==
159 new_scope.push_back(
scope_[i]);
166 std::size_t batch_size = is_batched ?
tensor_.
shape().back() : 1;
169 new_sizes.push_back(batch_size);
172 std::size_t num_elements = std::accumulate(new_sizes.begin(), new_sizes.end(),
173 1ULL, std::multiplies<>());
174 Factor result = allocator ?
Factor(new_scope, new_sizes,
176 :
Factor(new_scope, new_sizes);
182 std::size_t num_states_A =
tensor_.
size() / batch_size;
185 const std::size_t a_ndim =
scope_.size();
186 const std::size_t c_ndim = new_scope.size();
188 std::vector<std::size_t> a_strides(a_ndim);
190 a_strides[a_ndim - 1] = 1;
191 for (
int i =
static_cast<int>(a_ndim) - 2; i >= 0; --i)
192 a_strides[i] = a_strides[i + 1] *
tensor_.
shape()[i + 1];
195 std::vector<std::size_t> c_strides(c_ndim);
197 c_strides[c_ndim - 1] = 1;
198 for (
int i =
static_cast<int>(c_ndim) - 2; i >= 0; --i)
199 c_strides[i] = c_strides[i + 1] * new_sizes[i + 1];
203 std::vector<std::size_t> map_c(num_states_A);
204 for (std::size_t a_idx = 0; a_idx < num_states_A; ++a_idx) {
205 std::size_t c_idx = 0;
206 std::size_t remainder = a_idx;
208 for (std::size_t d = 0; d < a_ndim; ++d) {
209 std::size_t coord = remainder / a_strides[d];
210 remainder -= coord * a_strides[d];
213 for (std::size_t k = 0; k < c_ndim; ++k) {
214 if (c_in_a[k] ==
static_cast<int>(d)) {
215 c_idx += coord * c_strides[k];
220 map_c[a_idx] = c_idx;
227 if (batch_size == 1) {
229 for (std::size_t a_idx = 0; a_idx < num_states_A; ++a_idx) {
230 c_data[map_c[a_idx]] += a_data[a_idx];
233 for (std::size_t a_idx = 0; a_idx < num_states_A; ++a_idx) {
234 double *c_ptr = c_data + map_c[a_idx] * batch_size;
235 const double *a_ptr = a_data + a_idx * batch_size;
238 for (std::size_t b = 0; b < batch_size; ++b) {
239 c_ptr[b] += a_ptr[b];
bncore::BumpAllocator, a fixed-capacity arena of doubles for temporary factor storage.
Fixed-capacity scratch buffer of doubles with pointer-bump allocation.
double * allocate(std::size_t num_doubles)
Reserves the next num_doubles doubles of the buffer.
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 bind_data(double *ptr)
Redirects the tensor to a caller buffer.
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.
Factor(const std::vector< NodeId > &scope, const std::vector< std::size_t > &state_sizes)
Creates a factor that owns zero-initialized storage.
void bind_data(double *ptr)
Redirects the value table to a caller buffer. See DenseTensor::bind_data().
std::vector< NodeId > scope_
const std::vector< NodeId > & scope() const
Variables of the factor, in tensor dimension order.
Factor marginalize(const std::vector< NodeId > &marg_vars, class BumpAllocator *allocator=nullptr) const
Sums variables out of the factor.
DenseTensor & tensor()
The value table.
bncore::Factor, a table of doubles over a list of variables, with the product and sum-out operations ...