Version: 1.0
hypercube++.cpp
Go to the documentation of this file.
1 // Copyright (c) 2020, the GRAPHGEN contributors, as
2 // shown by the AUTHORS file. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file.
6 
7 #include "hypercube++.h"
8 
9 #include "utilities.h"
10 
11 using namespace std;
12 
13 namespace hyper {
14 
15 void HyperCube::CreateTreeRec(BinaryDrag<conact>& t, BinaryDrag<conact>::node *n, size_t idx) const {
16  const Node& node = data_[idx];
17  if (node.actions_ == 0) {
18  n->data.t = conact::type::CONDITION;
19  n->data.condition = rs_.conditions[node.max_gain_index_];
20 
21  size_t pow3 = pow3_[node.max_gain_index_];
22  size_t idx1 = idx - pow3;
23  size_t idx0 = idx1 - pow3;
24 
25  CreateTreeRec(t, n->left = t.make_node(), idx0);
26  CreateTreeRec(t, n->right = t.make_node(), idx1);
27  }
28  else {
29  n->data.t = conact::type::ACTION;
30  n->data.action = node.actions_;
31  }
32 }
33 
34 std::string BinaryWithIndifference(size_t value, size_t indif, size_t nbits) {
35  std::string s(nbits, '0');
36  size_t vbits = 0;
37  for (size_t pos = 0; pos < nbits; ++pos) {
38  if ((indif >> pos) & 1) {
39  s[nbits - 1 - pos] = '-';
40  }
41  else {
42  s[nbits - 1 - pos] = ((value >> vbits) & 1) + 48;
43  ++vbits;
44  }
45  }
46  return s;
47 }
48 
49 #ifdef _MSC_VER
50 static inline int __builtin_ctz(unsigned x) {
51  unsigned long ret;
52  _BitScanForward(&ret, x);
53  return (int)ret;
54 }
55 #endif
56 
57 //#define HYPERCUBE_VERBOSE
58 BinaryDrag<conact> HyperCube::Optimize()
59 {
60 #ifdef HYPERCUBE_VERBOSE
61  // Print the table
62  for (size_t i = 0; i < 1ull << nbits_; ++i) {
63  size_t idx = GetIndex(i);
64  std::cout << BinaryWithIndifference(i, 0, nbits_) << "\t" << data_[idx].frequency_ << "\t";
65  if (data_[idx].actions_ == 0) {
66  std::cout << "0";
67  }
68  else {
69  for (size_t i = 1; i < 128; i++) {
70  if (data_[idx].actions_[i - 1]) {
71  std::cout << i << ",";
72  }
73  }
74  }
75  std::cout << "\n";
76  }
77  std::cout << "------------------------\n";
78 #endif
79 
80  for (size_t num_indif = 1; num_indif <= nbits_; num_indif++) {
81  #ifndef HYPERCUBE_VERBOSE
82  std::cout << num_indif << " " << std::flush;
83  #endif
84  // Initialize the Indifferences
85  int indif = (1 << num_indif) - 1;
86  int last = indif << (nbits_ - num_indif);
87 
88  // Do all permutations of indifferences
89  while (true) {
90  // use permutation
91 
92  int max_value = 1 << (nbits_ - num_indif);
93  for (int i = 0; i < max_value; ++i) {
94  #ifdef HYPERCUBE_VERBOSE
95  vector<Node> nodes_;
96  #endif
97 
98  size_t idx = GetIndexWithIndifference(i, indif);
99  int tmp_indif = indif;
100  int pos_indif = 0;
101  Node& max_gain_node = data_[idx];
102  while (tmp_indif>0) { // there are more indifferences to check
103  if (tmp_indif & 1) { // this is and indifference
104  size_t pow3 = pow3_[pos_indif];
105  size_t idx1 = idx - pow3;
106  size_t idx0 = idx1 - pow3;
107 
108  Node& node0 = data_[idx0], node1 = data_[idx1];
109 
110  Node cur_node;
111  cur_node.actions_ = node0.actions_ & node1.actions_;
112  cur_node.frequency_ = node0.frequency_ + node1.frequency_;
113  cur_node.gain_ = node0.gain_ + node1.gain_;
114  cur_node.max_gain_index_ = pos_indif;
115  if (cur_node.actions_ != 0) {
116  cur_node.gain_ += cur_node.frequency_;
117  cur_node.num_equiv_ = 0;
118  }
119  else {
120  cur_node.num_equiv_ = node0.num_equiv_ * node1.num_equiv_;
121  }
122 
123  #ifdef HYPERCUBE_VERBOSE
124  nodes_.push_back(cur_node);
125  #endif
126 
127  if (max_gain_node.gain_ <= cur_node.gain_) {
128  if (max_gain_node.gain_ == cur_node.gain_) {
129  cur_node.num_equiv_ += max_gain_node.num_equiv_;
130  }
131  max_gain_node = cur_node;
132  }
133  }
134 
135  ++pos_indif;
136  tmp_indif >>= 1;
137  }
138  max_gain_node.num_equiv_ = std::max(max_gain_node.num_equiv_, 1u);
139 
140  #ifdef HYPERCUBE_VERBOSE
141  std::cout << BinaryWithIndifference(i, indif, nbits_) << "\t" << data_[idx].frequency_ << "\t";
142  if (data_[idx].actions_ == 0) {
143  std::cout << "0";
144  }
145  else {
146  for (size_t i = 1; i < 128; i++) {
147  if (data_[idx].actions_[i - 1]) {
148  std::cout << i << ",";
149  }
150  }
151  }
152  std::cout << "\t";
153  for (const auto& x : nodes_) {
154  std::cout << x.gain_;
155  if (x.max_gain_index_ == max_gain_node.max_gain_index_)
156  std::cout << "*";
157  else if (x.gain_ == max_gain_node.gain_)
158  std::cout << "#";
159  std::cout << "\t";
160  }
161  std::cout << max_gain_node.num_equiv_ << "\n";
162  #endif
163  }
164  #ifdef HYPERCUBE_VERBOSE
165  std::cout << "\n";
166  #endif
167 
168  // check if last
169  if (indif == last)
170  break;
171 
172  // next permutation (https://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation)
173  int t = indif | (indif - 1);
174  indif = (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctz(indif) + 1));
175  }
176  #ifdef HYPERCUBE_VERBOSE
177  std::cout << "------------------------\n";
178  #endif
179  }
180 
182  CreateTreeRec(t, t.make_root(), data_.size() - 1);
183  return t;
184 }
185 
187  TLOG("Allocating hypercube",
188  HyperCube hcube(rs);
189  );
190 
191  TLOG("Optimizing rules",
192  auto t = hcube.Optimize();
193  );
194 
195  return t;
196 }
197 
198 BinaryDrag<conact> GenerateOdt(const rule_set& rs, const string& filename)
199 {
200  auto t = GenerateOdt(rs);
201  WriteConactTree(t, filename);
202  return t;
203 }
204 
205 BinaryDrag<conact> GetOdt(const rule_set& rs, bool force_generation) {
206  string odt_filename = conf.odt_path_.string();
208  if (conf.force_odt_generation_ || force_generation || !LoadConactTree(t, odt_filename)) {
209  t = GenerateOdt(rs, odt_filename);
210  }
211  return t;
212 }
213 
214 BinaryDrag<conact> GetOdtWithFileSuffix(const rule_set& rs, const string& file_suffix, bool force_generation) {
215  string odt_filename = conf.GetCustomOdtPath(file_suffix).string();
217  if (conf.force_odt_generation_ || force_generation || !LoadConactTree(t, odt_filename)) {
218  t = GenerateOdt(rs, odt_filename);
219  }
220  return t;
221 }
222 
223 }
node * make_root()
Creates a new root updating the roots_ vector.
Definition: drag.h:145
node * make_node(Args &&... args)
Creates and returns a new node updating the nodes_ vector.
Definition: drag.h:53
bool WriteConactTree(const BinaryDrag< conact > &t, const string &filename)
Definition: conact_tree.cpp:76
bool LoadConactTree(BinaryDrag< conact > &t, const string &filename)
Definition: conact_tree.cpp:46
std::string BinaryWithIndifference(size_t value, size_t indif, size_t nbits)
Definition: hypercube++.cpp:34
BinaryDrag< conact > GetOdt(const rule_set &rs, bool force_generation)
Returns the optimal (or pseudo optimal) decision tree generated from the given rule set.
BinaryDrag< conact > GenerateOdt(const rule_set &rs, const string &filename)
BinaryDrag< conact > GetOdtWithFileSuffix(const rule_set &rs, const string &file_suffix, bool force_generation)
std::filesystem::path GetCustomOdtPath(const std::string &custom_suffix) const
Definition: config_data.h:110
std::filesystem::path odt_path_
Definition: config_data.h:45
bool force_odt_generation_
Definition: config_data.h:83
unsigned int num_equiv_
Definition: hypercube++.h:63
unsigned long long gain_
Definition: hypercube++.h:61
unsigned long long frequency_
Definition: hypercube++.h:60
std::bitset< 131 > actions_
Definition: hypercube++.h:59
ConfigData conf
Definition: utilities.cpp:9
#define TLOG(message, instructions)
Definition: utilities.h:93