From 0de7b6e41da7257aea1ebfbe603356c2de976e96 Mon Sep 17 00:00:00 2001 From: Matthew Andres Moreno Date: Mon, 28 Jan 2019 15:35:48 -0500 Subject: [PATCH] Move combine_hash function to tools/hash_utils Also, write tests for combine_hash. Note that this breaks existing levilization described in README.md... now meta depends on tools. Update README.md to acknowledge this. --- README.md | 4 +-- source/meta/meta.h | 6 ++-- source/tools/hash_utils.h | 8 +++++ tests/test_tools.cc | 61 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 916b43fa47..8932165bf4 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ files at lower levels. | Level | Folders | ---- | ---- | 0 | base/ -| 1 | meta/ -| 2 | tools/ +| 1 | tools/ +| 2 | meta/ | 3 | config/ control/ data/ games/ geometry/ hardware/ scholar/ | 4 | Evolve/ (data, control) web/ (config, control) diff --git a/source/meta/meta.h b/source/meta/meta.h index f1118535e9..8e34cdea13 100644 --- a/source/meta/meta.h +++ b/source/meta/meta.h @@ -17,6 +17,8 @@ #include #include +#include "tools/hash_utils.h" + namespace emp { // A function that will take any number of argument and do nothing with them. @@ -212,8 +214,8 @@ namespace emp { template std::size_t CombineHash(const T1 & x1, const T2 & x2, const EXTRA &... x_extra) { const std::size_t hash2 = CombineHash(x2, x_extra...); - //return std::hash()(x1) + 0x9e3779b9 + (hash2 << 19) + (hash2 >> 13); - return Hash(x1) + 0x9e3779b9 + (hash2 << 19) + (hash2 >> 13); + + return combine_hash(Hash(x1), hash2); } diff --git a/source/tools/hash_utils.h b/source/tools/hash_utils.h index 09945769c3..cb5f96cc21 100644 --- a/source/tools/hash_utils.h +++ b/source/tools/hash_utils.h @@ -15,6 +15,14 @@ namespace emp { + /// mixin: the additional value to mix in to the hash soup + /// acc: the accumulated value + /// RE implementation see, e.g., + /// https://www.boost.org/doc/libs/1_35_0/doc/html/boost/hash_combine_id241013.html + size_t combine_hash(size_t mixin, size_t acc) { + return mixin + 0x9e3779b9 + (acc << 19) + (acc >> 13); + } + /// generate a unique long from a pair of ints uint64_t szudzik_hash(uint32_t a_, uint32_t b_) { diff --git a/tests/test_tools.cc b/tests/test_tools.cc index b6605f1a10..c6761eab4d 100644 --- a/tests/test_tools.cc +++ b/tests/test_tools.cc @@ -535,6 +535,67 @@ TEST_CASE("Test Graph utils", "[tools]") TEST_CASE("Test hash_utils", "[tools]") { + // combine_hash + emp::vector v1 = {0,0,0,1,1,1}; + emp::vector v2 = {1,1,1,42,53,101,1,0,0,0,}; + emp::vector v3 = {42,42,42,1,1,1}; + emp::vector v4 = {11}; + + emp::Random r(1); + size_t target_len = 25; + while (v1.size() < target_len) { v1.push_back(r.GetUInt()); } + while (v2.size() < target_len) { v2.push_back(r.GetUInt()); } + while (v3.size() < target_len) { v3.push_back(r.GetUInt()); } + while (v4.size() < target_len) { v4.push_back(r.GetUInt()); } + + emp::vector h1, h2, h3, h4; + h1.push_back(0); + for (size_t v : v1) h1.push_back(emp::combine_hash(v,h1.back())); + h1.erase(h1.begin()); + h2.push_back(1); + for (size_t v : v2) h2.push_back(emp::combine_hash(v,h2.back())); + h2.erase(h2.begin()); + h3.push_back(1); + for (size_t v : v3) h3.push_back(emp::combine_hash(v,h3.back())); + h3.erase(h3.begin()); + h4.push_back(1); + for (size_t v : v4) h4.push_back(emp::combine_hash(v,h4.back())); + h4.erase(h4.begin()); + + auto s1 = std::unordered_set(h1.begin(),h1.end()); + REQUIRE(s1.size() == target_len); + auto s2 = std::unordered_set(h2.begin(),h2.end()); + REQUIRE(s2.size() == target_len); + auto s3 = std::unordered_set(h3.begin(),h3.end()); + REQUIRE(s3.size() == target_len); + auto s4 = std::unordered_set(h4.begin(),h4.end()); + REQUIRE(s4.size() == target_len); + + auto vs1 = std::unordered_set(v1.begin(),v1.end()); + auto vs2 = std::unordered_set(v2.begin(),v2.end()); + auto vs3 = std::unordered_set(v3.begin(),v3.end()); + auto vs4 = std::unordered_set(v4.begin(),v4.end()); + + s1.insert(vs1.begin(),vs1.end()); + s2.insert(vs2.begin(),vs2.end()); + s3.insert(vs3.begin(),vs3.end()); + s4.insert(vs4.begin(),vs4.end()); + + REQUIRE(s1.size() == target_len + vs1.size()); + REQUIRE(s2.size() == target_len + vs2.size()); + REQUIRE(s3.size() == target_len + vs3.size()); + REQUIRE(s4.size() == target_len + vs4.size()); + + s1.insert(s2.begin(),s2.end()); + s1.insert(s3.begin(),s3.end()); + s1.insert(s4.begin(),s4.end()); + vs1.insert(vs2.begin(), vs2.end()); + vs1.insert(vs3.begin(), vs3.end()); + vs1.insert(vs4.begin(), vs4.end()); + + REQUIRE(s1.size() == 4*target_len + vs1.size()); + + // szudzik_hash REQUIRE(emp::szudzik_hash((uint32_t)0, (uint32_t)0) == (uint64_t)0); REQUIRE(emp::szudzik_hash((uint32_t)0, (uint32_t)1) == (uint64_t)1);