Skip to content

Commit

Permalink
Merge pull request #7 from jdreo/solver-domain
Browse files Browse the repository at this point in the history
Solver: parameters, domain configuration and clean build
  • Loading branch information
yoann-dufresne authored Sep 9, 2024
2 parents b0f5592 + b2e3abd commit 56d93b9
Show file tree
Hide file tree
Showing 13 changed files with 327 additions and 75 deletions.
2 changes: 1 addition & 1 deletion app/example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ int main()

CLUTCHLOG(progress, "Try HashFunc");
// Create an instance of HashFunction with a value size of 64 bits
HashFunction<myuint> hashFunc("hash", value_size);
HashFunction<myuint> hashFunc(value_size, "hash");

// Add shift operators
hashFunc.add_operator(std::make_unique<Multiply<myuint>>(9, value_size));
Expand Down
225 changes: 185 additions & 40 deletions app/search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,40 +18,173 @@
#include "EvalFunc.hpp"
#include "log.h"

int main()
using myuint = uint32_t;
using Min = eoMinimizingFitness;
using Combi = moCombination<Min>;
using Nb = moCombinationNeighbor<Combi>;
using NbHood = moCombinationNeighborhood<Combi>;

//! Error codes returned on exit.
enum class Error : unsigned char {
No_Error = 0,
// No_File = 2, // ENOENT
Invalid_Argument = 22, // EINVAL
// Unreadable = 77, // EBADFD
Missing_Argument = 132,
InconsistentDomain,
Unknown = 255
};

//! Macro to hide the necessary verbose casting.
#ifdef WITH_CLUTCHLOG
#define EXIT_ON_ERROR(err_code, msg) { \
if(static_cast<unsigned char>(Error::err_code) > 0) { \
CLUTCHLOG(critical, "CRITICAL ERROR"); \
CLUTCHLOG(critical, msg); \
} \
exit(static_cast<unsigned char>(Error::err_code)); \
}
#else
#define EXIT_ON_ERROR(err_code, msg) { \
if(static_cast<unsigned char>(Error::err_code) > 0) { \
std::cerr << "CRITICAL ERROR: " << msg << std::endl; \
} \
exit(static_cast<unsigned char>(Error::err_code)); \
}
#endif

struct Range {
Range(size_t mi, size_t ma, size_t st) : min(mi), max(ma), step(st) {}
size_t min;
size_t max;
size_t step;
};

void make_domain(eoForgeVector< EvalFull<myuint,Combi>::OpItf >& forge, size_t value_size, Range shift_range, Range mult_range)
{
for(size_t i = shift_range.min; i < shift_range.max; i+=shift_range.step) {
forge.add< XorLeftShift<myuint> >(i, value_size);
CLUTCHLOG(xdebug, "XorLeftShift << " << i);

forge.add< XorRightShift<myuint> >(i, value_size);
CLUTCHLOG(xdebug, "XorRightShift << " << i);

forge.add< AddShift<myuint> >(i, value_size);
CLUTCHLOG(xdebug, "AddShift << " << i);

forge.add< Masking<myuint> >(i);
CLUTCHLOG(xdebug, "Masking & " << i);
}

#ifndef NDEBUG
size_t nb_multipliers = 0;
#endif
for(size_t i = mult_range.min; i < shift_range.max; i+=mult_range.step) {
if(i % 2 == 1) { // Only odd multipliers are allowed.
forge.add< Multiply<myuint> >(i, value_size);
CLUTCHLOG(xdebug, "Multiply * " << i);
#ifndef NDEBUG
nb_multipliers += 1;
#endif
}
}
ASSERT(nb_multipliers > 0);
}

