souffle  2.0.2-371-g6315b36
Public Member Functions
souffle::synthesiser::DirectRelation Class Reference

#include <Relation.h>

Inheritance diagram for souffle::synthesiser::DirectRelation:
Inheritance graph
Collaboration diagram for souffle::synthesiser::DirectRelation:
Collaboration graph

Public Member Functions

void computeIndices () override
 Generate index set for a direct indexed relation. More...
 
 DirectRelation (const ram::Relation &ramRel, const MinIndexSelection &indexSet, bool isProvenance)
 
void generateTypeStruct (std::ostream &out) override
 Generate type struct of a direct indexed relation. More...
 
std::string getTypeName () override
 Generate type name of a direct indexed relation. More...
 
- Public Member Functions inherited from souffle::ram::Relation
Relationclone () const override
 Create a clone (i.e. More...
 
unsigned getArity () const
 Get arity of relation. More...
 
const std::vector< std::string > & getAttributeNames () const
 Get attribute names. More...
 
const std::vector< std::string > & getAttributeTypes () const
 Get attribute types. More...
 
unsigned getAuxiliaryArity () const
 Get number of auxiliary attributes. More...
 
const std::string & getName () const
 Get name. More...
 
RelationRepresentation getRepresentation () const
 Relation representation type. More...
 
bool isNullary () const
 Is nullary relation. More...
 
bool isTemp () const
 Is temporary relation (for semi-naive evaluation) More...
 
bool operator< (const Relation &other) const
 Compare two relations via their name. More...
 
 Relation (std::string name, size_t arity, size_t auxiliaryArity, std::vector< std::string > attributeNames, std::vector< std::string > attributeTypes, RelationRepresentation representation)
 
- Public Member Functions inherited from souffle::ram::Node
virtual void apply (const NodeMapper &)
 Apply the mapper to all child nodes. More...
 
virtual std::vector< const Node * > getChildNodes () const
 Obtain list of all embedded child nodes. More...
 
bool operator!= (const Node &other) const
 Inequality check for two RAM nodes. More...
 
bool operator== (const Node &other) const
 Equivalence check for two RAM nodes. More...
 
virtual void rewrite (const Node *oldNode, Own< Node > newNode)
 Rewrite a child node. More...
 
virtual ~Node ()=default
 

Additional Inherited Members

- Protected Member Functions inherited from souffle::ram::Relation
bool equal (const Node &node) const override
 Equality check for two RAM nodes. More...
 
void print (std::ostream &out) const override
 Print RAM node. More...
 
- Protected Attributes inherited from souffle::ram::Relation
const size_t arity
 Arity, i.e., number of attributes. More...
 
const std::vector< std::string > attributeNames
 Name of attributes. More...
 
const std::vector< std::string > attributeTypes
 Type of attributes. More...
 
const size_t auxiliaryArity
 Number of auxiliary attributes (e.g. More...
 
const std::string name
 Name of relation. More...
 
const RelationRepresentation representation
 Data-structure representation. More...
 

Detailed Description

Definition at line 124 of file Relation.h.

Constructor & Destructor Documentation

◆ DirectRelation()

souffle::synthesiser::DirectRelation::DirectRelation ( const ram::Relation ramRel,
const MinIndexSelection indexSet,
bool  isProvenance 
)
inline

Definition at line 126 of file Relation.h.

127  : Relation(ramRel, indexSet, isProvenance) {}

Member Function Documentation

◆ computeIndices()

void souffle::synthesiser::DirectRelation::computeIndices ( )
override

Generate index set for a direct indexed relation.

Definition at line 117 of file Relation.cpp.

117  {
118  // Generate and set indices
119  MinIndexSelection::OrderCollection inds = indices.getAllOrders();
120 
121  // generate a full index if no indices exist
122  assert(!inds.empty() && "no full index in relation");
123 
124  size_t index_nr = 0;
125  // expand all search orders to be full
126  for (auto& ind : inds) {
127  // use a set as a cache for fast lookup
128  std::set<int> curIndexElems(ind.begin(), ind.end());
129 
130  // If this relation is used with provenance,
131  // we must expand all search orders to be full indices,
132  // since weak/strong comparators and updaters need this,
133  // and also add provenance annotations to the indices
134  if (isProvenance) {
135  // expand index to be full
136  for (size_t i = 0; i < getArity() - relation.getAuxiliaryArity(); i++) {
137  if (curIndexElems.find(i) == curIndexElems.end()) {
138  ind.push_back(i);
139  }
140  }
141  // remove any provenance annotations already in the index order
142  if (curIndexElems.find(getArity() - relation.getAuxiliaryArity() + 1) != curIndexElems.end()) {
143  ind.erase(std::find(ind.begin(), ind.end(), getArity() - relation.getAuxiliaryArity() + 1));
144  }
145 
146  if (curIndexElems.find(getArity() - relation.getAuxiliaryArity()) != curIndexElems.end()) {
147  ind.erase(std::find(ind.begin(), ind.end(), getArity() - relation.getAuxiliaryArity()));
148  }
149 
150  // add provenance annotations to the index, but in reverse order
151  ind.push_back(getArity() - relation.getAuxiliaryArity() + 1);
152  ind.push_back(getArity() - relation.getAuxiliaryArity());
153  masterIndex = 0;
154  } else if (ind.size() == getArity()) {
155  masterIndex = index_nr;
156  }
157  index_nr++;
158  }
159  assert(masterIndex < inds.size() && "no full index in relation");
160  computedIndices = inds;
161 }

References i, and relation.

◆ generateTypeStruct()

void souffle::synthesiser::DirectRelation::generateTypeStruct ( std::ostream &  out)
override

Generate type struct of a direct indexed relation.

Definition at line 188 of file Relation.cpp.

188  {
189  size_t arity = getArity();
190  size_t auxiliaryArity = relation.getAuxiliaryArity();
191  auto types = relation.getAttributeTypes();
192  const auto& inds = getIndices();
193  size_t numIndexes = inds.size();
194  std::map<MinIndexSelection::LexOrder, int> indexToNumMap;
195 
196  // struct definition
197  out << "struct " << getTypeName() << " {\n";
198  out << "static constexpr Relation::arity_type Arity = " << arity << ";\n";
199 
200  // stored tuple type
201  out << "using t_tuple = Tuple<RamDomain, " << arity << ">;\n";
202 
203  // generate an updater class for provenance
204  if (isProvenance) {
205  out << "struct updater_" << getTypeName() << " {\n";
206  out << "void update(t_tuple& old_t, const t_tuple& new_t) {\n";
207 
208  for (size_t i = arity - auxiliaryArity; i < arity; i++) {
209  out << "old_t[" << i << "] = new_t[" << i << "];\n";
210  }
211 
212  out << "}\n";
213  out << "};\n";
214  }
215 
216  // generate the btree type for each relation
217  for (size_t i = 0; i < inds.size(); i++) {
218  auto& ind = inds[i];
219 
220  if (i < getMinIndexSelection().getAllOrders().size()) {
221  indexToNumMap[getMinIndexSelection().getAllOrders()[i]] = i;
222  }
223 
224  std::vector<std::string> typecasts;
225  typecasts.reserve(types.size());
226 
227  for (auto type : types) {
228  switch (type[0]) {
229  case 'f': typecasts.push_back("ramBitCast<RamFloat>"); break;
230  case 'u': typecasts.push_back("ramBitCast<RamUnsigned>"); break;
231  default: typecasts.push_back("ramBitCast<RamSigned>");
232  }
233  }
234 
235  auto genstruct = [&](std::string name, size_t bound) {
236  out << "struct " << name << "{\n";
237  out << " int operator()(const t_tuple& a, const t_tuple& b) const {\n";
238  out << " return ";
239  std::function<void(size_t)> gencmp = [&](size_t i) {
240  size_t attrib = ind[i];
241  const auto& typecast = typecasts[attrib];
242 
243  out << "(" << typecast << "(a[" << attrib << "]) < " << typecast << "(b[" << attrib
244  << "])) ? -1 : (" << typecast << "(a[" << attrib << "]) > " << typecast << "(b[" << attrib
245  << "])) ? 1 :(";
246  if (i + 1 < bound) {
247  gencmp(i + 1);
248  } else {
249  out << "0";
250  }
251  out << ")";
252  };
253  gencmp(0);
254  out << ";\n }\n";
255  out << "bool less(const t_tuple& a, const t_tuple& b) const {\n";
256  out << " return ";
257  std::function<void(size_t)> genless = [&](size_t i) {
258  size_t attrib = ind[i];
259  const auto& typecast = typecasts[attrib];
260 
261  out << "(" << typecast << "(a[" << attrib << "]) < " << typecast << "(b[" << attrib << "]))";
262  if (i + 1 < bound) {
263  out << "|| (" << typecast << "(a[" << attrib << "]) == " << typecast << "(b[" << attrib
264  << "])) && (";
265  genless(i + 1);
266  out << ")";
267  }
268  };
269  genless(0);
270  out << ";\n }\n";
271  out << "bool equal(const t_tuple& a, const t_tuple& b) const {\n";
272  out << "return ";
273  std::function<void(size_t)> geneq = [&](size_t i) {
274  size_t attrib = ind[i];
275  const auto& typecast = typecasts[attrib];
276 
277  out << "(" << typecast << "(a[" << attrib << "]) == " << typecast << "(b[" << attrib << "]))";
278  if (i + 1 < bound) {
279  out << "&&";
280  geneq(i + 1);
281  }
282  };
283  geneq(0);
284  out << ";\n }\n";
285  out << "};\n";
286  };
287 
288  std::string comparator = "t_comparator_" + std::to_string(i);
289  genstruct(comparator, ind.size());
290 
291  // for provenance, all indices must be full so we use btree_set
292  // also strong/weak comparators and updater methods
293 
294  if (isProvenance) {
295  std::string comparator_aux;
296  if (provenanceIndexNumbers.find(i) == provenanceIndexNumbers.end()) {
297  // index for bottom up phase
298  comparator_aux = "t_comparator_" + std::to_string(i) + "_aux";
299  genstruct(comparator_aux, ind.size() - auxiliaryArity);
300  } else {
301  // index for top down phase
302  comparator_aux = comparator;
303  }
304  out << "using t_ind_" << i << " = btree_set<t_tuple," << comparator
305  << ",std::allocator<t_tuple>,256,typename "
306  "souffle::detail::default_strategy<t_tuple>::type,"
307  << comparator_aux << ",updater_" << getTypeName() << ">;\n";
308  } else {
309  if (ind.size() == arity) {
310  out << "using t_ind_" << i << " = btree_set<t_tuple," << comparator << ">;\n";
311  } else {
312  // without provenance, some indices may be not full, so we use btree_multiset for those
313  out << "using t_ind_" << i << " = btree_multiset<t_tuple," << comparator << ">;\n";
314  }
315  }
316  out << "t_ind_" << i << " ind_" << i << ";\n";
317  }
318 
319  // typedef master index iterator to be struct iterator
320  out << "using iterator = t_ind_" << masterIndex << "::iterator;\n";
321 
322  // create a struct storing hints for each btree
323  out << "struct context {\n";
324  for (size_t i = 0; i < numIndexes; i++) {
325  out << "t_ind_" << i << "::operation_hints hints_" << i << "_lower"
326  << ";\n";
327  out << "t_ind_" << i << "::operation_hints hints_" << i << "_upper"
328  << ";\n";
329  }
330  out << "};\n";
331  out << "context createContext() { return context(); }\n";
332 
333  // insert methods
334  out << "bool insert(const t_tuple& t) {\n";
335  out << "context h;\n";
336  out << "return insert(t, h);\n";
337  out << "}\n"; // end of insert(t_tuple&)
338 
339  out << "bool insert(const t_tuple& t, context& h) {\n";
340  out << "if (ind_" << masterIndex << ".insert(t, h.hints_" << masterIndex << "_lower"
341  << ")) {\n";
342  for (size_t i = 0; i < numIndexes; i++) {
343  if (i != masterIndex && provenanceIndexNumbers.find(i) == provenanceIndexNumbers.end()) {
344  out << "ind_" << i << ".insert(t, h.hints_" << i << "_lower"
345  << ");\n";
346  }
347  }
348  out << "return true;\n";
349  out << "} else return false;\n";
350  out << "}\n"; // end of insert(t_tuple&, context&)
351 
352  out << "bool insert(const RamDomain* ramDomain) {\n";
353  out << "RamDomain data[" << arity << "];\n";
354  out << "std::copy(ramDomain, ramDomain + " << arity << ", data);\n";
355  out << "const t_tuple& tuple = reinterpret_cast<const t_tuple&>(data);\n";
356  out << "context h;\n";
357  out << "return insert(tuple, h);\n";
358  out << "}\n"; // end of insert(RamDomain*)
359 
360  std::vector<std::string> decls;
361  std::vector<std::string> params;
362  for (size_t i = 0; i < arity; i++) {
363  decls.push_back("RamDomain a" + std::to_string(i));
364  params.push_back("a" + std::to_string(i));
365  }
366  out << "bool insert(" << join(decls, ",") << ") {\n";
367  out << "RamDomain data[" << arity << "] = {" << join(params, ",") << "};\n";
368  out << "return insert(data);\n";
369  out << "}\n"; // end of insert(RamDomain x1, RamDomain x2, ...)
370 
371  // contains methods
372  out << "bool contains(const t_tuple& t, context& h) const {\n";
373  out << "return ind_" << masterIndex << ".contains(t, h.hints_" << masterIndex << "_lower"
374  << ");\n";
375  out << "}\n";
376 
377  out << "bool contains(const t_tuple& t) const {\n";
378  out << "context h;\n";
379  out << "return contains(t, h);\n";
380  out << "}\n";
381 
382  // size method
383  out << "std::size_t size() const {\n";
384  out << "return ind_" << masterIndex << ".size();\n";
385  out << "}\n";
386 
387  // find methods
388  out << "iterator find(const t_tuple& t, context& h) const {\n";
389  out << "return ind_" << masterIndex << ".find(t, h.hints_" << masterIndex << "_lower"
390  << ");\n";
391  out << "}\n";
392 
393  out << "iterator find(const t_tuple& t) const {\n";
394  out << "context h;\n";
395  out << "return find(t, h);\n";
396  out << "}\n";
397 
398  // empty lowerUpperRange method
399  out << "range<iterator> lowerUpperRange_" << SearchSignature(arity)
400  << "(const t_tuple& /* lower */, const t_tuple& /* upper */, context& /* h */) const "
401  "{\n";
402 
403  out << "return range<iterator>(ind_" << masterIndex << ".begin(),ind_" << masterIndex << ".end());\n";
404  out << "}\n";
405 
406  out << "range<iterator> lowerUpperRange_" << SearchSignature(arity)
407  << "(const t_tuple& /* lower */, const t_tuple& /* upper */) const {\n";
408 
409  out << "return range<iterator>(ind_" << masterIndex << ".begin(),ind_" << masterIndex << ".end());\n";
410  out << "}\n";
411 
412  // lowerUpperRange methods for each pattern which is used to search this relation
413  for (auto search : getMinIndexSelection().getSearches()) {
414  auto& lexOrder = getMinIndexSelection().getLexOrder(search);
415  size_t indNum = indexToNumMap[lexOrder];
416 
417  out << "range<t_ind_" << indNum << "::iterator> lowerUpperRange_" << search;
418  out << "(const t_tuple& lower, const t_tuple& upper, context& h) const {\n";
419 
420  // count size of search pattern
421  size_t eqSize = 0;
422  for (size_t column = 0; column < arity; column++) {
423  if (search[column] == analysis::AttributeConstraint::Equal) {
424  eqSize++;
425  }
426  }
427 
428  out << "t_comparator_" << indNum << " comparator;\n";
429  out << "int cmp = comparator(lower, upper);\n";
430 
431  // if search signature is full we can apply this specialization
432  if (eqSize == arity) {
433  // use the more efficient find() method if lower == upper
434  out << "if (cmp == 0) {\n";
435  out << " auto pos = ind_" << indNum << ".find(lower, h.hints_" << indNum << "_lower);\n";
436  out << " auto fin = ind_" << indNum << ".end();\n";
437  out << " if (pos != fin) {fin = pos; ++fin;}\n";
438  out << " return make_range(pos, fin);\n";
439  out << "}\n";
440  }
441  // if lower_bound > upper_bound then we return an empty range
442  out << "if (cmp > 0) {\n";
443  out << " return make_range(ind_" << indNum << ".end(), ind_" << indNum << ".end());\n";
444  out << "}\n";
445  // otherwise use the general method
446  out << "return make_range(ind_" << indNum << ".lower_bound(lower, h.hints_" << indNum << "_lower"
447  << "), ind_" << indNum << ".upper_bound(upper, h.hints_" << indNum << "_upper"
448  << "));\n";
449 
450  out << "}\n";
451 
452  out << "range<t_ind_" << indNum << "::iterator> lowerUpperRange_" << search;
453  out << "(const t_tuple& lower, const t_tuple& upper) const {\n";
454 
455  out << "context h;\n";
456  out << "return lowerUpperRange_" << search << "(lower,upper,h);\n";
457  out << "}\n";
458  }
459 
460  // empty method
461  out << "bool empty() const {\n";
462  out << "return ind_" << masterIndex << ".empty();\n";
463  out << "}\n";
464 
465  // partition method for parallelism
466  out << "std::vector<range<iterator>> partition() const {\n";
467  out << "return ind_" << masterIndex << ".getChunks(400);\n";
468  out << "}\n";
469 
470  // purge method
471  out << "void purge() {\n";
472  for (size_t i = 0; i < numIndexes; i++) {
473  out << "ind_" << i << ".clear();\n";
474  }
475  out << "}\n";
476 
477  // begin and end iterators
478  out << "iterator begin() const {\n";
479  out << "return ind_" << masterIndex << ".begin();\n";
480  out << "}\n";
481 
482  out << "iterator end() const {\n";
483  out << "return ind_" << masterIndex << ".end();\n";
484  out << "}\n";
485 
486  // copyIndex method
487  if (!provenanceIndexNumbers.empty()) {
488  out << "void copyIndex() {\n";
489  out << "for (auto const &cur : ind_" << masterIndex << ") {\n";
490  for (auto const i : provenanceIndexNumbers) {
491  out << "ind_" << i << ".insert(cur);\n";
492  }
493  out << "}\n";
494  out << "}\n";
495  }
496 
497  // printStatistics method
498  out << "void printStatistics(std::ostream& o) const {\n";
499  for (size_t i = 0; i < numIndexes; i++) {
500  out << "o << \" arity " << arity << " direct b-tree index " << i << " lex-order " << inds[i]
501  << "\\n\";\n";
502  out << "ind_" << i << ".printStats(o);\n";
503  }
504  out << "}\n";
505 
506  // end struct
507  out << "};\n";
508 } // namespace souffle

References i, souffle::join(), relation, TCB_SPAN_NAMESPACE_NAME::detail::size(), and types.

Here is the call graph for this function:

◆ getTypeName()

std::string souffle::synthesiser::DirectRelation::getTypeName ( )
override

Generate type name of a direct indexed relation.

Definition at line 164 of file Relation.cpp.

164  {
165  // collect all attributes used in the lex-order
166  std::unordered_set<uint32_t> attributesUsed;
167  for (auto& ind : getIndices()) {
168  for (auto& attr : ind) {
169  attributesUsed.insert(attr);
170  }
171  }
172 
173  std::stringstream res;
174  res << "t_btree_" << getTypeAttributeString(relation.getAttributeTypes(), attributesUsed);
175 
176  for (auto& ind : getIndices()) {
177  res << "__" << join(ind, "_");
178  }
179 
180  for (auto& search : getMinIndexSelection().getSearches()) {
181  res << "__" << search;
182  }
183 
184  return res.str();
185 }

References souffle::join(), and relation.

Here is the call graph for this function:

The documentation for this class was generated from the following files:
souffle::ram::Relation::name
const std::string name
Name of relation.
Definition: Relation.h:136
souffle::interpreter::comparator
typename index_utils::get_full_index< Arity >::type::comparator comparator
Definition: Util.h:216
TCB_SPAN_NAMESPACE_NAME::detail::size
constexpr auto size(const C &c) -> decltype(c.size())
Definition: span.h:198
souffle::ram::Relation::arity
const size_t arity
Arity, i.e., number of attributes.
Definition: Relation.h:139
souffle::ram::Relation::auxiliaryArity
const size_t auxiliaryArity
Number of auxiliary attributes (e.g.
Definition: Relation.h:142
relation
Relation & relation
Definition: Reader.h:130
types
std::vector< Own< ast::Type > > types
Definition: ComponentInstantiation.cpp:64
souffle::synthesiser::DirectRelation::getTypeName
std::string getTypeName() override
Generate type name of a direct indexed relation.
Definition: Relation.cpp:164
souffle::ram::Relation::Relation
Relation(std::string name, size_t arity, size_t auxiliaryArity, std::vector< std::string > attributeNames, std::vector< std::string > attributeTypes, RelationRepresentation representation)
Definition: Relation.h:42
souffle::ram::Relation::getArity
unsigned getArity() const
Get arity of relation.
Definition: Relation.h:86
i
size_t i
Definition: json11.h:663
souffle::join
detail::joined_sequence< Iter, Printer > join(const Iter &a, const Iter &b, const std::string &sep, const Printer &p)
Creates an object to be forwarded to some output stream for printing sequences of elements interspers...
Definition: StreamUtil.h:175
souffle::ram::analysis::MinIndexSelection::OrderCollection
std::vector< LexOrder > OrderCollection
Definition: Index.h:217
std::type
ElementType type
Definition: span.h:640