-
Notifications
You must be signed in to change notification settings - Fork 0
/
f4_hash.hpp
339 lines (302 loc) · 11.9 KB
/
f4_hash.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
#ifndef __F4_HASH_
#define __F4_HASH_
#include <random>
using std::default_random_engine;
using std::uniform_int_distribution;
#include <chrono>
#include <list>
using std::list;
#include <vector>
using std::vector;
#include <utility>
using std::pair;
#include "system_constants.hpp"
#include "monomial.hpp"
#include "polynomial_hashed.hpp"
extern double get_index_time;
/**
information for the monomial: location in the table, when last used
*/
struct usage_information {
size_t location = 0;
size_t matrix_location = 0;
unsigned matrix_id = -1;
};
const usage_information NOT_FOUND;
/**
@ingroup GBComputation
@brief hash table class, based on discussions with Christian Eder
@details For a homogeneous system it is advisable to recreate this for each
matrix. For inhomogeneous systems it may be advantageous to reuse the table
as one proceeds through the system.
*/
class F4_Hash {
protected:
NVAR_TYPE n; /**< @brief number of variables in the monomials stored */
WT_TYPE * weights; /**< @brief randomized weighting for each exponent */
/**
@brief number of entries in table
*/
static const size_t MAXIMUM = 1 << 18;
/**
@brief the actual table
*/
vector< vector<pair<const Monomial *, usage_information> > > table;
/**
@brief signature for the table, to help with caching computations
*/
WT_TYPE signature;
public:
/** @brief maximum length of a list in the table; currently for info only */
size_t max_size = 0;
/**
@brief allocates the table and sets up randomized hash function
@param num_vars number of variables in the monomials this table will check
*/
explicit F4_Hash(NVAR_TYPE num_vars) : table(MAXIMUM) {
n = num_vars;
weights = new WT_TYPE[n];
default_random_engine generator(
std::chrono::system_clock::now().time_since_epoch().count()
);
uniform_int_distribution<int> rand(0, MAXIMUM / n - 1 );
for (NVAR_TYPE i = 0; i < n; ++i) {
weights[i] = rand(generator);
}
signature = ( rand(generator) + rand(generator) ) % MAXIMUM;
}
/**
@brief releases the table and the randomized hash function
*/
~F4_Hash() {
delete [] weights;
unsigned unused = 0, max_length = 0;
for ( auto i = 0; i < table.size(); ++i ) {
if ( table[i].size() == 0 )
++unused;
else if ( table[i].size() > max_length )
max_length = table[i].size();
}
cout << "unused " << unused << " of " << table.size() << endl;
cout << "maximum length is " << max_length << endl;
}
/**
@brief determines the index of @p t in the lookup table
@param t @c Monomial
@return which table entry contains the list that contains @p t
*/
size_t get_index(const Monomial & t) const {
DEG_TYPE index = t.cached_weighted_degree(weights);
return index % MAXIMUM;
}
/**
@brief determines the index of \f$tu\f$ in the lookup table
@param t @c Monomial
@param u @c Monomial
@return which table entry contains the list that contains \f$tu\f$
*/
size_t get_index(const Monomial & t, const Monomial & u) const {
DEG_TYPE index = t.cached_weighted_degree(weights) + u.cached_weighted_degree(weights);
return index % MAXIMUM;
}
/**
@brief determines the index of \f$tu\f$ in the lookup table
@param t @c Monomial
@param u @c exponent vector
@return which table entry contains the list that contains \f$tu\f$
*/
//size_t get_index(const Monomial & t, const EXP_TYPE * u) {
// DEG_TYPE index = 0;
// for (NVAR_TYPE i = 0; i < n; ++i)
// index += ((t[i] + u[i]) * weights[i]);
// return index % MAXIMUM;
//}
/**
@brief modifies the location of the specified monomial in the lookup table
@details Use only in conjunction with monomials allocated with
@c add_monomial(const Monomial *).
@warning Invoking this on a monomial whose location hasn't changed or
been reassigned will lead to errors and possibly termination.
Furthermore, there are no guards for whether the monomial actually
appears in the table, so if the monomial hasn't been added there will
be errors and probably termination.
@param t @c Monomial that has already been added to the table
@param matrix which matrix the location applies to
@param location column for coefficients of @c t
*/
void update_location(const Monomial * t, unsigned matrix, size_t location) {
auto & list = table[get_index(*t)];
auto curr = list.begin();
while (not t->is_like(*(curr->first))) ++curr;
curr->second = { curr->second.location, location, matrix };
}
/**
@brief indicates whether a monomial has been added to the table
@return @c true if and only if @p t has been added to the table
@param t @c Monomial whose presence we'd like to determine
@param matrix which matrix we want @c t to be in
*/
//usage_information contains_in_matrix(const Monomial * t, unsigned matrix) {
// auto & list = table[get_index(*t)];
// auto curr = list.begin();
// while (curr != list.end() and not curr->first->is_like(*t)) ++curr;
// if (curr == list.end()) return NOT_FOUND;
// else return curr->second;
//}
bool contains(const Monomial * t) {
auto & list = table[get_index(*t)];
auto curr = list.begin();
while (curr != list.end() and not curr->first->is_like(*t)) ++curr;
return curr != list.end();
}
/**
@brief indicates whether a product has been added to the table
@return @c true if and only if \f$ tu \f$ has been added to the table
@param t monomial whose product we're checking
@param u monomial whose product we're checking
@param matrix which matrix we want @c tu to be in
*/
usage_information contains_product_in_matrix(
const Monomial & t, const Monomial & u, unsigned matrix
) {
//std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
auto idx = get_index(t,u);
//std::chrono::high_resolution_clock::time_point stop = std::chrono::high_resolution_clock::now();
//get_index_time += std::chrono::duration_cast<std::chrono::duration<double> >(stop - start).count();
auto & list = table[idx];
auto curr = list.begin();
while (curr != list.end() and not curr->first->like_multiple(t, u)) ++curr;
if (curr == list.end()) return NOT_FOUND;
else return curr->second;
}
bool contains_product(const Monomial & t, const Monomial & u) {
auto & list = table[get_index(t, u)];
auto curr = list.begin();
//std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
while (curr != list.end() and not ( curr->first->like_multiple(t, u)) ) ++curr;
//std::chrono::high_resolution_clock::time_point stop = std::chrono::high_resolution_clock::now();
//get_index_time += std::chrono::duration_cast<std::chrono::duration<double> >(stop - start).count();
return curr != list.end();
}
/**
@brief adds a monomial without an index to the lookup table
@details Use only when creating the hash table.
This does not check for uniqueness; it simply adds monomials!
So make sure you are adding a monomial that isn't already there.
For this version of the command, you must subsequently call
@c update_location() so that the table has proper references.
@warning The location of this monomial is <b>invalid</b>.
Essentially, this function merely indicates that such an object
needs to be tracked, and will eventually be updated for the table.
@param t @c Monomial for future lookup
@param matrix which matrix the monomial is for
*/
void add_monomial_for_matrix(
const Monomial * t, size_t location, unsigned matrix = 0
) {
auto & list = table[get_index(*t)];
auto curr = list.begin();
while (curr != list.end() and not curr->first->is_like(*t)) ++curr;
if (curr != list.end()) {
curr->second.location = location;
curr->second.matrix_id = matrix;
} else {
usage_information new_value { location, 0, matrix };
list.emplace_back(t, new_value );
}
if (list.size() > max_size) max_size = list.size();
}
/**
@brief adds a monomial to the lookup table
@details Use only when creating the hash table.
This does not check for uniqueness; it simply adds monomials!
So make sure you are adding a monomial that isn't already there.
@param t @c Monomial for future lookup
@param location column for coefficients of @p t
*/
void add_monomial(const Monomial * t, const size_t location) {
auto & list = table[get_index(*t)];
usage_information new_value { location, 0, 0 };
list.emplace_back(t, new_value);
if (list.size() > max_size) max_size = list.size();
}
/**
@brief looks up the location of \f$tu\f$ in the array; that is,
the second argument to @c add_monomial() when \f$tu\f$ was the first
@details This will not add \f$tu\f$ if it isn't already there, so
call it only after the table has been completely set up!
@param t @c Monomial
@param u @c Monomial
@return location of \f$tu\f$ in the array
*/
size_t lookup_product(const Monomial & t, const Monomial & u) {
auto & list = table[get_index(t, u)];
auto curr = list.begin();
while (curr != list.end() and not curr->first->like_multiple(t, u))
++curr;
return curr->second.matrix_location;
}
/**
@brief looks up the location of \f$tu\f$ in the array; that is,
the second argument to @c add_monomial() when \f$tu\f$ was the first
@details This will not add \f$tu\f$ if it isn't already there, so
call it only after the table has been completely set up!
@param t @c Monomial
@param u @c exponent vector
@return location of \f$tu\f$ in the array
*/
//size_t lookup_product(const Monomial & t, const EXP_TYPE * u) {
// auto & list = table[get_index(t, u)];
// auto curr = list.begin();
// while (curr != list.end() and not curr->first->like_multiple(u, t))
// ++curr;
// return curr->second.matrix_location;
//}
/**
@brief indicates which location is associated with @p t
@param t @c Monomial
@return location of @p t in the array
*/
unsigned operator[](const Monomial & t) const {
auto & list = table[get_index(t)];
auto curr = list.begin();
while (not curr->first->is_like(t)) ++curr;
return curr->second.location;
}
unsigned matrix_location(const Monomial & t) const {
auto & list = table[get_index(t)];
auto curr = list.begin();
while (not curr->first->is_like(t)) ++curr;
return curr->second.matrix_location;
}
/** @brief list the monomials stored at this monomial's index */
void vomit(const Monomial & t) {
auto & list = table[get_index(t)];
cout << "bucket " << get_index(t) << endl;
for (auto storage : list) {
cout << "\t( " << *storage.first << " , { " << storage.second.location
<< ", " << storage.second.matrix_id << " } )\n";
}
}
friend ostream & operator << (ostream &, const F4_Hash &);
unsigned num_unused() {
unsigned result = 0;
for (auto entry : table) {
if (entry.size() == 0) ++result;
}
return result;
}
unsigned num_total() { return table.size(); }
unsigned load() {
unsigned result = 0;
for (auto entry : table) {
result += entry.size();
}
return result / table.size();
}
};
/**
@brief prints the non-empty locations as a list of tuples (monomial, location)
*/
ostream & operator << (ostream & os, const F4_Hash & ht);
#endif