int main(int argc, char* argv[])
{
using myuint = uint32_t;
using Min = eoMinimizingFitness;
using Combi = moCombination<Min>;
using Nb = moCombinationNeighbor<Combi>;
using NbHood = moCombinationNeighborhood<Combi>;
CLUTCHLOG(progress, "Set config...");
eoParser argparser(argc, argv);

/***** Classical arguments *****/

const std::string log_level = argparser.createParam<std::string>("Progress", "log-level",
"Maximum depth level of logging (Critical<Error<Warning<Progress<Note<Info<Debug<XDebug, default=Progress)", 'l', "Logging").value();

const std::string log_file = argparser.createParam<std::string>(".*", "log-file",
"Regexp indicating which source file is allowed logging (default=all)", 'f', "Logging").value();

const std::string log_func = argparser.createParam<std::string>(".*", "log-func",
"Regexp indicating which function is allowed logging (default=all)", 'F', "Logging").value();

const size_t log_depth = argparser.createParam<size_t>(9999, "log-depth",
"Maximum stack depth above which logging is not allowed (default=no limit)", 'D', "Logging").value();

unsigned long long seed = argparser.createParam<long>(0, "seed",
"Seed of the pseudo-random generator (0 = Epoch)", 's', "Parameters").value();

/***** Search domain arguments *****/

const size_t value_size = argparser.createParam<size_t>(31, "value-size",
"Value size (in bits)", 'v', "Search domain").value();

const size_t func_len = argparser.createParam<size_t>(3, "func-len",
"Number of operations in the hash function", 'n', "Search domain").value();

const size_t shift_min = argparser.createParam<size_t>(2, "shift-min",
"Minimum number of shifts", 't', "Search domain").value();
const size_t shift_max = argparser.createParam<size_t>(31, "shift-max",
"Maximum number of shifts", 'T', "Search domain").value();
const size_t shift_step = argparser.createParam<size_t>(1, "shift-step",
"Increment step for number of shifts", 'i', "Search domain").value();
Range shift_range(shift_min, shift_max, shift_step);

const size_t mult_min = argparser.createParam<size_t>(3, "mult-min",
"Smallest multiplier", 'm', "Search domain").value();
const size_t mult_max = argparser.createParam<size_t>(65, "mult-max",
"Largest multiplier", 'M', "Search domain").value();
const size_t mult_step = argparser.createParam<size_t>(2, "mult-step",
"Increment step for multipliers (note: only odd multipliers will be allowed)", 'u', "Search domain").value();
Range mult_range(mult_min, mult_max, mult_step);

make_verbose(argparser);
make_help(argparser);

CLUTCHLOG(progress, "Set config");
clutchlog_config(); // common config
auto& log = clutchlog::logger();
log.threshold("Debug");
ASSERT(log.levels().contains(log_level));
log.threshold(log_level);
log.depth(log_depth);
log.file(log_file);
log.func(log_func);

if(seed == 0) {
seed = std::time(nullptr); // Epoch
}
CLUTCHLOG(info, "seed = " << seed);
CLUTCHLOG(info, "log-level = " << log_level);
CLUTCHLOG(info, "log-file = " << log_file);
CLUTCHLOG(info, "log-func = " << log_func);
CLUTCHLOG(info, "log-depth = " << log_depth);
CLUTCHLOG(info, "value-size = " << value_size);
CLUTCHLOG(info, "func-len = " << func_len);
CLUTCHLOG(info, "shift-min = " << shift_min);
CLUTCHLOG(info, "shift-max = " << shift_max);
CLUTCHLOG(info, "shift-step = " << shift_step);
CLUTCHLOG(info, "mult-min = " << mult_min);
CLUTCHLOG(info, "mult-max = " << mult_max);
CLUTCHLOG(info, "mult-step = " << mult_step);

if(shift_min == 0) {
EXIT_ON_ERROR(InconsistentDomain, "It makes no sense to set `--shift-min` to zero.");
}
if(shift_max < value_size) {
EXIT_ON_ERROR(InconsistentDomain, "It makes no sense to set --shift-max=" << shift_max << " < --value-size=" << value_size <<"");
}
if(mult_min < 3) {
CLUTCHLOG(warning, "It is probably wrong that `--mult-min` is less than 3.");
}
if(mult_step % 2 == 1) {
CLUTCHLOG(warning, "It is probably wrong that `--mult-step` is not even. Remember that only odd multipliers are actually allowed. Even multipliers will be filtered out.");
}

const size_t sol_size = 10;
const size_t value_size = 31;
size_t seed = 0;
CLUTCHLOG(note, "OK");

CLUTCHLOG(progress, "Create the search domain...");
eoForgeVector< EvalFull<myuint,Combi>::OpItf > forge(/*always_reinstantiate*/true);
forge.add< Multiply <myuint> >( 9, value_size);
forge.add< XorLeftShift <myuint> >(17, value_size);
forge.add< XorLeftShift <myuint> >( 5, value_size);
forge.add< AddShift <myuint> >(19, value_size);
forge.add< XorRightShift<myuint> >( 3, value_size);
forge.add< Multiply <myuint> >( 9, value_size);

CLUTCHLOG(progress, "Pick a random solution...");
std::vector<size_t> v;
v.reserve(sol_size);
std::mt19937 rng(seed);
std::uniform_int_distribution<int> uni(0, forge.size()-1);
for(size_t i=0; i<sol_size; ++i) {
v.push_back(uni(rng));
}
Combi sol(v, forge.size());
make_domain(forge, value_size, shift_range, mult_range);
CLUTCHLOG(info, forge.size() << " operators");
ASSERT(forge.size() > 0);
CLUTCHLOG(note, "OK");

CLUTCHLOG(progress, "Instantiate solver...");
Expand All @@ -71,6 +204,24 @@ int main()
moRandomBestHC<Nb> search(neighborhood, feval, peval, check);
CLUTCHLOG(note, "OK");

CLUTCHLOG(progress, "Pick a random solution...");
std::vector<size_t> v;
v.reserve(func_len);
std::mt19937 rng(seed);
std::uniform_int_distribution<int> uni(0, forge.size()-1);
for(size_t i=0; i<func_len; ++i) {
v.push_back(uni(rng));
}
Combi sol(v, forge.size());
CLUTCHLOG(info, "Initial solution: " << sol);
auto hf = feval.make_hashfuncs(sol);
CLUTCHLOG(info, "Initial hash function: " << hf.forward.get_name() << " / " << hf.reverse.get_name());

hf = feval.make_hashfuncs(sol);
std::clog << hf.forward.to_string() << std::endl;
std::clog << hf.reverse.to_string() << std::endl;
CLUTCHLOG(note, "OK");

CLUTCHLOG(progress, "Evaluate first signature...");
feval(sol);
CLUTCHLOG(note, "Initial signature: " << sol);
Expand All @@ -80,23 +231,17 @@ int main()
search(sol);
CLUTCHLOG(note, "OK");

CLUTCHLOG(progress, "Found signature:");
CLUTCHLOG(progress, "Optimized solution:");
CLUTCHLOG(note, sol );

CLUTCHLOG(progress, "Output optimized hash function:");
// Real output.
HashFunction<myuint> hff("optimized_hash", value_size);
for(size_t i : sol.get()) {
CLUTCHLOG(xdebug, "Instantiate " << i << "th operator");
hff.add_operator( forge.instantiate_ptr(i) );
}
CLUTCHLOG(debug, "Complete with masks");
hff.complete_with_masks();

CLUTCHLOG(debug, "Invert");
HashFunction<myuint> hfr{hff.invert()};
hf = feval.make_hashfuncs(sol);
ASSERT(hf.forward.size() > 0);
ASSERT(hf.reverse.size() > 0);

std::cout << hff.to_string() << std::endl;
std::cout << hfr.to_string() << std::endl;
std::cout << hf.forward.to_string() << std::endl;
std::cout << hf.reverse.to_string() << std::endl;

CLUTCHLOG(progress, "Done.");
}
2 changes: 1 addition & 1 deletion external/paradiseo
19 changes: 13 additions & 6 deletions src/include/AddShift.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class AddShift : public Operator<myuint>
std::vector<std::unique_ptr<Operator<myuint>>> invert() const override
{
std::vector<std::unique_ptr<Operator<myuint>>> inverted;

Multiply<myuint> const mlt(1 + (1 << m_shifts), m_val_size, true);
std::vector<std::unique_ptr<Operator<myuint>>> invert = mlt.invert();
Multiply<myuint> const *invert_ptr = dynamic_cast<Multiply<myuint> const *>(invert[0].get());
Expand All @@ -40,9 +40,16 @@ class AddShift : public Operator<myuint>
// Implement the to_string function
std::string to_string() const override
{
std::stringstream ss;
ss << "val += val << " << m_shifts << ";";
return ss.str();
std::ostringstream os;
os << "val += val << " << m_shifts << ";";
return os.str();
}

std::string to_short() const override
{
std::ostringstream os;
os << "a" << m_shifts;
return os.str();
}

myuint apply (myuint value) const override
Expand All @@ -54,7 +61,7 @@ class AddShift : public Operator<myuint>
{
return true;
}

bool clean_leftbits_needed() const override
{
return false;
Expand All @@ -63,4 +70,4 @@ class AddShift : public Operator<myuint>



#endif // ADDSHIFT_HPP
#endif // ADDSHIFT_HPP
30 changes: 26 additions & 4 deletions src/include/EvalFunc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,31 @@ class EvalFull : public eoEvalFunc< EOT >
m_forge(forge)
{ }

virtual void operator()(EOT& sol) {
CLUTCHLOG(xdebug, "Evaluate solution: " << sol);
//! Instantiate the forward and reverse HashFunc from the given solution.
HashFunctionPair<myuint> make_hashfuncs( EOT& sol ) const
{
ASSERT(sol.size() > 0);
ASSERT(sol.nb_options() == m_forge.size());

HashFunction<myuint> hff("optimized_hash", m_value_size);
HashFunction<myuint> hff(m_value_size);

CLUTCHLOG(xdebug, "Instantiate " << sol.size() << " operators:");
ASSERT(sol.size() > 0);
for(size_t i : sol.get()) {
// CLUTCHLOG(debug, "Instantiate " << i << "th operator");
// NOTE: this is why we need a shared_ptr and cannot have a unique_ptr.
hff.add_operator( m_forge.instantiate_ptr(i) );
}
ASSERT(hff.size() == sol.size());
CLUTCHLOG(xdebug, "Got hash function: " << hff.get_name());
CLUTCHLOG(xdebug, "Complete with masks");
hff.complete_with_masks();
// ASSERT(hff.size() >= sol.size());

CLUTCHLOG(xdebug, "Invert");
HashFunction<myuint> hfr{hff.invert()};
// ASSERT(hfr.size() > 0);
CLUTCHLOG(xdebug, "Got inverted hash function: " << hfr.get_name());

#ifndef NDEBUG
CLUTCHLOG(xdebug, "Check inversion");
Expand All @@ -48,9 +55,24 @@ class EvalFull : public eoEvalFunc< EOT >
ASSERT(value == recovered);
#endif

return HashFunctionPair<myuint>(hff, hfr);
}

//! Call interface.
virtual void operator()(EOT& sol) {
CLUTCHLOG(xdebug, "Evaluate solution: " << sol);

auto hffr = make_hashfuncs(sol);

HashFunction<myuint> hff = hffr.forward;
HashFunction<myuint> hfr = hffr.reverse;

// TODO: have a real objective function.
const double quality = hff.size() + hfr.size();

sol.fitness( quality );
CLUTCHLOG(debug, "Evaluated solution: " << sol);
CLUTCHLOG(xdebug, "Evaluated solution: " << sol);
CLUTCHLOG(xdebug, "Evaluated hash function: " << hff.get_name());

ASSERT(not sol.invalid());
}
Expand Down
Loading

0 comments on commit 56d93b9

Please sign in to comment.