Branch data Line data Source code
1 : : #include "Spn.hpp"
2 : :
3 : : #include <iomanip>
4 : : #include "mutable/util/AdjacencyMatrix.hpp"
5 : : #include <mutable/util/fn.hpp>
6 : : #include "util/Kmeans.hpp"
7 : : #include "util/RDC.hpp"
8 : :
9 : :
10 : : using namespace m;
11 : : using namespace Eigen;
12 : :
13 : :
14 : : namespace {
15 : :
16 : : std::size_t MIN_INSTANCE_SLICE = 0;
17 : : const int MAX_K = 7;
18 : : const float RDC_THRESHOLD = 0.3f;
19 : :
20 : 18 : MatrixXf normalize_minmax(const MatrixXf &data)
21 : : {
22 : 18 : const RowVectorXf mins = data.colwise().minCoeff();
23 [ + - + - : 18 : const RowVectorXf maxs = data.colwise().maxCoeff() - mins;
+ - + - ]
24 : :
25 [ - + ]: 18 : MatrixXf normalized(data.rows(), data.cols());
26 [ + + ]: 68 : for (unsigned i = 0; i != data.cols(); ++i) {
27 [ + - + - : 50 : if (maxs.array()[i] == 0) // min == max => empty range [min, max)
+ + ]
28 [ + - + - : 20 : normalized.col(i) = VectorXf::Zero(data.rows());
+ - ]
29 : : else
30 [ + - + - : 30 : normalized.col(i) = (data.col(i).array() - mins.array()[i]) / maxs.array()[i];
+ - + - +
- + - + -
+ - + - +
- ]
31 : 50 : }
32 : 18 : return normalized;
33 [ + - ]: 18 : }
34 : :
35 : : /** Compute the splitting of the columns (attributes) of the given data with the RDC algorithm.
36 : : *
37 : : * @param data the data to be split
38 : 1 : * @param variables the variable scope of the data
39 : : * @return the variable id and column id splitting candidates
40 : : */
41 : 412 : std::pair<std::vector<SmallBitset>, std::vector<SmallBitset>>rdc_split(const MatrixXf &data, SmallBitset variables)
42 : : {
43 : 412 : const auto num_cols = data.cols();
44 : 412 : AdjacencyMatrix adjacency_matrix(num_cols);
45 [ + - ]: 412 : std::vector<MatrixXf> CDF_matrices(num_cols);
46 : :
47 : : /* precompute CDF matrices */
48 [ + + + - : 1250 : for (int i = 0; i < num_cols; i++) { CDF_matrices[i] = create_CDF_matrix(data.col(i)); }
+ - + - ]
49 : :
50 : : /* build a graph with edges between correlated columns (attributes) */
51 [ + + ]: 838 : for (unsigned i = 0; i < num_cols - 1; i++) {
52 [ + + ]: 866 : for (unsigned j = i+1; j < num_cols; j++) {
53 [ + - + - ]: 440 : const float rdc_value = rdc_precomputed_CDF(CDF_matrices[i], CDF_matrices[j]);
54 : : /* if the rdc value is greater or equal to the threshold, consider columns dependent */
55 [ + + ]: 440 : if (rdc_value >= RDC_THRESHOLD) {
56 [ + - + - ]: 407 : adjacency_matrix(i,j) = true;
57 [ + - + - ]: 408 : adjacency_matrix(j,i) = true;
58 : 407 : }
59 : 440 : }
60 : 426 : }
61 : :
62 [ + - ]: 412 : SmallBitset remaining = SmallBitset::All(num_cols);
63 : 412 : std::vector<SmallBitset> connected_subgraphs;
64 : :
65 : : /* build the connected subgraphs(dependent subsets of attributes) */
66 [ + - + + ]: 843 : while (remaining) {
67 [ + - ]: 431 : auto next = remaining.begin();
68 [ + - + - ]: 431 : const SmallBitset CSG = adjacency_matrix.reachable(next.as_set());
69 [ + - ]: 431 : connected_subgraphs.emplace_back(CSG);
70 [ + - ]: 431 : remaining -= CSG;
71 : : }
72 : :
73 [ + - ]: 412 : std::vector<SmallBitset> variable_candidates(connected_subgraphs.size());
74 : 1 :
75 : 412 : std::vector<std::size_t> temp_variables;
76 [ + - ]: 412 : temp_variables.reserve(num_cols);
77 [ + - + - : 1250 : for (auto it = variables.begin(); it != variables.end(); ++it) { temp_variables.emplace_back(*it); }
+ - + + +
- + - +
- ]
78 : :
79 : : /* translate SmallBitset to correct output */
80 [ + + ]: 843 : for (std::size_t i = 0; i < connected_subgraphs.size(); ++i) {
81 [ + - + - : 1269 : for (auto it = connected_subgraphs[i].begin(); it != connected_subgraphs[i].end(); ++it) {
+ - + + +
- ]
82 [ + - + - : 838 : variable_candidates[i][temp_variables[*it]] = true;
+ - ]
83 : 838 : }
84 : 431 : }
85 : :
86 [ + - ]: 412 : return std::make_pair(connected_subgraphs, variable_candidates);
87 : 412 : }
88 : :
89 : : }
90 : :
91 : :
92 : 0 : void Spn::Node::dump() const { dump(std::cerr); }
93 : 0 : void Spn::Node::dump(std::ostream &out) const { print(out, 0); }
94 : :
95 : :
96 : : /*======================================================================================================================
97 : : * Sum Node
98 : : *====================================================================================================================*/
99 : :
100 : 70 : std::pair<float, float> Spn::Sum::evaluate(const Filter &filter, unsigned leaf_id, EvalType eval_type) const
101 : : {
102 : 70 : float expectation_result = 0.f;
103 : 70 : float likelihood_result = 0.f;
104 [ + + ]: 250 : for (auto &child : children) {
105 : 540 : auto [expectation, likelihood] = child->child->evaluate(filter, leaf_id, eval_type);
106 : 360 : expectation_result += child->weight * expectation;
107 : 360 : likelihood_result += child->weight * likelihood;
108 : : }
109 : :
110 : 70 : return { expectation_result, likelihood_result };
111 : : }
112 : :
113 : 0 : void Spn::Sum::update(VectorXf &row, SmallBitset variables, Spn::UpdateType update_type)
114 : : {
115 : : /* compute nearest cluster */
116 : 0 : unsigned nearest_centroid = 0;
117 : 0 : std::size_t num_clusters = children.size();
118 : 0 : float delta = (children[0]->centroid - row).squaredNorm();
119 [ # # ]: 0 : for (std::size_t i = 1; i < num_clusters; i++) {
120 : 0 : float next_delta = (children[i]->centroid - row).squaredNorm();
121 [ # # ]: 0 : if (next_delta < delta) {
122 : 0 : delta = next_delta;
123 : 0 : nearest_centroid = i;
124 : 0 : }
125 : 0 : }
126 : :
127 : : /* adjust weights of the sum nodes */
128 : 0 : children[nearest_centroid]->child->num_rows++;
129 : 0 : num_rows++;
130 : :
131 [ # # ]: 0 : for (std::size_t i = 0; i < num_clusters; i++) {
132 : 0 : children[i]->weight = children[i]->child->num_rows / float(num_rows);
133 : 0 : }
134 : :
135 : 0 : children[nearest_centroid]->child->update(row, variables, update_type);
136 : 0 : }
137 : :
138 : 0 : std::size_t Spn::Sum::estimate_number_distinct_values(unsigned id) const
139 : : {
140 : 0 : std::size_t result = 0;
141 [ # # ]: 0 : for (auto &child : children) {
142 : 0 : result += child->child->estimate_number_distinct_values(id);
143 : : }
144 : :
145 : 0 : return std::min(result, num_rows);
146 : : }
147 : :
148 : 0 : void Spn::Sum::print(std::ostream &out, std::size_t num_tabs) const
149 : : {
150 [ # # ]: 0 : for (std::size_t i = 0; i < children.size(); i++) {
151 [ # # ]: 0 : for (std::size_t n = 0; n < num_tabs; n++) { out << "\t"; }
152 [ # # ]: 0 : if (i != 0) { out << "+ "; }
153 : 0 : out << children[i]->weight << "\n";
154 : 0 : children[i]->child->print(out, num_tabs+1);
155 : 0 : }
156 : 0 : }
157 : :
158 : :
159 : : /*======================================================================================================================
160 : : * Product Node
161 : : *====================================================================================================================*/
162 : :
163 : 133 : std::pair<float, float> Spn::Product::evaluate(const Filter &filter, unsigned leaf_id, EvalType eval_type) const
164 : : {
165 : 133 : float expectation_result = 1.f;
166 : 133 : float likelihood_result = 1.f;
167 [ + + ]: 399 : for (auto &child : children) {
168 [ + + ]: 399 : for (const auto & it : filter) {
169 [ + + ]: 266 : if (child->variables[it.first]) {
170 : 266 : auto [expectation, likelihood] = child->child->evaluate(filter, it.first, eval_type);
171 : 134 : expectation_result *= expectation;
172 : 133 : likelihood_result *= likelihood;
173 : 133 : break;
174 : : }
175 : : }
176 : : }
177 : 133 : return {expectation_result, likelihood_result };
178 : : }
179 : :
180 : 0 : void Spn::Product::update(VectorXf &row, SmallBitset variables, UpdateType update_type)
181 : : {
182 : 0 : std::unordered_map<unsigned, unsigned> variable_to_index;
183 : 0 : unsigned index = 0;
184 [ # # # # : 0 : for (auto it = variables.begin(); it != variables.end(); ++it) { variable_to_index.emplace(*it, index++); }
# # # # #
# # # #
# ]
185 : :
186 : : /* update each child of the product node with the according subset of attributes of the row */
187 [ # # ]: 0 : for (auto &child : children) {
188 [ # # ]: 0 : std::size_t num_cols = child->variables.size();
189 [ # # ]: 1 : VectorXf proj_row(num_cols);
190 [ # # ]: 0 : auto it = child->variables.begin();
191 [ # # ]: 0 : for (std::size_t i = 0; i < num_cols; ++i) {
192 [ # # # # : 0 : proj_row(i) = row(variable_to_index[*it]);
# # # # ]
193 [ # # ]: 0 : ++it;
194 : 0 : }
195 [ # # ]: 0 : child->child->update(proj_row, child->variables, update_type);
196 : 0 : }
197 : 0 : }
198 : :
199 : 0 : std::size_t Spn::Product::estimate_number_distinct_values(unsigned id) const
200 : : {
201 [ # # ]: 0 : for (auto &child : children) {
202 [ # # ]: 0 : if (child->variables[id]) {
203 : 0 : return child->child->estimate_number_distinct_values(id);
204 : : }
205 : : }
206 : :
207 : 0 : return 0;
208 : 0 : }
209 : :
210 : 0 : void Spn::Product::print(std::ostream &out, std::size_t num_tabs) const
211 : : {
212 [ # # ]: 0 : for (std::size_t i = 0; i < children.size(); i++) {
213 [ # # ]: 0 : for (std::size_t n = 0; n < num_tabs; n++) { out << "\t"; }
214 [ # # ]: 0 : if (i != 0) { out << "* "; }
215 : 0 : out << "variable scope=(";
216 [ # # ]: 0 : for (auto it = children[i]->variables.begin(); it != children[i]->variables.end(); ++it) { out << *it; }
217 : 0 : out << "):" << "\n";
218 : 0 : children[i]->child->print(out, num_tabs+1);
219 : 0 : }
220 : 0 : }
221 : :
222 : :
223 : : /*======================================================================================================================
224 : : * DiscreteLeaf Node
225 : : *====================================================================================================================*/
226 : :
227 : 62 : std::pair<float, float> Spn::DiscreteLeaf::evaluate(const Filter &filter, unsigned leaf_id, EvalType eval_type) const
228 : : {
229 : 114 : auto [spn_operator, value] = filter.at(leaf_id);
230 : :
231 [ + + ]: 62 : if (spn_operator == IS_NULL) { return { null_probability, null_probability }; }
232 : :
233 [ - + ]: 50 : if (bins.empty()) { return { 0.f, 0.f }; }
234 : :
235 [ + + ]: 50 : if (spn_operator == EXPECTATION) {
236 : 1 : float expectation = bins[0].cumulative_probability * bins[0].value;
237 : 1 : float prev_probability = bins[0].cumulative_probability;
238 [ - + ]: 1 : for (std::size_t bin_id = 1; bin_id < bins.size(); bin_id++) {
239 : 0 : float cumulative_probability = bins[bin_id].cumulative_probability;
240 : 0 : float probability = cumulative_probability - prev_probability;
241 : 0 : expectation += probability * bins[bin_id].value;
242 : 0 : prev_probability = cumulative_probability;
243 : 0 : }
244 : 1 : return {expectation, 1.f };
245 : : }
246 : : /* probability of last bin, because the cumulative probability of the last bin is not always 1 because of NULL */
247 : 49 : float last_prob = std::prev(bins.end())->cumulative_probability;
248 : 49 : float probability = 0.f;
249 : :
250 [ + + ]: 49 : if (spn_operator == SpnOperator::EQUAL) {
251 [ - + - + : 2 : if (bins.begin()->value > value or (std::prev(bins.end()))->value < value) { return { 0.f, 0.f }; }
- + ]
252 : 2 : auto lower_bound = std::lower_bound(bins.begin(), bins.end(), value);
253 [ - + - + ]: 2 : if (lower_bound->value == value) {
254 : 1 : probability = lower_bound->cumulative_probability;
255 [ + - ]: 1 : if (lower_bound != bins.begin()) { probability -= (--lower_bound)->cumulative_probability; }
256 : 1 : }
257 : 1 : return { probability, probability };
258 : : }
259 : :
260 [ + + ]: 48 : if (spn_operator == SpnOperator::LESS) {
261 [ - + - + ]: 24 : if (bins.begin()->value >= value) { return { 0.f, 0.f }; }
262 [ + - + - ]: 24 : if ((std::prev(bins.end()))->value < value) { return { last_prob, last_prob }; }
263 : 0 : auto lower_bound = std::lower_bound(bins.begin(), bins.end(), value);
264 : 0 : probability = (--lower_bound)->cumulative_probability;
265 : 0 : return { probability, probability };
266 : : }
267 : :
268 [ + + ]: 36 : if (spn_operator == SpnOperator::LESS_EQUAL) {
269 [ - + - + ]: 24 : if (bins.begin()->value > value) { return { 0.f, 0.f }; }
270 [ + - + - ]: 24 : if ((std::prev(bins.end()))->value <= value) { return { last_prob, last_prob }; }
271 : 0 : auto upper_bound = std::upper_bound(bins.begin(), bins.end(), value,
272 : 0 : [](float bin_value, DiscreteLeaf::Bin bin) { return bin_value < bin.value; }
273 : : );
274 : 0 : probability = (--upper_bound)->cumulative_probability;
275 : 0 : return { probability, probability };
276 : : }
277 : :
278 [ + + ]: 24 : if (spn_operator == SpnOperator::GREATER) {
279 [ + - + - ]: 24 : if (bins.begin()->value > value) { return { last_prob, last_prob }; }
280 [ # # # # ]: 0 : if ((std::prev(bins.end()))->value <= value) { return { 0.f, 0.f }; }
281 : 0 : auto upper_bound = std::upper_bound(bins.begin(), bins.end(), value,
282 : 0 : [](float bin_value, DiscreteLeaf::Bin bin) { return bin_value < bin.value; }
283 : : );
284 : 0 : probability = last_prob - (--upper_bound)->cumulative_probability;
285 : 0 : return { probability, probability };
286 : : }
287 : :
288 [ + - ]: 12 : if (spn_operator == SpnOperator::GREATER_EQUAL) {
289 [ + - + - ]: 24 : if (bins.begin()->value >= value) { return { last_prob, last_prob }; }
290 [ # # # # ]: 0 : if ((std::prev(bins.end()))->value < value) { return { 0.f, 0.f }; }
291 : 0 : auto lower_bound = std::lower_bound(bins.begin(), bins.end(), value);
292 : 0 : probability = last_prob - (--lower_bound)->cumulative_probability;
293 : 0 : return { probability, probability };
294 : : }
295 : :
296 : 0 : return { 0.f, 0.f };
297 : 62 : }
298 : :
299 : 0 : void Spn::DiscreteLeaf::update(VectorXf &row, SmallBitset variables, Spn::UpdateType update_type)
300 : : {
301 : 0 : const float value = row(0);
302 : :
303 [ # # ]: 0 : if (bins.empty()) {
304 [ # # ]: 0 : if (update_type == INSERT) {
305 : 0 : bins.emplace_back(value, 1.f/(num_rows + 1));
306 : 0 : null_probability = (null_probability * num_rows) / (num_rows + 1);
307 : 0 : num_rows++;
308 : 0 : }
309 : 0 : return;
310 : : }
311 : :
312 : : /* copy bins with the actual number of values in a bin */
313 : 0 : std::vector<Bin> updated_bins;
314 [ # # ]: 0 : updated_bins.reserve(bins.size());
315 [ # # ]: 0 : updated_bins.emplace_back(bins[0].value, bins[0].cumulative_probability * num_rows);
316 [ # # ]: 0 : for (std::size_t i = 1; i < bins.size(); i++) {
317 [ # # ]: 0 : updated_bins.emplace_back(
318 : 0 : bins[i].value,
319 : 0 : (bins[i].cumulative_probability - bins[i-1].cumulative_probability) * num_rows
320 : : );
321 : 0 : }
322 : :
323 : : /* insert the update value into the correct bin or create a new one if the bin does not exist */
324 [ # # ]: 0 : if (update_type == INSERT) {
325 [ # # ]: 0 : auto lower_bound = std::lower_bound(updated_bins.begin(), updated_bins.end(), value);
326 [ # # # # ]: 0 : if (lower_bound == updated_bins.end()) { updated_bins.emplace_back(value, 1.f); }
327 [ # # # # ]: 0 : else if (lower_bound->value != value) { updated_bins.emplace(lower_bound, value, 1.f); }
328 : 0 : else { lower_bound->cumulative_probability += 1; }
329 : 0 : num_rows++;
330 : 0 : }
331 : : /* delete the update value from the correct bin */
332 : : else {
333 [ # # ]: 0 : auto lower_bound = std::lower_bound(updated_bins.begin(), updated_bins.end(), value);
334 [ # # ]: 0 : if (lower_bound->value != value) { return; }
335 : 0 : lower_bound->cumulative_probability -= 1;
336 : 0 : num_rows--;
337 : : }
338 : :
339 : : /* calculate the cumulative probability */
340 : 0 : updated_bins[0].cumulative_probability /= float(num_rows);
341 [ # # ]: 0 : for (std::size_t i = 1; i < updated_bins.size(); i++) {
342 : 0 : updated_bins[i].cumulative_probability /= float(num_rows);
343 : 0 : updated_bins[i].cumulative_probability += updated_bins[i-1].cumulative_probability;
344 : 0 : }
345 : :
346 : 0 : bins = std::move(updated_bins);
347 [ # # ]: 0 : }
348 : :
349 : 0 : std::size_t Spn::DiscreteLeaf::estimate_number_distinct_values(unsigned id) const
350 : : {
351 : 0 : return bins.size();
352 : : }
353 : :
354 : 0 : void Spn::DiscreteLeaf::print(std::ostream &out, std::size_t num_tabs) const
355 : : {
356 [ # # ]: 0 : for (std::size_t n = 0; n < num_tabs; n++) { out << "\t"; }
357 : 0 : out << "[";
358 [ # # ]: 0 : if (not bins.empty()) {
359 : 0 : out << bins[0].value << ":" << bins[0].cumulative_probability;
360 [ # # ]: 0 : for (std::size_t i = 1; i < bins.size(); i++) {
361 : 0 : out << ", " << bins[i].value << ":" << bins[i].cumulative_probability - bins[i-1].cumulative_probability;
362 : 0 : }
363 : 0 : out << " ";
364 : 0 : }
365 : 0 : out << "null_prob:" << null_probability;
366 : 0 : out << "]";
367 : 0 : out << "\n";
368 : 0 : }
369 : :
370 : :
371 : : /*======================================================================================================================
372 : : * ContinuousLeaf Node
373 : : *====================================================================================================================*/
374 : :
375 : 61 : std::pair<float, float> Spn::ContinuousLeaf::evaluate(const Filter &filter, unsigned leaf_id, EvalType eval_type) const
376 : : {
377 : 172 : auto [spn_operator, value] = filter.at(leaf_id);
378 : 61 : float probability = 0.f;
379 [ + + ]: 61 : if (spn_operator == IS_NULL) { return { null_probability, null_probability }; }
380 [ - + ]: 49 : if (bins.empty()) { return { 0.f, 0.f }; }
381 : :
382 [ - + ]: 49 : if (spn_operator == SpnOperator::EXPECTATION) {
383 : 0 : float expectation = lower_bound * lower_bound_probability;
384 : 0 : expectation += bins[0].cumulative_probability * ((bins[0].upper_bound + lower_bound) / 2.f);
385 : 0 : float prev_probability = bins[0].cumulative_probability;
386 : 0 : float prev_upper_bound = bins[0].upper_bound;
387 [ # # ]: 0 : for (std::size_t bin_id = 1; bin_id < bins.size(); bin_id++) {
388 : 0 : float upper_bound = bins[bin_id].upper_bound;
389 : 0 : float cumulative_probability = bins[bin_id].cumulative_probability;
390 : 0 : float current_probability = cumulative_probability - prev_probability;
391 : 0 : expectation += current_probability * ((prev_upper_bound + upper_bound) / 2);
392 : 0 : prev_probability = cumulative_probability;
393 : 0 : prev_upper_bound = upper_bound;
394 : 0 : }
395 : 0 : return std::make_pair(expectation, 1.f);
396 : : }
397 : :
398 [ + + ]: 49 : if (spn_operator == SpnOperator::EQUAL) {
399 [ - + - + : 2 : if (lower_bound > value or (std::prev(bins.end()))->upper_bound < value) { return { 0.f, 0.f }; }
- + ]
400 [ + - + - ]: 2 : if (lower_bound == value) { return { lower_bound_probability, lower_bound_probability }; }
401 [ # # ]: 0 : if (value <= bins[0].upper_bound) {
402 : 0 : probability = bins[0].cumulative_probability - lower_bound_probability;
403 : 0 : return { probability, probability };
404 : : }
405 : 0 : auto std_lower_bound = std::lower_bound(bins.begin(), bins.end(), value);
406 : 0 : probability = std_lower_bound->cumulative_probability - (--std_lower_bound)->cumulative_probability;
407 : 0 : return { probability, probability };
408 : : }
409 : :
410 [ + + + + ]: 48 : if (spn_operator == LESS or spn_operator == LESS_EQUAL) {
411 [ - + - + ]: 48 : if (lower_bound >= value) {
412 [ # # # # : 0 : if (lower_bound == value and spn_operator == LESS_EQUAL) {
# # ]
413 : 0 : return { lower_bound_probability, lower_bound_probability };
414 : : }
415 : 0 : return { 0.f, 0.f };
416 : : }
417 [ + - + - ]: 48 : if ((std::prev(bins.end()))->upper_bound <= value) {
418 : 24 : probability = std::prev(bins.end())->cumulative_probability;
419 : 24 : return { probability, probability };
420 : : }
421 [ # # ]: 0 : if (value <= bins[0].upper_bound) {
422 : 0 : float prob = bins[0].cumulative_probability - lower_bound_probability;
423 : 0 : probability = lower_bound_probability +
424 : 0 : (prob * ((value - lower_bound) / (bins[0].upper_bound - lower_bound)));
425 : 0 : return { probability, probability };
426 : : }
427 : 0 : auto std_lower_bound = std::lower_bound(bins.begin(), bins.end(), value);
428 : 0 : auto &bin = *std_lower_bound;
429 : 0 : auto &prev_bin = *(--std_lower_bound);
430 : 0 : float bin_probability = bin.cumulative_probability - prev_bin.cumulative_probability;
431 : 0 : float percentile = (value - prev_bin.upper_bound) / (bin.upper_bound - prev_bin.upper_bound);
432 [ # # # # ]: 0 : switch (eval_type) {
433 : : case APPROXIMATE:
434 : 0 : probability = prev_bin.cumulative_probability + (bin_probability * percentile);
435 : : case UPPER_BOUND:
436 : 0 : probability = bin.cumulative_probability;
437 : : case LOWER_BOUND:
438 : 0 : probability = prev_bin.cumulative_probability;
439 : 0 : }
440 : 0 : return { probability, probability };
441 : : }
442 : :
443 [ + + + - ]: 24 : if (spn_operator == GREATER_EQUAL or spn_operator == GREATER) {
444 : 24 : float last_prob = std::prev(bins.end())->cumulative_probability;
445 [ + - + - ]: 48 : if (lower_bound >= value) {
446 [ + + + + : 48 : if (lower_bound == value and spn_operator == GREATER) {
+ - ]
447 : 0 : probability = last_prob - lower_bound_probability;
448 : 0 : return { probability, probability };
449 : : }
450 : 24 : return { last_prob, last_prob };
451 : : }
452 [ # # # # ]: 0 : if ((std::prev(bins.end()))->upper_bound <= value) { return { 0.f, 0.f }; }
453 [ # # ]: 0 : if (value <= bins[0].upper_bound) {
454 : 0 : float prob = bins[0].cumulative_probability - lower_bound_probability;
455 : 0 : float split_bin_prob = (prob * (1.f - ((value - lower_bound) / (bins[0].upper_bound - lower_bound))));
456 : 0 : probability = last_prob - bins[0].cumulative_probability + split_bin_prob;
457 : 0 : return { probability, probability };
458 : : }
459 : 0 : auto std_lower_bound = std::lower_bound(bins.begin(), bins.end(), value);
460 : 0 : auto &bin = *std_lower_bound;
461 : 0 : auto &prev_bin = *(--std_lower_bound);
462 : 0 : float bin_probability = bin.cumulative_probability - prev_bin.cumulative_probability;
463 : 0 : float percentile = 1.f - ((value - prev_bin.upper_bound) / (bin.upper_bound - prev_bin.upper_bound));
464 [ # # # # ]: 0 : switch (eval_type) {
465 : : case APPROXIMATE:
466 : 0 : probability = last_prob - bin.cumulative_probability + (bin_probability * percentile);
467 : : case UPPER_BOUND:
468 : 0 : probability = last_prob - prev_bin.cumulative_probability;
469 : : case LOWER_BOUND:
470 : 0 : probability = last_prob - bin.cumulative_probability;
471 : 0 : }
472 : 0 : return { probability, probability };
473 : : }
474 : :
475 : 0 : return { 0.f, 0.f };
476 : 61 : }
477 : :
478 : 0 : void Spn::ContinuousLeaf::update(VectorXf &row, SmallBitset variables, Spn::UpdateType update_type)
479 : : {
480 : 0 : const float value = row(0);
481 : :
482 [ # # ]: 0 : if (bins.empty()) {
483 [ # # ]: 0 : if (update_type == INSERT) {
484 : 0 : bins.emplace_back(value, 1.f/(num_rows + 1));
485 : 0 : null_probability = (null_probability * num_rows) / (num_rows + 1);
486 : 0 : num_rows++;
487 : 0 : }
488 : 0 : return;
489 : : }
490 : :
491 : : /* copy bins with the actual number of values in a bin */
492 : 0 : std::vector<Bin> updated_bins;
493 : 0 : float updated_lower_bound_prob = lower_bound_probability * num_rows;
494 [ # # ]: 0 : updated_bins.reserve(bins.size());
495 [ # # ]: 0 : updated_bins.emplace_back(
496 : 0 : bins[0].upper_bound,
497 : 0 : (bins[0].cumulative_probability - lower_bound_probability) * num_rows
498 : : );
499 [ # # ]: 0 : for (std::size_t i = 1; i < bins.size(); i++) {
500 [ # # ]: 0 : updated_bins.emplace_back(
501 : 0 : bins[i].upper_bound,
502 : 0 : (bins[i].cumulative_probability - bins[i-1].cumulative_probability) * num_rows
503 : : );
504 : 0 : }
505 : :
506 : : /* insert the update value into the correct bin or create a new one if the bin does not exist */
507 [ # # ]: 0 : if (update_type == INSERT) {
508 [ # # ]: 0 : if (value == lower_bound) {
509 : 0 : updated_lower_bound_prob += 1;
510 [ # # ]: 0 : } else if (value < lower_bound) {
511 : 0 : updated_bins[0].cumulative_probability += updated_lower_bound_prob;
512 : 0 : lower_bound = value;
513 : 0 : updated_lower_bound_prob = 1.f;
514 [ # # ]: 0 : } else if (value > updated_bins[updated_bins.size() - 1].upper_bound) {
515 [ # # ]: 0 : updated_bins.emplace_back(value, 1.f);
516 : 0 : } else {
517 [ # # ]: 0 : auto std_lower_bound = std::lower_bound(updated_bins.begin(), updated_bins.end(), value);
518 : 0 : std_lower_bound->cumulative_probability += 1;
519 : : }
520 : 0 : num_rows++;
521 : 0 : }
522 : : /* delete the update value from the correct bin */
523 : : else {
524 [ # # # # ]: 0 : if (value < lower_bound or value > updated_bins[updated_bins.size() - 1].upper_bound) { return; }
525 [ # # ]: 0 : if (value == lower_bound) {
526 : 0 : updated_lower_bound_prob += 1;
527 : 0 : } else {
528 [ # # ]: 0 : auto std_lower_bound = std::lower_bound(updated_bins.begin(), updated_bins.end(), value);
529 [ # # ]: 0 : if (std_lower_bound->cumulative_probability == 0) { return; }
530 : 0 : std_lower_bound->cumulative_probability -= 1;
531 : : }
532 : 0 : num_rows--;
533 : : }
534 : :
535 : : /* calculate the cumulative probability */
536 : 0 : updated_lower_bound_prob /= float(num_rows);
537 : 0 : updated_bins[0].cumulative_probability /= float(num_rows);
538 : 0 : updated_bins[0].cumulative_probability += updated_lower_bound_prob;
539 [ # # ]: 0 : for (std::size_t i = 1; i < updated_bins.size(); i++) {
540 : 0 : updated_bins[i].cumulative_probability /= float(num_rows);
541 : 0 : updated_bins[i].cumulative_probability += updated_bins[i-1].cumulative_probability;
542 : 0 : }
543 : 0 : lower_bound_probability = updated_lower_bound_prob;
544 : 0 : bins = std::move(updated_bins);
545 [ # # ]: 0 : }
546 : :
547 : 0 : std::size_t Spn::ContinuousLeaf::estimate_number_distinct_values(unsigned id) const
548 : : {
549 : 0 : return num_rows;
550 : : }
551 : :
552 : 0 : void Spn::ContinuousLeaf::print(std::ostream &out, std::size_t num_tabs) const
553 : : {
554 [ # # ]: 0 : for (std::size_t n = 0; n < num_tabs; n++) { out << "\t"; }
555 : 0 : out << "[";
556 [ # # ]: 0 : if (not bins.empty()) {
557 : 0 : out << lower_bound_probability << ": ";
558 : 0 : out << lower_bound;
559 : 0 : out << " :" << bins[0].cumulative_probability - lower_bound_probability << ": " << bins[0].upper_bound;
560 [ # # ]: 0 : for (std::size_t i = 1; i < bins.size(); i++) {
561 : 0 : out << " :" << bins[i].cumulative_probability - bins[i-1].cumulative_probability;
562 : 0 : out << ": " << bins[i].upper_bound;
563 : 0 : }
564 : 0 : out << " ";
565 : 0 : }
566 : 0 : out << "null_prob:" << null_probability;
567 : 0 : out << "]";
568 : 0 : out << "\n";
569 : 0 : }
570 : :
571 : : /*======================================================================================================================
572 : : * Spn
573 : : *====================================================================================================================*/
574 : :
575 : : /*----- Learning helper methods --------------------------------------------------------------------------------------*/
576 : :
577 : 170 : std::unique_ptr<Spn::Product> Spn::create_product_min_slice(LearningData &ld)
578 : : {
579 : 170 : std::vector<std::unique_ptr<Product::ChildWithVariables>> children;
580 [ + - ]: 170 : auto variable_it = ld.variables.begin();
581 [ + + ]: 511 : for (auto i = 0; i < ld.data.cols(); i++) {
582 [ + - + - ]: 341 : const MatrixXf &data = ld.data.col(i);
583 [ + - + - ]: 341 : const MatrixXf &normalized = ld.normalized.col(i);
584 [ + - + - ]: 341 : const MatrixXi &null_matrix = ld.null_matrix.col(i);
585 [ + - ]: 341 : SmallBitset variables(variable_it.as_set());
586 [ + - ]: 341 : ++variable_it;
587 [ + - ]: 341 : std::vector<LeafType> split_leaf_types{ld.leaf_types[i]};
588 [ - + ]: 341 : LearningData split_data(
589 : 341 : data,
590 : 341 : normalized,
591 : 341 : null_matrix,
592 : 341 : variables,
593 [ - + ]: 341 : split_leaf_types
594 : : );
595 [ - + - + ]: 341 : auto child = std::make_unique<Product::ChildWithVariables>(learn_node(split_data), variables);
596 [ - + ]: 341 : children.push_back(std::move(child));
597 : 341 : }
598 [ + - ]: 170 : return std::make_unique<Product>(std::move(children), ld.data.rows());
599 : 170 : }
600 : :
601 : :
602 : 16 : std::unique_ptr<Spn::Product> Spn::create_product_rdc(
603 : : LearningData &ld,
604 : : std::vector<SmallBitset> &column_candidates,
605 : : std::vector<SmallBitset> &variable_candidates
606 : : )
607 : : {
608 : 16 : std::vector<std::unique_ptr<Product::ChildWithVariables>> children;
609 [ + + ]: 48 : for (std::size_t current_split = 0; current_split < column_candidates.size(); current_split++) {
610 [ + - ]: 32 : std::size_t split_size = column_candidates[current_split].size();
611 : 32 : std::vector<LeafType> split_leaf_types;
612 [ + - ]: 32 : split_leaf_types.reserve(split_size);
613 : 32 : std::vector<unsigned> column_index;
614 [ + - ]: 32 : column_index.reserve(split_size);
615 [ + - + - : 78 : for (auto it = column_candidates[current_split].begin(); it != column_candidates[current_split].end(); ++it) {
+ - + + +
- ]
616 [ + - + - ]: 46 : split_leaf_types.push_back(ld.leaf_types[*it]);
617 [ + - + - ]: 46 : column_index.push_back(*it);
618 : 46 : }
619 : :
620 [ + - + - ]: 32 : const MatrixXf &data = ld.data(all, column_index);
621 [ + - + - ]: 32 : const MatrixXf &normalized = ld.normalized(all, column_index);
622 [ + - + - ]: 32 : const MatrixXi &null_matrix = ld.null_matrix(all, column_index);
623 [ + - + - ]: 32 : LearningData split_data(data, normalized, null_matrix, variable_candidates[current_split], split_leaf_types);
624 [ - + ]: 32 : auto child = std::make_unique<Product::ChildWithVariables>(
625 [ + - ]: 32 : learn_node(split_data),
626 : 32 : variable_candidates[current_split]
627 : : );
628 [ - + ]: 32 : children.push_back(std::move(child));
629 : 32 : }
630 [ + - ]: 16 : return std::make_unique<Product>(std::move(children), ld.data.rows());
631 : 16 : }
632 : :
633 : 99 : std::unique_ptr<Spn::Sum> Spn::create_sum(Spn::LearningData &ld)
634 : : {
635 : 99 : const auto num_rows = ld.data.rows();
636 : :
637 : 99 : int k = 2;
638 : :
639 : 99 : unsigned prev_num_split_nodes = 0;
640 : 99 : std::vector<std::vector<SmallBitset>> prev_cluster_column_candidates;
641 : 99 : std::vector<std::vector<SmallBitset>> prev_cluster_variable_candidates;
642 : 99 : std::vector<std::vector<unsigned>> prev_cluster_row_ids;
643 [ + - ]: 99 : MatrixRXf prev_centroids;
644 : :
645 : : /* increment k of Kmeans until we get a good clustering according to RDC splits in the clusters */
646 : 254 : while (true) {
647 : 254 : unsigned num_split_nodes = 0;
648 : :
649 [ + - ]: 11629 : auto [labels, centroids] = kmeans_with_centroids(ld.normalized, k);
650 : :
651 [ + - ]: 254 : std::vector<std::vector<SmallBitset>> cluster_column_candidates(k);
652 [ + - ]: 254 : std::vector<std::vector<SmallBitset>> cluster_variable_candidates(k);
653 [ + - ]: 254 : std::vector<std::vector<unsigned>> cluster_row_ids(k);
654 : :
655 [ + + ]: 11474 : for (unsigned current_row = 0; current_row < num_rows; current_row++) {
656 [ + - ]: 11220 : cluster_row_ids[labels[current_row]].push_back(current_row);
657 : 11220 : }
658 : :
659 : : /* if only one cluster, split this cluster into k clusters */
660 : 254 : bool only_one_cluster = not cluster_row_ids[0].empty();
661 [ + + ]: 803 : for (int i = 1; i < k; i++)
662 [ + + ]: 549 : only_one_cluster = only_one_cluster and cluster_row_ids[i].empty();
663 : :
664 [ + - ]: 254 : if (only_one_cluster) {
665 : 0 : const std::size_t subvector_size = cluster_row_ids[0].size() / k;
666 [ # # ]: 0 : std::vector<std::vector<unsigned>> new_cluster_row_ids(k);
667 [ # # ]: 0 : for (std::size_t cluster_id = 0; cluster_id < k - 1; cluster_id++) {
668 [ # # ]: 0 : std::vector<unsigned> subvector(
669 : 0 : cluster_row_ids[0].begin() + (cluster_id * subvector_size),
670 : 0 : cluster_row_ids[0].begin() + ((cluster_id + 1) * subvector_size)
671 : : );
672 : 0 : new_cluster_row_ids[cluster_id] = std::move(subvector);
673 : 0 : }
674 [ # # ]: 0 : std::vector<unsigned> last_subvector(
675 : 0 : cluster_row_ids[0].begin() + ((k - 1) * subvector_size),
676 : 0 : cluster_row_ids[0].end()
677 : : );
678 : 0 : new_cluster_row_ids[k - 1] = std::move(last_subvector);
679 : :
680 : 0 : cluster_row_ids = std::move(new_cluster_row_ids);
681 : 0 : }
682 : :
683 : : /* check the splitting of attributes in each cluster */
684 [ + + ]: 1057 : for (unsigned label_id = 0; label_id < k; label_id++) {
685 : 803 : std::size_t cluster_size = cluster_row_ids[label_id].size();
686 [ + - ]: 803 : if (cluster_size == 0) { continue; }
687 : :
688 [ + - + - ]: 803 : const MatrixXf &data = ld.data(cluster_row_ids[label_id], all);
689 : :
690 [ + + ]: 803 : if (cluster_size <= MIN_INSTANCE_SLICE) {
691 : 420 : num_split_nodes++;
692 : 420 : cluster_column_candidates[label_id] = std::vector<SmallBitset>();
693 : 420 : cluster_variable_candidates[label_id] = std::vector<SmallBitset>();
694 : 420 : } else {
695 [ + - ]: 393 : auto [current_column_candidates, current_variable_candidates] = rdc_split(data, ld.variables);
696 [ + + ]: 383 : if (current_column_candidates.size() > 1) { num_split_nodes++; }
697 : 383 : cluster_column_candidates[label_id] = std::move(current_column_candidates);
698 : 383 : cluster_variable_candidates[label_id] = std::move(current_variable_candidates);
699 : 383 : }
700 : 803 : }
701 : :
702 : : /* if the number of split attributes does not increase or if there is a split in each cluster, build sum node */
703 : : if (
704 [ + + ]: 409 : ((num_split_nodes <= prev_num_split_nodes or prev_num_split_nodes == prev_cluster_row_ids.size())
705 [ + + ]: 254 : and prev_num_split_nodes != 0) or k >= MAX_K
706 : : ) {
707 : 99 : std::vector<std::unique_ptr<Sum::ChildWithWeight>> children;
708 [ + + ]: 353 : for (std::size_t cluster_id = 0; cluster_id < k - 1; cluster_id++) {
709 [ + - + - ]: 254 : const MatrixXf &data = ld.data(prev_cluster_row_ids[cluster_id], all);
710 [ + - + - ]: 254 : const MatrixXf &normalized = ld.normalized(prev_cluster_row_ids[cluster_id], all);
711 [ + - + - ]: 254 : const MatrixXi &null_matrix = ld.null_matrix(prev_cluster_row_ids[cluster_id], all);
712 [ + - + - ]: 254 : LearningData cluster_data(data, normalized, null_matrix, ld.variables, ld.leaf_types);
713 : 254 : const float weight = float(data.rows())/float(num_rows);
714 : 254 : std::size_t cluster_vertical_partitions = prev_cluster_column_candidates[cluster_id].size();
715 : :
716 : : /* since we already determined the node type of each cluster (child of the sum node), we can
717 : : * directly build the children of the sum node */
718 : 254 : std::unique_ptr<Node> child_node;
719 [ + + ]: 254 : if (cluster_vertical_partitions == 0) {
720 [ + - ]: 168 : child_node = create_product_min_slice(cluster_data);
721 [ + + ]: 254 : } else if (cluster_vertical_partitions == 1) {
722 [ + - ]: 84 : child_node = create_sum(cluster_data);
723 : 84 : } else {
724 [ + - ]: 2 : child_node = create_product_rdc(
725 : : cluster_data,
726 : 2 : prev_cluster_column_candidates[cluster_id],
727 : 2 : prev_cluster_variable_candidates[cluster_id]
728 : : );
729 : : }
730 [ + - ]: 254 : std::unique_ptr<Sum::ChildWithWeight> child = std::make_unique<Sum::ChildWithWeight>(
731 : : std::move(child_node),
732 : : weight,
733 [ + - ]: 254 : prev_centroids.row(cluster_id)
734 : : );
735 : :
736 [ + - ]: 254 : children.push_back(std::move(child));
737 : 254 : }
738 : :
739 [ + - ]: 99 : return std::make_unique<Sum>(std::move(children), num_rows);
740 : 99 : }
741 : 155 : prev_num_split_nodes = num_split_nodes;
742 : 155 : prev_cluster_column_candidates = std::move(cluster_column_candidates);
743 : 155 : prev_cluster_variable_candidates = std::move(cluster_variable_candidates);
744 : 155 : prev_cluster_row_ids = std::move(cluster_row_ids);
745 [ + - ]: 155 : prev_centroids = centroids;
746 : 155 : k++;
747 [ + + ]: 254 : }
748 : 99 : }
749 : :
750 : :
751 : 391 : std::unique_ptr<Spn::Node> Spn::learn_node(LearningData &ld)
752 : : {
753 : 391 : const auto num_rows = ld.data.rows();
754 : 391 : const auto num_cols = ld.data.cols();
755 : :
756 : : /* build leaf */
757 [ + + ]: 391 : if (num_cols == 1) {
758 [ + + ]: 360 : if (ld.leaf_types[0] == DISCRETE) {
759 : 185 : std::vector<DiscreteLeaf::Bin> bins;
760 : : /* If every value is NULL */
761 [ + - - + ]: 185 : if (ld.null_matrix.minCoeff() == 1) {
762 [ # # ]: 0 : return std::make_unique<DiscreteLeaf>(std::move(bins), 1.f, num_rows);
763 : : }
764 : 185 : unsigned null_counter = 0;
765 [ + + ]: 2311 : for (auto i = 0; i < num_rows; i++) {
766 [ + - + - ]: 2126 : if (ld.null_matrix(i, 0) == 1) {
767 : 0 : null_counter++;
768 : 0 : continue;
769 : : }
770 [ + - ]: 2126 : const auto current_value = ld.data(i, 0);
771 : : /* insert value into sorted vector of bins */
772 [ + - ]: 2126 : auto lower_bound = std::lower_bound(bins.begin(), bins.end(), current_value);
773 [ + + + - ]: 2126 : if (lower_bound == bins.end()) { bins.emplace_back(current_value, 1.f); }
774 : : /* if bin already exists, increment counter */
775 [ - + ]: 701 : else if (lower_bound->value == current_value) { lower_bound->cumulative_probability++; }
776 [ # # ]: 0 : else { bins.emplace(lower_bound, current_value, 1.f); }
777 : 2126 : }
778 : :
779 : 185 : bins[0].cumulative_probability /= float(num_rows);
780 [ + + ]: 1425 : for (std::size_t i = 1; i < bins.size(); i++) {
781 : 1240 : bins[i].cumulative_probability /= float(num_rows);
782 : 1240 : bins[i].cumulative_probability += bins[i-1].cumulative_probability;
783 : 1240 : }
784 [ + - ]: 185 : return std::make_unique<DiscreteLeaf>(std::move(bins), null_counter / float(num_rows), num_rows);
785 : 185 : } else {
786 : 175 : std::vector<ContinuousLeaf::Bin> bins;
787 : : /* If every value is NULL */
788 [ + - - + ]: 175 : if (ld.null_matrix.minCoeff() == 1) {
789 [ # # ]: 0 : return std::make_unique<ContinuousLeaf>(std::move(bins), 0.f, 0.f, 1.f, num_rows);
790 : : }
791 [ + - ]: 175 : const float max = ld.data.maxCoeff();
792 [ + - ]: 175 : const float min = ld.data.minCoeff();
793 [ + - ]: 175 : const auto num_bins = 1 + log2_ceil(std::size_t(num_rows));
794 : 175 : const float bin_width = (max - min) / float(num_bins);
795 : 175 : const float lower_bound = min;
796 : 175 : unsigned lower_bound_counter = 0.f;
797 : 175 : unsigned null_counter = 0.f;
798 : :
799 : : /* initialise bins */
800 [ + - ]: 175 : bins.reserve(num_bins);
801 [ + + ]: 959 : for(std::size_t bin_id = 0; bin_id < num_bins; bin_id++) {
802 [ + - ]: 784 : bins.emplace_back(min + (float(bin_id + 1) * bin_width), 0.f);
803 : 784 : }
804 : :
805 : : /* fill the values into the existing bins */
806 [ + + ]: 2275 : for (unsigned i = 0; i < num_rows; i++) {
807 [ + - + - ]: 2100 : if (ld.null_matrix(i, 0) == 1) {
808 : 0 : null_counter++;
809 : 0 : continue;
810 : : }
811 [ + - ]: 2100 : const auto current_value = ld.data(i, 0);
812 [ + + ]: 2100 : if (current_value == lower_bound) {
813 : 868 : lower_bound_counter++;
814 : 868 : continue;
815 : : }
816 [ + - ]: 1232 : auto std_lower_bound = std::lower_bound(bins.begin(), bins.end(), current_value);
817 : 1232 : std_lower_bound->cumulative_probability++;
818 : 1232 : }
819 : :
820 : 175 : float lower_bound_probability = lower_bound_counter / float(num_rows);
821 : 175 : bins[0].cumulative_probability /= float(num_rows);
822 : 175 : bins[0].cumulative_probability += lower_bound_probability;
823 : :
824 [ + + ]: 784 : for (std::size_t i = 1; i < bins.size(); i++) {
825 : 609 : bins[i].cumulative_probability /= float(num_rows);
826 : 609 : bins[i].cumulative_probability += bins[i-1].cumulative_probability;
827 : 609 : }
828 [ + - ]: 175 : return std::make_unique<ContinuousLeaf>(
829 : : std::move(bins),
830 : : lower_bound,
831 : : lower_bound_probability,
832 : 175 : null_counter / float(num_rows),
833 : : num_rows
834 : : );
835 : 175 : }
836 : : }
837 : :
838 : : /* build product node with the minimum instance slice */
839 [ + + ]: 31 : if (num_rows <= MIN_INSTANCE_SLICE) { return create_product_min_slice(ld); }
840 : :
841 : : /* build product node with the RDC algorithm */
842 : 29 : auto [column_candidates, variable_candidates] = rdc_split(ld.data, ld.variables);
843 [ + + + - : 29 : if (column_candidates.size() != 1) { return create_product_rdc(ld, column_candidates, variable_candidates); }
+ - ]
844 : :
845 : : /* build sum node */
846 [ + - ]: 15 : else { return create_sum(ld); }
847 : 391 : }
848 : :
849 : : /*----- Learning -----------------------------------------------------------------------------------------------------*/
850 : :
851 : 19 : Spn Spn::learn_spn(Eigen::MatrixXf &data, Eigen::MatrixXi &null_matrix, std::vector<LeafType> &leaf_types)
852 : : {
853 : 19 : std::size_t num_rows = data.rows();
854 : 19 : MIN_INSTANCE_SLICE = std::max<std::size_t>((0.1 * num_rows), 1);
855 : :
856 [ + + ]: 19 : if (num_rows == 0) {
857 : 1 : std::vector<DiscreteLeaf::Bin> bins;
858 [ + - - + ]: 1 : return Spn(0, std::make_unique<DiscreteLeaf>(std::move(bins), 0, 0));
859 : 1 : }
860 : :
861 : : /* replace NULL in the data matrix with the mean of the attribute */
862 [ + + ]: 68 : for (std::size_t col_id = 0; col_id < data.cols(); col_id++) {
863 [ + - ]: 50 : if (null_matrix.col(col_id).maxCoeff() == 0) { continue; } // there is no NULL
864 [ # # ]: 0 : if (null_matrix.col(col_id).minCoeff() == 1) { continue; } // there is only NULL
865 : 0 : float mean = 0;
866 : 0 : int num_not_null = 0;
867 [ # # ]: 0 : for (std::size_t row_id = 0; row_id < data.rows(); row_id++) {
868 [ # # ]: 0 : if (null_matrix(row_id, col_id) == 1) { continue; }
869 : 0 : mean += (data(row_id, col_id) - mean) / ++num_not_null; // iterative mean
870 : 0 : }
871 [ # # ]: 0 : for (std::size_t row_id = 0; row_id < data.rows(); row_id++) {
872 [ # # ]: 0 : if (null_matrix(row_id, col_id) == 1) { data(row_id, col_id) = mean; }
873 : 0 : }
874 : 0 : }
875 : :
876 : 18 : SmallBitset variables = SmallBitset::All(data.cols());
877 : :
878 : 18 : auto normalized = normalize_minmax(data);
879 [ + - ]: 18 : LearningData ld(
880 : 18 : data,
881 : : normalized,
882 : 18 : null_matrix,
883 : 18 : variables,
884 : 18 : std::move(leaf_types)
885 : : );
886 : :
887 [ + - + - ]: 18 : return Spn(num_rows, learn_node(ld));
888 : 19 : }
889 : :
890 : : /*----- Inference ----------------------------------------------------------------------------------------------------*/
891 : :
892 : 0 : void Spn::update(VectorXf &row, UpdateType update_type)
893 : : {
894 : 0 : SmallBitset variables((1 << row.size()) - 1);
895 : 0 : root_->update(row, variables, update_type);
896 : 0 : }
897 : :
898 : 12 : float Spn::likelihood(const Filter &filter) const
899 : : {
900 : 12 : return root_->evaluate(filter, filter.begin()->first, APPROXIMATE).second;
901 : : }
902 : :
903 : 0 : float Spn::upper_bound(const Filter &filter) const
904 : : {
905 : 0 : return root_->evaluate(filter, filter.begin()->first, UPPER_BOUND).second;
906 : : }
907 : :
908 : 0 : float Spn::lower_bound(const Filter &filter) const
909 : : {
910 : 0 : return root_->evaluate(filter, filter.begin()->first, LOWER_BOUND).second;
911 : : }
912 : :
913 : 1 : float Spn::expectation(unsigned attribute_id, const Filter &filter) const
914 : : {
915 : 1 : auto filter_copy = filter;
916 [ + - + - ]: 1 : filter_copy.emplace(attribute_id, std::make_pair(EXPECTATION, 0.f));
917 : :
918 [ + - ]: 1 : auto [cond_expectation, likelihood] = root_->evaluate(filter_copy, filter_copy.begin()->first, APPROXIMATE);
919 : 1 : float llh = 1.f;
920 [ + - ]: 1 : if (!filter.empty()) {
921 [ # # ]: 0 : if (likelihood == 0) { return 0; }
922 : 0 : llh = likelihood;
923 : 0 : }
924 : :
925 : 1 : return cond_expectation / llh;
926 : 1 : }
927 : :
928 : 0 : void Spn::update_row(VectorXf &old_row, VectorXf &updated_row)
929 : : {
930 : 0 : delete_row(old_row);
931 : 0 : insert_row(updated_row);
932 : 0 : }
933 : :
934 : 0 : void Spn::insert_row(VectorXf &row)
935 : : {
936 : 0 : update(row, INSERT);
937 : 0 : num_rows_++;
938 : 0 : }
939 : :
940 : 0 : void Spn::delete_row(VectorXf &row)
941 : : {
942 : 0 : update(row, DELETE);
943 : 0 : num_rows_--;
944 : 0 : }
945 : :
946 : 0 : std::size_t Spn::estimate_number_distinct_values(unsigned attribute_id) const
947 : : {
948 : 0 : return root_->estimate_number_distinct_values(attribute_id);
949 : : }
950 : :
951 : 0 : void Spn::dump() const { dump(std::cerr); }
952 : :
953 : 0 : void Spn::dump(std::ostream &out) const
954 : : {
955 : 0 : out << "\n";
956 : 0 : root_->dump(out);
957 : 0 : }
|