├── .gitmodules ├── CMakeLists.txt ├── CMakeModules ├── AppendCompilerFlags.cmake └── MultiIndexHelper.cmake ├── README.md ├── data ├── CMakeLists.txt └── README.md ├── exp0.config ├── exp1.config ├── include └── multi_idx │ ├── aux_bits.hpp │ ├── linear_scan.hpp │ ├── multi_idx.hpp │ ├── multi_idx_helper.hpp │ ├── multi_idx_red.hpp │ ├── perm.hpp.cmake │ ├── simd_utils.hpp │ ├── simple_buckets_binsearch.hpp │ ├── simple_buckets_binvector.hpp │ ├── simple_buckets_binvector_split.hpp │ ├── simple_buckets_binvector_split_xor.hpp │ ├── simple_buckets_binvector_unaligned.hpp │ ├── simple_buckets_vector.hpp │ ├── triangle_buckets_binvector_split_simd.hpp │ ├── triangle_clusters_binvector_split.hpp │ ├── triangle_clusters_binvector_split_threshold.hpp │ ├── tuple_foreach.hpp │ └── xor_buckets_binvector_split.hpp ├── install.sh ├── lib └── CMakeLists.txt ├── scripts ├── CodeGeneration.py ├── CodeGeneration.sh ├── Gen.py ├── Permutations.py ├── calcperm.cpp ├── calcperm.ini ├── exp0.R ├── exp1.R ├── extract_tweets.py ├── general.c ├── general.h ├── input_info.R ├── multi_index.R ├── perm_b64.h ├── perm_bas.c ├── perm_bas.h └── perm_bxx.h └── src ├── bench_scan.cpp ├── bench_scan2.cpp ├── bench_scan3.cpp ├── cluster_statistics.cpp ├── gen_bench_data.cpp ├── gen_debug_data.cpp ├── gen_hash_file.cpp ├── ham_distribution.cpp ├── index.cpp ├── info.cpp └── sim_hash.cpp /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/sdsl-lite"] 2 | path = external/sdsl-lite 3 | url = https://github.com/simongog/sdsl-lite.git 4 | -------------------------------------------------------------------------------- /CMakeModules/AppendCompilerFlags.cmake: -------------------------------------------------------------------------------- 1 | include(CheckCSourceCompiles) 2 | include(CheckCXXSourceCompiles) 3 | 4 | macro(append_c_compiler_flags _flags _name _result) 5 | set(SAFE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) 6 | string(REGEX REPLACE "[-+/ ]" "_" cname "${_name}") 7 | string(TOUPPER "${cname}" cname) 8 | foreach(flag ${_flags}) 9 | string(REGEX REPLACE "^[-+/ ]+(.*)[-+/ ]*$" "\\1" flagname "${flag}") 10 | string(REGEX REPLACE "[-+/ ]" "_" flagname "${flagname}") 11 | string(TOUPPER "${flagname}" flagname) 12 | set(have_flag "HAVE_${cname}_${flagname}") 13 | set(CMAKE_REQUIRED_FLAGS "${flag}") 14 | check_c_source_compiles("int main() { return 0; }" ${have_flag}) 15 | if(${have_flag}) 16 | set(${_result} "${${_result}} ${flag}") 17 | endif(${have_flag}) 18 | endforeach(flag) 19 | set(CMAKE_REQUIRED_FLAGS ${SAFE_CMAKE_REQUIRED_FLAGS}) 20 | endmacro(append_c_compiler_flags) 21 | 22 | macro(append_cxx_compiler_flags _flags _name _result) 23 | set(SAFE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) 24 | string(REGEX REPLACE "[-+/ ]" "_" cname "${_name}") 25 | string(TOUPPER "${cname}" cname) 26 | foreach(flag ${_flags}) 27 | string(REGEX REPLACE "^[-+/ ]+(.*)[-+/ ]*$" "\\1" flagname "${flag}") 28 | string(REGEX REPLACE "[-+/ ]" "_" flagname "${flagname}") 29 | string(TOUPPER "${flagname}" flagname) 30 | set(have_flag "HAVE_${cname}_${flagname}") 31 | set(CMAKE_REQUIRED_FLAGS "${flag}") 32 | check_cxx_source_compiles("int main() { return 0; }" ${have_flag}) 33 | if(${have_flag}) 34 | set(${_result} "${${_result}} ${flag}") 35 | endif(${have_flag}) 36 | endforeach(flag) 37 | set(CMAKE_REQUIRED_FLAGS ${SAFE_CMAKE_REQUIRED_FLAGS}) 38 | endmacro(append_cxx_compiler_flags) 39 | -------------------------------------------------------------------------------- /CMakeModules/MultiIndexHelper.cmake: -------------------------------------------------------------------------------- 1 | # Extract template parameters from class definition. 2 | # A list of template paramters is stored in the output_variable. 3 | FUNCTION(GET_TPARAMS class_name output_variable min_args) 4 | # MESSAGE("class_name=${class_name}") 5 | SET(${output_variable} "") 6 | IF ( ${class_name} MATCHES "^([^<]+)<(.*)>" ) 7 | STRING(REGEX REPLACE "^([^<]+)<(.*)>" "\\2" class_name_suffix ${class_name}) 8 | STRING(REGEX MATCHALL "." class_char_list "${class_name_suffix}") 9 | LIST(APPEND class_char_list ",") 10 | SET(level 0) 11 | SET(tparam "") 12 | FOREACH(char ${class_char_list}) 13 | IF ( "${char}" MATCHES "<" ) 14 | MATH(EXPR level "1+(${level})") 15 | ELSEIF ( "${char}" MATCHES ">" ) 16 | MATH(EXPR level "(${level})-1") 17 | ENDIF() 18 | IF ( "${char}" MATCHES "," AND level EQUAL 0 ) 19 | #MESSAGE("APPEND to ${output_variable} ${tparam}" ) 20 | # LIST(APPEND _tparam ${tparam}) 21 | SET(${output_variable} ${${output_variable}} ${tparam}) 22 | #MESSAGE("${output_variable} = ${${output_variable}}") 23 | SET(tparam "") 24 | ELSE() 25 | SET(tparam "${tparam}${char}") 26 | ENDIF() 27 | ENDFOREACH() 28 | # SET(output_variable ${_tparam} PARENT_SCOPE) 29 | # MESSAGE("!!! ${${output_variable}}") 30 | ENDIF() 31 | LIST(LENGTH ${output_variable} tparam_args) 32 | IF ( ${tparam_args} LESS ${min_args} ) 33 | MESSAGE(FATAL_ERROR "Expect at least ${min_args} template arguments\ 34 | for class ${class_name}.\n ${tparam_args} provided but\ 35 | ${min_args} expected. Parsing: ${${output_variable}}.") 36 | ENDIF() 37 | #MESSAGE("__tparams=${${output_variable}}") 38 | SET(${output_variable} ${${output_variable}} PARENT_SCOPE) 39 | ENDFUNCTION() 40 | 41 | # Generate permutation header for underlying index class and get the number 42 | # of blocks in the multi_index 43 | # Two params 44 | # - type definition of multi_idx 45 | # - output variable for number of blocks 46 | FUNCTION(GEN_PERM_FILE index_type blocks) 47 | STRING(REGEX REPLACE "^([^<]+)(.*)" "\\1" index_type_prefix ${index_type}) 48 | GET_TPARAMS(${index_type} tparams 2) 49 | # MESSAGE("tparams= ${tparams}") 50 | LIST(LENGTH tparams n) 51 | LIST(GET tparams 1 t_k) 52 | IF( ${index_type_prefix} MATCHES "^multi_idx_red$" ) 53 | SET(t_block_error 1) 54 | IF( n GREATER 2 ) 55 | LIST(GET tparams 2 t_block_error) 56 | ENDIF() 57 | MATH(EXPR t_b "1+(${t_k}/(${t_block_error}+1))") 58 | MATH(EXPR t_match 1) 59 | MESSAGE("multi_idx_red t_k=${t_k} t_b=${t_b}") 60 | ELSE() 61 | MATH(EXPR t_b "${t_k}+1") 62 | IF( n GREATER 2 ) 63 | LIST(GET tparams 2 t_b) 64 | ENDIF() 65 | MATH(EXPR t_match ${t_b}-${t_k}) 66 | MESSAGE("multi_idx t_k=${t_k} t_b=${t_b}") 67 | ENDIF() 68 | SET(${blocks} ${t_b} PARENT_SCOPE) 69 | 70 | SET(req_file perm_${t_b}_${t_match}) 71 | SET(cppfile "${CMAKE_HOME_DIRECTORY}/lib/${req_file}.cpp") 72 | SET(hppfile "${CMAKE_HOME_DIRECTORY}/include/multi_idx/${req_file}.hpp") 73 | FILE(APPEND ${perm_header} "#include \"multi_idx/${req_file}.hpp\"\n") 74 | IF ((NOT EXISTS ${cppfile}) OR (NOT EXISTS ${hppfile})) 75 | MESSAGE("GEN_PERM_FILE ${req_file}") 76 | MESSAGE("Code for struct perm<${t_b},${t_match}> does not exist. Generating...") 77 | EXECUTE_PROCESS(COMMAND g++ -o ${CMAKE_HOME_DIRECTORY}/scripts/calcperm ${CMAKE_HOME_DIRECTORY}/scripts/calcperm.cpp) 78 | EXECUTE_PROCESS(COMMAND ./CodeGeneration.sh ${t_b} ${t_match} 79 | WORKING_DIRECTORY ${CMAKE_HOME_DIRECTORY}/scripts 80 | RESULT_VARIABLE code_geni 81 | OUTPUT_VARIABLE log_perm_gen_${t_b}_${t_match} 82 | ERROR_VARIABLE log_perm_gen_${t_b}_${t_match}) 83 | IF (code_geni EQUAL 0) 84 | MESSAGE("Success!") 85 | ELSE (code_geni EQUAL 0) 86 | MESSAGE(FATAL_ERROR "Failed! Log is stored in log_perm_gen_${t_b}_${t_match}") 87 | ENDIF(code_geni EQUAL 0) 88 | MESSAGE("CodeGeneration.sh returned ${code_gen}") 89 | ENDIF() 90 | ENDFUNCTION() 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Multi-Index 2 | ========= 3 | 4 | Installation 5 | ------------ 6 | 7 | To be described 8 | ```bash 9 | ./install.sh 10 | mkdir build 11 | cd build 12 | cmake .. && make exp0 13 | ``` 14 | 15 | Getting Started 16 | ------------ 17 | 18 | To be described 19 | 20 | Reference 21 | --------- 22 | 23 | @inproceedings{SIGIR16b, 24 | author = {Simon Gog and Rossano Venturini}, 25 | title = {Fast and compact {H}amming distance index}, 26 | booktitle = {{SIGIR} 2016: {P}roceedings of the 38th {A}nnual {I}nternational {ACM SIGIR} {C}onference on {R}esearch and {D}evelopment in {I}nformation {R}etrieval}, 27 | year = {2016}, 28 | pages = {--} 29 | } 30 | -------------------------------------------------------------------------------- /data/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | SET(base_url "http://xor.di.unipi.it/~rossano") 2 | 3 | # A samll default data set for testing 4 | SET(local_test_cases test.hash) 5 | ## Uncomment the following line to use the datasets from the SIGIR 2016 paper 6 | #SET(local_test_cases Clueweb09-Full.SimHash Clueweb09-Full.OddSketch lsh_sift_64.hash mlh_sift_64.hash) 7 | SET(test_cases ${local_test_cases} PARENT_SCOPE) 8 | 9 | FOREACH(test_case ${local_test_cases}) 10 | SET(abs_test_case ${CMAKE_HOME_DIRECTORY}/data/${test_case}) 11 | IF(NOT EXISTS ${abs_test_case}) 12 | SET(tc_url ${base_url}/${test_case}) 13 | MESSAGE(${tc_url}) 14 | FILE(DOWNLOAD ${tc_url} ${abs_test_case} SHOW_PROGRESS) 15 | endif(NOT EXISTS ${abs_test_case}) 16 | ENDFOREACH(test_case) 17 | -------------------------------------------------------------------------------- /data/README.md: -------------------------------------------------------------------------------- 1 | This folder is used to store hash files. The format of a hash file sretty 2 | simple. Each file is the concatenation of 64-bit integers, each integer 3 | represent a hash value. 4 | 5 | CMake will download the files specified in `CMakeLists.txt`. The default file 6 | is `test.hash` which contains 12.5 million 64-bit hash values. 7 | You can also get the files of the SIGIR 2016 paper by removing the hash symbol 8 | at the beginning of line 6 in `CMakeLists.txt`. 9 | -------------------------------------------------------------------------------- /exp0.config: -------------------------------------------------------------------------------- 1 | # idx id; idx class ;errors comma separated 2 | mi_bs;multi_idx;2,3,4,5 3 | mi_bs_red;multi_idx_red;2,3,4,5 4 | mi_bs_red2;multi_idx_red;3,4,5 5 | #mi_bv;multi_idx,t_k>;3,4,5 6 | #mi_bv_red;multi_idx_red,t_k,1>;4,5 7 | -------------------------------------------------------------------------------- /exp1.config: -------------------------------------------------------------------------------- 1 | # idx id; idx class ;errors comma separated 2 | mi_bs_red;multi_idx_red;3,4 3 | mi_bv_red;multi_idx_red,t_k>;3,4 4 | -------------------------------------------------------------------------------- /include/multi_idx/aux_bits.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | constexpr uint64_t rol(uint64_t x, int rot) { 6 | // Rotate left a complete word. 7 | // x: value to be rotated 8 | // rot: rotate count, negative values will rotate right 9 | // 1 cycle (should be if inlined) 10 | 11 | // A shift by >= bits is undefined by the C/C++ standard. 12 | // We take care for this case with the "& (bits - 1)" below. 13 | // For many CPUs this stunt is not neccessary. 14 | return (x << (rot & (64 - 1))) | (x >> ((-rot) & (64 - 1))); 15 | } 16 | 17 | 18 | // Generalized Bit Reversal 19 | 20 | const uint64_t a_bfly_mask[]={ 21 | // 0..ld_bits 22 | // For butterfly ops 23 | // = all_bits / ((1 << (1 << i)) + 1) 24 | 0x5555555555555555, // 0 25 | 0x3333333333333333, // 1 26 | 0x0f0f0f0f0f0f0f0f, // 2 27 | 0x00ff00ff00ff00ff, // 3 28 | 0x0000ffff0000ffff, // 4 29 | 0x00000000ffffffff, // 5 30 | 0xffffffffffffffff}; // 6 31 | 32 | inline uint64_t bit_permute_step_simple(uint64_t x, uint64_t m, uint64_t shift) { 33 | // INLINE 34 | // Simplified replacement of bit_permute_step 35 | // Can always be replaced by bit_permute_step (not vice-versa). 36 | // x86: >= 5/4 (5/3) cycles 37 | // ARM: >= 3/2 cycles 38 | 39 | // assert(((m << shift) & m) == 0); 40 | // assert(((m << shift) >> shift) == m); 41 | // assert(((m << shift) | m) == all_bits); // for permutations 42 | return ((x & m) << shift) | ((x >> shift) & m); 43 | } 44 | 45 | 46 | inline uint64_t general_reverse_bits(uint64_t x, uint64_t k) { 47 | // Swap all subwords of given levels. 48 | // See Hacker's Delight, 7.1 "Generalized Bit Reversal" 49 | // k: set of t_subword, i.e. one bit per subword size. 50 | 51 | int i,j; 52 | uint64_t m; 53 | for (i = 0; i <= 6-1; ++i) { // UNROLL 54 | j = 1 << i; 55 | if ((k & j) != 0) { 56 | // x = bit_index_complement(x,j); 57 | m = a_bfly_mask[i]; 58 | x = bit_permute_step_simple(x,m,j); 59 | } 60 | } 61 | return x; 62 | } 63 | 64 | inline uint64_t bswap(uint64_t x) { 65 | // INLINE 66 | // Exchange byte order. 67 | // This can be expressed in assembler: 68 | // bits = 8: n/a 69 | // bits = 16: "xchg al,ah" or "rol ax,16" 70 | // bits = 32: "bswap eax" 71 | // bits = 64: "bswap rax" 72 | // bits = 128: "xchg rax,rdx; bswap rax; bswap rdx" 73 | 74 | return general_reverse_bits(x, ~7); 75 | } 76 | -------------------------------------------------------------------------------- /include/multi_idx/linear_scan.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "sdsl/io.hpp" 6 | 7 | namespace multi_index { 8 | 9 | template 10 | class linear_scan { 11 | public: 12 | typedef uint64_t size_type; 13 | private: 14 | std::vector m_keys; 15 | 16 | public: 17 | linear_scan() = default; 18 | linear_scan(const linear_scan &) = default; 19 | linear_scan(linear_scan &&) = default; 20 | linear_scan &operator=(const linear_scan &) = default; 21 | linear_scan &operator=(linear_scan &&) = default; 22 | 23 | /*! 24 | * \param keys Vector of hash values 25 | * \pre Items are all different (no duplicates) 26 | */ 27 | linear_scan(const std::vector& keys, bool async=false) { 28 | m_keys = keys; 29 | } 30 | 31 | std::pair,uint64_t> match(const uint64_t query, const bool find_only_candidates=false) { 32 | std::vector matches; 33 | uint64_t candidates = 0; 34 | for(auto it = m_keys.begin(); it!=m_keys.end(); ++it){ 35 | if ( sdsl::bits::cnt((*it)^query) < t_k ) { 36 | matches.push_back(*it); 37 | } 38 | } 39 | candidates = m_keys.size(); 40 | return {matches, candidates}; 41 | } 42 | 43 | //! Serializes the data structure into the given ostream 44 | uint64_t serialize(std::ostream &out, sdsl::structure_tree_node *v = nullptr, 45 | std::string name = "") const { 46 | using namespace sdsl; 47 | structure_tree_node *child = 48 | structure_tree::add_child(v, name, util::class_name(*this)); 49 | structure_tree::add_size(child, 0); 50 | return 0; 51 | } 52 | 53 | //! Loads the data structure from the given istream. 54 | void load(std::istream &in) { 55 | uint64_t x; 56 | m_keys.clear(); 57 | while(in.read((char*) &x,sizeof(x))){ 58 | m_keys.push_back(x); 59 | } 60 | std::cout<<"loaded "< 4 | #include 5 | #include 6 | #include "sdsl/io.hpp" 7 | #include "multi_idx/perm.hpp" 8 | #include "multi_idx/tuple_foreach.hpp" 9 | #include "multi_idx/multi_idx_helper.hpp" 10 | #include "multi_idx/simple_buckets_binsearch.hpp" 11 | #include "multi_idx/simple_buckets_binvector.hpp" 12 | #include "multi_idx/simple_buckets_vector.hpp" 13 | #include "multi_idx/simple_buckets_binvector_unaligned.hpp" 14 | #include "multi_idx/simple_buckets_binvector_split.hpp" 15 | #include "multi_idx/simple_buckets_binvector_split_xor.hpp" 16 | #include "multi_idx/triangle_buckets_binvector_split_simd.hpp" 17 | #include "multi_idx/triangle_clusters_binvector_split.hpp" 18 | #include "multi_idx/triangle_clusters_binvector_split_threshold.hpp" 19 | #include "multi_idx/xor_buckets_binvector_split.hpp" 20 | 21 | namespace multi_index { 22 | 23 | /*! Basic multi-index implementation. 24 | * \tparam t_idx_strategy Index class for matching under a fixed permutation. 25 | * \tparam t_k Number of allowed errors. 26 | * \tparam t_b Keys are split into t_b blocks. 27 | * 28 | * \par The default value of blocks t_b is t_k+1. This way, a key K in the 29 | * index has at least one block in common with the query key Q if 30 | * H(K,Q) <= t_k. It is also possible to specify t_b > t_k+1. In general 31 | * a key K with H(K,Q) has at least t_b-t_k blocks in common with the 32 | * query key Q. 33 | * \sa multi_index_red 34 | */ 35 | template 39 | class multi_idx { 40 | public: 41 | typedef uint64_t size_type; 42 | typedef perm perm_b_k; 43 | private: 44 | static constexpr size_t m_num_perms = std::tuple_size::value; 45 | typename perm_type_gen::type m_idx; 46 | 47 | public: 48 | multi_idx() = default; 49 | multi_idx(const multi_idx &) = default; 50 | multi_idx(multi_idx &&) = default; 51 | multi_idx &operator=(const multi_idx &) = default; 52 | multi_idx &operator=(multi_idx &&) = default; 53 | 54 | /*! 55 | * \param keys Vector of hash values 56 | * \pre Items are all different (no duplicates) 57 | */ 58 | multi_idx(const std::vector& keys, bool async=false) { 59 | constructor c{keys, async}; 60 | tuple_foreach(m_idx, c); 61 | } 62 | 63 | std::pair,uint64_t> match(const uint64_t query, const bool find_only_candidates=false) { 64 | std::vector matches; 65 | uint64_t candidates = 0; 66 | matcher m{matches, candidates, query, find_only_candidates}; 67 | tuple_foreach(m_idx, m); 68 | return {matches, candidates}; 69 | } 70 | 71 | //! Serializes the data structure into the given ostream 72 | uint64_t serialize(std::ostream &out, sdsl::structure_tree_node *v = nullptr, 73 | std::string name = "") const { 74 | using namespace sdsl; 75 | structure_tree_node *child = 76 | structure_tree::add_child(v, name, util::class_name(*this)); 77 | uint64_t written_bytes = 0; 78 | serializer s{written_bytes, child, out}; 79 | tuple_foreach(m_idx, s); 80 | structure_tree::add_size(child, written_bytes); 81 | return written_bytes; 82 | } 83 | 84 | //! Loads the data structure from the given istream. 85 | void load(std::istream &in) { 86 | loader l{in}; 87 | tuple_foreach(m_idx, l); 88 | } 89 | 90 | uint64_t size() const { 91 | if (m_num_perms > 0) { 92 | return std::get<0>(m_idx).size(); 93 | } 94 | return 0; 95 | } 96 | 97 | private: 98 | // Functors which do the actual work on the tuple of indexes 99 | 100 | struct constructor { 101 | const std::vector& items; 102 | bool is_async; 103 | std::vector> futures; 104 | 105 | constructor(const std::vector& f_items, bool asy=false):items(f_items),is_async(asy){}; 106 | template 107 | void operator()(T&& t, std::size_t i) { 108 | if ( is_async ) { 109 | std::cout<<"start construction thread"<::type){items}; 112 | return 1; 113 | })); 114 | } else { 115 | t = (typename std::remove_reference::type){items}; 116 | } 117 | } 118 | }; 119 | 120 | struct serializer { 121 | uint64_t& wb; 122 | sdsl::structure_tree_node *c; 123 | std::ostream& o; 124 | serializer(uint64_t& written_bytes, sdsl::structure_tree_node *child, std::ostream &out) : 125 | wb(written_bytes), c(child), o(out) {}; 126 | template 127 | void operator()(T&& t, std::size_t i) const { 128 | wb += sdsl::serialize(t, o, c, "multi_idx"); 129 | } 130 | }; 131 | 132 | struct loader { 133 | std::istream& in; 134 | loader(std::istream &f_in) : in(f_in) {}; 135 | template 136 | void operator()(T&& t, std::size_t i) const { 137 | sdsl::load(t, in); 138 | } 139 | }; 140 | 141 | struct matcher { 142 | std::vector& matches; 143 | uint64_t& candidates; 144 | uint64_t query; 145 | bool only_cands; 146 | matcher(std::vector& mats, uint64_t& cands, uint64_t qry, bool only_cand) : 147 | matches(mats), candidates(cands), query(qry), only_cands(only_cand) 148 | { }; 149 | 150 | template 151 | void operator()(T&& t, std::size_t i) const { 152 | auto res = t.match(query, t_k, only_cands); 153 | matches.insert(matches.end(), std::get<0>(res).begin(), std::get<0>(res).end()); 154 | candidates += std::get<1>(res); 155 | } 156 | }; 157 | 158 | }; 159 | 160 | } 161 | -------------------------------------------------------------------------------- /include/multi_idx/multi_idx_helper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "multi_idx/tuple_foreach.hpp" 5 | 6 | namespace multi_index { 7 | 8 | static std::unordered_map stat_counter; 9 | //static std::map bucket_distr; 10 | //static std::map cluster_distr; 11 | 12 | template 13 | std::map 14 | get_bucket_dist(const t_index& index){ 15 | std::map res; 16 | uint64_t buckets = (typename decltype(index.m_C)::rank_1_type){&(index.m_C)}(index.m_C.size()); 17 | for(uint64_t bucket=0; bucket cnt; 27 | template 28 | void operator()(T&& t, std::size_t) { 29 | auto res = get_bucket_dist(t); 30 | for(auto x : res){ 31 | cnt[x.first] += x.second; 32 | } 33 | } 34 | }; 35 | 36 | template 37 | std::map 38 | get_cluster_dist(const t_index& index){ 39 | std::map res; 40 | for(auto it = index.m_first_level.begin(); it+3 < index.m_first_level.end(); it+=3){ 41 | ++res[(*(it+3)) - (*it)]; 42 | } 43 | return res; 44 | } 45 | 46 | struct cluster_accumulator { 47 | std::map cnt; 48 | template 49 | void operator()(T&& t, std::size_t) { 50 | auto res = get_cluster_dist(t); 51 | for(auto x : res){ 52 | cnt[x.first] += x.second; 53 | } 54 | } 55 | }; 56 | 57 | 58 | template 59 | std::map 60 | get_error_dist(const t_index& index){ 61 | std::map res; 62 | for(auto it = index.m_first_level.begin()+2; it < index.m_first_level.end(); it+=3){ 63 | ++res[*it]; 64 | } 65 | return res; 66 | } 67 | 68 | struct error_accumulator { 69 | std::map cnt; 70 | template 71 | void operator()(T&& t, std::size_t) { 72 | auto res = get_error_dist(t); 73 | for(auto x : res){ 74 | cnt[x.first] += x.second; 75 | } 76 | } 77 | }; 78 | 79 | 80 | template 81 | std::map 82 | get_dist(const t_index& index){ 83 | t_accum acc; 84 | tuple_foreach(index.m_idx, acc); 85 | return acc.cnt; 86 | } 87 | 88 | 89 | // Helper structs to generate a tuple of strategy classes 90 | template> 91 | struct perm_type_gen{ 92 | using perm_type = std::tuple>; 93 | using type = decltype(std::tuple_cat(perm_type(), std::declval::type>())); 94 | }; 95 | 96 | template 97 | struct perm_type_gen<0, t_b, t_k, t_idx_strategy, t_perm>{ 98 | using type = std::tuple<>; 99 | }; 100 | 101 | // Trait for the type of mid_entries for the split strategy classes 102 | template 103 | struct mid_entries_trait{ 104 | using type = sdsl::int_vector<>; 105 | static type get_instance(typename type::size_type n, typename type::value_type x) { 106 | return type(n, x, t_w); 107 | } 108 | static void assign(type& rac, uint64_t i, uint64_t x) { rac[i] = x; } 109 | }; 110 | 111 | template<> 112 | struct mid_entries_trait<16>{ 113 | using type = sdsl::int_vector<16>; 114 | static type get_instance(typename type::size_type n, typename type::value_type x) { 115 | return type(n, x); 116 | } 117 | static void assign(type& rac, uint64_t i, uint64_t x) { rac[i] = x; } 118 | }; 119 | 120 | template<> 121 | struct mid_entries_trait<8>{ 122 | using type = sdsl::int_vector<8>; 123 | static type get_instance(typename type::size_type n, typename type::value_type x) { 124 | return type(n, x); 125 | } 126 | static void assign(type& rac, uint64_t i, uint64_t x) { rac[i] = x; } 127 | }; 128 | 129 | template<> 130 | struct mid_entries_trait<0>{ 131 | struct type : public sdsl::int_vector<>{ 132 | uint64_t operator[](uint64_t) const{ 133 | return 0; 134 | } 135 | }; 136 | 137 | static type get_instance(typename type::size_type n, typename type::value_type x) { 138 | return type{}; 139 | } 140 | static void assign(type&, uint64_t, uint64_t) { } 141 | }; 142 | 143 | template 144 | void check_permutation(const std::vector &input_entries) { 145 | std::cout << "Check permuting functions\n"; 146 | for(auto x : input_entries) { 147 | if (x != t_strat::perm_b_k::mi_rev_permute[t_id](t_strat::perm_b_k::mi_permute[t_id](x))) 148 | std::cout << "ERROR permuting " << x << std::endl; 149 | } 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /include/multi_idx/multi_idx_red.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "sdsl/io.hpp" 7 | #include "multi_idx/perm.hpp" 8 | #include "multi_idx/multi_idx.hpp" 9 | 10 | namespace multi_index { 11 | 12 | /*! multi index-index implementation 13 | * \tparam t_idx_strategy Index class for matching in a fixed permutation. 14 | * \tparam t_k Number of allowed errors. 15 | * \tparam t_b The hash is split into t_b blocks. 16 | * \sa multi_idx 17 | */ 18 | template 21 | class multi_idx_red { 22 | static_assert(t_k >= t_block_errors,"It should hold that t_k >= t_block_errors"); 23 | public: 24 | static constexpr uint8_t t_b = (t_k/(t_block_errors+1)) + 1; 25 | typedef uint64_t size_type; 26 | typedef perm perm_b_k; 27 | 28 | private: 29 | static constexpr size_t m_num_perms = std::tuple_size::value; 30 | public: 31 | typename perm_type_gen>::type m_idx; 32 | 33 | public: 34 | multi_idx_red() = default; 35 | multi_idx_red(const multi_idx_red &) = default; 36 | multi_idx_red(multi_idx_red &&) = default; 37 | multi_idx_red &operator=(const multi_idx_red &) = default; 38 | multi_idx_red &operator=(multi_idx_red &&) = default; 39 | 40 | /*! 41 | * \param keys Vector of hash values 42 | * \pre Items are all different (no duplicates) 43 | */ 44 | multi_idx_red(const std::vector& keys, bool async=false) { 45 | constructor c{keys, async}; 46 | tuple_foreach(m_idx, c); 47 | } 48 | 49 | std::pair,uint64_t> match(const uint64_t query, const bool find_only_candidates=false) { 50 | std::vector matches; 51 | uint64_t candidates = 0; 52 | matcher m{matches, candidates, query, find_only_candidates}; 53 | tuple_foreach(m_idx, m); 54 | return {matches, candidates}; 55 | } 56 | 57 | //! Serializes the data structure into the given ostream 58 | uint64_t serialize(std::ostream &out, sdsl::structure_tree_node *v = nullptr, 59 | std::string name = "") const { 60 | using namespace sdsl; 61 | structure_tree_node *child = 62 | structure_tree::add_child(v, name, util::class_name(*this)); 63 | uint64_t written_bytes = 0; 64 | serializer s{written_bytes, child, out}; 65 | tuple_foreach(m_idx, s); 66 | structure_tree::add_size(child, written_bytes); 67 | return written_bytes; 68 | } 69 | 70 | //! Loads the data structure from the given istream. 71 | void load(std::istream &in) { 72 | loader l{in}; 73 | tuple_foreach(m_idx, l); 74 | } 75 | 76 | uint64_t size() const { 77 | if (m_num_perms > 0) { 78 | return std::get<0>(m_idx).size(); 79 | } 80 | return 0; 81 | } 82 | 83 | private: 84 | // Functors which do the actual work on the tuple of indexes 85 | 86 | struct constructor { 87 | const std::vector& items; 88 | bool is_async; 89 | std::vector> futures; 90 | 91 | constructor(const std::vector& f_items, bool asy=false):items(f_items),is_async(asy) {}; 92 | 93 | template 94 | void operator()(T&& t, std::size_t i) { 95 | if ( is_async ) { 96 | std::cout<<"start construction thread"<::type){items}; 99 | return 1; 100 | })); 101 | } else { 102 | t = (typename std::remove_reference::type){items}; 103 | } 104 | } 105 | }; 106 | 107 | struct serializer { 108 | uint64_t& wb; 109 | sdsl::structure_tree_node *c; 110 | std::ostream& o; 111 | serializer(uint64_t& written_bytes, sdsl::structure_tree_node *child, std::ostream &out) : 112 | wb(written_bytes), c(child), o(out) {}; 113 | template 114 | void operator()(T&& t, std::size_t i) const { 115 | wb += sdsl::serialize(t, o, c, "multi_idx_red"); 116 | } 117 | }; 118 | 119 | struct loader { 120 | std::istream& in; 121 | loader(std::istream &f_in) : in(f_in) {}; 122 | template 123 | void operator()(T&& t, std::size_t i) const { 124 | sdsl::load(t, in); 125 | } 126 | }; 127 | 128 | struct matcher { 129 | std::vector& matches; 130 | uint64_t& candidates; 131 | uint64_t query; 132 | bool only_cands; 133 | matcher(std::vector& mats, uint64_t& cands, uint64_t qry, bool only_cand) : 134 | matches(mats), candidates(cands), query(qry), only_cands(only_cand) 135 | { }; 136 | 137 | template 138 | void operator()(T&& t, std::size_t i) const { 139 | using TT = typename std::remove_reference::type; 140 | // For all block_errors <= t_block_errors match 141 | // with flipping block_errors bits for t_k errors 142 | for (auto block_mask : splitter_mask::precomp.data) { 143 | uint64_t query_flipped = TT::perm::mi_permute[TT::id](query); 144 | query_flipped = query_flipped ^ block_mask; 145 | query_flipped = TT::perm::mi_rev_permute[TT::id](query_flipped); 146 | uint32_t block_errors = sdsl::bits::cnt(block_mask); 147 | auto res = t.match(query_flipped, t_k-block_errors, only_cands); 148 | matches.insert(matches.end(), std::get<0>(res).begin(), std::get<0>(res).end()); 149 | candidates += std::get<1>(res); 150 | } 151 | } 152 | }; 153 | 154 | }; 155 | 156 | } 157 | -------------------------------------------------------------------------------- /include/multi_idx/perm.hpp.cmake: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "sdsl/bit_vectors.hpp" 7 | 8 | template 9 | struct splitter_mask{ 10 | static constexpr size_t binomial(size_t n, size_t k){ 11 | if ( n < k ) 12 | return 0; 13 | if ( k == 0 or n == k ) 14 | return 1; 15 | if ( k == 1 ) 16 | return n; 17 | return (n*binomial(n-1, k-1))/k; 18 | } 19 | 20 | static constexpr size_t all_binomial(size_t n, size_t k) { 21 | return k==0 ? 1 : binomial(n, k) + all_binomial(n, k-1); 22 | } 23 | 24 | static struct impl { 25 | std::array data; 26 | 27 | impl(){ 28 | size_t idx=0; 29 | data[idx++] = 0ULL; 30 | for (size_t block_errors=1; block_errors <= t_block_errors; ++block_errors) { 31 | sdsl::bit_vector mask(t_splitter_bits, 0); 32 | for(size_t i=0; i 42 | typename splitter_mask::impl splitter_mask::precomp; 43 | 44 | template 45 | struct perm{ 46 | typedef uint64_t (*perm_fun_t)(uint64_t); 47 | typedef std::vector t_fun_vec; 48 | 49 | static const uint8_t max_dist = 0; 50 | static const uint8_t match_len = 0; 51 | 52 | static const t_fun_vec mi_permute; 53 | static const t_fun_vec mi_rev_permute; 54 | static const std::vector> mi_perms; 55 | static const std::vector mi_permute_block_sizes; 56 | 57 | static const t_fun_vec chi_permute; 58 | static const t_fun_vec chi_rev_permute; 59 | static const std::vector> chi_perms; 60 | static const std::vector chi_permute_block_sizes; 61 | }; 62 | 63 | -------------------------------------------------------------------------------- /include/multi_idx/simd_utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | const __m128i lookup = _mm_setr_epi8( 4 | /* 0 */ 0, /* 1 */ 1, /* 2 */ 1, /* 3 */ 2, 5 | /* 4 */ 1, /* 5 */ 2, /* 6 */ 2, /* 7 */ 3, 6 | /* 8 */ 1, /* 9 */ 2, /* a */ 2, /* b */ 3, 7 | /* c */ 2, /* d */ 3, /* e */ 3, /* f */ 4 8 | ); 9 | 10 | const __m128i low_mask = _mm_set1_epi8(0x0f); 11 | const __m128i cleaning_mask = _mm_set1_epi32(0x00ff); 12 | 13 | inline __m128i popcount_epi32(const __m128i vec) { 14 | const __m128i lo = vec & low_mask; 15 | const __m128i hi = _mm_and_si128((_mm_srli_epi16(vec, 4)), low_mask); 16 | 17 | const __m128i popcnt1 = _mm_shuffle_epi8(lookup, lo); 18 | const __m128i popcnt2 = _mm_shuffle_epi8(lookup, hi); 19 | 20 | __m128i popcounts = _mm_add_epi8(popcnt1, popcnt2); 21 | popcounts = _mm_add_epi8(popcounts, _mm_srli_si128(popcounts, 1)); 22 | popcounts = _mm_and_si128( _mm_add_epi8(popcounts, _mm_srli_si128(popcounts, 2)), cleaning_mask); 23 | return popcounts; 24 | } 25 | 26 | #define LIKELY(x) (__builtin_expect((x), 1)) 27 | #define UNLIKELY(x) (__builtin_expect((x), 0)) -------------------------------------------------------------------------------- /include/multi_idx/simple_buckets_binsearch.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "multi_idx/perm.hpp" 7 | #include "sdsl/io.hpp" 8 | #include "sdsl/int_vector.hpp" 9 | #include "sdsl/sd_vector.hpp" 10 | #include "sdsl/bit_vectors.hpp" 11 | 12 | namespace multi_index { 13 | 14 | template 18 | > 19 | class _simple_buckets_binsearch { 20 | public: 21 | typedef uint64_t size_type; 22 | typedef uint64_t entry_type; 23 | typedef perm_b_k perm; 24 | enum {id = t_id}; 25 | 26 | 27 | private: 28 | static constexpr uint8_t init_splitter_bits(size_t i=0){ 29 | return i < perm_b_k::match_len ? perm_b_k::mi_permute_block_widths[t_id][t_b-1-i] + init_splitter_bits(i+1) : 0; 30 | } 31 | uint64_t m_n; // number of items 32 | sdsl::int_vector<64> m_entries; 33 | 34 | public: 35 | static constexpr uint8_t splitter_bits = init_splitter_bits(); 36 | 37 | _simple_buckets_binsearch() = default; 38 | 39 | _simple_buckets_binsearch(const std::vector &input_entries) { 40 | std::cout << "Splitter bits " << (uint16_t) splitter_bits << std::endl; 41 | 42 | m_n = input_entries.size(); 43 | sdsl::int_vector<64>(input_entries.size(), 0).swap(m_entries); 44 | 45 | if(splitter_bits <= 28) build_small_universe(input_entries); 46 | else build_large_universe(input_entries); 47 | } 48 | 49 | inline std::pair, uint64_t> match(const entry_type q, uint8_t errors=t_k, const bool find_only_candidates=false) { 50 | uint64_t bucket = get_bucket_id(q); 51 | 52 | auto begin = std::lower_bound(m_entries.begin(), 53 | m_entries.end(), 54 | bucket, 55 | [&](const entry_type &a, const entry_type &bucket) { 56 | return get_bucket_id(a) < bucket;} 57 | ); 58 | auto end = std::upper_bound(begin, 59 | m_entries.end(), 60 | bucket, 61 | [&](const entry_type &bucket, const entry_type &b) { 62 | return bucket < get_bucket_id(b);} 63 | ); 64 | 65 | uint64_t candidates = std::distance(begin,end); 66 | std::vector res; 67 | 68 | if (find_only_candidates) return {res, candidates}; 69 | if (errors >= 6) res.reserve(128); 70 | 71 | for (auto it = begin; it != end; ++it) { 72 | if (sdsl::bits::cnt(q^*it) <= errors) { 73 | res.push_back(*it); 74 | } 75 | } 76 | return {res, candidates}; 77 | } 78 | 79 | _simple_buckets_binsearch& operator=(const _simple_buckets_binsearch& idx) { 80 | if ( this != &idx ) { 81 | m_n = std::move(idx.m_n); 82 | m_entries = std::move(idx.m_entries); 83 | } 84 | return *this; 85 | } 86 | 87 | _simple_buckets_binsearch& operator=(_simple_buckets_binsearch&& idx) { 88 | if ( this != &idx ) { 89 | m_n = std::move(idx.m_n); 90 | m_entries = std::move(idx.m_entries); 91 | } 92 | return *this; 93 | } 94 | 95 | _simple_buckets_binsearch(const _simple_buckets_binsearch& idx) { 96 | *this = idx; 97 | } 98 | 99 | _simple_buckets_binsearch(_simple_buckets_binsearch&& idx){ 100 | *this = std::move(idx); 101 | } 102 | 103 | //! Serializes the data structure into the given ostream 104 | size_type serialize(std::ostream& out, sdsl::structure_tree_node* v=nullptr, std::string name="")const { 105 | using namespace sdsl; 106 | structure_tree_node* child = structure_tree::add_child(v, name, util::class_name(*this)); 107 | uint64_t written_bytes = 0; 108 | written_bytes += write_member(m_n, out, child, "n"); 109 | written_bytes += m_entries.serialize(out, child, "entries"); 110 | structure_tree::add_size(child, written_bytes); 111 | return written_bytes; 112 | } 113 | 114 | //! Loads the data structure from the given istream. 115 | void load(std::istream& in) { 116 | using namespace sdsl; 117 | read_member(m_n, in); 118 | m_entries.load(in); 119 | } 120 | 121 | size_type size() const{ 122 | return m_n; 123 | } 124 | 125 | private: 126 | 127 | inline uint64_t get_bucket_id(const uint64_t x) const { 128 | return perm_b_k::mi_permute[t_id](x) >> (64-splitter_bits); 129 | } 130 | 131 | 132 | void build_large_universe(const std::vector &input_entries) { 133 | std::copy(input_entries.begin(), input_entries.end(), m_entries.begin()); 134 | std::cout << "Start sorting\n"; 135 | std::sort(m_entries.begin(), m_entries.end(), 136 | [&](const entry_type &a, const entry_type &b) { 137 | return get_bucket_id(a) < get_bucket_id(b); 138 | }); 139 | std::cout << "End sorting\n"; 140 | } 141 | 142 | void build_small_universe(const std::vector &input_entries) { 143 | // countingSort-like strategy to order entries accordingly to bucket_id 144 | uint64_t splitter_universe = ((uint64_t) 1) << (splitter_bits); 145 | std::vector prefix_sums(splitter_universe + 1, 0); // includes a sentinel 146 | for (auto x : input_entries) { 147 | prefix_sums[get_bucket_id(x)]++; 148 | } 149 | 150 | uint64_t sum = prefix_sums[0]; 151 | prefix_sums[0] = 0; 152 | for(uint64_t i = 1; i < prefix_sums.size(); ++i) { 153 | uint64_t curr = prefix_sums[i]; 154 | prefix_sums[i] = sum; 155 | sum += curr; 156 | } 157 | for (auto x : input_entries) { 158 | uint64_t bucket = get_bucket_id(x); 159 | m_entries[prefix_sums[bucket]] = x; 160 | prefix_sums[bucket]++; 161 | } 162 | } 163 | 164 | }; 165 | 166 | struct simple_buckets_binsearch { 167 | template 168 | using type = _simple_buckets_binsearch; 169 | }; 170 | 171 | } 172 | -------------------------------------------------------------------------------- /include/multi_idx/simple_buckets_binvector.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "multi_idx/perm.hpp" 7 | #include "sdsl/io.hpp" 8 | #include "sdsl/int_vector.hpp" 9 | #include "sdsl/sd_vector.hpp" 10 | #include "sdsl/bit_vectors.hpp" 11 | #include "multi_idx/multi_idx_helper.hpp" 12 | 13 | namespace multi_index { 14 | 15 | // we can match everything with less than t_k errors 16 | template, 20 | typename t_bv=sdsl::bit_vector, 21 | typename t_sel=typename t_bv::select_1_type> 22 | class _simple_buckets_binvector { 23 | public: 24 | typedef uint64_t size_type; 25 | typedef uint64_t entry_type; 26 | typedef perm_b_k perm; 27 | enum {id = t_id}; 28 | 29 | private: 30 | static constexpr uint8_t init_splitter_bits(size_t i=0){ 31 | return i < perm_b_k::match_len ? perm_b_k::mi_permute_block_widths[t_id][t_b-1-i] + init_splitter_bits(i+1) : 0; 32 | } 33 | 34 | uint64_t m_n; // number of items 35 | sdsl::int_vector<64> m_entries; 36 | t_bv m_C; // bit vector for prefix sums of meta-symbols 37 | t_sel m_C_sel; // select1 structure for m_C 38 | 39 | public: 40 | static constexpr uint8_t splitter_bits = init_splitter_bits(); 41 | 42 | _simple_buckets_binvector() = default; 43 | 44 | _simple_buckets_binvector(const std::vector &input_entries) { 45 | std::cout << "Splitter bits " << (uint16_t) splitter_bits << std::endl; 46 | 47 | m_n = input_entries.size(); 48 | m_entries = sdsl::int_vector<64>(input_entries.size(), 0); 49 | 50 | build_small_universe(input_entries); 51 | } 52 | 53 | // assert(errors <= t_k) 54 | inline std::pair, uint64_t> match(const entry_type q, uint8_t errors=t_k, const bool find_only_candidates=false) { 55 | uint64_t bucket = get_bucket_id(q); 56 | 57 | const auto l = bucket == 0 ? 0 : m_C_sel(bucket) - bucket +1; 58 | const auto r = m_C_sel(bucket+1) - (bucket+1) +1; 59 | 60 | auto begin = m_entries.begin() + l; 61 | auto end = m_entries.begin() + r; 62 | 63 | uint64_t candidates = std::distance(begin,end); 64 | std::vector res; 65 | 66 | if(find_only_candidates) return {res, candidates}; 67 | for (auto it = begin; it != end; ++it) { 68 | if (sdsl::bits::cnt(q^*it) <= errors) 69 | res.push_back(*it); 70 | } 71 | return {res, candidates}; 72 | } 73 | 74 | _simple_buckets_binvector& operator=(const _simple_buckets_binvector& idx) { 75 | if ( this != &idx ) { 76 | m_n = std::move(idx.m_n); 77 | m_entries = std::move(idx.m_entries); 78 | m_C = std::move(idx.m_C); 79 | m_C_sel = std::move(idx.m_C_sel); 80 | m_C_sel.set_vector(&m_C); 81 | } 82 | return *this; 83 | } 84 | 85 | _simple_buckets_binvector& operator=(_simple_buckets_binvector&& idx) { 86 | if ( this != &idx ) { 87 | m_n = std::move(idx.m_n); 88 | m_entries = std::move(idx.m_entries); 89 | m_C = std::move(idx.m_C); 90 | m_C_sel = std::move(idx.m_C_sel); 91 | m_C_sel.set_vector(&m_C); 92 | } 93 | return *this; 94 | } 95 | 96 | _simple_buckets_binvector(const _simple_buckets_binvector& idx) { 97 | *this = idx; 98 | } 99 | 100 | _simple_buckets_binvector(_simple_buckets_binvector&& idx){ 101 | *this = std::move(idx); 102 | } 103 | 104 | //! Serializes the data structure into the given ostream 105 | size_type serialize(std::ostream& out, sdsl::structure_tree_node* v=nullptr, std::string name="")const { 106 | using namespace sdsl; 107 | structure_tree_node* child = structure_tree::add_child(v, name, util::class_name(*this)); 108 | uint64_t written_bytes = 0; 109 | written_bytes += write_member(m_n, out, child, "n"); 110 | written_bytes += m_entries.serialize(out, child, "entries"); 111 | written_bytes += m_C.serialize(out, child, "C"); 112 | written_bytes += m_C_sel.serialize(out, child, "C_sel"); 113 | structure_tree::add_size(child, written_bytes); 114 | return written_bytes; 115 | } 116 | 117 | //! Loads the data structure from the given istream. 118 | void load(std::istream& in) { 119 | using namespace sdsl; 120 | read_member(m_n, in); 121 | m_entries.load(in); 122 | m_C.load(in); 123 | m_C_sel.load(in, &m_C); 124 | } 125 | 126 | size_type size() const{ 127 | return m_n; 128 | } 129 | 130 | private: 131 | 132 | inline uint64_t get_bucket_id(const uint64_t x) const { 133 | return perm_b_k::mi_permute[t_id](x) >> (64-splitter_bits); 134 | } 135 | 136 | void build_small_universe(const std::vector &input_entries) { 137 | // countingSort-like strategy to order entries accordingly to bucket_id 138 | uint64_t splitter_universe = ((uint64_t) 1) << (splitter_bits); 139 | 140 | std::vector prefix_sums(splitter_universe + 1, 0); 141 | for (auto x: input_entries) { 142 | prefix_sums[get_bucket_id(x)]++; 143 | } 144 | 145 | m_C = t_bv(splitter_universe+input_entries.size(), 0); 146 | size_t idx = 0; 147 | for(auto x : prefix_sums) { 148 | for(size_t i = 0; i < x; ++i, ++idx) 149 | m_C[idx] = 0; 150 | m_C[idx++] = 1; 151 | } 152 | m_C_sel = t_sel(&m_C); 153 | 154 | uint64_t sum = prefix_sums[0]; 155 | prefix_sums[0] = 0; 156 | for(uint64_t i = 1; i < prefix_sums.size(); ++i) { 157 | uint64_t curr = prefix_sums[i]; 158 | prefix_sums[i] = sum; 159 | sum += curr; 160 | } 161 | for (auto x : input_entries) { 162 | uint64_t bucket = get_bucket_id(x); 163 | m_entries[prefix_sums[bucket]] = x; 164 | prefix_sums[bucket]++; 165 | } 166 | } 167 | }; 168 | 169 | template 171 | struct simple_buckets_binvector { 172 | template 173 | using type = _simple_buckets_binvector; 174 | }; 175 | 176 | } 177 | -------------------------------------------------------------------------------- /include/multi_idx/simple_buckets_binvector_split.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "multi_idx/perm.hpp" 9 | #include "sdsl/io.hpp" 10 | #include "sdsl/int_vector.hpp" 11 | #include "sdsl/sd_vector.hpp" 12 | #include "sdsl/bit_vectors.hpp" 13 | #include "multi_idx/multi_idx_helper.hpp" 14 | #include "simd_utils.hpp" 15 | 16 | #define LIKELY(x) (__builtin_expect((x), 1)) 17 | #define UNLIKELY(x) (__builtin_expect((x), 0)) 18 | 19 | namespace multi_index { 20 | 21 | template 28 | class _simple_buckets_binvector_split_common { 29 | public: 30 | typedef uint64_t size_type; 31 | typedef uint64_t entry_type; 32 | typedef perm_b_k perm; 33 | enum {id = t_id}; 34 | 35 | protected: 36 | static constexpr uint8_t init_splitter_bits(size_t i=0){ 37 | return i < perm_b_k::match_len ? perm_b_k::mi_permute_block_widths[t_id][t_b-1-i] + init_splitter_bits(i+1) : 0; 38 | } 39 | 40 | /* Low_* stuff control how many of the less signifigant bits form the lower part */ 41 | static constexpr uint8_t low_bits = 32; // PLS, keep this a power of 2, better if word aligned 42 | static constexpr uint64_t low_mask = (1ULL<::type; 49 | 50 | uint64_t m_n; // number of items 51 | sdsl::int_vector m_low_entries; 52 | mid_entries_type m_mid_entries; 53 | //sdsl::int_vector<64> fake_entries; for DEBUG USE ONLY 54 | t_bv m_C; // bit vector for prefix sums of meta-symbols 55 | t_sel m_C_sel; // select1 structure for m_C 56 | 57 | public: 58 | _simple_buckets_binvector_split_common() = default; 59 | 60 | _simple_buckets_binvector_split_common(const std::vector &input_entries){ 61 | std::cout << "Splitter bits " << (uint16_t) splitter_bits << std::endl; 62 | m_n = input_entries.size(); 63 | m_low_entries = sdsl::int_vector(input_entries.size(), 0); 64 | m_mid_entries = mid_entries_trait::get_instance(input_entries.size(), 0); 65 | build_small_universe(input_entries); 66 | } 67 | 68 | inline std::pair, uint64_t> match(const entry_type q, uint8_t errors=t_k, const bool find_only_candidates=false) const { 69 | 70 | const uint64_t bucket = get_bucket_id(q); 71 | 72 | auto l = bucket == 0 ? 0 : m_C_sel(bucket) - bucket +1; 73 | const auto r = m_C_sel(bucket+1) - (bucket+1) + 1; 74 | 75 | // std::cout << "q " << q << " b " << bucket << " l " << l << " r " << r << std::endl; 76 | 77 | const uint64_t candidates = r-l; 78 | std::vector res; 79 | 80 | if(find_only_candidates) return {res, candidates}; 81 | if(errors >= 6) res.reserve(128); 82 | 83 | const uint64_t q_permuted = perm_b_k::mi_permute[t_id](q); 84 | const uint64_t q_high = (q_permuted>>(high_shift))<(it)), query); // need to be aligned! 107 | const __m128i popcounts = popcount_epi32(vec); // not an intrinsics, see simd_utils.hpp 108 | 109 | uint16_t mask = (_mm_movemask_epi8(_mm_cmpgt_epi32(tk, popcounts))) & 0x1111; 110 | 111 | while(UNLIKELY(mask)) { 112 | size_t i = (__builtin_ffs(mask))-1; 113 | mask = mask ^ (1 << i); 114 | i = i/4; 115 | const uint64_t curr_el = q_high | (((uint64_t) m_mid_entries[l+i]) << mid_shift) | it[i]; 116 | if (_mm_popcnt_u64(q_permuted^curr_el) <= errors) 117 | res.push_back(perm_b_k::mi_rev_permute[t_id](curr_el)); 118 | } 119 | } 120 | 121 | for (; it != end; ++it, ++l) { 122 | const uint32_t item_low = ((uint64_t) *it); 123 | if UNLIKELY(_mm_popcnt_u32(q_low^item_low) <= errors) { 124 | const uint64_t curr_el = q_high | (((uint64_t) m_mid_entries[l]) << mid_shift) | item_low; 125 | if (_mm_popcnt_u64(q_permuted^curr_el) <= errors) 126 | res.push_back(perm_b_k::mi_rev_permute[t_id](curr_el)); 127 | } 128 | } 129 | } else { 130 | for (auto it = begin; it != end; ++it, ++l) { 131 | const uint64_t item_low = ((uint64_t) *it); 132 | // uint64_t curr_el = q_high | (((uint64_t) m_mid_entries[l]) << mid_shift) | item_low; 133 | // if(curr_el != fake_entries[i]){ 134 | // std::cout <(q_high).to_string() << std::endl; 136 | // std::cout << "mi " << std::bitset<64>(m_mid_entries[l]).to_string() << std::endl; 137 | // std::cout << "lo " << std::bitset<64>(item_low).to_string() << std::endl; 138 | // std::cout << "cu " << std::bitset<64>(curr_el).to_string() << std::endl; 139 | //std::cout << "fe " << std::bitset<64>(fake_entries[i]).to_string() << std::endl; 140 | // } 141 | 142 | if (sdsl::bits::cnt(q_low^item_low) <= errors) { 143 | const uint64_t curr_el = q_high | (((uint64_t) m_mid_entries[l]) << mid_shift) | item_low; 144 | if (sdsl::bits::cnt(q_permuted^curr_el) <= errors) 145 | res.push_back(perm_b_k::mi_rev_permute[t_id](curr_el)); 146 | } 147 | } 148 | } 149 | return {res, candidates}; 150 | } 151 | 152 | _simple_buckets_binvector_split_common& operator=(const _simple_buckets_binvector_split_common& idx) { 153 | if ( this != &idx ) { 154 | m_n = std::move(idx.m_n); 155 | m_low_entries = std::move(idx.m_low_entries); 156 | m_mid_entries = std::move(idx.m_mid_entries); 157 | //fake_entries = std::move(idx.fake_entries); 158 | m_C = std::move(idx.m_C); 159 | m_C_sel = std::move(idx.m_C_sel); 160 | m_C_sel.set_vector(&m_C); 161 | } 162 | return *this; 163 | } 164 | 165 | _simple_buckets_binvector_split_common& operator=(_simple_buckets_binvector_split_common&& idx) { 166 | if ( this != &idx ) { 167 | m_n = std::move(idx.m_n); 168 | m_low_entries = std::move(idx.m_low_entries); 169 | m_mid_entries = std::move(idx.m_mid_entries); 170 | //fake_entries = std::move(idx.fake_entries); 171 | m_C = std::move(idx.m_C); 172 | m_C_sel = std::move(idx.m_C_sel); 173 | m_C_sel.set_vector(&m_C); 174 | } 175 | return *this; 176 | } 177 | 178 | _simple_buckets_binvector_split_common(const _simple_buckets_binvector_split_common& idx) { 179 | *this = idx; 180 | } 181 | 182 | _simple_buckets_binvector_split_common(_simple_buckets_binvector_split_common&& idx){ 183 | *this = std::move(idx); 184 | } 185 | 186 | //! Serializes the data structure into the given ostream 187 | size_type serialize(std::ostream& out, sdsl::structure_tree_node* v=nullptr, std::string name="")const { 188 | using namespace sdsl; 189 | structure_tree_node* child = structure_tree::add_child(v, name, util::class_name(*this)); 190 | uint64_t written_bytes = 0; 191 | written_bytes += write_member(m_n, out, child, "n"); 192 | written_bytes += m_low_entries.serialize(out, child, "low_entries"); 193 | written_bytes += m_mid_entries.serialize(out, child, "mid_entries"); 194 | written_bytes += m_C.serialize(out, child, "C"); 195 | written_bytes += m_C_sel.serialize(out, child, "C_sel"); 196 | //written_bytes += fake_entries.serialize(out, child, "fake"); 197 | structure_tree::add_size(child, written_bytes); 198 | return written_bytes; 199 | } 200 | 201 | //! Loads the data structure from the given istream. 202 | void load(std::istream& in) { 203 | using namespace sdsl; 204 | read_member(m_n, in); 205 | m_low_entries.load(in); 206 | m_mid_entries.load(in); 207 | m_C.load(in); 208 | m_C_sel.load(in, &m_C); 209 | //fake_entries.load(in); 210 | } 211 | 212 | size_type size() const{ 213 | return m_n; 214 | } 215 | 216 | protected: 217 | 218 | inline uint64_t get_bucket_id(const uint64_t x) const { 219 | return perm_b_k::mi_permute[t_id](x) >> (64-splitter_bits); 220 | } 221 | 222 | void build_small_universe(const std::vector &input_entries) { 223 | // Implement a countingSort-like strategy to order entries accordingly to 224 | // their splitter_bits MOST significant bits 225 | // Ranges of keys having the same MSB are not sorted. 226 | uint64_t splitter_universe = ((uint64_t) 1) << (splitter_bits); 227 | 228 | std::vector prefix_sums(splitter_universe + 1, 0); // includes a sentinel 229 | for (auto x: input_entries) { 230 | prefix_sums[get_bucket_id(x)]++; 231 | } 232 | 233 | m_C = t_bv(splitter_universe+input_entries.size(), 0); 234 | size_t idx = 0; 235 | for(auto x : prefix_sums) { 236 | for(size_t i = 0; i < x; ++i, ++idx) 237 | m_C[idx] = 0; 238 | m_C[idx++] = 1; 239 | } 240 | m_C_sel = t_sel(&m_C); 241 | 242 | uint64_t sum = prefix_sums[0]; 243 | prefix_sums[0] = 0; 244 | for(uint64_t i = 1; i < prefix_sums.size(); ++i) { 245 | uint64_t curr = prefix_sums[i]; 246 | prefix_sums[i] = sum + i; // +i is to obtain a striclty monotone sequence as we would have with binary vectors 247 | sum += curr; 248 | } 249 | 250 | // Partition elements into buckets accordingly to their less significant bits 251 | for (auto x : input_entries) { 252 | uint64_t bucket = get_bucket_id(x); 253 | uint64_t permuted_item = perm_b_k::mi_permute[t_id](x); 254 | //fake_entries[prefix_sums[bucket]-bucket] = permuted_item; 255 | //m_mid_entries[prefix_sums[bucket]-bucket] = (permuted_item>>mid_shift) & mid_mask; // -bucket is because we have a striclty monotone sequence 256 | mid_entries_trait::assign(m_mid_entries, prefix_sums[bucket]-bucket, (permuted_item>>mid_shift) & mid_mask); 257 | m_low_entries[prefix_sums[bucket]-bucket] = (permuted_item & low_mask); 258 | prefix_sums[bucket]++; 259 | } 260 | } 261 | }; 262 | 263 | 264 | template, 268 | typename t_bv=sdsl::bit_vector, 269 | typename t_sel=typename t_bv::select_1_type, 270 | bool use_simd=false> 271 | class _simple_buckets_binvector_split : public _simple_buckets_binvector_split_common{ 272 | typedef _simple_buckets_binvector_split_common base; 273 | using base::base; 274 | }; 275 | 276 | 277 | template 280 | struct simple_buckets_binvector_split { 281 | template 282 | using type = _simple_buckets_binvector_split; 283 | }; 284 | } 285 | -------------------------------------------------------------------------------- /include/multi_idx/simple_buckets_binvector_split_xor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "multi_idx/perm.hpp" 9 | #include "sdsl/io.hpp" 10 | #include "sdsl/int_vector.hpp" 11 | #include "sdsl/sd_vector.hpp" 12 | #include "sdsl/bit_vectors.hpp" 13 | #include "multi_idx/multi_idx_helper.hpp" 14 | #include "simd_utils.hpp" 15 | 16 | #define LIKELY(x) (__builtin_expect((x), 1)) 17 | #define UNLIKELY(x) (__builtin_expect((x), 0)) 18 | 19 | namespace multi_index { 20 | 21 | template 28 | class _simple_buckets_binvector_split_xor_common { 29 | public: 30 | typedef uint64_t size_type; 31 | typedef uint64_t entry_type; 32 | typedef perm_b_k perm; 33 | enum {id = t_id}; 34 | 35 | protected: 36 | static constexpr uint8_t init_splitter_bits(size_t i=0){ 37 | return i < perm_b_k::match_len ? perm_b_k::mi_permute_block_widths[t_id][t_b-1-i] + init_splitter_bits(i+1) : 0; 38 | } 39 | 40 | /* Low_* stuff control how many of the less signifigant bits form the lower part */ 41 | static constexpr uint8_t low_bits = 32; // PLS, keep this a power of 2, better if word aligned 42 | static constexpr uint64_t low_mask = (1ULL<::type; 49 | 50 | uint64_t m_n; // number of items 51 | sdsl::int_vector m_low_entries; 52 | mid_entries_type m_mid_entries; 53 | //sdsl::int_vector<64> fake_entries; for DEBUG USE ONLY 54 | t_bv m_C; // bit vector for prefix sums of meta-symbols 55 | t_sel m_C_sel; // select1 structure for m_C 56 | 57 | public: 58 | _simple_buckets_binvector_split_xor_common() = default; 59 | 60 | _simple_buckets_binvector_split_xor_common(const std::vector &input_entries){ 61 | std::cout << "Splitter bits " << (uint16_t) splitter_bits << std::endl; 62 | m_n = input_entries.size(); 63 | m_low_entries = sdsl::int_vector(input_entries.size(), 0); 64 | m_mid_entries = mid_entries_trait::get_instance(input_entries.size(), 0); 65 | build_small_universe(input_entries); 66 | } 67 | 68 | inline std::pair, uint64_t> match(const entry_type q, uint8_t errors=t_k, const bool find_only_candidates=false) const { 69 | 70 | const uint64_t bucket = get_bucket_id(q); 71 | 72 | auto l = bucket == 0 ? 0 : m_C_sel(bucket) - bucket +1; 73 | const auto r = m_C_sel(bucket+1) - (bucket+1) + 1; 74 | 75 | // std::cout << "q " << q << " b " << bucket << " l " << l << " r " << r << std::endl; 76 | 77 | const uint64_t candidates = r-l; 78 | std::vector res; 79 | 80 | if(find_only_candidates) return {res, candidates}; 81 | if(errors >= 6) res.reserve(128); 82 | 83 | const uint64_t q_permuted = perm_b_k::mi_permute[t_id](q); 84 | const uint64_t q_high = (q_permuted>>(high_shift))<> mid_shift) & mid_mask; // 0|0|0|B 87 | const uint64_t q_xor = q_low^q_mid; 88 | 89 | const auto begin = m_low_entries.begin() + l; 90 | const auto end = m_low_entries.begin() + r; 91 | 92 | if ( use_simd ) { 93 | auto it = begin; 94 | for (; ((size_t)it)%16 != 0 and it != end; ++it, ++l) { 95 | const uint32_t item_xor = ((uint64_t) *it); 96 | if UNLIKELY(_mm_popcnt_u32(q_xor^item_xor) <= errors) { 97 | const uint64_t item_mid = m_mid_entries[l]; 98 | const uint64_t item_low = item_xor^item_mid;; 99 | const uint64_t curr_el = q_high | (item_mid << mid_shift) | item_low; 100 | //candidates++; 101 | if (_mm_popcnt_u64(q_permuted^curr_el) <= errors) 102 | res.push_back(perm_b_k::mi_rev_permute[t_id](curr_el)); 103 | } 104 | } 105 | 106 | const __m128i query = _mm_set1_epi32(q_xor); 107 | const __m128i tk = _mm_set1_epi32(errors+1); 108 | 109 | for(; it < end-4; it+=4, l+=4) { 110 | _mm_prefetch((const char *) (it+4), _MM_HINT_T0); 111 | const __m128i vec = _mm_xor_si128(_mm_load_si128(reinterpret_cast(it)), query); // need to be aligned! 112 | const __m128i popcounts = popcount_epi32(vec); // not an intrinsics, see simd_utils.hpp 113 | 114 | uint16_t mask = (_mm_movemask_epi8(_mm_cmpgt_epi32(tk, popcounts))) & 0x1111; 115 | 116 | while(UNLIKELY(mask)) { 117 | size_t i = (__builtin_ffs(mask))-1; 118 | mask = mask ^ (1 << i); 119 | i = i/4; 120 | const uint64_t item_mid = m_mid_entries[l+i]; 121 | const uint64_t item_xor = (it[i]); 122 | const uint64_t item_low = item_xor^item_mid;; 123 | const uint64_t curr_el = q_high | (item_mid << mid_shift) | item_low; 124 | if (_mm_popcnt_u64(q_permuted^curr_el) <= errors) 125 | res.push_back(perm_b_k::mi_rev_permute[t_id](curr_el)); 126 | } 127 | } 128 | 129 | for (; it != end; ++it, ++l) { 130 | const uint64_t item_xor = ((uint64_t) *it); 131 | 132 | if (sdsl::bits::cnt(q_xor^item_xor) <= errors) { 133 | const uint64_t item_mid = m_mid_entries[l]; 134 | const uint64_t item_low = item_xor^item_mid;; 135 | const uint64_t curr_el = q_high | (item_mid << mid_shift) | item_low; 136 | if (sdsl::bits::cnt(q_permuted^curr_el) <= errors) 137 | res.push_back(perm_b_k::mi_rev_permute[t_id](curr_el)); 138 | } 139 | } 140 | } else { 141 | for (auto it = begin; it != end; ++it, ++l) { 142 | const uint64_t item_xor = ((uint64_t) *it); 143 | 144 | if (sdsl::bits::cnt(q_xor^item_xor) <= errors) { 145 | const uint64_t item_mid = m_mid_entries[l]; 146 | const uint64_t item_low = item_xor^item_mid;; 147 | const uint64_t curr_el = q_high | (item_mid << mid_shift) | item_low; 148 | if (sdsl::bits::cnt(q_permuted^curr_el) <= errors) 149 | res.push_back(perm_b_k::mi_rev_permute[t_id](curr_el)); 150 | } 151 | } 152 | } 153 | return {res, candidates}; 154 | } 155 | 156 | _simple_buckets_binvector_split_xor_common& operator=(const _simple_buckets_binvector_split_xor_common& idx) { 157 | if ( this != &idx ) { 158 | m_n = std::move(idx.m_n); 159 | m_low_entries = std::move(idx.m_low_entries); 160 | m_mid_entries = std::move(idx.m_mid_entries); 161 | //fake_entries = std::move(idx.fake_entries); 162 | m_C = std::move(idx.m_C); 163 | m_C_sel = std::move(idx.m_C_sel); 164 | m_C_sel.set_vector(&m_C); 165 | } 166 | return *this; 167 | } 168 | 169 | _simple_buckets_binvector_split_xor_common& operator=(_simple_buckets_binvector_split_xor_common&& idx) { 170 | if ( this != &idx ) { 171 | m_n = std::move(idx.m_n); 172 | m_low_entries = std::move(idx.m_low_entries); 173 | m_mid_entries = std::move(idx.m_mid_entries); 174 | //fake_entries = std::move(idx.fake_entries); 175 | m_C = std::move(idx.m_C); 176 | m_C_sel = std::move(idx.m_C_sel); 177 | m_C_sel.set_vector(&m_C); 178 | } 179 | return *this; 180 | } 181 | 182 | _simple_buckets_binvector_split_xor_common(const _simple_buckets_binvector_split_xor_common& idx) { 183 | *this = idx; 184 | } 185 | 186 | _simple_buckets_binvector_split_xor_common(_simple_buckets_binvector_split_xor_common&& idx){ 187 | *this = std::move(idx); 188 | } 189 | 190 | //! Serializes the data structure into the given ostream 191 | size_type serialize(std::ostream& out, sdsl::structure_tree_node* v=nullptr, std::string name="")const { 192 | using namespace sdsl; 193 | structure_tree_node* child = structure_tree::add_child(v, name, util::class_name(*this)); 194 | uint64_t written_bytes = 0; 195 | written_bytes += write_member(m_n, out, child, "n"); 196 | written_bytes += m_low_entries.serialize(out, child, "low_entries"); 197 | written_bytes += m_mid_entries.serialize(out, child, "mid_entries"); 198 | written_bytes += m_C.serialize(out, child, "C"); 199 | written_bytes += m_C_sel.serialize(out, child, "C_sel"); 200 | //written_bytes += fake_entries.serialize(out, child, "fake"); 201 | structure_tree::add_size(child, written_bytes); 202 | return written_bytes; 203 | } 204 | 205 | //! Loads the data structure from the given istream. 206 | void load(std::istream& in) { 207 | using namespace sdsl; 208 | read_member(m_n, in); 209 | m_low_entries.load(in); 210 | m_mid_entries.load(in); 211 | m_C.load(in); 212 | m_C_sel.load(in, &m_C); 213 | //fake_entries.load(in); 214 | } 215 | 216 | size_type size() const{ 217 | return m_n; 218 | } 219 | 220 | protected: 221 | 222 | inline uint64_t get_bucket_id(const uint64_t x) const { 223 | return perm_b_k::mi_permute[t_id](x) >> (64-splitter_bits); 224 | } 225 | 226 | void build_small_universe(const std::vector &input_entries) { 227 | // Implement a countingSort-like strategy to order entries accordingly to 228 | // their splitter_bits MOST significant bits 229 | // Ranges of keys having the same MSB are not sorted. 230 | uint64_t splitter_universe = ((uint64_t) 1) << (splitter_bits); 231 | 232 | std::vector prefix_sums(splitter_universe + 1, 0); // includes a sentinel 233 | for (auto x: input_entries) { 234 | prefix_sums[get_bucket_id(x)]++; 235 | } 236 | 237 | m_C = t_bv(splitter_universe+input_entries.size(), 0); 238 | size_t idx = 0; 239 | for(auto x : prefix_sums) { 240 | for(size_t i = 0; i < x; ++i, ++idx) 241 | m_C[idx] = 0; 242 | m_C[idx++] = 1; 243 | } 244 | m_C_sel = t_sel(&m_C); 245 | 246 | uint64_t sum = prefix_sums[0]; 247 | prefix_sums[0] = 0; 248 | for(uint64_t i = 1; i < prefix_sums.size(); ++i) { 249 | uint64_t curr = prefix_sums[i]; 250 | prefix_sums[i] = sum + i; // +i is to obtain a striclty monotone sequence as we would have with binary vectors 251 | sum += curr; 252 | } 253 | 254 | // Partition elements into buckets accordingly to their less significant bits 255 | for (auto x : input_entries) { 256 | uint64_t bucket = get_bucket_id(x); 257 | uint64_t permuted_item = perm_b_k::mi_permute[t_id](x); 258 | /* 259 | Let A|B|C|D be the key. 260 | Assume each metasymbol is 16 bits. A is searched with the binary vector becuase it is the prefix. 261 | We compute low_xor = C|D xor B and we store low_xor in the low_entries and B in the mid_entries. 262 | At query time, we scan the low_entries and, if we find an entry such that 263 | the number of errors is smaller than t_k, we access the corresponding B and 264 | we reconstruct low_part = low_xor xor B, and we match. 265 | */ 266 | 267 | const uint64_t low_item = permuted_item & low_mask; // C|D 268 | const uint64_t mid_item = (permuted_item >> mid_shift) & mid_mask; // B 269 | const uint64_t low_xor = (low_item^mid_item); // C|D xor B 270 | mid_entries_trait::assign(m_mid_entries, prefix_sums[bucket]-bucket, mid_item); 271 | m_low_entries[prefix_sums[bucket]-bucket] = low_xor; 272 | prefix_sums[bucket]++; 273 | } 274 | } 275 | }; 276 | 277 | 278 | template, 282 | typename t_bv=sdsl::bit_vector, 283 | typename t_sel=typename t_bv::select_1_type, 284 | bool use_simd=false> 285 | class _simple_buckets_binvector_split_xor : public _simple_buckets_binvector_split_xor_common{ 286 | typedef _simple_buckets_binvector_split_xor_common base; 287 | using base::base; 288 | }; 289 | 290 | template 293 | struct simple_buckets_binvector_split_xor { 294 | template 295 | using type = _simple_buckets_binvector_split_xor; 296 | }; 297 | } 298 | -------------------------------------------------------------------------------- /include/multi_idx/simple_buckets_binvector_unaligned.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "multi_idx/perm.hpp" 7 | #include "sdsl/io.hpp" 8 | #include "sdsl/int_vector.hpp" 9 | #include "sdsl/sd_vector.hpp" 10 | #include "sdsl/bit_vectors.hpp" 11 | #include "multi_idx/multi_idx_helper.hpp" 12 | 13 | namespace multi_index { 14 | 15 | // we can match everything with less than t_k errors 16 | template, 20 | typename t_bv=sdsl::bit_vector, 21 | typename t_sel=typename t_bv::select_1_type> 22 | class _simple_buckets_binvector_unaligned { 23 | public: 24 | typedef uint64_t size_type; 25 | typedef uint64_t entry_type; 26 | typedef perm_b_k perm; 27 | enum {id = t_id}; 28 | 29 | private: 30 | static constexpr uint8_t init_splitter_bits(size_t i=0){ 31 | return i < perm_b_k::match_len ? perm_b_k::mi_permute_block_widths[t_id][t_b-1-i] + init_splitter_bits(i+1) : 0; 32 | } 33 | static constexpr uint8_t splitter_bits = init_splitter_bits(); 34 | 35 | uint64_t m_n; // number of items 36 | sdsl::int_vector<> m_entries; 37 | t_bv m_C; // bit vector for prefix sums of meta-symbols 38 | t_sel m_C_sel; // select1 structure for m_C 39 | 40 | public: 41 | 42 | _simple_buckets_binvector_unaligned() = default; 43 | 44 | _simple_buckets_binvector_unaligned(const std::vector &input_entries) { 45 | std::cout << "Splitter bits " << (uint16_t) splitter_bits << std::endl; 46 | 47 | m_n = input_entries.size(); 48 | // check_permutation<_simple_buckets_binvector_unaligned, t_id>(input_entries); 49 | m_entries = sdsl::int_vector<>(input_entries.size(), 0, 64-splitter_bits); 50 | 51 | build_small_universe(input_entries); 52 | } 53 | 54 | // k with passed to match function 55 | // assert(k<=t_k) 56 | inline std::pair, uint64_t> match(const entry_type q, uint8_t errors=t_k, const bool find_only_candidates=false) { 57 | uint64_t bucket = get_bucket_id(q); 58 | 59 | const auto l = bucket == 0 ? 0 : m_C_sel(bucket) - bucket +1; 60 | const auto r = m_C_sel(bucket+1) - (bucket+1) +1; 61 | 62 | auto begin = m_entries.begin() + l; 63 | auto end = m_entries.begin() + r; 64 | 65 | uint64_t candidates = std::distance(begin,end); 66 | std::vector res; 67 | 68 | if(find_only_candidates) return {res, candidates}; 69 | uint64_t p = perm_b_k::mi_permute[t_id](q) & sdsl::bits::lo_set[64-splitter_bits]; 70 | uint64_t mask = bucket << (64-splitter_bits); 71 | for (auto it = begin; it != end; ++it) { 72 | if (sdsl::bits::cnt(p^*it) <= errors) { 73 | res.push_back( perm_b_k::mi_rev_permute[t_id](*it | mask) ); 74 | } 75 | } 76 | return {res, candidates}; 77 | } 78 | 79 | _simple_buckets_binvector_unaligned& operator=(const _simple_buckets_binvector_unaligned& idx) { 80 | if ( this != &idx ) { 81 | m_n = std::move(idx.m_n); 82 | m_entries = std::move(idx.m_entries); 83 | m_C = std::move(idx.m_C); 84 | m_C_sel = std::move(idx.m_C_sel); 85 | m_C_sel.set_vector(&m_C); 86 | } 87 | return *this; 88 | } 89 | 90 | _simple_buckets_binvector_unaligned& operator=(_simple_buckets_binvector_unaligned&& idx) { 91 | if ( this != &idx ) { 92 | m_n = std::move(idx.m_n); 93 | m_entries = std::move(idx.m_entries); 94 | m_C = std::move(idx.m_C); 95 | m_C_sel = std::move(idx.m_C_sel); 96 | m_C_sel.set_vector(&m_C); 97 | } 98 | return *this; 99 | } 100 | 101 | _simple_buckets_binvector_unaligned(const _simple_buckets_binvector_unaligned& idx) { 102 | *this = idx; 103 | } 104 | 105 | _simple_buckets_binvector_unaligned(_simple_buckets_binvector_unaligned&& idx){ 106 | *this = std::move(idx); 107 | } 108 | 109 | //! Serializes the data structure into the given ostream 110 | size_type serialize(std::ostream& out, sdsl::structure_tree_node* v=nullptr, std::string name="")const { 111 | using namespace sdsl; 112 | structure_tree_node* child = structure_tree::add_child(v, name, util::class_name(*this)); 113 | uint64_t written_bytes = 0; 114 | written_bytes += write_member(m_n, out, child, "n"); 115 | written_bytes += m_entries.serialize(out, child, "entries"); 116 | written_bytes += m_C.serialize(out, child, "C"); 117 | written_bytes += m_C_sel.serialize(out, child, "C_sel"); 118 | structure_tree::add_size(child, written_bytes); 119 | return written_bytes; 120 | } 121 | 122 | //! Loads the data structure from the given istream. 123 | void load(std::istream& in) { 124 | using namespace sdsl; 125 | read_member(m_n, in); 126 | m_entries.load(in); 127 | m_C.load(in); 128 | m_C_sel.load(in, &m_C); 129 | } 130 | 131 | size_type size() const{ 132 | return m_n; 133 | } 134 | 135 | private: 136 | 137 | inline uint64_t get_bucket_id(const uint64_t x) const { 138 | return perm_b_k::mi_permute[t_id](x) >> (64-splitter_bits); // take the most significant bits 139 | } 140 | 141 | void build_small_universe(const std::vector &input_entries) { 142 | // Implement a countingSort-like strategy to order entries accordingly to 143 | // their splitter_bits MOST significant bits 144 | // Ranges of keys having the same MSB are not sorted. 145 | uint64_t splitter_universe = ((uint64_t) 1) << (splitter_bits); 146 | 147 | std::vector prefix_sums(splitter_universe + 1, 0); // includes a sentinel 148 | for (auto x: input_entries) { 149 | prefix_sums[get_bucket_id(x)]++; 150 | } 151 | 152 | m_C = t_bv(splitter_universe+input_entries.size(), 0); 153 | size_t idx = 0; 154 | for(auto x : prefix_sums) { 155 | for(size_t i = 0; i < x; ++i, ++idx) 156 | m_C[idx] = 0; 157 | m_C[idx++] = 1; 158 | } 159 | m_C_sel = t_sel(&m_C); 160 | 161 | uint64_t sum = prefix_sums[0]; 162 | prefix_sums[0] = 0; 163 | for(uint64_t i = 1; i < prefix_sums.size(); ++i) { 164 | uint64_t curr = prefix_sums[i]; 165 | prefix_sums[i] = sum + i; // +i is to obtain a striclty monotone sequence as we would have with binary vectors 166 | sum += curr; 167 | } 168 | 169 | // Partition elements into buckets accordingly to their less significant bits 170 | for (auto x : input_entries) { 171 | uint64_t bucket = get_bucket_id(x); 172 | uint64_t permuted_x = perm_b_k::mi_permute[t_id](x); 173 | m_entries[prefix_sums[bucket]-bucket] = permuted_x; // -bucket is because we have a striclty monotone sequence 174 | prefix_sums[bucket]++; 175 | } 176 | } 177 | }; 178 | 179 | template 181 | struct simple_buckets_binvector_unaligned { 182 | template 183 | using type = _simple_buckets_binvector_unaligned; 184 | }; 185 | 186 | } 187 | -------------------------------------------------------------------------------- /include/multi_idx/simple_buckets_vector.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "multi_idx/perm.hpp" 7 | #include "sdsl/io.hpp" 8 | #include "sdsl/int_vector.hpp" 9 | #include "sdsl/sd_vector.hpp" 10 | #include "sdsl/bit_vectors.hpp" 11 | #include "multi_idx/multi_idx_helper.hpp" 12 | 13 | namespace multi_index { 14 | 15 | // we can match everything with less than t_k errors 16 | template 20 | > 21 | class _simple_buckets_vector { 22 | public: 23 | typedef uint64_t size_type; 24 | typedef uint64_t entry_type; 25 | typedef perm_b_k perm; 26 | enum {id = t_id}; 27 | 28 | private: 29 | static constexpr uint8_t init_splitter_bits(size_t i=0){ 30 | return i < perm_b_k::match_len ? perm_b_k::mi_permute_block_widths[t_id][t_b-1-i] + init_splitter_bits(i+1) : 0; 31 | } 32 | static constexpr uint8_t splitter_bits = init_splitter_bits(); 33 | 34 | uint64_t m_n; // number of items 35 | sdsl::int_vector<64> m_entries; 36 | sdsl::int_vector<64> m_prefix_sums; 37 | 38 | public: 39 | 40 | _simple_buckets_vector() = default; 41 | 42 | _simple_buckets_vector(const std::vector &input_entries) { 43 | std::cout << "Splitter bits " << (uint16_t) splitter_bits << std::endl; 44 | 45 | m_n = input_entries.size(); 46 | // check_permutation<_simple_buckets_vector, t_id>(input_entries); 47 | m_entries = sdsl::int_vector<64>(input_entries.size(), 0); 48 | 49 | build_small_universe(input_entries); 50 | } 51 | 52 | // k with passed to match function 53 | // assert(k<=t_k) 54 | inline std::pair, uint64_t> match(const entry_type q, uint8_t errors=t_k, const bool find_only_candidates=false) { 55 | uint64_t bucket = get_bucket_id(q); 56 | 57 | /* DEBUG 58 | std::cout << "---------------------\n"; 59 | for(auto x: m_perm.mi_perms[t_id]) 60 | std::cout << (uint64_t) x << " "; 61 | std::cout << std::endl; 62 | std::cout << "Splitter_bits " << (uint64_t) splitter_bits << std::endl; 63 | std::bitset<64> a(q); 64 | uint64_t l = m_perm.mi_permute_block_sizes[0], j = 0; 65 | for(size_t i = 0; i < 64; i++) { 66 | if (i == l) {std::cout << " | "; j++; l += m_perm.mi_permute_block_sizes[j];} 67 | std::cout << (uint64_t) a[i]; 68 | } 69 | std::cout << std::endl; 70 | 71 | std::cout << " q " << std::bitset<64>(q) <(m_perm.mi_permute[t_id](q)) <(bucket) < res; 85 | 86 | if(find_only_candidates) return {res, candidates}; 87 | for (auto it = begin; it != end; ++it) { 88 | if (sdsl::bits::cnt(q^*it) <= errors) 89 | res.push_back(*it); 90 | } 91 | return {res, candidates}; 92 | } 93 | 94 | _simple_buckets_vector& operator=(const _simple_buckets_vector& idx) { 95 | if ( this != &idx ) { 96 | m_n = std::move(idx.m_n); 97 | m_entries = std::move(idx.m_entries); 98 | m_prefix_sums = std::move(idx.m_prefix_sums); 99 | } 100 | return *this; 101 | } 102 | 103 | _simple_buckets_vector& operator=(_simple_buckets_vector&& idx) { 104 | if ( this != &idx ) { 105 | m_n = std::move(idx.m_n); 106 | m_entries = std::move(idx.m_entries); 107 | m_prefix_sums = std::move(idx.m_prefix_sums); 108 | } 109 | return *this; 110 | } 111 | 112 | _simple_buckets_vector(const _simple_buckets_vector& idx) { 113 | *this = idx; 114 | } 115 | 116 | _simple_buckets_vector(_simple_buckets_vector&& idx){ 117 | *this = std::move(idx); 118 | } 119 | 120 | //! Serializes the data structure into the given ostream 121 | size_type serialize(std::ostream& out, sdsl::structure_tree_node* v=nullptr, std::string name="")const { 122 | using namespace sdsl; 123 | structure_tree_node* child = structure_tree::add_child(v, name, util::class_name(*this)); 124 | uint64_t written_bytes = 0; 125 | written_bytes += write_member(m_n, out, child, "n"); 126 | written_bytes += m_entries.serialize(out, child, "entries"); 127 | written_bytes += m_prefix_sums.serialize(out, child, "prefix_sums"); 128 | structure_tree::add_size(child, written_bytes); 129 | return written_bytes; 130 | } 131 | 132 | //! Loads the data structure from the given istream. 133 | void load(std::istream& in) { 134 | using namespace sdsl; 135 | read_member(m_n, in); 136 | m_entries.load(in); 137 | m_prefix_sums.load(in); 138 | } 139 | 140 | size_type size() const{ 141 | return m_n; 142 | } 143 | 144 | private: 145 | 146 | inline uint64_t get_bucket_id(const uint64_t x) const { 147 | return perm_b_k::mi_permute[t_id](x) >> (64-splitter_bits); // take the most significant bits 148 | } 149 | 150 | void build_small_universe(const std::vector &input_entries) { 151 | // Implement a countingSort-like strategy to order entries accordingly to 152 | // their splitter_bits MOST significant bits 153 | // Ranges of keys having the same MSB are not sorted. 154 | uint64_t splitter_universe = ((uint64_t) 1) << (splitter_bits); 155 | 156 | std::vector prefix_sums(splitter_universe + 1, 0); // includes a sentinel 157 | m_prefix_sums = sdsl::int_vector<64>(prefix_sums.size(), 0); 158 | 159 | for (auto x: input_entries) { 160 | prefix_sums[get_bucket_id(x)]++; 161 | } 162 | 163 | uint64_t sum = prefix_sums[0]; 164 | prefix_sums[0] = 0; 165 | m_prefix_sums[0] = prefix_sums[0]; 166 | for(uint64_t i = 1; i < prefix_sums.size(); ++i) { 167 | uint64_t curr = prefix_sums[i]; 168 | prefix_sums[i] = sum + i; // +i is to obtain a striclty monotone sequence as we would have with binary vectors 169 | m_prefix_sums[i] = prefix_sums[i]; 170 | sum += curr; 171 | } 172 | 173 | // Partition elements into buckets accordingly to their less significant bits 174 | for (auto x : input_entries) { 175 | uint64_t bucket = get_bucket_id(x); 176 | m_entries[prefix_sums[bucket]-bucket] = x; // -bucket is because we have a striclty monotone sequence 177 | prefix_sums[bucket]++; 178 | } 179 | } 180 | }; 181 | 182 | struct simple_buckets_vector { 183 | template 184 | using type = _simple_buckets_vector; 185 | }; 186 | 187 | } 188 | -------------------------------------------------------------------------------- /include/multi_idx/triangle_buckets_binvector_split_simd.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "sdsl/io.hpp" 7 | #include "sdsl/int_vector.hpp" 8 | #include "sdsl/sd_vector.hpp" 9 | #include "sdsl/bit_vectors.hpp" 10 | #include "simd_utils.hpp" 11 | #include "multi_idx/multi_idx_helper.hpp" 12 | 13 | #define LIKELY(x) (__builtin_expect((x), 1)) 14 | #define UNLIKELY(x) (__builtin_expect((x), 0)) 15 | 16 | namespace multi_index { 17 | 18 | /* 19 | * This implementation exploits triangle inequality to reduce the number of comparisons at 20 | * query time. Let Q be the query, R be a reference and A be any entry, we denote with 21 | * H(A, B) the Hamming distance between keys A and B. 22 | * Assume we know the distances H(Q, R) and H(A, R), then, by triangle inequality, we can 23 | * avoid a comparison between Q and A if |H(Q, R) - H(A, R)| > N_ERRORS. 24 | * The reference R could be arbitrary, and, for example, it may be a different random key 25 | * for each bucket. For simplicity in this implementation we set R=0, i.e., H(A,R) is 26 | * the number of ones in A. 27 | * Thus, in this index entries are group accordingly to their prefix AND, within the same 28 | * bucket, by their number of ones. This way, we can answer a query by scanning only a 29 | * portion of the bucket. 30 | */ 31 | 32 | template, 36 | typename t_bv=sdsl::bit_vector, 37 | typename t_sel=typename t_bv::select_1_type> 38 | class _triangle_buckets_binvector_split_simd { 39 | public: 40 | typedef uint64_t size_type; 41 | typedef uint64_t entry_type; 42 | typedef perm_b_k perm; 43 | enum {id = t_id}; 44 | 45 | private: 46 | static constexpr uint8_t distance_bits = 6; 47 | static constexpr uint8_t init_splitter_bits(size_t i=0){ 48 | return i < perm_b_k::match_len ? perm_b_k::mi_permute_block_widths[t_id][t_b-1-i] + init_splitter_bits(i+1) : 0; 49 | } 50 | 51 | /* Low_* stuff control how many of the less signifigant bits form the lower part */ 52 | static constexpr uint8_t low_bits = 32; // PLS, keep this a power of 2, better if word aligned 53 | static constexpr uint64_t low_mask = (1ULL<::type; 60 | 61 | uint64_t m_n; // number of items 62 | sdsl::int_vector m_low_entries; 63 | mid_entries_type m_mid_entries; 64 | t_bv m_C; // bit vector for prefix sums of meta-symbols 65 | t_sel m_C_sel; // select1 structure for m_C 66 | 67 | 68 | public: 69 | 70 | _triangle_buckets_binvector_split_simd() = default; 71 | 72 | _triangle_buckets_binvector_split_simd(const std::vector &input_entries) { 73 | std::cout << "Splitter bits " << (uint16_t) splitter_bits << std::endl; 74 | m_n = input_entries.size(); 75 | m_low_entries = sdsl::int_vector(input_entries.size(), 0); 76 | m_mid_entries = mid_entries_trait::get_instance(input_entries.size(), 0); 77 | build_small_universe(input_entries); 78 | } 79 | 80 | inline std::pair, uint64_t> match(const entry_type q, uint8_t errors=t_k, const bool find_only_candidates=false) const { 81 | assert(n_errors <= perm_b_k::max_errors); 82 | 83 | const uint64_t bucket_left = get_bucket_left(q, errors); 84 | const uint64_t bucket_right = get_bucket_right(q, errors); 85 | 86 | auto l = bucket_left == 0 ? 0 : m_C_sel(bucket_left) - bucket_left +1; 87 | const auto r = m_C_sel(bucket_right+1) - (bucket_right+1) +1; 88 | 89 | uint64_t candidates = r-l; 90 | std::vector res; 91 | 92 | if(find_only_candidates) return {res, candidates}; 93 | if(errors >= 6) res.reserve(128); 94 | 95 | const uint64_t q_permuted = perm_b_k::mi_permute[t_id](q); 96 | const uint64_t q_high = (q_permuted>>(high_shift))<> mid_shift) & mid_mask; // 0|0|0|B 99 | const uint64_t q_xor = q_low^q_mid; 100 | 101 | const auto begin = m_low_entries.begin() + l; 102 | const auto end = m_low_entries.begin() + r; 103 | 104 | auto it = begin; 105 | for (; ((size_t)it)%16 != 0 and it != end; ++it, ++l) { 106 | const uint32_t item_xor = ((uint64_t) *it); 107 | if UNLIKELY(_mm_popcnt_u32(q_xor^item_xor) <= errors) { 108 | const uint64_t item_mid = m_mid_entries[l]; 109 | const uint64_t item_low = item_xor^item_mid;; 110 | const uint64_t curr_el = q_high | (item_mid << mid_shift) | item_low; 111 | //candidates++; 112 | if (_mm_popcnt_u64(q_permuted^curr_el) <= errors) 113 | res.push_back(perm_b_k::mi_rev_permute[t_id](curr_el)); 114 | } 115 | } 116 | 117 | const __m128i query = _mm_set1_epi32(q_xor); 118 | const __m128i tk = _mm_set1_epi32(errors+1); 119 | 120 | for(; it < end-4; it+=4, l+=4) { 121 | _mm_prefetch((const char *) (it+4), _MM_HINT_T0); 122 | const __m128i vec = _mm_xor_si128(_mm_load_si128(reinterpret_cast(it)), query); // need to be aligned! 123 | const __m128i popcounts = popcount_epi32(vec); // not an intrinsics, see simd_utils.hpp 124 | 125 | uint16_t mask = (_mm_movemask_epi8(_mm_cmpgt_epi32(tk, popcounts))) & 0x1111; 126 | 127 | while(UNLIKELY(mask)) { 128 | size_t i = (__builtin_ffs(mask))-1; 129 | mask = mask ^ (1 << i); 130 | i = i/4; 131 | const uint64_t item_mid = m_mid_entries[l+i]; 132 | const uint64_t item_xor = (it[i]); 133 | const uint64_t item_low = item_xor^item_mid;; 134 | const uint64_t curr_el = q_high | (item_mid << mid_shift) | item_low; 135 | if (_mm_popcnt_u64(q_permuted^curr_el) <= errors) 136 | res.push_back(perm_b_k::mi_rev_permute[t_id](curr_el)); 137 | } 138 | } 139 | 140 | for (; it != end; ++it, ++l) { 141 | const uint64_t item_xor = ((uint64_t) *it); 142 | 143 | if (sdsl::bits::cnt(q_xor^item_xor) <= errors) { 144 | const uint64_t item_mid = m_mid_entries[l]; 145 | const uint64_t item_low = item_xor^item_mid;; 146 | const uint64_t curr_el = q_high | (item_mid << mid_shift) | item_low; 147 | if (sdsl::bits::cnt(q_permuted^curr_el) <= errors) 148 | res.push_back(perm_b_k::mi_rev_permute[t_id](curr_el)); 149 | } 150 | } 151 | 152 | return {res, candidates}; 153 | } 154 | 155 | _triangle_buckets_binvector_split_simd& operator=(const _triangle_buckets_binvector_split_simd& idx) { 156 | if ( this != &idx ) { 157 | m_n = std::move(idx.m_n); 158 | m_low_entries = std::move(idx.m_low_entries); 159 | m_mid_entries = std::move(idx.m_mid_entries); 160 | m_C = std::move(idx.m_C); 161 | m_C_sel = std::move(idx.m_C_sel); 162 | m_C_sel.set_vector(&m_C); 163 | } 164 | return *this; 165 | } 166 | 167 | _triangle_buckets_binvector_split_simd& operator=(_triangle_buckets_binvector_split_simd&& idx) { 168 | if ( this != &idx ) { 169 | m_n = std::move(idx.m_n); 170 | m_low_entries = std::move(idx.m_low_entries); 171 | m_mid_entries = std::move(idx.m_mid_entries); 172 | m_C = std::move(idx.m_C); 173 | m_C_sel = std::move(idx.m_C_sel); 174 | m_C_sel.set_vector(&m_C); 175 | } 176 | return *this; 177 | } 178 | 179 | _triangle_buckets_binvector_split_simd(const _triangle_buckets_binvector_split_simd& idx) { 180 | *this = idx; 181 | } 182 | 183 | _triangle_buckets_binvector_split_simd(_triangle_buckets_binvector_split_simd&& idx){ 184 | *this = std::move(idx); 185 | } 186 | 187 | //! Serializes the data structure into the given ostream 188 | size_type serialize(std::ostream& out, sdsl::structure_tree_node* v=nullptr, std::string name="")const { 189 | using namespace sdsl; 190 | structure_tree_node* child = structure_tree::add_child(v, name, util::class_name(*this)); 191 | uint64_t written_bytes = 0; 192 | written_bytes += write_member(m_n, out, child, "n"); 193 | written_bytes += m_low_entries.serialize(out, child, "low_entries"); 194 | written_bytes += m_mid_entries.serialize(out, child, "mid_entries"); 195 | written_bytes += m_C.serialize(out, child, "C"); 196 | written_bytes += m_C_sel.serialize(out, child, "C_sel"); 197 | structure_tree::add_size(child, written_bytes); 198 | return written_bytes; 199 | } 200 | 201 | //! Loads the data structure from the given istream. 202 | void load(std::istream& in) { 203 | using namespace sdsl; 204 | read_member(m_n, in); 205 | m_low_entries.load(in); 206 | m_mid_entries.load(in); 207 | m_C.load(in); 208 | m_C_sel.load(in, &m_C); 209 | } 210 | 211 | size_type size() const{ 212 | return m_n; 213 | } 214 | 215 | private: 216 | 217 | inline uint64_t get_bucket_id(const uint64_t x) const { 218 | uint64_t cardin = sdsl::bits::cnt(x); 219 | return (perm_b_k::mi_permute[t_id](x) >> (64-(splitter_bits-distance_bits))) << distance_bits | cardin; 220 | } 221 | 222 | inline uint64_t get_bucket_left(const uint64_t x, const uint8_t n_errors) const { 223 | uint64_t cardin = sdsl::bits::cnt(x); 224 | cardin = cardin > n_errors ? cardin - n_errors : 0; 225 | return (perm_b_k::mi_permute[t_id](x) >> (64-(splitter_bits-distance_bits))) << distance_bits | cardin; 226 | } 227 | 228 | inline uint64_t get_bucket_right(uint64_t x, uint8_t n_errors) const { 229 | uint64_t cardin = sdsl::bits::cnt(x); 230 | cardin = cardin + n_errors < 64 ? cardin + n_errors : 64; 231 | return (perm_b_k::mi_permute[t_id](x) >> (64-(splitter_bits-distance_bits))) << distance_bits | cardin; 232 | } 233 | 234 | void build_small_universe(const std::vector &input_entries) { 235 | // Implement a countingSort-like strategy to order entries accordingly to 236 | // their splitter_bits MOST significant bits AND by their number of bits set to 1. 237 | // Ranges of keys having the same MSB are not sorted. 238 | 239 | uint64_t splitter_universe = ((uint64_t) 1) << (splitter_bits); 240 | std::vector prefix_sums(splitter_universe + 1, 0); // includes a sentinel 241 | for (auto x : input_entries) { 242 | prefix_sums[get_bucket_id(x)]++; 243 | } 244 | 245 | m_C = t_bv(splitter_universe+input_entries.size(), 0); 246 | size_t idx = 0; 247 | for(auto x : prefix_sums) { 248 | for(size_t i = 0; i < x; ++i, ++idx) 249 | m_C[idx] = 0; 250 | m_C[idx++] = 1; 251 | } 252 | m_C_sel = t_sel(&m_C); 253 | 254 | uint64_t sum = prefix_sums[0]; 255 | prefix_sums[0] = 0; 256 | for(uint64_t i = 1; i < prefix_sums.size(); ++i) { 257 | uint64_t curr = prefix_sums[i]; 258 | prefix_sums[i] = sum + i; // +i is to obtain a striclty monotone sequence as we would have with binary vectors 259 | sum += curr; 260 | } 261 | 262 | // Partition elements into buckets accordingly to their less significant bits 263 | for (auto x : input_entries) { 264 | uint64_t bucket = get_bucket_id(x); 265 | uint64_t permuted_item = perm_b_k::mi_permute[t_id](x); 266 | /* 267 | Let A|B|C|D be the key. 268 | Assume each metasymbol is 16 bits. A is searched with the binary vector becuase it is the prefix. 269 | We compute low_xor = C|D xor B and we store low_xor in the low_entries and B in the mid_entries. 270 | At query time, we scan the low_entries and, if we find an entry such that 271 | the number of errors is smaller than t_k, we access the corresponding B and 272 | we reconstruct low_part = low_xor xor B, and we match. 273 | */ 274 | 275 | const uint64_t low_item = permuted_item & low_mask; // C|D 276 | const uint64_t mid_item = (permuted_item >> mid_shift) & mid_mask; // B 277 | const uint64_t low_xor = (low_item^mid_item); // C|D xor B 278 | mid_entries_trait::assign(m_mid_entries, prefix_sums[bucket]-bucket, mid_item); 279 | m_low_entries[prefix_sums[bucket]-bucket] = low_xor; 280 | prefix_sums[bucket]++; 281 | } 282 | } 283 | }; 284 | 285 | template 287 | struct triangle_buckets_binvector_split_simd { 288 | template 289 | using type = _triangle_buckets_binvector_split_simd; 290 | }; 291 | 292 | } 293 | -------------------------------------------------------------------------------- /include/multi_idx/triangle_clusters_binvector_split.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "multi_idx/perm.hpp" 9 | #include "sdsl/io.hpp" 10 | #include "sdsl/int_vector.hpp" 11 | #include "sdsl/sd_vector.hpp" 12 | #include "sdsl/bit_vectors.hpp" 13 | #include "multi_idx/multi_idx_helper.hpp" 14 | 15 | 16 | namespace multi_index { 17 | 18 | template, 22 | uint8_t cluster_error=12, 23 | typename t_bv=sdsl::bit_vector, 24 | typename t_sel=typename t_bv::select_1_type> 25 | class _triangle_clusters_binvector_split { 26 | public: 27 | typedef uint64_t size_type; 28 | typedef uint64_t entry_type; 29 | typedef perm_b_k perm; 30 | enum {id = t_id}; 31 | private: 32 | static constexpr uint8_t init_splitter_bits(size_t i=0){ 33 | return i < perm_b_k::match_len ? perm_b_k::mi_permute_block_widths[t_id][t_b-1-i] + init_splitter_bits(i+1) : 0; 34 | } 35 | 36 | /* Low_* stuff control how many of the less signifigant bits form the lower part */ 37 | static constexpr uint8_t low_bits = 32; // PLS, keep this a power of 2, better if word aligned 38 | static constexpr uint64_t low_mask = (1ULL<::type; 45 | 46 | uint64_t m_n; // number of items 47 | sdsl::int_vector<64> m_first_level; 48 | sdsl::int_vector m_low_entries; 49 | mid_entries_type m_mid_entries; 50 | t_bv m_C; // bit vector for prefix sums of meta-symbols 51 | t_sel m_C_sel; // select1 structure for m_C 52 | 53 | public: 54 | _triangle_clusters_binvector_split() = default; 55 | 56 | _triangle_clusters_binvector_split(const std::vector &input_entries) { 57 | std::cout << "Splitter bits " << (uint16_t) splitter_bits << std::endl; 58 | m_n = input_entries.size(); 59 | m_low_entries = sdsl::int_vector(input_entries.size(), 0); 60 | m_mid_entries = mid_entries_trait::get_instance(input_entries.size(), 0); 61 | build_small_universe(input_entries); 62 | } 63 | 64 | inline std::pair, uint64_t> match(const entry_type q, uint8_t errors=t_k, const bool find_only_candidates=false) const { 65 | 66 | const uint64_t bucket = get_bucket_id(q); 67 | 68 | const auto l = bucket == 0 ? 0 : m_C_sel(bucket) - bucket +1; 69 | const auto r = m_C_sel(bucket+1) - (bucket+1) + 1; 70 | 71 | // std::cout << bucket << " - " << l << std::endl; 72 | 73 | uint64_t candidates = r-l; 74 | std::vector res; 75 | 76 | if(find_only_candidates) return {res, candidates}; 77 | 78 | const uint64_t q_permuted = perm_b_k::mi_permute[t_id](q); 79 | const uint64_t q_high = (q_permuted>>(high_shift))<> (64-splitter_bits); 184 | } 185 | 186 | 187 | void build_small_universe(const std::vector &input_entries) { 188 | // Implement a countingSort-like strategy to order entries accordingly to 189 | // their splitter_bits MOST significant bits 190 | // Ranges of keys having the same MSB are not sorted. 191 | uint64_t splitter_universe = ((uint64_t) 1) << (splitter_bits); 192 | 193 | std::vector prefix_sums(splitter_universe + 1, 0); // includes a sentinel 194 | for (auto x: input_entries) { 195 | prefix_sums[get_bucket_id(x)]++; 196 | } 197 | 198 | // m_C = t_bv(splitter_universe+input_entries.size(), 0); 199 | // size_t idx = 0; 200 | // for(auto x : prefix_sums) { 201 | // for(size_t i = 0; i < x; ++i, ++idx) 202 | // m_C[idx] = 0; 203 | // m_C[idx++] = 1; 204 | // } 205 | // m_C_sel = t_sel(&m_C); 206 | 207 | uint64_t sum = prefix_sums[0]; 208 | prefix_sums[0] = 0; 209 | for(uint64_t i = 1; i < prefix_sums.size(); ++i) { 210 | uint64_t curr = prefix_sums[i]; 211 | prefix_sums[i] = sum; 212 | sum += curr; 213 | } 214 | 215 | std::vector bucket_xor_ids(input_entries.size(), 0); 216 | std::vector keys(input_entries.size(), 0); 217 | // Partition elements into buckets accordingly to their less significant bits 218 | for (auto &x : input_entries) { 219 | uint64_t bucket = get_bucket_id(x); 220 | uint64_t permuted_item = perm_b_k::mi_permute[t_id](x); 221 | bucket_xor_ids[prefix_sums[bucket]] = bucket; 222 | keys[prefix_sums[bucket]] = permuted_item; 223 | prefix_sums[bucket]++; 224 | } 225 | 226 | size_t binvector_size = 1ULL << splitter_bits; 227 | 228 | std::vector fl; 229 | std::vector bv; 230 | bv.reserve(binvector_size); 231 | 232 | uint64_t prev_bucket = 0, curr_bucket = 0; 233 | 234 | size_t start = 0; 235 | while(start < bucket_xor_ids.size()) { 236 | size_t end = start; 237 | prev_bucket = curr_bucket; 238 | curr_bucket = bucket_xor_ids[start]; 239 | for(size_t j = prev_bucket; j < curr_bucket; j++) { 240 | bv.push_back(1); 241 | } 242 | 243 | while(end < bucket_xor_ids.size() and curr_bucket == bucket_xor_ids[end]) end++; 244 | 245 | size_t next = start; 246 | while (next < end) { 247 | uint64_t pivot = keys[next]; 248 | auto it = std::partition( keys.begin()+next, 249 | keys.begin()+end, 250 | [&](const uint64_t &e) {return sdsl::bits::cnt(pivot^e) <= cluster_error;}); 251 | start = next; 252 | next = std::distance(keys.begin(), it); 253 | for(size_t k = start; k < next; ++k) { 254 | m_mid_entries[k] = (keys[k]>>mid_shift) & mid_mask; 255 | m_low_entries[k] = (keys[k] & low_mask); 256 | } 257 | 258 | uint64_t max_key_pos = next; 259 | uint8_t max = 0; 260 | for(size_t k = next; k < end; ++k) { 261 | uint8_t err = sdsl::bits::cnt(pivot^keys[k]); 262 | if(err > max) { 263 | max = err; 264 | max_key_pos = k; 265 | } 266 | } 267 | uint64_t tmp = keys[next]; 268 | keys[next] = keys[max_key_pos]; 269 | keys[max_key_pos] = tmp; 270 | 271 | fl.push_back(start); 272 | fl.push_back(pivot); 273 | bv.push_back(0); 274 | } 275 | 276 | start = end; 277 | } 278 | bv.push_back(1); 279 | fl.push_back(keys.size()+1); 280 | fl.push_back(keys.size()+1); // sentinel. We will access only pos on extreme cases. 281 | 282 | std::cout << "FL " << fl.size() << " BV " << bv.size() << std::endl; 283 | 284 | m_first_level = sdsl::int_vector<64>(fl.size(),0); 285 | for(size_t i = 0; i < fl.size(); ++i) 286 | m_first_level[i] = fl[i]; 287 | m_C = t_bv(bv.size(), 0); 288 | 289 | for(size_t i = 0; i < bv.size(); ++i) 290 | m_C[i] = bv[i]; 291 | m_C_sel = t_sel(&m_C); 292 | 293 | } 294 | }; 295 | 296 | template 299 | struct triangle_clusters_binvector_split { 300 | template 301 | using type = _triangle_clusters_binvector_split; 302 | }; 303 | 304 | } 305 | -------------------------------------------------------------------------------- /include/multi_idx/tuple_foreach.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | template 4 | inline typename std::enable_if::type 5 | tuple_foreach(std::tuple& t, t_f& f) 6 | {} 7 | 8 | template 9 | inline typename std::enable_if::type 10 | tuple_foreach(std::tuple& t, t_f& f) 11 | { 12 | f(std::get(t), t_idx); 13 | tuple_foreach(t, f); 14 | } 15 | 16 | template 17 | inline typename std::enable_if::type 18 | tuple_foreach(const std::tuple& t, t_f& f) 19 | {} 20 | 21 | template 22 | inline typename std::enable_if::type 23 | tuple_foreach(const std::tuple& t, t_f& f) 24 | { 25 | f(std::get(t), t_idx); 26 | tuple_foreach(t, f); 27 | } 28 | -------------------------------------------------------------------------------- /include/multi_idx/xor_buckets_binvector_split.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "multi_idx/perm.hpp" 9 | #include "sdsl/io.hpp" 10 | #include "sdsl/int_vector.hpp" 11 | #include "sdsl/sd_vector.hpp" 12 | #include "sdsl/bit_vectors.hpp" 13 | #include "multi_idx/multi_idx_helper.hpp" 14 | 15 | namespace multi_index { 16 | 17 | template, 21 | uint8_t xor_len=8, 22 | typename t_bv=sdsl::bit_vector, 23 | typename t_sel=typename t_bv::select_1_type> 24 | class _xor_buckets_binvector_split { 25 | public: 26 | typedef uint64_t size_type; 27 | typedef uint64_t entry_type; 28 | typedef perm_b_k perm; 29 | enum {id = t_id}; 30 | 31 | private: 32 | static constexpr uint8_t init_splitter_bits(size_t i=0){ 33 | return i < perm_b_k::match_len ? perm_b_k::mi_permute_block_widths[t_id][t_b-1-i] + init_splitter_bits(i+1) : 0; 34 | } 35 | 36 | /* Low_* stuff control how many of the less signifigant bits form the lower part */ 37 | static constexpr uint8_t low_bits = 32; // PLS, keep this a power of 2, better if word aligned 38 | static constexpr uint64_t low_mask = (1ULL<::type; 45 | 46 | sdsl::int_vector<64> m_first_level; 47 | sdsl::int_vector m_low_entries; 48 | mid_entries_type m_mid_entries; 49 | uint64_t m_n; // number of items 50 | //sdsl::int_vector<64> fake_entries; for DEBUG USE ONLY 51 | t_bv m_C; // bit vector for prefix sums of meta-symbols 52 | t_sel m_C_sel; // select1 structure for m_C 53 | 54 | public: 55 | _xor_buckets_binvector_split() = default; 56 | 57 | _xor_buckets_binvector_split(const std::vector &input_entries) { 58 | std::cout << "Splitter bits " << (uint16_t) splitter_bits << std::endl; 59 | m_n = input_entries.size(); 60 | m_low_entries = sdsl::int_vector(input_entries.size(), 0); 61 | m_mid_entries = mid_entries_trait::get_instance(input_entries.size(), 0); 62 | 63 | build_small_universe(input_entries); 64 | } 65 | 66 | inline std::pair, uint64_t> match(const entry_type q, uint8_t errors=t_k, const bool find_only_candidates=false) const { 67 | const uint64_t bucket = get_bucket_id(q); 68 | 69 | const auto l = bucket == 0 ? 0 : m_C_sel(bucket) - bucket +1; 70 | const auto r = m_C_sel(bucket+1) - (bucket+1) + 1; 71 | 72 | uint64_t candidates = r-l; 73 | std::vector res; 74 | 75 | if(find_only_candidates) return {res, candidates}; 76 | 77 | const uint64_t q_permuted = perm_b_k::mi_permute[t_id](q); 78 | const uint64_t q_high = (q_permuted>>(high_shift))<> xor_len; 90 | const uint64_t pos_r = *(fl_it+1) >> xor_len; 91 | 92 | const auto begin = m_low_entries.begin() + pos_l; 93 | const auto end = m_low_entries.begin() + pos_r; 94 | for (auto it = begin; it != end; ++it, ++pos_l) { 95 | const uint64_t item_low = *it; 96 | if(sdsl::bits::cnt(q_low^item_low) <= errors) { 97 | const uint64_t curr_el = q_high | (((uint64_t) m_mid_entries[pos_l]) << mid_shift) | item_low; 98 | if(sdsl::bits::cnt(q_permuted^curr_el) <= errors) { 99 | res.push_back(perm_b_k::mi_rev_permute[t_id](curr_el)); 100 | } 101 | } 102 | } 103 | } 104 | } 105 | return {res, candidates}; 106 | } 107 | 108 | _xor_buckets_binvector_split& operator=(const _xor_buckets_binvector_split& idx) { 109 | if ( this != &idx ) { 110 | m_n = std::move(idx.m_n); 111 | m_low_entries = std::move(idx.m_low_entries); 112 | m_mid_entries = std::move(idx.m_mid_entries); 113 | m_first_level = std::move(idx.m_first_level); 114 | //fake_entries = std::move(idx.fake_entries); 115 | m_C = std::move(idx.m_C); 116 | m_C_sel = std::move(idx.m_C_sel); 117 | m_C_sel.set_vector(&m_C); 118 | } 119 | return *this; 120 | } 121 | 122 | _xor_buckets_binvector_split& operator=(_xor_buckets_binvector_split&& idx) { 123 | if ( this != &idx ) { 124 | m_n = std::move(idx.m_n); 125 | m_low_entries = std::move(idx.m_low_entries); 126 | m_mid_entries = std::move(idx.m_mid_entries); 127 | m_first_level = std::move(idx.m_first_level); 128 | //fake_entries = std::move(idx.fake_entries); 129 | m_C = std::move(idx.m_C); 130 | m_C_sel = std::move(idx.m_C_sel); 131 | m_C_sel.set_vector(&m_C); 132 | } 133 | return *this; 134 | } 135 | 136 | _xor_buckets_binvector_split(const _xor_buckets_binvector_split& idx) { 137 | *this = idx; 138 | } 139 | 140 | _xor_buckets_binvector_split(_xor_buckets_binvector_split&& idx){ 141 | *this = std::move(idx); 142 | } 143 | 144 | //! Serializes the data structure into the given ostream 145 | size_type serialize(std::ostream& out, sdsl::structure_tree_node* v=nullptr, std::string name="")const { 146 | using namespace sdsl; 147 | structure_tree_node* child = structure_tree::add_child(v, name, util::class_name(*this)); 148 | uint64_t written_bytes = 0; 149 | written_bytes += write_member(m_n, out, child, "n"); 150 | written_bytes += m_low_entries.serialize(out, child, "low_entries"); 151 | written_bytes += m_mid_entries.serialize(out, child, "mid_entries"); 152 | written_bytes += m_first_level.serialize(out, child, "first_level"); 153 | written_bytes += m_C.serialize(out, child, "C"); 154 | written_bytes += m_C_sel.serialize(out, child, "C_sel"); 155 | //written_bytes += fake_entries.serialize(out, child, "fake"); 156 | structure_tree::add_size(child, written_bytes); 157 | return written_bytes; 158 | } 159 | 160 | //! Loads the data structure from the given istream. 161 | void load(std::istream& in) { 162 | using namespace sdsl; 163 | read_member(m_n, in); 164 | m_low_entries.load(in); 165 | m_mid_entries.load(in); 166 | m_first_level.load(in); 167 | m_C.load(in); 168 | m_C_sel.load(in, &m_C); 169 | //fake_entries.load(in); 170 | } 171 | 172 | size_type size() const{ 173 | return m_n; 174 | } 175 | 176 | private: 177 | 178 | inline uint64_t get_bucket_id(const uint64_t x) const { 179 | return perm_b_k::mi_permute[t_id](x) >> (64-splitter_bits); 180 | } 181 | 182 | inline uint64_t get_bucket_xor_id(const uint64_t x) const { 183 | return (get_bucket_id(x)<> i) & mask; 190 | res = res ^ l; 191 | } 192 | return res; 193 | } 194 | 195 | void build_small_universe(const std::vector &input_entries) { 196 | // Implement a countingSort-like strategy to order entries accordingly to 197 | // their splitter_bits MOST significant bits 198 | // Ranges of keys having the same MSB are not sorted. 199 | uint64_t splitter_universe = ((uint64_t) 1) << (splitter_bits+xor_len); 200 | 201 | std::vector prefix_sums(splitter_universe + 1, 0); // includes a sentinel 202 | for (auto x: input_entries) { 203 | prefix_sums[get_bucket_xor_id(x)]++; 204 | } 205 | 206 | // m_C = t_bv(splitter_universe+input_entries.size(), 0); 207 | // size_t idx = 0; 208 | // for(auto x : prefix_sums) { 209 | // for(size_t i = 0; i < x; ++i, ++idx) 210 | // m_C[idx] = 0; 211 | // m_C[idx++] = 1; 212 | // } 213 | // m_C_sel = t_sel(&m_C); 214 | 215 | uint64_t sum = prefix_sums[0]; 216 | prefix_sums[0] = 0; 217 | for(uint64_t i = 1; i < prefix_sums.size(); ++i) { 218 | uint64_t curr = prefix_sums[i]; 219 | prefix_sums[i] = sum + i; // +i is to obtain a striclty monotone sequence as we would have with binary vectors 220 | sum += curr; 221 | } 222 | 223 | std::vector bucket_xor_ids(input_entries.size(), 0); 224 | // Partition elements into buckets accordingly to their less significant bits 225 | for (auto &x : input_entries) { 226 | uint64_t bucket = get_bucket_xor_id(x); 227 | uint64_t permuted_item = perm_b_k::mi_permute[t_id](x); 228 | //fake_entries[prefix_sums[bucket]-bucket] = permuted_item; 229 | m_mid_entries[prefix_sums[bucket]-bucket] = (permuted_item>>mid_shift) & mid_mask; // -bucket is because we have a striclty monotone sequence 230 | m_low_entries[prefix_sums[bucket]-bucket] = (permuted_item & low_mask); 231 | bucket_xor_ids[prefix_sums[bucket]-bucket] = bucket; 232 | prefix_sums[bucket]++; 233 | } 234 | 235 | const uint64_t mask = (1<> xor_len; 237 | size_t binvector_size = 1ULL << splitter_bits; 238 | uint64_t prev_xor = bucket_xor_ids[0] & mask; 239 | uint64_t pos = 0; 240 | 241 | std::vector fl; 242 | std::vector bv; 243 | 244 | for(size_t j = 0; j < prev_bucket; j++) 245 | bv.push_back(1); 246 | bv.push_back(0); 247 | fl.push_back((pos << xor_len) | prev_xor); 248 | 249 | for(auto &x: bucket_xor_ids) { 250 | const uint64_t curr_bucket = x >> xor_len; 251 | const uint64_t curr_xor = x & mask; 252 | if(curr_bucket != prev_bucket) { 253 | for(size_t j = prev_bucket; j < curr_bucket; j++) { 254 | bv.push_back(1); 255 | } 256 | bv.push_back(0); 257 | prev_bucket = curr_bucket; 258 | fl.push_back((pos << xor_len) | curr_xor); 259 | prev_xor = curr_xor; 260 | binvector_size++; 261 | pos++; 262 | continue; 263 | } 264 | if(prev_xor > curr_xor) std::cout << "ERROR " << std::endl; 265 | if(prev_xor != curr_xor) { 266 | fl.push_back((pos << xor_len) | curr_xor); 267 | bv.push_back(0); 268 | prev_xor = curr_xor; 269 | binvector_size++; 270 | } 271 | pos++; 272 | } 273 | bv.push_back(1); 274 | fl.push_back((pos << xor_len) | 0); // sentinel. We will access only pos on extreme cases. 275 | 276 | std::cout << "FL " << fl.size() << " BV " << bv.size() << std::endl; 277 | 278 | 279 | m_first_level = sdsl::int_vector<64>(fl.size(),0); 280 | for(size_t i = 0; i < fl.size(); ++i) 281 | m_first_level[i] = fl[i]; 282 | m_C = t_bv(bv.size(), 0); 283 | 284 | for(size_t i = 0; i < bv.size(); ++i) 285 | m_C[i] = bv[i]; 286 | m_C_sel = t_sel(&m_C); 287 | 288 | } 289 | }; 290 | 291 | template 295 | struct xor_buckets_binvector_split { 296 | template 297 | using type = _xor_buckets_binvector_split; 298 | }; 299 | 300 | } 301 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | git submodule init 2 | git submodule update 3 | cd external/sdsl-lite 4 | ./install.sh ../.. 5 | mkdir -p build 6 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories("${CMAKE_CURRENT_BINARY_DIR}/../include") 2 | 3 | file(GLOB libFiles RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") # select all .cpp-files 4 | 5 | set(multi_idx_SRCS ${libFiles} ) 6 | 7 | add_library(multi_idx ${multi_idx_SRCS} ) 8 | 9 | install(TARGETS multi_idx RUNTIME DESTINATION bin 10 | LIBRARY DESTINATION lib 11 | ARCHIVE DESTINATION lib) 12 | 13 | set_target_properties(multi_idx PROPERTIES 14 | VERSION "1.0.0" 15 | SOVERSION "1" 16 | ) 17 | -------------------------------------------------------------------------------- /scripts/CodeGeneration.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import ast 3 | import subprocess 4 | 5 | def write_function(perm, function_name, orig_perm, hpp_file): 6 | print orig_perm 7 | hpp_file.write("\t// " + str(orig_perm)+ "\n") 8 | hpp_file.write("\tstatic constexpr uint64_t " + function_name +"(uint64_t x) {\n") 9 | proc = subprocess.Popen(["./calcperm", " ".join([str(x) for x in perm])], stderr=subprocess.PIPE) 10 | code = proc.stderr.read().replace("\n","\n\t\t") 11 | hpp_file.write("\t\t" + code) 12 | hpp_file.write("return x;\n") 13 | hpp_file.write("\t}\n\n") 14 | 15 | 16 | def write_code_set(selected_perms, permute_name, rev_permute_name, struct_name, hpp_file, cpp_file): 17 | # write the set of perms perms 18 | fun_vec_name = permute_name + "_permute" 19 | rev_fun_vec_name = rev_permute_name + "_permute" 20 | permute_array_name = permute_name + "_perms" 21 | perm_function_name = permute_name + "_{}" # permutation_id 22 | revperm_function_name = rev_permute_name + "_{}" # permutation_id 23 | perm_cnt = str(len(selected_perms)) 24 | block_cnt = str(len(sizes)) 25 | array_str = "std::array"; 26 | 27 | id = 0 28 | for perm in selected_perms: 29 | permutation = [] # it says where each of the 64 bits has to go accordingly to perm 30 | for x in perm: 31 | permutation += bits[x] 32 | write_function(permutation, perm_function_name.format(id), perm, hpp_file) 33 | 34 | inverse = [0] * len(permutation) 35 | for i, p in enumerate(permutation): 36 | inverse[p] = i 37 | write_function(inverse, revperm_function_name.format(id), perm, hpp_file) 38 | id += 1 39 | 40 | hpp_file.write("\tstatic constexpr t_fun_vec_"+permute_name+ " " + fun_vec_name + " = {\n") 41 | for id in xrange(len(selected_perms)-1): 42 | hpp_file.write("\t\t&" + struct_name + "::" + perm_function_name.format(id) + ",\n") 43 | hpp_file.write("\t\t&" + struct_name + "::"+ perm_function_name.format(len(selected_perms)-1) + "\n};\n\n") 44 | 45 | 46 | hpp_file.write("\tstatic constexpr t_fun_vec_"+permute_name+ " " + rev_fun_vec_name + " = {\n") 47 | for id in xrange(len(selected_perms)-1): 48 | hpp_file.write("\t\t&" + struct_name + "::"+ revperm_function_name.format(id)+",\n") 49 | hpp_file.write("\t\t&" + struct_name + "::"+ revperm_function_name.format(len(selected_perms)-1)+"\n};\n\n") 50 | 51 | hpp_file.write("\tstatic constexpr std::array<"+array_str+","+perm_cnt+"> " + permute_array_name + "={{\n") 52 | for id in xrange(len(selected_perms)-1): 53 | hpp_file.write("\t\t{" + ", ".join(str(x) for x in selected_perms[id]) + "},\n") 54 | hpp_file.write("\t\t{" + ", ".join(str(x) for x in selected_perms[-1]) + "}\n}};\n\n") 55 | 56 | hpp_file.write("\tstatic constexpr "+array_str+ " " + fun_vec_name + "_block_sizes = {{\n\t" + ", ".join(str(x) for x in sizes) + "\n}};\n") 57 | 58 | widths = [] 59 | widths_sums =[] 60 | for perm in selected_perms: 61 | l = [] 62 | for el in perm: 63 | l.append(sizes[el]) 64 | p = [0]*len(l) 65 | for i in xrange(1, len(l)): 66 | p[i] = p[i-1] + l[i-1] 67 | 68 | widths.append("\t{" + ",".join([str(x) for x in l]) + "}") 69 | widths_sums.append("\t{" + ",".join([str(x) for x in p]) + "}") 70 | 71 | # write width of each block in each permutation 72 | hpp_file.write("\tstatic constexpr std::array<"+array_str+","+perm_cnt+"> " + fun_vec_name + "_block_widths = {{ // width of the blocks in the 64-bit word \n") 73 | hpp_file.write(",\n".join(widths) + "\n\t}};\n\n") 74 | 75 | # write prefix sums of blocks' width in each permutation 76 | hpp_file.write("\tstatic constexpr std::array<"+array_str+","+perm_cnt+"> " + fun_vec_name + "_block_widths_sums = {{ // prefix sums of widths of the blocks in the 64-bit word \n") 77 | hpp_file.write(",\n".join(widths_sums) + "\n\t}};\n\n") 78 | 79 | # write vectors of functions pointers 80 | cpp_file.write("constexpr "+ struct_name + "::t_fun_vec_"+permute_name+" " + struct_name + "::" + fun_vec_name + ";") 81 | cpp_file.write("constexpr "+ struct_name + "::t_fun_vec_"+permute_name+" " + struct_name + "::" + rev_fun_vec_name + ";") 82 | cpp_file.write("constexpr std::array<"+array_str+","+perm_cnt+"> " + struct_name + "::" + permute_array_name + ";\n") 83 | 84 | cpp_file.write("\nconstexpr "+array_str+" " + struct_name + "::" + fun_vec_name + "_block_sizes;\n"); 85 | cpp_file.write("\nconstexpr std::array<"+array_str+","+perm_cnt+"> " + struct_name + "::" + fun_vec_name + "_block_widths;\n") 86 | cpp_file.write("\nconstexpr std::array<"+array_str+","+perm_cnt+"> " + struct_name + "::" + fun_vec_name + "_block_widths_sums;\n") 87 | 88 | if len(sys.argv) < 3: 89 | print "Usage: python", sys.argv[0], "permutation_filename code_filename" 90 | print "Make sure you compiled calcperm with g++ -o calcperm calcperm.cpp" 91 | sys.exit(1) 92 | 93 | # Read permutation file 94 | # Input format: 95 | # - n 96 | # - k 97 | # - Multi_index permutations 98 | 99 | perm_file = open(sys.argv[1], "r") 100 | hpp_file = open(sys.argv[2] + ".hpp", "w") 101 | cpp_file = open(sys.argv[2] + ".cpp", "w") 102 | 103 | n = int(perm_file.readline()) 104 | k = int(perm_file.readline()) 105 | mi_perms = ast.literal_eval(perm_file.readline()) 106 | 107 | print "Multi Index permutations:" 108 | for perm in mi_perms: 109 | print perm 110 | 111 | # Generate permuting/inverting code 112 | 113 | # block sizes are w_b or w_b +1. There are exactly w_b_rem of the latter size. 114 | w_b = 64/n 115 | w_b_rem = 64%n 116 | 117 | #sizes = [w_b+1]*w_b_rem + [w_b]*(n-w_b_rem) 118 | sizes = [w_b]*(n-w_b_rem) + [w_b+1]*w_b_rem 119 | print "Sizes of the blocks are:" 120 | print sizes 121 | 122 | if (sum(sizes) != 64): 123 | print "ERROR: sizes do not sum to 64" 124 | 125 | bits = [] 126 | start = 0 127 | for x in sizes: 128 | end = start + x 129 | bits.append([x for x in xrange(start, end)]) 130 | start = end 131 | print bits 132 | 133 | # generate code for the perms and their inverted. 134 | 135 | # Header 136 | hpp_file.write("#pragma once\n\n#include \n#include \"multi_idx/perm.hpp\"\n#include \"multi_idx/aux_bits.hpp\"\t\n") 137 | 138 | struct_name = "perm<" +str(n) + "," + str(k)+">" 139 | hpp_file.write("template<>\nstruct " + struct_name + " {\n\n") 140 | hpp_file.write("\ttypedef uint64_t (*perm_fun_t)(uint64_t);\n") 141 | hpp_file.write("\ttypedef std::array t_fun_vec_mi;\n\n") 142 | hpp_file.write("\tstatic constexpr uint8_t max_dist = " + str(n-k) + ";\n") 143 | hpp_file.write("\tstatic constexpr uint8_t match_len = " + str(k) + ";\n\n") 144 | 145 | cpp_file.write("#include \"multi_idx/"+sys.argv[2]+".hpp\"\n\n") 146 | cpp_file.write("// initialize static const members\n\n") 147 | 148 | # generate functions 149 | write_code_set(mi_perms, "mi", "mi_rev", struct_name, hpp_file, cpp_file) 150 | 151 | # Footer 152 | hpp_file.write("};") 153 | -------------------------------------------------------------------------------- /scripts/CodeGeneration.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $# -lt 2 ]; then 4 | echo "Usage: ./CodeGeneration n k" 5 | echo "Produces output files perm_n_k.hpp and perm_n_k.cpp" 6 | exit 1 7 | else 8 | python Permutations.py $1 $2 2> out && python CodeGeneration.py out perm_$1_$2 9 | mv perm_$1_$2.hpp ../include/multi_idx/perm_$1_$2.hpp 10 | mv perm_$1_$2.cpp ../lib/perm_$1_$2.cpp 11 | exit 0 12 | fi 13 | -------------------------------------------------------------------------------- /scripts/Gen.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import ast 3 | import subprocess 4 | 5 | def write_function(perm, function_name, orig_perm, hpp_file): 6 | print orig_perm 7 | hpp_file.write("\t// " + str(orig_perm)+ "\n") 8 | hpp_file.write("\tstatic constexpr uint64_t " + function_name +"(uint64_t x) {\n") 9 | proc = subprocess.Popen(["./calcperm", " ".join([str(x) for x in perm])], stderr=subprocess.PIPE) 10 | code = proc.stderr.read().replace("\n","\n\t\t") 11 | hpp_file.write("\t\t" + code) 12 | hpp_file.write("return x;\n") 13 | hpp_file.write("\t}\n\n") 14 | 15 | 16 | def write_code_set(selected_perms, permute_name, rev_permute_name, struct_name, hpp_file, cpp_file): 17 | # write the set of perms perms 18 | fun_vec_name = permute_name + "_permute" 19 | rev_fun_vec_name = rev_permute_name + "_permute" 20 | permute_array_name = permute_name + "_perms" 21 | perm_function_name = permute_name + "_{}" # permutation_id 22 | revperm_function_name = rev_permute_name + "_{}" # permutation_id 23 | perm_cnt = str(len(selected_perms)) 24 | block_cnt = str(len(sizes)) 25 | array_str = "std::array"; 26 | 27 | id = 0 28 | for perm in selected_perms: 29 | permutation = [] # it says where each of the 64 bits has to go accordingly to perm 30 | for x in perm: 31 | permutation += bits[x] 32 | write_function(permutation, perm_function_name.format(id), perm, hpp_file) 33 | 34 | inverse = [0] * len(permutation) 35 | for i, p in enumerate(permutation): 36 | inverse[p] = i 37 | write_function(inverse, revperm_function_name.format(id), perm, hpp_file) 38 | id += 1 39 | 40 | hpp_file.write("\tstatic constexpr t_fun_vec_"+permute_name+ " " + fun_vec_name + " = {\n") 41 | for id in xrange(len(selected_perms)-1): 42 | hpp_file.write("\t\t&" + struct_name + "::" + perm_function_name.format(id) + ",\n") 43 | hpp_file.write("\t\t&" + struct_name + "::"+ perm_function_name.format(len(selected_perms)-1) + "\n};\n\n") 44 | 45 | 46 | hpp_file.write("\tstatic constexpr t_fun_vec_"+permute_name+ " " + rev_fun_vec_name + " = {\n") 47 | for id in xrange(len(selected_perms)-1): 48 | hpp_file.write("\t\t&" + struct_name + "::"+ revperm_function_name.format(id)+",\n") 49 | hpp_file.write("\t\t&" + struct_name + "::"+ revperm_function_name.format(len(selected_perms)-1)+"\n};\n\n") 50 | 51 | hpp_file.write("\tstatic constexpr std::array<"+array_str+","+perm_cnt+"> " + permute_array_name + "={{\n") 52 | for id in xrange(len(selected_perms)-1): 53 | hpp_file.write("\t\t{" + ", ".join(str(x) for x in selected_perms[id]) + "},\n") 54 | hpp_file.write("\t\t{" + ", ".join(str(x) for x in selected_perms[-1]) + "}\n}};\n\n") 55 | 56 | hpp_file.write("\tstatic constexpr "+array_str+ " " + fun_vec_name + "_block_sizes = {{\n\t" + ", ".join(str(x) for x in sizes) + "\n}};\n") 57 | 58 | widths = [] 59 | widths_sums =[] 60 | for perm in selected_perms: 61 | l = [] 62 | for el in perm: 63 | l.append(sizes[el]) 64 | p = [0]*len(l) 65 | for i in xrange(1, len(l)): 66 | p[i] = p[i-1] + l[i-1] 67 | 68 | widths.append("\t{" + ",".join([str(x) for x in l]) + "}") 69 | widths_sums.append("\t{" + ",".join([str(x) for x in p]) + "}") 70 | 71 | # write width of each block in each permutation 72 | hpp_file.write("\tstatic constexpr std::array<"+array_str+","+perm_cnt+"> " + fun_vec_name + "_block_widths = {{ // width of the blocks in the 64-bit word \n") 73 | hpp_file.write(",\n".join(widths) + "\n\t}};\n\n") 74 | 75 | # write prefix sums of blocks' width in each permutation 76 | hpp_file.write("\tstatic constexpr std::array<"+array_str+","+perm_cnt+"> " + fun_vec_name + "_block_widths_sums = {{ // prefix sums of widths of the blocks in the 64-bit word \n") 77 | hpp_file.write(",\n".join(widths_sums) + "\n\t}};\n\n") 78 | 79 | # write vectors of functions pointers 80 | cpp_file.write("constexpr "+ struct_name + "::t_fun_vec_"+permute_name+" " + struct_name + "::" + fun_vec_name + ";") 81 | cpp_file.write("constexpr "+ struct_name + "::t_fun_vec_"+permute_name+" " + struct_name + "::" + rev_fun_vec_name + ";") 82 | cpp_file.write("constexpr std::array<"+array_str+","+perm_cnt+"> " + struct_name + "::" + permute_array_name + ";\n") 83 | 84 | cpp_file.write("\nconstexpr "+array_str+" " + struct_name + "::" + fun_vec_name + "_block_sizes;\n"); 85 | cpp_file.write("\nconstexpr std::array<"+array_str+","+perm_cnt+"> " + struct_name + "::" + fun_vec_name + "_block_widths;\n") 86 | cpp_file.write("\nconstexpr std::array<"+array_str+","+perm_cnt+"> " + struct_name + "::" + fun_vec_name + "_block_widths_sums;\n") 87 | 88 | if len(sys.argv) < 3: 89 | print "Usage: python", sys.argv[0], "permutation_filename code_filename" 90 | print "Make sure you compiled calcperm with g++ -o calcperm calcperm.cpp" 91 | sys.exit(1) 92 | 93 | # Read permutation file 94 | # Input format: 95 | # - n 96 | # - k 97 | # - Chi permutations 98 | # - Multi_index permutations 99 | 100 | perm_file = open(sys.argv[1], "r") 101 | hpp_file = open(sys.argv[2] + ".hpp", "w") 102 | cpp_file = open(sys.argv[2] + ".cpp", "w") 103 | 104 | n = int(perm_file.readline()) 105 | k = int(perm_file.readline()) 106 | chi_perms = ast.literal_eval(perm_file.readline()) 107 | mi_perms = ast.literal_eval(perm_file.readline()) 108 | 109 | print "Chi permutations:" 110 | for perm in chi_perms: 111 | print perm 112 | 113 | 114 | print "Multi Index permutations:" 115 | for perm in mi_perms: 116 | print perm 117 | 118 | # Generate permuting/inverting code 119 | 120 | # block sizes are w_b or w_b +1. There are exactly w_b_rem of the latter size. 121 | w_b = 64/n 122 | w_b_rem = 64%n 123 | 124 | sizes = [w_b+1]*w_b_rem + [w_b]*(n-w_b_rem) 125 | print "Sizes of the blocks are:" 126 | print sizes 127 | 128 | if (sum(sizes) != 64): 129 | print "ERROR: sizes do not sum to 64" 130 | 131 | bits = [] 132 | start = 0 133 | for x in sizes: 134 | end = start + x 135 | bits.append([x for x in xrange(start, end)]) 136 | start = end 137 | print bits 138 | 139 | # generate code for the perms and their inverted. 140 | 141 | # Header 142 | hpp_file.write("#pragma once\n\n#include \n#include \"multi_idx/perm.hpp\"\n#include \"multi_idx/aux_bits.hpp\"\t\n") 143 | 144 | struct_name = "perm<" +str(n) + "," + str(k)+">" 145 | hpp_file.write("template<>\nstruct " + struct_name + " {\n\n") 146 | hpp_file.write("\ttypedef uint64_t (*perm_fun_t)(uint64_t);\n") 147 | hpp_file.write("\ttypedef std::array t_fun_vec_mi;\n\n") 148 | #hpp_file.write("\ttypedef std::array t_fun_vec_chi;\n\n") 149 | hpp_file.write("\tstatic constexpr uint8_t max_dist = " + str(n-k) + ";\n") 150 | hpp_file.write("\tstatic constexpr uint8_t match_len = " + str(k) + ";\n\n") 151 | 152 | cpp_file.write("#include " + "\"multi_idx/"+ sys.argv[2] +".hpp\"\n\n") 153 | cpp_file.write("// initialize static const members\n\n") 154 | 155 | # generate functions 156 | write_code_set(mi_perms, "mi", "mi_rev", struct_name, hpp_file, cpp_file) 157 | #write_code_set(chi_perms, "chi", "chi_rev", struct_name, hpp_file, cpp_file) 158 | 159 | # Footer 160 | hpp_file.write("};") 161 | -------------------------------------------------------------------------------- /scripts/Permutations.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | import sys 3 | import random 4 | from subprocess import call 5 | 6 | def choose(n, k): 7 | """ 8 | A fast way to calculate binomial coefficients by Andrew Dalke (contrib). 9 | """ 10 | if 0 <= k <= n: 11 | ntok = 1 12 | ktok = 1 13 | for t in xrange(1, min(k, n - k) + 1): 14 | ntok *= n 15 | ktok *= t 16 | n -= 1 17 | return ntok // ktok 18 | else: 19 | return 0 20 | 21 | def TAoCPpermutation(n,k): 22 | """ 23 | Implements solution presented in The Art of Computer Programming III, Exercise 6.5-1 24 | to generate a set of (n choose k) perms such that, 25 | for any t-subset of [n], there exists a permutation prefixed by the subset, 26 | where t \leq k or t \geq n-k 27 | """ 28 | perms = [] 29 | for subset in itertools.combinations(range(n), k): 30 | A = []; B = []; C = []; min = 0; j = 0; up = 0 31 | for i in xrange(n): 32 | if(j>=k or i != subset[j]): 33 | B.append(i) 34 | up +=1 35 | else: 36 | up -=1 37 | j += 1 38 | if(up < min): 39 | min = up 40 | B.append(i) 41 | else: 42 | A.append(i) 43 | C.append(B.pop()) 44 | perms.append(A+B+C) 45 | return perms 46 | 47 | 48 | def get_k_sets(perm, k): 49 | curr_set = [] 50 | for i in xrange(n): 51 | l = (perm*2)[i: i+k] 52 | curr_set.append(tuple(sorted(l))) 53 | return curr_set 54 | 55 | def is_intersetion_empty(big, small): 56 | for x in small: 57 | if x in big: 58 | return False 59 | return True 60 | 61 | print "Usage: python", sys.argv[0], "n k" 62 | 63 | n = int(sys.argv[1]) # number of blocks 64 | k = int(sys.argv[2]) # number of blocks that have to be consecutive at least once in some permutation 65 | 66 | sys.stderr.write(str(n)+"\n") 67 | sys.stderr.write(str(k)+"\n") 68 | sys.stderr.write(str(TAoCPpermutation(n,k))+"\n") 69 | -------------------------------------------------------------------------------- /scripts/calcperm.ini: -------------------------------------------------------------------------------- 1 | # Parameter file for calcperm 2 | 3 | # (c) 2011..2014 by Jasper L. Neumann 4 | # www.sirrida.de / programming.sirrida.de 5 | # E-Mail: info@sirrida.de 6 | 7 | # Granted to the public domain 8 | # First version: 2012-10-03 9 | # Last change: 2013-03-20 10 | 11 | # Here you can define the default settings. 12 | # 13 | # Parameters start with a '-' or a '/'. 14 | # The parameter value is separarated from the name with '=' or ':'. 15 | # You can override the settings on the command line as well. 16 | # Example: calcperm /in_base=16 f e d c b a 9 8 7 6 5 * 3 2 1 -self_test:1 17 | # The main program parameters are index numbers, 18 | # see also parameters starting with /in_. 19 | # Numbers can also start with '$' denoting a hexadecimal number. 20 | # 21 | # A parameter starting with '@', e.g. @"x.ini", includes all parameters 22 | # from the file with the given name. 23 | # The '"' characters are stripped from the file name. 24 | 25 | # Output options 26 | /dump_input=1 # dump input vector 27 | /dump_inverse=1 # dump inverse vector 28 | /verbose=0 # verbose output 29 | /brief=1 # Brief info about BPC / Butterfly routing 30 | 31 | # Language dependent settings 32 | # You might activate /output_pas or /output_c 33 | # /output_pas # Pascal-style output, implies the following: 34 | # /comment_prefix=// 35 | # /comment_postfix= 36 | # /hex_prefix=$ 37 | # /hex_postfix= 38 | # /op_assign=:= 39 | # /op_and=and 40 | # /op_or=or 41 | # /op_xor=xor 42 | # /op_shl=shl 43 | # /op_shr=shr 44 | 45 | # /output_c # C-style output, implies the following: 46 | # /comment_prefix=// 47 | # /comment_postfix= 48 | # /hex_prefix=0x 49 | # /hex_postfix= 50 | # /op_assign== 51 | # /op_and=& 52 | # /op_or=| 53 | # /op_xor=^ 54 | # /op_shl=<< 55 | # /op_shr=>> 56 | 57 | # Function names 58 | /op_pstep=bit_permute_step 59 | /op_pstep_simple=bit_permute_step_simple 60 | /op_rol=rol 61 | /op_gather=pext 62 | /op_scatter=pdep 63 | /op_bswap=bswap 64 | 65 | # Input options 66 | /in_origin=0 # input index origin 67 | /in_base=10 # input number base 68 | /in_indexes=source # meaning of given indexes (source/target) 69 | 70 | # General costs 71 | /cost_rotate_shift=1 # cost for shifting and rotating 72 | /cost_bool=1 # cost for and/or/xor 73 | /cost_bswap=1 # cost for bswap 74 | /cost_mul=4 # cost for mul 75 | /cost_gs=3 # cost for pext/pdep 76 | /cost_mask=0 # cost for a mask 77 | 78 | # Special costs # (default) 79 | # /cost_rotate=1 # cost_rotate_shift 80 | # /cost_shift=1 # cost_rotate_shift 81 | # /cost_and=1 # cost_bool 82 | # /cost_or=1 # cost_bool 83 | # /cost_xor=1 # cost_bool 84 | # /cost_scatter=3 # cost_gs 85 | # /cost_gather=3 # cost_gs 86 | # /cost_bit_permute_step=6 # cost_shift*2+cost_xor*3+cost_and 87 | # /cost_bit_permute_step_simple=5 # cost_shift*2+cost_and*2+cost_or 88 | 89 | # Superscalar boni 90 | /bonus_bit_permute_step=1 # implicitely parallel 91 | /bonus_bit_permute_step_simple=1 # implicitely parallel 92 | /bonus_gs=3 # 2 parallel pext/pdep 93 | /bonus_mask_rol=1 # 2 parallel mask_rol/mask_shift ops 94 | /bonus_gs_rol=2 # parallel mask_rol/mask_shift and gs ops 95 | 96 | # Calculation options 97 | /allow_bswap=1 # Allow BSWAP command 98 | /allow_bmi=0 # Allow gather/scatter (PEXT/PDEP, BMI-2) 99 | /test_bpc=1 # try BPC routing 100 | /test_bfly=1 # try Butterfly routing 101 | /test_ibfly=1 # try inverse Butterfly routing 102 | /test_benes=1 # find best Benes network 103 | /test_bit_groups=1 # Bit group moving 104 | /test_mul=1 # Shift bits by multiplication 105 | /test_gather_scatter=1 # Gather/scatter [bmi] 106 | /test_gather_shift=1 # Gather/shift [bmi] 107 | /test_gather_shift_sloppy=1 # Gather/shift (modified) [bmi] 108 | /test_shift_scatter=1 # Shift/scatter [bmi] 109 | /test_shift_scatter_sloppy=1 # Shift/scatter (modified) [bmi] 110 | /test_sag=1 # Sheep and goats / Sort indexes [bmi] 111 | /opt_gs=1 # Post-optimize scatter/gather methods? 112 | /opt_rol=1 # Optimize with rol? 113 | /opt_rol_ex=0 # Optimize Benes/BPC with rol? (costs time) 114 | /opt_bswap=1 # Optimize with bswap? [bswap] 115 | 116 | /self_test=0 # Perform an additional self-test? 117 | 118 | # EOF. 119 | -------------------------------------------------------------------------------- /scripts/exp0.R: -------------------------------------------------------------------------------- 1 | library(ggplot2) 2 | source("../scripts/multi_index.R") 3 | source("../external/sdsl-lite/benchmark/basic_functions.R") 4 | 5 | options(scipen=999) 6 | 7 | data <- data_frame_from_key_value_pairs("../build/results/exp0.result.txt") 8 | data[["hash_file"]] <- pretty_input(data[["hash_file"]]) 9 | data[["time"]] <- data[["time_per_full_query_in_us"]] #/ ( data[["check_unique_matches_full"]] +1 ) 10 | data[["query"]] <- data[["qry_file"]] 11 | d <- data 12 | 13 | # Check that for each (k, hash_file, hashes, queries)-combination the 14 | # checksum is the same 15 | 16 | dd <- d[c("index","k","hash_file","hashes","queries","check_unique_matches_full")] 17 | 18 | if ( sum(duplicated(unique(dd[-1])[-5])) > 0 ){ 19 | pdf("exp0.pdf") 20 | plot(c(),c(),xlim=c(0,1),ylim=c(0,0.1),axes=F,ylab=NA,xlab=NA,main="checksum error in exp0") 21 | dev.off() 22 | q() 23 | } 24 | 25 | # Generate plot 26 | plot <- ggplot(d, aes(x=factor(k), y=time, fill=index), 27 | ylab="Average query time [$\\mu s$]", 28 | xlab="$k$" 29 | ) + 30 | geom_bar(position='dodge',stat='identity') + 31 | scale_y_continuous(name="Average query time [$\\mu s$]", limits=c(1,2*max(d$time)), trans='log10') + 32 | scale_x_discrete("k", factor(d$k)) + 33 | facet_wrap(~hash_file) + 34 | scale_fill_discrete(guide = guide_legend(title="Index") ) + 35 | geom_text(aes(y=2*max(time), ymax=2*max(time), label=sprintf("(%.1f)",index_size_in_bytes/(8*hashes)), color=index), 36 | position = position_dodge(width=1), 37 | data=d, size=3 ) + 38 | scale_color_discrete(guide = guide_legend(title="Index") ) + 39 | geom_text(aes(y=time/1.5, ymax=max(time), label=sprintf("%d",round(time))), 40 | position = position_dodge(width=1), 41 | data=d, size=3,#,angle=-90 42 | color="white" 43 | ) 44 | pdf("exp0.pdf") 45 | print(plot) 46 | dev.off() 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /scripts/exp1.R: -------------------------------------------------------------------------------- 1 | source("../scripts/multi_index.R") 2 | source("../external/sdsl-lite/benchmark/basic_functions.R") 3 | library(ggplot2) 4 | 5 | options(scipen=999) 6 | 7 | data <- data_frame_from_key_value_pairs("../build/results/exp1.result.txt") 8 | 9 | data[["hash_file"]] <- pretty_input(data[["hash_file"]]) 10 | 11 | 12 | # Check that for each (k, hash_file, hashes, queries)-combination the 13 | # checksum is the same 14 | 15 | dd <- data[c("index","k","hash_file","hashes","queries","check_unique_matches_full")] 16 | 17 | if ( sum(duplicated(unique(dd[-1])[-5])) > 0 ){ 18 | pdf("exp1.pdf") 19 | plot(c(),c(),xlim=c(0,1),ylim=c(0,0.1),axes=F,ylab=NA,xlab=NA,main="checksum error in exp1") 20 | dev.off() 21 | q() 22 | } 23 | 24 | d <- data[c("hash_file","index","qry_file","b","k","time_per_full_query_in_us","time_per_search_query_in_us")] 25 | 26 | colnames(d) <- c("Input","Index","Queries","b","e","time","stime") 27 | 28 | d[["etime"]] <- d[["time"]]-d[["stime"]] 29 | d[["Candidates"]] <- data[["candidates_per_query"]] 30 | 31 | d[["Queries"]] <- basename(as.character(data[["qry_file"]])) 32 | d[["Queries"]] <- gsub("\\.query$","",d[["Queries"]]) 33 | d[["Queries"]] <- gsub("(.*\\.)(.*)","\\2",d[["Queries"]]) 34 | d <- cbind(d,"size_per_key" = data[["index_size_in_bytes"]]/data[["hashes"]]) 35 | 36 | d[["Index"]] <- gsub("mi_(.*)_red$","mi_red_\\1",d[["Index"]]) 37 | d[["Method"]] <- gsub(".*_(.*)$","\\1",d[["Index"]]) 38 | d[["Index"]] <- gsub("(.*)_(.*)$","\\1",d[["Index"]]) 39 | 40 | dd <- d[c("size_per_key","Index","Queries","e","Method","time","stime","etime","Input")] 41 | ddd <- dd 42 | 43 | ddd[["Index"]] <- gsub("_","\\\\_",ddd[["Index"]]) 44 | ddd[["Method"]] <- gsub("bv","succinct",ddd[["Method"]]) 45 | ddd[["Method"]] <- gsub("bs","bin. search",ddd[["Method"]]) 46 | 47 | d4 <- ddd[ddd$e %in% c(3,4),] 48 | d4 <- subset(d4,d4$Method %in% c("bin. search","succinct")) 49 | d4 <- d4[grep("red",d4$Index),] 50 | 51 | d4$stimepercent <- d4[["stime"]]/d4[["time"]]*100 52 | 53 | d4c <- rbind( 54 | cbind(d4,"part"=rep("searching",nrow(d4)),"parttime"=d4$stime) , 55 | cbind(d4,"part"=rep("checking",nrow(d4)),"parttime"=d4$etime) 56 | ) 57 | 58 | d4c$k <- gsub("(.*)","$k=\\1$",as.character(d4c$e)) 59 | d4c$check_percentage <- sprintf("(%.0f \\%%)", d4c$stime/d4c$time*100) 60 | d4c[d4c$part=="checking",]$check_percentage <- "" 61 | 62 | plot <- qplot(Method, 63 | data=d4c, 64 | fill=part, 65 | weight=parttime, 66 | xlab="Search method", 67 | ylab="Average query time [$\\mu s$]" 68 | ) + 69 | facet_grid(k ~ Input) + 70 | scale_fill_discrete(guide = guide_legend(title = "Query phase", reverse = TRUE)) + 71 | geom_text(aes(y=parttime + 0.03*max(time), ymax=max(parttime), label=check_percentage), 72 | data=d4c, size=4, 73 | color= scales::hue_pal()(3)[1], 74 | vjust=0 75 | ) 76 | 77 | 78 | pdf("exp1.pdf") 79 | print(plot) 80 | dev.off() 81 | -------------------------------------------------------------------------------- /scripts/extract_tweets.py: -------------------------------------------------------------------------------- 1 | # Script used to extract text from a collecion of tweets. 2 | # Tokenized and stemmed tweets are collected in a .gz file, one tweet per row. 3 | # We also use a binary file to store tweets ids, 8 bytes each. 4 | 5 | import sys 6 | import codecs 7 | import struct 8 | import json,re,os,sys 9 | import gzip 10 | from nltk.tokenize import word_tokenize 11 | from unidecode import unidecode 12 | from nltk.stem.snowball import EnglishStemmer 13 | from nltk.corpus import stopwords 14 | STOPWORDS = set(stopwords.words('english')) 15 | 16 | emoticons_str = r""" 17 | (?: 18 | [:=;] # Eyes 19 | [oO\-]? # Nose (optional) 20 | [D\)\]\(\]/\\OpP] # Mouth 21 | )""" 22 | 23 | regex_str = [ 24 | emoticons_str, 25 | r'<[^>]+>', # HTML tags 26 | r'(?:@[\w_]+)', # @-mentions 27 | r"(?:\#+[\w_]+[\w\'_\-]*[\w_]+)", # hash-tags 28 | r'http[s]?://(?:[a-z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-f][0-9a-f]))+', # URLs 29 | r'(?:(?:\d+,?)+(?:\.?\d+)?)', # numbers 30 | r"(?:[a-z][a-z'\-_]+[a-z])", # words with - and ' 31 | r'(?:[\w_]+)', # other words 32 | r'(?:\S)' # anything else 33 | ] 34 | 35 | tokens_re = re.compile(r'('+'|'.join(regex_str)+')', re.VERBOSE | re.IGNORECASE) 36 | emoticon_re = re.compile(r'^'+emoticons_str+'$', re.VERBOSE | re.IGNORECASE) 37 | 38 | def filter_words(words): 39 | return filter(lambda w: len(w)>=3 and w not in STOPWORDS, words) 40 | 41 | def stem_words(words): 42 | s = EnglishStemmer() 43 | return map(s.stem, words) 44 | 45 | def normalize_words(words): 46 | return map(lambda w: unidecode(w.lower()), words) 47 | 48 | print "Usage:", sys.argv[0], "Directory OutputFile" 49 | 50 | def tokenize(s): 51 | return tokens_re.findall(s) 52 | 53 | def preprocess(s, lowercase=False): 54 | tokens = tokenize(s) 55 | if lowercase: 56 | tokens = [token if emoticon_re.search(token) else token.lower() for token in tokens] 57 | filtered_t = filter_words(normalize_words(tokens)) 58 | stemmed = stem_words(filtered_t) 59 | return stemmed 60 | 61 | fids = open(sys.argv[2]+".ids", "wb") 62 | 63 | 64 | with gzip.GzipFile(sys.argv[2]+".tweets.gz", 'w') as myzip: 65 | for root, dirs, files in os.walk(sys.argv[1]): 66 | for file in files: 67 | print file 68 | if 'json.gz' in file: 69 | print "Processing file: ", file 70 | zipfr = gzip.open("{}".format(os.path.join(root, file)), mode='rb') 71 | for line in zipfr: 72 | tweet = json.loads(line) 73 | tokens = preprocess(tweet['text'],True) 74 | if len(tokens) < 4: 75 | continue 76 | text = " ".join(tokens) 77 | # print text 78 | myzip.write(text + "\n") 79 | fids.write(struct.pack(' 29 | 30 | #if 0 31 | // Allow for Posix thread synchronization primitives 32 | #define USE_MUTEX 33 | #endif 34 | 35 | #define CONSOLE_PROGRAM 36 | // Has the program a console output facility? 37 | // Allow init_general() to dump error messages onto the console 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | // Tell the compiler of an unused parameter 45 | #ifdef __GNUC__ 46 | #define UNUSED(x) x __attribute__((unused)) 47 | #else 48 | #define UNUSED(x) x 49 | #endif 50 | 51 | #ifndef mycall 52 | // Define your favourite function options here 53 | // #define mycall __attribute__((fastcall)) 54 | // #define mycall __attribute__((regparm(3))) 55 | #define mycall 56 | #endif 57 | 58 | #if 0 59 | 60 | // Types via stdint.h 61 | #include 62 | 63 | // Fixed size integer types 64 | typedef uint8_t t_8u; 65 | typedef uint16_t t_16u; 66 | typedef uint32_t t_32u; 67 | typedef uint64_t t_64u; 68 | // typedef __uint128_t t_128u; // not a standard type 69 | 70 | // General integer type; >= 16 bit 71 | typedef int_fast16_t t_int; 72 | // General unsigned integer type; >= 16 bit 73 | typedef uint_fast16_t t_uint; 74 | // General integer type; >= 32 bit 75 | typedef int_fast32_t t_longint; 76 | 77 | typedef char t_char; 78 | 79 | // The boolean type should be predefined as bool (C++) or _Bool (C99, stdbool.h) 80 | // Since there is no usable standard way, we do it on our own. Sigh! 81 | typedef unsigned char t_bool; // Poor replacement for boolean type 82 | #define false (0!=0) 83 | #define true (0==0) 84 | 85 | #else 86 | 87 | // Types self-made 88 | // Fixed size integer types 89 | typedef signed char t_8s; 90 | typedef unsigned char t_8u; 91 | typedef signed short int t_16s; 92 | typedef unsigned short int t_16u; 93 | typedef signed int t_32s; 94 | typedef unsigned int t_32u; 95 | typedef signed long long int t_64s; 96 | typedef unsigned long long int t_64u; 97 | // typedef __int128_t t_128s; // not a standard type 98 | // typedef __uint128_t t_128u; // not a standard type 99 | 100 | // General integer type; >= 16 bit 101 | typedef int t_int; 102 | // General unsigned integer type; >= 16 bit 103 | typedef unsigned int t_uint; 104 | // General integer type; >= 32 bit 105 | typedef int t_longint; 106 | 107 | typedef char t_char; 108 | 109 | // The boolean type; might be predefined 110 | typedef unsigned char t_bool; // Poor replacement for boolean type 111 | #ifndef false 112 | #define false ((t_bool)(0!=0)) 113 | #endif 114 | #ifndef true 115 | #define true ((t_bool)(0==0)) 116 | #endif 117 | 118 | #endif 119 | 120 | #ifdef USE_MUTEX 121 | #include 122 | extern pthread_mutex_t global_mutex; 123 | // Access to a global general purpose mutex 124 | #define GLOBAL_MUTEX_WAIT pthread_mutex_lock(&global_mutex); 125 | #define GLOBAL_MUTEX_SIGNAL pthread_mutex_unlock(&global_mutex); 126 | // Memory fence / barrier 127 | #define SYNC_SYNCHRONIZE __sync_synchronize(); 128 | #define SYNC_SYNCHRONIZE_LOAD __sync_synchronize(); 129 | #define SYNC_SYNCHRONIZE_STORE __sync_synchronize(); 130 | #else 131 | #define GLOBAL_MUTEX_WAIT 132 | #define GLOBAL_MUTEX_SIGNAL 133 | #define SYNC_SYNCHRONIZE 134 | #define SYNC_SYNCHRONIZE_LOAD 135 | #define SYNC_SYNCHRONIZE_STORE 136 | #endif 137 | 138 | mycall t_int random_int(t_int x); 139 | mycall t_bool init_general(); 140 | 141 | #endif 142 | 143 | // eof. 144 | -------------------------------------------------------------------------------- /scripts/input_info.R: -------------------------------------------------------------------------------- 1 | require("ggplot2") 2 | 3 | if ( !exists( "tikzDeviceLoaded" ) ){ 4 | require(tikzDevice) #if not installed call install.packages("tikzDevice", repos="http://R-Forge.R-project.org") 5 | options(tikzLatexPackages = c(getOption('tikzLatexPackages'), 6 | paste("\\input{",getwd(),"/../paper/defs.tex}",sep=""))) 7 | tikzDeviceLoaded = T 8 | } 9 | 10 | args <- commandArgs(trailingOnly = TRUE) 11 | info_file <- args[1] 12 | 13 | files <- list.files("../data/", ".distr") 14 | files <- gsub(".data.distr$","",files) 15 | 16 | df <- data.frame("x" = seq(0,64), "y" = choose(64, seq(0,64))/2**64, "Input" = "uniform" ) 17 | 18 | for(f in files){ 19 | d <- read.csv(paste("../data/",f,".data.distr",sep=""),header=F) 20 | colnames(d) <- c("x","y") 21 | d["y"] <- d["y"]/sum(d["y"]) 22 | d["Input"] <- rep(f,65) 23 | df <- rbind(df, d) 24 | } 25 | 26 | df[["Input"]] <- gsub("Clueweb09-Full.SimHash","\\\\cluesim",df[["Input"]]) 27 | df[["Input"]] <- gsub("Clueweb09-Full.OddSketch","\\\\clueodd",df[["Input"]]) 28 | df[["Input"]] <- gsub("lsh_sift_64.hash","\\\\siftlsh",df[["Input"]]) 29 | df[["Input"]] <- gsub("mlh_sift_64.hash","\\\\siftmlh",df[["Input"]]) 30 | 31 | tikz('input_info.tex', standAlone = FALSE, width=3.5, height=3.2) 32 | 33 | plot <- qplot(x,y,data=df, 34 | color=Input, 35 | # shape=Input, 36 | xlab="Hamming weight", 37 | ylab="Fraction of keys" 38 | # ,log="y" 39 | ) + theme_bw(base_size = 12, base_family = "") + 40 | theme(legend.position="top") + geom_line() + 41 | guides(color=guide_legend(ncol=3)) + 42 | scale_color_discrete("") + 43 | scale_x_continuous(breaks=c(0,16,32,48,64)) 44 | 45 | print(plot) 46 | 47 | dev.off() 48 | -------------------------------------------------------------------------------- /scripts/multi_index.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | pretty_input <- function( input_col ) { 5 | input_col <- basename(as.character(input_col)) 6 | input_col <- gsub(".data$","",input_col) 7 | input_col <- gsub("Clueweb09-Full.SimHash","\\\\cluesim",input_col) 8 | input_col <- gsub("Clueweb09-Full.OddSketch","\\\\clueodd",input_col) 9 | input_col <- gsub("lsh_sift_64.hash","\\\\siftlsh",input_col) 10 | input_col <- gsub("mlh_sift_64.hash","\\\\siftmlh",input_col) 11 | return (input_col) 12 | } 13 | 14 | pretty_query <- function ( query_col ) { 15 | query_col <- basename(as.character(query_col)) 16 | query_col <- gsub("\\.query$","",query_col) 17 | query_col <- gsub("(.*\\.)(.*)","\\2",query_col) 18 | if ( sum( data$query %in% c("existing","real","random") ) == 0 ){ 19 | query_col <- "query" 20 | } 21 | return (query_col) 22 | } 23 | -------------------------------------------------------------------------------- /scripts/perm_b64.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "general.h" 4 | 5 | ////// 6 | // Intro 7 | 8 | // Some bit hacks and permutations 9 | 10 | // (c) 2011..2014 by Jasper L. Neumann 11 | // www.sirrida.de / programming.sirrida.de 12 | // E-Mail: info@sirrida.de 13 | 14 | // Granted to the public domain 15 | // First version: 2011-02 16 | // Last change: 2012-09-19 17 | 18 | // Here the adaptations are made to enable perm_bas.c 19 | // to be compiled for a word size of 64 bit. 20 | // perm_bas.c must be included afterwards. 21 | 22 | 23 | ////// 24 | // Our base for the bit hacks 25 | 26 | #define ld_bits 6 27 | // log_2 of used bit size (here: 64 bit) 28 | #define ld_bits_factorial (1*2*3*4*5*6) 29 | 30 | typedef uint64_t t_bits; // 1< a_bfly_mask[0] 64 | 0x1111111111111111, // 2 65 | 0x0101010101010101, // 3 66 | 0x0001000100010001, // 4 67 | 0x0000000100000001, // 5 68 | 0x0000000000000001}; // 6 69 | 70 | const t_bits a_bfly_hi[]={ 71 | // Inverted a_bfly_mask with only highest bit of runs set, index off by 1. 72 | // = (a_bfly_lo[] >> 1)+hi_bit 73 | // = a_bfly_lo[] << ((1 << sw)-1) 74 | // = a_bfly_lo[] ror 1 75 | 0xffffffffffffffff, // 0 76 | 0xaaaaaaaaaaaaaaaa, // 1 => ~a_bfly_mask[0] 77 | 0x8888888888888888, // 2 78 | 0x8080808080808080, // 3 79 | 0x8000800080008000, // 4 80 | 0x8000000080000000, // 5 81 | 0x8000000000000000}; // 6 82 | 83 | const t_bits a_sw_base[]={ 84 | // 0..ld_bits 85 | // (lo_bit << (1 << sw)) - 1; correct even for sw=ld_bits 86 | 0x0000000000000001, // 0 87 | 0x0000000000000003, // 1 88 | 0x000000000000000f, // 2 89 | 0x00000000000000ff, // 3 90 | 0x000000000000ffff, // 4 91 | 0x00000000ffffffff, // 5 92 | 0xffffffffffffffff}; // 6 93 | 94 | const t_bits a_shuffle_mask[]={ 95 | // 0..ld_bits-2 96 | // For [un]shuffle 97 | // a_shuffle_mask[i] = a_bfly_mask[i+1] & ~a_bfly_mask[i] 98 | // => bit_index_swap 99 | 0x2222222222222222, // 0 100 | 0x0c0c0c0c0c0c0c0c, // 1 101 | 0x00f000f000f000f0, // 2 102 | 0x0000ff000000ff00, // 3 103 | 0x00000000ffff0000}; // 4 104 | 105 | const t_bits a_prim_swap[]={ 106 | // 0..ld_bits-1 107 | // For prim_swap 108 | // Sum must fill all but highest bit 109 | // a_prim_swap[i] = a_bfly_lo[i+1] << ((1 << i) - 1) 110 | 0x5555555555555555, // 0 111 | 0x2222222222222222, // 1 112 | 0x0808080808080808, // 2 113 | 0x0080008000800080, // 3 114 | 0x0000800000008000, // 5 115 | 0x0000000080000000}; // 6 116 | 117 | // eof. 118 | -------------------------------------------------------------------------------- /scripts/perm_bas.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "general.h" 4 | 5 | ////// 6 | // Intro 7 | 8 | // Some bit hacks and permutations 9 | 10 | // (c) 2011..2014 by Jasper L. Neumann 11 | // www.sirrida.de / programming.sirrida.de 12 | // E-Mail: info@sirrida.de 13 | 14 | // Granted to the public domain 15 | // First version: 2011-02 16 | // Last change: 2014-09-25 17 | 18 | // This is a collection of some bit fiddling procedures. 19 | // All routines act on a word size depending on the 20 | // steering file included before (perm_b*.c). 21 | // These routines naturally adapt to several word (working) sizes 22 | // of a bit length of a power of 2 (as long as the compiler allows). 23 | 24 | // Auxiliary routines 25 | // - odd 26 | // - gcd 27 | // - mul_inv 28 | // - rol 29 | // - rol_lo 30 | // - rolc_lo 31 | // - gray_code 32 | // - inv_gray_code 33 | // - nr_1bits 34 | // - nr_leading_0bits 35 | // - nr_trailing_0bits 36 | // - is_contiguous_1bits 37 | // - tbm 38 | // - blend 39 | // - simd_odd 40 | // - bit_permute_step 41 | // - bit_permute_step_simple 42 | // - bit_permute_step2 43 | // - identity_perm 44 | // - invert_perm 45 | // - random_perm 46 | // - used_source_bits 47 | // - used_target_bits 48 | // Bit index operations 49 | // - bit_index_complement 50 | // - bit_index_swap 51 | // - bit_index_swap_complement 52 | // - bit_index_ror 53 | // - transpose 54 | // - shuffle_power 55 | // - unshuffle_power 56 | // - permute_bpc 57 | // - invert_bpc 58 | // Generalized Bit Reversal 59 | // - general_reverse_bits 60 | // - bswap 61 | // Swap by primitives 62 | // - prim_swap 63 | // Shuffle and unshuffle 64 | // - shuffle 65 | // - unshuffle 66 | // Compress and expand 67 | // * Compress bit masks 68 | // - compress_mask_right 69 | // - compress_mask_left 70 | // - compress_mask 71 | // * Generate configuration 72 | // - gen_ce_right 73 | // - gen_ce_left 74 | // * Usage 75 | // - apply_compress_right 76 | // - apply_compress_left 77 | // - apply_expand_right 78 | // - apply_expand_left 79 | // * Compound 80 | // - compress_right 81 | // - compress_left 82 | // - compress 83 | // - expand_right 84 | // - expand_left 85 | // - expand 86 | // Butterfly network 87 | // - butterfly 88 | // - bfly 89 | // - ibfly 90 | // - bfly_parity 91 | // Rotate via butterfly 92 | // * Generate configuration 93 | // - gen_frot 94 | // - gen_vrot 95 | // * Compound 96 | // - fror_bfly 97 | // - frol_bfly 98 | // - frot_bfly 99 | // - vror_bfly 100 | // - vrol_bfly 101 | // - vrot_bfly 102 | // SWAR rotate 103 | // * frol 104 | // * fror 105 | // * frot 106 | // * frolc 107 | // * frorc 108 | // * frotc 109 | // * vrol 110 | // * vror 111 | // * vrot 112 | // Compress/expand-flip via butterfly 113 | // * Generate configuration 114 | // - gen_cef_right 115 | // - gen_cef_left 116 | // * Compound 117 | // - compress_flip_right 118 | // - compress_flip_left 119 | // - compress_flip 120 | // - expand_flip_right 121 | // - expand_flip_left 122 | // - expand_flip 123 | // Omega/flip 124 | // - omega 125 | // - flip 126 | // Permutations via Benes network 127 | // - gen_benes_ex 128 | // - gen_benes 129 | // - benes_fwd 130 | // - benes_bwd 131 | // - benes_fwd_ex 132 | // - benes_bwd_ex 133 | // - benes_parity 134 | 135 | 136 | ////// 137 | // Random replacement 138 | 139 | t_bits my_randseed; 140 | 141 | mycall t_bits random_bits(); 142 | 143 | 144 | ////// 145 | // Auxiliary stuff 146 | 147 | typedef enum {right,left} t_direction; 148 | 149 | mycall t_bool odd(t_int x); 150 | mycall t_longint gcd(t_longint a, t_longint b); 151 | mycall t_bits mul_inv(t_bits x); 152 | mycall t_bits rol(t_bits x, t_int rot); 153 | mycall t_bits rol_lo(t_bits x, t_int rot, t_subword sw); 154 | mycall t_bits rolc_lo(t_bits x, t_int rot, t_subword sw); 155 | mycall t_bits gray_code(t_bits x); 156 | mycall t_bits inv_gray_code(t_bits x); 157 | mycall t_int nr_1bits(t_bits x); 158 | mycall t_int nr_leading_0bits(t_bits x); 159 | mycall t_int nr_trailing_0bits(t_bits x); 160 | mycall t_bool is_contiguous_1bits(t_bits x); 161 | mycall t_bits tbm(t_bits x, t_int mode); 162 | 163 | mycall t_bits blend(t_bits m, t_bits x, t_bits y); 164 | mycall t_bits simd_odd(t_bits x, t_subword sw); 165 | 166 | mycall t_bits bit_permute_step(t_bits x, t_bits m, t_uint shift); 167 | mycall t_bits bit_permute_step_simple(t_bits x, t_bits m, t_uint shift); 168 | mycall void bit_permute_step2(t_bits* x1, t_bits* x2, t_bits m, t_uint shift); 169 | 170 | mycall void identity_perm(ta_index tgt); 171 | mycall void invert_perm(const ta_index src, ta_index tgt); 172 | mycall void random_perm(ta_index tgt); 173 | mycall t_bits used_source_bits(const ta_index perm); 174 | mycall t_bits used_target_bits(const ta_index perm); 175 | 176 | 177 | ////// 178 | // Bit index operations 179 | 180 | mycall t_bits bit_index_complement(t_bits x, t_subword k); 181 | mycall t_bits bit_index_swap(t_bits x, t_subword j, t_subword k); 182 | mycall t_bits bit_index_swap_complement(t_bits x, t_subword j, t_subword k); 183 | 184 | mycall t_bits bit_index_ror(t_bits x, t_subword ofs, t_subword field, t_int rot); 185 | mycall t_bits transpose(t_bits x, t_subword ld_fields, t_subword ld_col, t_subword ld_row); 186 | mycall t_bits shuffle_power(t_bits x, t_subword sw1, t_subword sw2, t_int pwr); 187 | mycall t_bits unshuffle_power(t_bits x, t_subword sw1, t_subword sw2, t_int pwr); 188 | 189 | mycall t_bits permute_bpc(t_bits x, const ta_subword tgt, t_subword_set k); 190 | mycall void invert_bpc(const ta_subword src, t_subword_set src_k, ta_subword tgt, t_subword_set* tgt_k); 191 | 192 | 193 | ////// 194 | // Generalized Bit Reversal 195 | 196 | mycall t_bits general_reverse_bits(t_bits x, t_int k); 197 | mycall t_bits bswap(t_bits x); 198 | 199 | 200 | ////// 201 | // Swap by primitives 202 | 203 | mycall t_bits prim_swap(t_bits x, t_bits m); 204 | 205 | 206 | ////// 207 | // Shuffle and unshuffle 208 | 209 | mycall t_bits shuffle(t_bits x, t_subword sw1, t_subword sw2); 210 | mycall t_bits unshuffle(t_bits x, t_subword sw1, t_subword sw2); 211 | 212 | 213 | ////// 214 | // A "class" for butterfly and other operations 215 | 216 | typedef struct { 217 | // This structure is used to hold the configuration of 218 | // butterfly-based operations as well as compress and expand. 219 | 220 | t_bits cfg[ld_bits]; // butterfly configuration 221 | t_bits mask; // saved mask, for compress/expand 222 | 223 | // Here is sketched how to convert this to a class: 224 | // Include all the generator and usage functions as private methods 225 | // and replace the parameter self by the implicit object pointer this. 226 | // Add the many compound routines. 227 | // Remove the name suffix for all methods. 228 | // If you want to cache the configuration, add here: 229 | // kind: the generator kind 230 | // enum (initialized, frot, vrot, ce_right, ce_left, cef_right, cef_left) 231 | // sw: the used subword size (t_subword) 232 | // Add an initializer/constructor which sets kind to initialized. 233 | // The generator routines must set the keys (kind, mask, sw). 234 | // The compound routines check the cached keys (kind, mask, sw); 235 | // if not equal, call the generator routine and update the configuration; 236 | // finally they call the usage routine. 237 | } tr_bfly; 238 | 239 | 240 | ////// 241 | // Compress and expand 242 | 243 | ////// 244 | // Compress and expand: Compress bit masks 245 | 246 | mycall t_bits compress_mask_right(t_bits m, t_subword sw); 247 | mycall t_bits compress_mask_left(t_bits m, t_subword sw); 248 | mycall t_bits compress_mask(t_bits m, t_subword sw, t_direction d); 249 | 250 | 251 | ////// 252 | // Compress and expand: Generate configuration 253 | 254 | mycall void gen_ce_right(tr_bfly* self, t_bits m, t_subword sw); 255 | mycall void gen_ce_left(tr_bfly* self, t_bits m, t_subword sw); 256 | 257 | 258 | ////// 259 | // Compress and expand: Usage 260 | 261 | mycall t_bits apply_compress_right(const tr_bfly* self, t_bits x); 262 | mycall t_bits apply_compress_left(const tr_bfly* self, t_bits x); 263 | mycall t_bits apply_expand_right(const tr_bfly* self, t_bits x); 264 | mycall t_bits apply_expand_left(const tr_bfly* self, t_bits x); 265 | 266 | 267 | ////// 268 | // Compress and expand: Compound 269 | 270 | mycall t_bits compress_right(t_bits x, t_bits m, t_subword sw); 271 | mycall t_bits compress_left(t_bits x, t_bits m, t_subword sw); 272 | mycall t_bits compress(t_bits x, t_bits m, t_subword sw, t_direction d); 273 | mycall t_bits expand_right(t_bits x, t_bits m, t_subword sw); 274 | mycall t_bits expand_left(t_bits x, t_bits m, t_subword sw); 275 | mycall t_bits expand(t_bits x, t_bits m, t_subword sw, t_direction d); 276 | 277 | 278 | ////// 279 | // Butterfly network 280 | 281 | mycall t_bits butterfly(t_bits x, t_bits m, t_subword sw); 282 | mycall t_bits bfly(const tr_bfly* self, t_bits x); 283 | mycall t_bits ibfly(const tr_bfly* self, t_bits x); 284 | mycall t_bool bfly_parity(const tr_bfly* self); 285 | 286 | 287 | ////// 288 | // Rotate via butterfly 289 | 290 | ////// 291 | // Rotate via butterfly: Generate configuration 292 | 293 | mycall void gen_frot(tr_bfly* self, t_int rot, t_subword sw); 294 | mycall void gen_vrot(tr_bfly* self, t_bits rot, t_subword sw); 295 | 296 | 297 | ////// 298 | // Rotate via butterfly: Compound 299 | 300 | mycall t_bits fror_bfly(t_bits x, t_int rot, t_subword sw); 301 | mycall t_bits frol_bfly(t_bits x, t_int rot, t_subword sw); 302 | mycall t_bits frot_bfly(t_bits x, t_int rot, t_subword sw, t_direction d); 303 | 304 | mycall t_bits vror_bfly(t_bits x, t_bits rot, t_subword sw); 305 | mycall t_bits vrol_bfly(t_bits x, t_bits rot, t_subword sw); 306 | mycall t_bits vrot_bfly(t_bits x, t_bits rot, t_subword sw, t_direction d); 307 | 308 | mycall t_bits frol(t_bits x, t_int rot, t_subword sw); 309 | mycall t_bits fror(t_bits x, t_int rot, t_subword sw); 310 | mycall t_bits frot(t_bits x, t_int rot, t_subword sw, t_direction d); 311 | 312 | mycall t_bits frolc(t_bits x, t_int rot, t_subword sw); 313 | mycall t_bits frorc(t_bits x, t_int rot, t_subword sw); 314 | mycall t_bits frotc(t_bits x, t_int rot, t_subword sw, t_direction d); 315 | 316 | mycall t_bits vrol(t_bits x, t_bits rot, t_subword sw); 317 | mycall t_bits vror(t_bits x, t_bits rot, t_subword sw); 318 | mycall t_bits vrot(t_bits x, t_bits rot, t_subword sw, t_direction d); 319 | 320 | 321 | ////// 322 | // Compress/expand-flip via butterfly 323 | 324 | ////// 325 | // Compress/expand-flip via butterfly: Generate configuration 326 | 327 | mycall void gen_cef_right(tr_bfly* self, t_bits m, t_subword sw); 328 | mycall void gen_cef_left(tr_bfly* self, t_bits m, t_subword sw); 329 | 330 | 331 | ////// 332 | // Compress/expand-flip via butterfly: Compound 333 | 334 | mycall t_bits compress_flip_right(t_bits x, t_bits m, t_subword sw); 335 | mycall t_bits compress_flip_left(t_bits x, t_bits m, t_subword sw); 336 | mycall t_bits compress_flip(t_bits x, t_bits m, t_subword sw, t_direction d); 337 | mycall t_bits expand_flip_right(t_bits x, t_bits m, t_subword sw); 338 | mycall t_bits expand_flip_left(t_bits x, t_bits m, t_subword sw); 339 | mycall t_bits expand_flip(t_bits x, t_bits m, t_subword sw, t_direction d); 340 | 341 | 342 | ////// 343 | // Omega/flip 344 | 345 | mycall t_bits omega(t_bits x, t_bits m, t_subword sw); 346 | mycall t_bits flip(t_bits x, t_bits m, t_subword sw); 347 | 348 | 349 | ////// 350 | // Permutations via Benes network 351 | 352 | typedef struct { 353 | tr_bfly b1,b2; 354 | } tr_benes; 355 | 356 | mycall void gen_benes_ex(tr_benes* self, const ta_index c_tgt, const ta_subword a_stage); 357 | mycall void gen_benes(tr_benes* self, const ta_index c_tgt); 358 | mycall t_bits benes_fwd(const tr_benes* self, t_bits x); 359 | mycall t_bits benes_bwd(const tr_benes* self, t_bits x); 360 | mycall t_bits benes_fwd_ex(const tr_benes* self, t_bits x, const ta_subword a_stage); 361 | mycall t_bits benes_bwd_ex(const tr_benes* self, t_bits x, const ta_subword a_stage); 362 | mycall t_bool benes_parity(const tr_benes* self); 363 | 364 | // eof. 365 | -------------------------------------------------------------------------------- /scripts/perm_bxx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "general.h" 4 | 5 | ////// 6 | // Intro 7 | 8 | // Some bit hacks and permutations 9 | 10 | // (c) 2011..2014 by Jasper L. Neumann 11 | // www.sirrida.de / programming.sirrida.de 12 | // E-Mail: info@sirrida.de 13 | 14 | // Granted to the public domain 15 | // First version: 2013-02-15 16 | // Last change: 2013-02-15 17 | 18 | // Local include file to define some derived constants. 19 | 20 | #define bits (1 << ld_bits) 21 | #define lo_bit (t_bits)(1) 22 | #define hi_bit (lo_bit << (bits-1)) 23 | #define all_bits (t_bits)(-1) 24 | 25 | typedef t_int t_bit_index; // subrange 0..bits-1; -1:don't care 26 | // used to specify bit indexes 27 | 28 | typedef t_bit_index t_subword_set; 29 | // used to specify a set of t_subword 30 | 31 | #define no_index ((t_bit_index)(-1)) 32 | // don't care / wildcard 33 | 34 | typedef t_uint t_subword; // subrange 0..ld_bits 35 | // used to specify log_2 of subword size, see a_subword 36 | 37 | typedef t_subword ta_subword[ld_bits]; 38 | // bit index indexes 39 | 40 | typedef t_bit_index ta_index[bits]; 41 | // bit indexes 42 | 43 | const t_char* a_subword[]={ 44 | // sw bits 45 | "Bit", // 0 1 46 | "Nyp", // 1 2, name stolen from Donald E. Knuth 47 | "Nibble", // 2 4 48 | "Byte", // 3 8 49 | "Word", // 4 16 50 | "DWord", // 5 32 51 | "QWord", // 6 64 52 | "OWord", // 7 128 53 | "YWord", // 8 256 54 | "ZWord"}; // 9 512 55 | 56 | -------------------------------------------------------------------------------- /src/bench_scan.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "sdsl/int_vector.hpp" 4 | 5 | using timer = std::chrono::high_resolution_clock; 6 | 7 | template 8 | struct scanning { 9 | uint64_t sum; 10 | 11 | scanning(size_t n, size_t m, size_t runs) { 12 | sdsl::int_vector keys(n); 13 | 14 | for(size_t i = 0; i < n; ++i) 15 | keys[i] = i; 16 | 17 | 18 | std::vector queries; 19 | queries.reserve(runs); 20 | srand(42); // make everything dterministic 21 | for(size_t i = 0; i < runs; ++i) 22 | queries.push_back(rand() % (n-m-1)); 23 | 24 | auto start = timer::now(); 25 | sum = 0; 26 | for(auto x: queries) { 27 | auto begin = keys.begin() + x; 28 | auto end = keys.begin() + x + m; 29 | for (auto it = begin; it != end; ++it) 30 | sum += *it; 31 | } 32 | auto stop = timer::now(); 33 | std::cout << BITS << " " << std::chrono::duration_cast(stop-start).count()/(double)(runs*m) << " " << sum << std::endl; 34 | } 35 | }; 36 | 37 | template 38 | void scanning_range(size_t n, size_t m, size_t runs){ 39 | if ( B0 <= B1 ) { 40 | { scanning(n, m, runs); } 41 | scanning_range(n, m, runs); 42 | } 43 | } 44 | 45 | int main() {//int argc, char** argv) { 46 | 47 | size_t runs = 100000; 48 | size_t n = (1ULL << 30); 49 | size_t m = (1ULL << 14); 50 | 51 | std::cout << "# number of keys " << n << " run length " << m << " number of runs " << runs << std::endl; 52 | std::cout << "# bits,nanosecs_per_key" << std::endl; 53 | scanning_range<8, 64>(n, m, runs); 54 | } 55 | -------------------------------------------------------------------------------- /src/bench_scan2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "sdsl/int_vector.hpp" 4 | #include "multi_idx/perm.hpp" 5 | 6 | using timer = std::chrono::high_resolution_clock; 7 | 8 | template 9 | struct scanning { 10 | uint64_t sum; 11 | 12 | scanning(size_t n, size_t m, size_t runs) { 13 | sdsl::int_vector keys(n); 14 | 15 | for(size_t i = 0; i < n; ++i) 16 | keys[i] = i; 17 | 18 | std::vector queries; 19 | queries.reserve(runs); 20 | srand(42); // make everything dterministic 21 | for(size_t i = 0; i < runs; ++i) 22 | queries.push_back(rand() % (n-m-1)); 23 | 24 | auto start = timer::now(); 25 | sum = 0; 26 | for(auto x: queries) { 27 | auto begin = keys.begin() + x; 28 | auto end = keys.begin() + x + m; 29 | for (auto it = begin; it != end; ++it) 30 | if ( sdsl::bits::cnt(*it)%2 == BITS%2 ) { 31 | sum += *it; 32 | } 33 | } 34 | auto stop = timer::now(); 35 | std::cout << BITS << " " << std::chrono::duration_cast(stop-start).count()/(double)(runs*m) << " " << sum << std::endl; 36 | } 37 | }; 38 | 39 | template 40 | void scanning_range(size_t n, size_t m, size_t runs){ 41 | if ( B0 <= B1 ) { 42 | { scanning(n, m, runs); } 43 | scanning_range(n, m, runs); 44 | } 45 | } 46 | 47 | int main() {//int argc, char** argv) { 48 | 49 | size_t runs = 100000; 50 | size_t n = (1ULL << 30); 51 | size_t m = (1ULL << 14); 52 | 53 | std::cout << "# number of keys " << n << " run length " << m << " number of runs " << runs << std::endl; 54 | std::cout << "# bits,nanosecs_per_key" << std::endl; 55 | scanning_range<8, 64>(n, m, runs); 56 | } 57 | -------------------------------------------------------------------------------- /src/bench_scan3.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "sdsl/int_vector.hpp" 4 | #include "multi_idx/perm.hpp" 5 | 6 | using timer = std::chrono::high_resolution_clock; 7 | 8 | template 9 | struct scanning { 10 | uint64_t sum; 11 | 12 | scanning(size_t n, size_t m, size_t runs) { 13 | sdsl::int_vector<32> keys(n); 14 | uint64_t mask = n/2; 15 | 16 | for(size_t i = 0; i < n; ++i) 17 | keys[i] = i; 18 | 19 | std::vector queries; 20 | queries.reserve(runs); 21 | srand(42); // make everything deterministic 22 | for(size_t i = 0; i < runs; ++i) 23 | queries.push_back(rand() % (n-m-1)); 24 | 25 | auto start = timer::now(); 26 | sum = 0; 27 | size_t iter = 0; 28 | for(auto x: queries) { 29 | auto it = keys.begin() + x; 30 | auto end = keys.begin() + x + m; 31 | 32 | if(STEP == 1) { 33 | for (; it != end; ++it) { 34 | if ( sdsl::bits::cnt(*it^mask) <= 3 ) 35 | sum += perm<4,1>::mi_rev_permute[0](*it); 36 | iter += 1; 37 | } 38 | } else { // Skip STEP elements every STEP elements 39 | while(it < end) { 40 | auto end2 = it + STEP; 41 | for (; it != end2; ++it) { 42 | if ( sdsl::bits::cnt(*it^mask) <= 3 ) 43 | sum += perm<4,1>::mi_rev_permute[0](*it); 44 | iter += 1; 45 | } 46 | it += STEP; 47 | } 48 | } 49 | } 50 | auto stop = timer::now(); 51 | size_t v = STEP == 1 ? 0 : STEP; 52 | std::cout << v << " " << std::chrono::duration_cast(stop-start).count()/(double)(runs) << " " << sum << " " << iter << std::endl; 53 | } 54 | }; 55 | 56 | template 57 | void scanning_range(size_t n, size_t m, size_t runs){ 58 | if ( S0 <= S1 ) { 59 | { scanning(n, m, runs); } 60 | scanning_range(n, m, runs); 61 | } 62 | } 63 | 64 | int main() {//int argc, char** argv) { 65 | 66 | size_t runs = 100000; 67 | size_t n = (1ULL << 30); 68 | size_t m = (1ULL << 14); 69 | 70 | std::cout << "# number of keys " << n << " run length " << m << " number of runs " << runs << std::endl; 71 | std::cout << "# step,nanosecs_per_key" << std::endl; 72 | scanning_range<1, 256>(n, m, runs); 73 | } 74 | -------------------------------------------------------------------------------- /src/cluster_statistics.cpp: -------------------------------------------------------------------------------- 1 | #include "multi_idx/multi_idx.hpp" 2 | #include "multi_idx/multi_idx_red.hpp" 3 | #include "multi_idx/multi_idx_helper.hpp" 4 | #include "chi/chi_idx.hpp" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace std; 11 | using namespace phi; 12 | using namespace sdsl; 13 | using namespace multi_index; 14 | 15 | //#define N 4 16 | //#define K 7 17 | //#define INDEX_NAME "triangle_clusters_bv_split_threshold100_red" 18 | //#define INDEX_TYPE multi_idx_red,t_k> 19 | 20 | const string index_name = INDEX_NAME; 21 | string filtered_index_name; 22 | 23 | constexpr uint8_t t_n = N; 24 | constexpr uint8_t t_k = K; 25 | typedef INDEX_TYPE index_type; 26 | 27 | template std::map get_dist(const index_type&); 28 | template std::map get_dist(const index_type&); 29 | template std::map get_dist(const index_type&); 30 | 31 | template 32 | struct idx_file_trait{ 33 | static std::string value(){ 34 | return to_string(t_n)+"_"+to_string(t_k)+"."+filtered_index_name; 35 | } 36 | }; 37 | 38 | 39 | template 40 | struct idx_file_trait>{ 41 | static std::string value(){ 42 | return to_string(t_n)+"."+filtered_index_name; 43 | } 44 | }; 45 | 46 | 47 | vector unique_vec(vector v){ 48 | if(v.size()>0){ 49 | std::sort(v.begin(), v.end()); 50 | auto end = std::unique(v.begin(), v.end()); 51 | v.erase(end, v.end()); 52 | } 53 | return v; 54 | } 55 | 56 | 57 | vector load_keys(string hash_file) 58 | { 59 | vector keys; 60 | int_vector_buffer<64> key_buf(hash_file, ios::in, 1<<20, 64, true); 61 | for (size_t i=0; i 69 | void output(t_index& pi, string id, string hash_file){ 70 | auto res = t_acc(pi); 71 | for(auto x : res){ 72 | cout << index_name << " " << hash_file << " " << N << " "<< id <<" " << x.first << " " << x.second << endl; 73 | } 74 | } 75 | */ 76 | 77 | 78 | int main(int argc, char* argv[]){ 79 | 80 | filtered_index_name = index_name; 81 | 82 | if ( argc < 2 ) { 83 | cout << "Usage: ./" << argv[0] << " hash_file" << endl; 84 | return 1; 85 | } 86 | 87 | string hash_file = argv[1]; 88 | string idx_file = hash_file + "." + idx_file_trait::value(); 89 | index_type pi; 90 | 91 | if ( !load_from_file(pi, idx_file) ) { 92 | vector keys = load_keys(hash_file); 93 | { 94 | pi = index_type(keys,true); 95 | } 96 | store_to_file(pi, idx_file); 97 | } 98 | 99 | { 100 | auto res = get_dist(pi); 101 | for(auto x : res){ 102 | cout << index_name << " " << hash_file << " " << N << " b " << x.first << " " << x.second << endl; 103 | } 104 | } 105 | 106 | { 107 | auto res = get_dist(pi); 108 | for(auto x : res){ 109 | cout << index_name << " " << hash_file << " " << N << " c " << x.first << " " << x.second << endl; 110 | } 111 | } 112 | 113 | { 114 | auto res = get_dist(pi); 115 | for(auto x : res){ 116 | cout << index_name << " " << hash_file << " " << N << " e " << x.first << " " << x.second << endl; 117 | } 118 | } 119 | 120 | // output>(pi, string("b"), hash_file); 121 | // output>(pi, string("c"), hash_file); 122 | 123 | return 0; 124 | } 125 | -------------------------------------------------------------------------------- /src/gen_bench_data.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | using namespace sdsl; 8 | 9 | 10 | vector unique_vec(vector v){ 11 | if(v.size()>0){ 12 | std::sort(v.begin(), v.end()); 13 | auto end = std::unique(v.begin(), v.end()); 14 | v.erase(end, v.end()); 15 | } 16 | return v; 17 | } 18 | 19 | vector load_keys(string hash_file) 20 | { 21 | vector keys; 22 | int_vector_buffer<64> key_buf(hash_file, ios::in, 1<<20, 64, true); 23 | for (size_t i=0; i v, string &filename) { 31 | ofstream out(filename, std::ofstream::binary); 32 | for (size_t i=0; i < v.size(); ++i){ 33 | out.write((char*)&v[i], sizeof(v[i])); 34 | } 35 | out.close(); 36 | } 37 | 38 | 39 | int main(int argc, char* argv[]){ 40 | 41 | uint64_t n_queries = 10000; 42 | if ( argc < 2 ) { 43 | cout << "Usage: ./" << argv[0] << " hash_file [num_queries]" << endl; 44 | return 1; 45 | } 46 | 47 | string hash_file = argv[1]; 48 | if ( argc > 2 ) { 49 | n_queries = stoull(argv[2]); 50 | } 51 | 52 | vector keys = load_keys(hash_file); 53 | random_shuffle (keys.begin(), keys.end()); 54 | 55 | /* Select and write real but not existing keys as queries */ 56 | vector queries (n_queries, 0); 57 | copy(keys.end()-n_queries, keys.end(), queries.begin()); 58 | string query_file = hash_file + string(".real.query"); 59 | store_keys(queries, query_file); 60 | keys.erase(keys.end()-n_queries, keys.end()); 61 | 62 | /* Select and write existing keys as queries */ 63 | copy(keys.begin(), keys.begin()+n_queries, queries.begin()); 64 | query_file = hash_file + string(".existing.query"); 65 | store_keys(queries, query_file); 66 | /* 67 | std::random_device rd; 68 | std::mt19937 gen(rd()); 69 | std::uniform_int_distribution<> dis; 70 | 71 | // Select and random keys as queries 72 | for(size_t i = 0; i < n_queries; i++) 73 | queries[i] = dis(gen); 74 | query_file = hash_file+string(".random.query"); 75 | store_keys(queries, query_file); 76 | */ 77 | /* Write remaining keys as data */ 78 | hash_file = hash_file + string(".data"); 79 | store_keys(keys, hash_file); 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/gen_debug_data.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | int main(){ 9 | vector data = {0x1111222233334444, 10 | 0x1011222233334444, 11 | 0x1111222233334454, 12 | }; 13 | ofstream out("debug.SimHash"); 14 | out.write((char*)data.data(), sizeof(data[0])*data.size()); 15 | } 16 | -------------------------------------------------------------------------------- /src/gen_hash_file.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | int main(int argc, char* argv[]){ 7 | if ( argc < 2 ) { 8 | cout << "Usage: ./" << argv[0] << "file" << endl; 9 | return 1; 10 | } 11 | vector keys = {0x00000000000003FF, 12 | 0xFF800000000003FF}; 13 | ofstream out(argv[1], std::ofstream::binary); 14 | for (size_t i=0; i < keys.size(); ++i){ 15 | out.write((char*)&keys[i], sizeof(keys[i])); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/ham_distribution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | int main(int argc, char* argv[]){ 10 | if ( argc < 2 ){ 11 | cout << "Usage: ./ham_distribution file " << endl; 12 | return 1; 13 | } 14 | ifstream file(argv[1], std::ifstream::binary); 15 | if ( file.is_open()){ 16 | array cnt = {0}; 17 | uint64_t x; 18 | while ( file.read((char*)&x, sizeof(x) ) ){ 19 | ++cnt[sdsl::bits::cnt(x)]; 20 | } 21 | for(size_t i=0; i 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | using namespace sdsl; 11 | using namespace multi_index; 12 | 13 | using namespace std::chrono; 14 | using timer = std::chrono::high_resolution_clock; 15 | 16 | const string index_name = INDEX_NAME; 17 | string filtered_index_name; 18 | 19 | template 20 | struct my_timer{ 21 | string phase; 22 | time_point start; 23 | 24 | my_timer() = delete; 25 | my_timer(string _phase) : phase(_phase) { 26 | std::cout << "Start phase ``" << phase << "''" << std::endl; 27 | start = timer::now(); 28 | }; 29 | ~my_timer(){ 30 | auto stop = timer::now(); 31 | std::cout << "End phase ``" << phase << "'' "; 32 | std::cout << " ("<< duration_cast(stop-start).count() << " "; 33 | std::cout << " "; 34 | std::cout << " elapsed)" << std::endl; 35 | } 36 | }; 37 | 38 | vector unique_vec(vector v){ 39 | if(v.size()>0){ 40 | std::sort(v.begin(), v.end()); 41 | auto end = std::unique(v.begin(), v.end()); 42 | v.resize(end-v.begin()); 43 | } 44 | return v; 45 | } 46 | 47 | template 48 | struct idx_file_trait{ 49 | static std::string value(std::string hash_file){ 50 | return hash_file + "." + to_string(t_b)+"_"+to_string(t_k)+"."+filtered_index_name; 51 | } 52 | }; 53 | 54 | 55 | template 56 | struct idx_file_trait>{ 57 | static std::string value(std::string hash_file){ 58 | return hash_file + "." + to_string(t_b)+"."+filtered_index_name; 59 | } 60 | }; 61 | 62 | template 63 | struct idx_file_trait>{ 64 | static std::string value(std::string hash_file){ 65 | return hash_file; 66 | } 67 | }; 68 | 69 | vector load_keys(string hash_file) 70 | { 71 | vector keys; 72 | int_vector_buffer<64> key_buf(hash_file, ios::in, 1<<20, 64, true); 73 | for (size_t i=0; i v(1ULL<<23, 0xABCDABCDABCDABCDULL); // generate 8*4M = 64 MB data 82 | for(size_t i=0; i::value(hash_file); 115 | index_type pi; 116 | 117 | { 118 | ifstream idx_ifs(idx_file); 119 | if ( !idx_ifs.good() ){ 120 | vector keys = load_keys(hash_file); 121 | { 122 | // my_timer<> t("index construction"); 123 | // auto temp = index_type(keys, async); 124 | auto temp = index_type(keys, false); 125 | // std::cout<<"temp.size()="<(pi, idx_file+".html"); 130 | } 131 | } 132 | 133 | string line; 134 | while ( argc >=3 or std::getline (std::cin, line, ':') ) { 135 | string qry_file = ""; 136 | size_t search_only = 0; 137 | size_t check_mode = 0; 138 | bool print_info = false; 139 | bool async = false; 140 | 141 | if ( argc >= 3 ) { 142 | qry_file = argv[2]; 143 | if ( argc > 3 ) { search_only = stoull(argv[3]); } 144 | if ( argc > 4 ) { check_mode = stoull(argv[4]); } 145 | if ( argc > 5 ) { print_info = stoull(argv[5]); } 146 | if ( argc > 6 ) { async = stoull(argv[6]); } 147 | } else { 148 | stringstream linestream(line); 149 | linestream >> qry_file; 150 | if ( qry_file.size() == 0 ) 151 | continue; 152 | if ( linestream ) linestream >> search_only; 153 | if ( linestream ) linestream >> check_mode; 154 | if ( linestream ) linestream >> print_info; 155 | if ( linestream ) linestream >> async; 156 | } 157 | 158 | 159 | 160 | if ( pi.size() == 0 and !load_from_file(pi, idx_file) ) { 161 | std::cout<<"ERROR. Index size == 0 and index could not be loaded from disk."< qry; 168 | if ( !load_vector_from_file(qry, qry_file, 8) ){ 169 | cout << "Error: Could not load query file " << qry_file << "." << endl; 170 | return 1; 171 | } else { 172 | cout << "Loaded " << qry.size()<< " queries form file "<::type::threshold); 210 | cout << " " << stat_counter["cum_clusters"]; 211 | cout << " " << stat_counter["cum_sub_queries"]; 212 | cout << " " << stat_counter["cum_clusters_checked"]; 213 | cout << " " << stat_counter["cum_clusters_visited"]; 214 | cout << " " << stat_counter["cum_cluster_sizes"]; 215 | cout << " " << stat_counter["cum_cluster_survivors"]; 216 | cout << " " << stat_counter["early_exits"]; 217 | cout << endl; 218 | stat_counter.clear(); 219 | #endif 220 | } 221 | auto stop = timer::now(); 222 | cout << "# time_per_full_query_in_us = " << duration_cast(stop-start).count()/(double)qry.size() << endl; 223 | } 224 | cout << "# check_cnt_full = " << check_cnt << endl; 225 | cout << "# check_match_full = " << match_cnt << endl; 226 | cout << "# check_unique_matches_full = " << unique_cnt << endl; 227 | cout << "# matches_per_query = " << ((double)match_cnt)/qry.size() << endl; 228 | cout << "# unique_matches_per_query = " << ((double)unique_cnt)/qry.size() << endl; 229 | cout << "# candidates_per_query = " << ((double)check_cnt)/qry.size() << endl; 230 | cout << "# check_cnt_search = " << check_cnt << endl; 231 | } else { 232 | check_cnt = 0; 233 | { 234 | auto start = timer::now(); 235 | for (size_t i=0; i(pi.match(qry[i], true)); 237 | } 238 | auto stop = timer::now(); 239 | cout << "# time_per_search_query_in_us = " << duration_cast(stop-start).count()/(double)qry.size() << endl; 240 | } 241 | } 242 | } 243 | else 244 | { 245 | auto keys = load_keys(hash_file); 246 | if (argc > 5){ 247 | qry = int_vector<64>(1, stoull(argv[5])); 248 | cout << "Check only one key" << endl; 249 | } 250 | cout << "Checking results "<< endl; 251 | for (size_t i=0; i(pi.match(qry[i])); 254 | res = unique_vec(res); 255 | /* 256 | std::cout<<"+++++++++++++++"< res_check; 266 | for (size_t j=0; j= 3 ) { 335 | break; 336 | } 337 | } 338 | return 0; 339 | } 340 | -------------------------------------------------------------------------------- /src/info.cpp: -------------------------------------------------------------------------------- 1 | #include "chi/chi_idx.hpp" 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | using namespace phi; 8 | using namespace sdsl; 9 | 10 | using namespace std::chrono; 11 | using timer = std::chrono::high_resolution_clock; 12 | 13 | const string index_name = INDEX_NAME; 14 | 15 | 16 | vector unique_vec(vector v){ 17 | if(v.size()>0){ 18 | std::sort(v.begin(), v.end()); 19 | auto end = std::unique(v.begin(), v.end()); 20 | v.erase(end, v.end()); 21 | } 22 | return v; 23 | } 24 | 25 | vector load_keys(string hash_file) 26 | { 27 | vector keys; 28 | int_vector_buffer<64> key_buf(hash_file, ios::in, 1<<20, 64, true); 29 | // cout << "# hashes: " << key_buf.size() << endl; 30 | for (size_t i=0; i keys = load_keys(hash_file); 50 | { 51 | // my_timer<> t("index construction"); 52 | auto temp = index_type(keys); 53 | // std::cout<<"temp.size()="< 2 | #include 3 | #include 4 | #include 5 | #include "sim_hash/sim_hash.hpp" 6 | 7 | /* Compute SimHash and OddSketch for each row read from stdin. Hashes are stored in two binary files, 8 bytes each */ 8 | 9 | int main(int argc, char* argv[]) { 10 | 11 | if( argc < 2 ) { 12 | std::cout << "Usage: ./" << argv[0] << " output_hash_filename [window_size]" << std::endl; 13 | std::cout << "\twindow_size: Size of the window sliding over the text. Default window_size=2" << std::endl; 14 | return 1; 15 | } 16 | size_t window_size = 2; 17 | if ( argc > 2 ) { 18 | window_size = std::stoull(argv[2]); 19 | } 20 | 21 | std::string outfilename1 = std::string(argv[1]) + std::string(".SimHash"); 22 | std::string outfilename2 = std::string(argv[1]) + std::string(".OddSketch"); 23 | std::ofstream file1 (outfilename1.c_str()); 24 | std::ofstream file2 (outfilename2.c_str()); 25 | 26 | std::string line; 27 | uint64_t simhash, oddsketch; 28 | uint64_t tot = 0; 29 | while (std::getline(std::cin, line)) { 30 | simhash = simhash64(line, window_size); 31 | oddsketch = oddsketch64(line, window_size); 32 | file1.write(reinterpret_cast(&simhash), sizeof(simhash)); 33 | file2.write(reinterpret_cast(&oddsketch), sizeof(oddsketch)); 34 | tot++; 35 | if(tot % 10000000 == 0) std::cout << "Processed " << tot/1000000 << " Million rows." << std::endl; 36 | } 37 | return 0; 38 | } 39 | --------------------------------------------------------------------------------