diff --git a/include/hash.h b/include/hash.h index 74cf3d0..c70958a 100644 --- a/include/hash.h +++ b/include/hash.h @@ -1,20 +1,74 @@ +/***************************************************************** + * + * Package : omf5 - orthogonal modular forms of rank 5 + * Filename : hash.h + * + * Description: Hash table for quickly testing + * isometry between lattices. + * + ***************************************************************** + */ + #ifndef __HASH_H__ #define __HASH_H__ +// System dependencies + #include +// Required packacges dependencies + #include #include +// Self dependencies + #include "isometry.h" #include "square_matrix.h" #include "typedefs.h" +// hash_t is the type used for the hash function. // We do not anticipate more than 2^32 forms in the genus... // Maybe changing to W16 could improve that, have to check typedef W32 hash_t; +/*********************************************************************** + * + * Type: hash_table_t + * + * Description: A (closed) hash table for isometry classes of lattices. + * The keys are gram matrices of lattices, representing + * the isometry classes. + * + * Fields: + * + keys (square_matrix_t*) - the list of keys stored in the table + * + vals (hash_t*) - the list of corresponding hash values (full word) + * Note that the actual hashing index is obtained by + * considering only some of its bits + * + key_ptr (hash_t*) - for any hash value v, key_ptr[v] is the index + * in the vals array in which v appears + * + counts (W32*) - the number of times every hash value appears, + * for resolving collisions + * + probs (fmpq_t*) - probabilities of hitting values in the table + * + num_stored (hash_t) - number of keys stored in the table + * + mask (hash_t) - mask used to determine which bits of the full + * hash value to use + * + capacity (hash_t) - capacity of the table + * (how many entries can the allocated memory + * currently support) + * + theta_prec (W32) - Our hash function uses the theta series + * associated to the lattice. This determines + * how many terms from the theta series + * to consider. + * + red_on_isom (bool) - a flag which determines, when checking + * for an isometry, whether to first reduce + * the lattice using a naive reduction + * algorithm. + * + *********************************************************************** + */ + typedef struct { square_matrix_t* keys; hash_t* vals; @@ -32,28 +86,241 @@ typedef struct { typedef hash_table hash_table_t[1]; +/********************************************************************** + * + * Function: hash_form + * + * Description: Hash function - hash the lattice into a hash_value. + * + * Arguments: + * + Q (const square_matrix_t) - the gram matrix of the lattice + * + theta_prec (W32) - the number of terms in the theta series + * f the lattice to use. + * + * Returns: + * + (hash_t) - the hash value + * + ********************************************************************** + */ + /* hash the form Q into an index between 0 and hash_size */ hash_t hash_form(const square_matrix_t Q, W32 theta_prec); +/********************************************************************** + * + * Function: hash_table_init + * + * Description: Initializes a hash table of given size. + * + * Arguments: + * + hash_size (hash_t) - size of the hash table + * + * Returns: + * + table (hash_table_t) - a hash table of that size + * + ********************************************************************** + */ + void hash_table_init(hash_table_t table, hash_t hash_size); +/********************************************************************** + * + * Function: hash_table_recalibrate + * + * Description: Change the number of terms we are taking from the + * theta series of the lattices, to optimize performance. + * + * Arguments: + * + old_table (const hash_table_t) - current hash_table + * + * Returns: + * + new_table (hash_table_t) - a hash_table with the same + * keys, where the hash function + * uses the new number of terms + * from the theta series + * + ********************************************************************** + */ + void hash_table_recalibrate(hash_table_t new_table, const hash_table_t old_table); +/********************************************************************** + * + * Function: hash_table_clear + * + * Description: Clears memory allocated for hash table. + * + * Arguments: + * + table (hash_table_t) - the hash table + * + ********************************************************************** + */ + void hash_table_clear(hash_table_t table); +/********************************************************************** + * + * Function: hash_table_add + * + * Description: Adds an entry to the hash table. + * + * Arguments: + * + table (hash_table_t) - the hash table + * + key (const square_matrix_t) - the gram matrix of a lattice + * in the isometry class. + * + * Returns: + * + (int) - 0 if there is already a lattice from this isometry + * class in the table, 1 otherwise. + * + ********************************************************************** + */ + int hash_table_add(hash_table_t table, const square_matrix_t key); -bool hash_table_get_key(square_matrix_t val, const hash_table_t table, const square_matrix_t key, int* index); +/********************************************************************** + * + * Function: hash_table_get_key + * + * Description: Given a gram matrix of a lattice, obtain a gram + * matrix of a lattice stored in the table, with the same + * hash value. (not necessarily isometric) + * + * Arguments: + * + table (const hash_table_t) - the hash table + * + key (const square_matrix_t) - the gram matrix of the lattice + * + index (int*) - a pointer for resolving collisions, + * which points to the first index in the table + * from which to search (for the + * first occurrence, set *index = -1) + * + * Returns: + * + new_key (square_matrix_t) - a gram matrix of a lattice with + * the same hash value. + * + (bool) - True if such a lattice was found. + * + ********************************************************************** + */ + +bool hash_table_get_key(square_matrix_t new_key, const hash_table_t table, const square_matrix_t key, int* index); + +/********************************************************************** + * + * Function: hash_table_exists + * + * Description: Given a gram matrix of a lattice, check whether + * there is a lattice stored in the table with the same + * hash value. If check_isom is set, check whether + * there exists such an isometric lattice. + * + * Arguments: + * + table (const hash_table_t) - the hash table + * + key (const square_matrix_t) - the gram matrix of the lattice + * + check_isom (int) - 1 for checking an isometry as well + * + * Returns: + * + (int) - 1 if such a lattice was found, 0 otherwise. + * + ********************************************************************** + */ int hash_table_exists(const hash_table_t table, const square_matrix_t key, int check_isom); +/********************************************************************** + * + * Function: hash_table_indexof + * + * Description: Given a gram matrix of a lattice, find the index of + * a lattice stored in the table with the same + * hash value. If check_isom is set, find the index of + * an isometric lattice stored in the table. + * + * Arguments: + * + table (const hash_table_t) - the hash table + * + key (const square_matrix_t) - the gram matrix of the lattice + * + check_isom (int) - 1 for checking an isometry as well + * + * Returns: + * + (int) - the index in the table in which such a lattice occurs. + * If not found, returns -1. + * + theta_time (double*) - for recalibration, the amount of time + * spent computing the theta series + * + isom_time (double*) - for recalibration, the amount of time + * spent checking isometries + * + num_isom (int *) - for recalibration, the number of isometry + * tests performed + * + ********************************************************************** + */ + int hash_table_indexof(const hash_table_t table, const square_matrix_t key, int check_isom, double* theta_time, double* isom_time, int* num_isom); +/********************************************************************** + * + * Function: hash_table_index_and_isom + * + * Description: Given a gram matrix of a lattice, find the index of + * a lattice stored in the table with the same + * hash value. If check_isom is set, find the index of + * an isometric lattice stored in the table. + * In addition, returns an isometry between the + * input lattice and the stored lattice. + * + * Arguments: + * + table (const hash_table_t) - the hash table + * + key (const square_matrix_t) - the gram matrix of the lattice + * + check_isom (int) - 1 for checking an isometry as well + * + * Returns: + * + (int) - the index in the table in which such a lattice occurs. + * If not found, returns -1. + * + isom (isometry_t) - an isometry between key and the stored + * lattice + * + theta_time (double*) - for recalibration, the amount of time + * spent computing the theta series + * + isom_time (double*) - for recalibration, the amount of time + * spent checking isometries + * + num_isom (int *) - for recalibration, the number of isometry + * tests performed + * + ********************************************************************** + */ + int hash_table_index_and_isom(const hash_table_t table, const square_matrix_t key, isometry_t isom, double* theta_time, double* isom_time, int* num_isom); +/********************************************************************** + * + * Function: hash_table_expand + * + * Description: Expand the hash table (increase its capacity). + * + * Arguments: + * + table (hash_table_t) - the hash table + * + ********************************************************************** + */ + void hash_table_expand(hash_table_t table); +/********************************************************************** + * + * Function: hash_table_insert + * + * Description: Inserts an isometry class to the hash table, at + * a specific index. + * + * Arguments: + * + table (hash_table_t) - the hash table + * + key (const square_matrix_t) - the gram matrix of the lattice + * + val (hash_t) - the value of the hash function + * + index (int) - the index in which to insert the class + * + do_push_back (int) - 1 to store the lattice, 0 otherwise. + * + ********************************************************************** + */ + +// !! TODO - this seems to be an internal function, why do we need it in the include file? int hash_table_insert(hash_table_t table, const square_matrix_t key, hash_t val, int index, int do_push_back); diff --git a/src/hash.c b/src/hash.c index f444b5d..bf90941 100644 --- a/src/hash.c +++ b/src/hash.c @@ -1,6 +1,24 @@ +/***************************************************************** + * + * Package : omf5 - orthogonal modular forms of rank 5 + * Filename : hash.c + * + * Description: Hash table for quickly testing + * isometry between lattices. + * + ***************************************************************** + */ + +// System dependencies + #include + +// Required packages dependencies + #include +// Self dependencies + #include "aut_grp.h" #include "hash.h" #include "isometry.h" @@ -8,16 +26,20 @@ #include "nbr_data.h" #include "neighbor.h" +// an initial number of terms to use from the theta series // TODO : We should be able to determine thes in a smarter way #define INITIAL_THETA_PREC 25 +/* hash_vec is a hash function on a vector of integers */ // hash_vec is defined in the end, as different choices of hash functions might lead to extremely // different performance times hash_t hash_vec(const int* vec, unsigned int n); +/* a helper function to add an entry to the table */ int _add(hash_table_t table, const square_matrix_t key, hash_t val, int do_push_back); +/* hash a form using its theta series */ // This should be the declaration, but for some reason short_vectors doesn't restrict the pointer to be constant // Since we don't want to copy the matrix, we leave it at that. hash_t hash_form(const square_matrix_t q, W32 theta_prec) @@ -43,6 +65,7 @@ hash_t hash_form(const square_matrix_t q, W32 theta_prec) return x; } +/* initialize a hash table of given size */ void hash_table_init(hash_table_t table, hash_t hash_size) { hash_t i, log_hash_size, n; @@ -52,7 +75,8 @@ void hash_table_init(hash_table_t table, hash_t hash_size) while (n >>= 1) { log_hash_size++; } - + + // It is easier to work with powers of 2 for memory allocation table->capacity = 1LL << log_hash_size; table->mask = (1LL << (log_hash_size + 1)) - 1; table->keys = (square_matrix_t*) malloc(table->capacity * sizeof(square_matrix_t)); @@ -74,6 +98,8 @@ void hash_table_init(hash_table_t table, hash_t hash_size) return; } +/* Compute the time spent for an isometry testing, red_cost returns the amount of type spent on reduction */ +// At the moment, done by sampling neighbors, and testing isometries between them double get_isom_cost(const hash_table_t table, double* red_cost) { double isom_cost; @@ -164,6 +190,13 @@ double get_isom_cost(const hash_table_t table, double* red_cost) return isom_cost; } +/* get_total_cost returns the average time spent when looking up an isometry class. + This is done by computing the time spent for computing the theta series, + using the collision counts and the sizes of automorphism groups (probs) to estimate the average number + of isometry tests on every call, and computing the weighted average based on the given timing + of a single isometry test. We also input the time of checking an isometry with reduction. + Given the timings, we decide whether to reduce when performing isometry testing (returned in red_on_isom), + and output the total cost accordingly. */ double get_total_cost(const hash_table_t table, W32 theta_prec, double isom_cost, double red_isom_cost, fmpq_t* wt_cnts, W32* counts, hash_t* vals, bool* red_on_isom, const fmpq_t* probs) { @@ -217,6 +250,7 @@ double get_total_cost(const hash_table_t table, W32 theta_prec, double isom_cost return total_cost; } +/* recalibrate the hash table to use a different number of terms from the theta series, to optimize performance. */ // This can be done better, by first computing the theta series and // only then going over them. Since this is not a critical path, // we postpone doing that. @@ -294,6 +328,7 @@ void hash_table_recalibrate(hash_table_t new_table, const hash_table_t table) return; } +/* insert a key to the hash table at a specific index */ // key is non-const to prevent copying the matrices int hash_table_insert(hash_table_t table, const square_matrix_t key, hash_t val, int index, int do_push_back) @@ -323,6 +358,7 @@ int hash_table_insert(hash_table_t table, const square_matrix_t key, hash_t val, return 1; } +/* a helper function to add an entry to the hash table */ int _add(hash_table_t table, const square_matrix_t key, hash_t val, int do_push_back) { int offset, i; @@ -355,6 +391,7 @@ int _add(hash_table_t table, const square_matrix_t key, hash_t val, int do_push_ } +/* add a lattice to the table */ int hash_table_add(hash_table_t table, const square_matrix_t key) { return _add(table, key, hash_form(key, table->theta_prec), 1); @@ -390,6 +427,8 @@ bool hash_table_get_key(square_matrix_t new_key, const hash_table_t table, const return true; } +/* Checks whether there exists a lattice with the same hash in the table. + If check_isom is set, checks whether there is an isometric one. */ int hash_table_exists(const hash_table_t table, const square_matrix_t key, int check_isom) { int offset, i; @@ -420,6 +459,9 @@ int hash_table_exists(const hash_table_t table, const square_matrix_t key, int c return 0; } +/* Returns the index in the hash table of a lattice with the same hash value, + or an isometric lattice if check_isom is set. */ + int hash_table_indexof(const hash_table_t table, const square_matrix_t key, int check_isom, double* theta_time, double* isom_time, int* num_isom) { int offset, i; @@ -465,6 +507,8 @@ int hash_table_indexof(const hash_table_t table, const square_matrix_t key, int return -1; } +/* Returns also an isometry between the lattices */ + int hash_table_index_and_isom(const hash_table_t table, const square_matrix_t key, isometry_t isom, double* theta_time, double* isom_time, int* num_isom) { @@ -526,6 +570,8 @@ int hash_table_index_and_isom(const hash_table_t table, const square_matrix_t ke return -1; } +/* Expand the size of the table in order to support more entries. */ + void hash_table_expand(hash_table_t table) { int i, stored; @@ -561,6 +607,8 @@ void hash_table_expand(hash_table_t table) return; } +/* clear the memory allocated for the table */ + void hash_table_clear(hash_table_t table) { hash_t i;