Skip to content

Instantly share code, notes, and snippets.

@DeCarabas
Created August 20, 2014 15:10
Show Gist options
  • Save DeCarabas/31cd390ea6eb20d77a4b to your computer and use it in GitHub Desktop.
Save DeCarabas/31cd390ea6eb20d77a4b to your computer and use it in GitHub Desktop.
Fuzzy Rules
#include <algorithm>
#include <limits>
#include <memory>
#include <string>
#include <utility>
#include <random>
#include <vector>
struct fact
{
std::string key;
float value;
fact(const std::string &key, const float value)
: key(key), value(value) {
}
fact(const fact& other)
: key(other.key), value(other.value) {
}
fact(fact &&other) {
swap(*this, other);
}
friend void swap(fact& first, fact& second) {
using std::swap;
swap(first.key, second.key);
swap(first.value, second.value);
}
};
inline bool operator==(const fact& lhs, const fact& rhs){ return lhs.key == rhs.key; }
inline bool operator!=(const fact& lhs, const fact& rhs){ return !operator==(lhs, rhs); }
inline bool operator< (const fact& lhs, const fact& rhs){ return lhs.key < rhs.key; }
inline bool operator> (const fact& lhs, const fact& rhs){ return operator< (rhs, lhs); }
inline bool operator<=(const fact& lhs, const fact& rhs){ return !operator> (lhs, rhs); }
inline bool operator>=(const fact& lhs, const fact& rhs){ return !operator< (lhs, rhs); }
struct knowlege
{
std::shared_ptr<knowlege> parent;
bool sorted;
std::vector<fact> facts;
std::vector<fact>::iterator cursor;
knowlege(std::shared_ptr<knowlege> parent) : parent(parent), sorted(false), facts() {
cursor = facts.begin();
}
knowlege(const knowlege& other)
: parent(other.parent), sorted(other.sorted), facts(other.facts) {
cursor = facts.begin() + (other.cursor - other.facts.begin());
}
knowlege(knowlege &&other) {
swap(*this, other);
}
friend void swap(knowlege& first, knowlege& second) {
using std::swap;
swap(first.parent, second.parent);
swap(first.sorted, second.sorted);
swap(first.facts, second.facts);
swap(first.cursor, second.cursor);
}
void sort() {
if (!sorted) {
std::sort(facts.begin(), facts.end());
cursor = facts.begin();
sorted = true;
}
}
void reset() {
if (parent != nullptr) { parent->reset(); }
sort();
cursor = facts.begin();
}
float *next(const std::string &key) {
while (cursor != facts.end()) {
int comparison = cursor->key.compare(key);
if (comparison == 0) { return &(cursor->value); }
if (comparison > 0) { break; }
++cursor;
}
return (parent != nullptr)
? parent->next(key)
: nullptr;
}
};
inline static float add_epsilon(const float value) {
// TODO: make this add an ULP instead of a float epsilon; I think it is more accurate maybe? Or maybe the floats
// round properly? I don't think so... not for small numbers anyway.
return value + std::numeric_limits<float>::epsilon();
}
inline static float sub_epsilon(const float value) {
// TODO: make this add an ULP instead of a float epsilon; I think it is more accurate maybe? Or maybe the floats
// round properly? I don't think they do... not for small numbers anyway.
return value - std::numeric_limits<float>::epsilon();
}
struct criterion
{
std::string key;
float min;
float max;
criterion(const std::string &key, float min, float max)
: key(key), min(min), max(max) {
}
criterion(const criterion& other)
: key(other.key), min(other.min), max(other.max) {
}
criterion(criterion &&other) {
swap(*this, other);
}
inline bool matches(float value) {
return value >= min && max >= value;
}
criterion& operator=(criterion other) {
swap(*this, other);
return *this;
}
friend void swap(criterion& first, criterion& second) {
using std::swap;
swap(first.key, second.key);
swap(first.min, second.min);
swap(first.max, second.max);
}
inline static criterion equals(const std::string &key, const float value) {
return criterion(key, value, value);
}
inline static criterion greater_than(const std::string &key, const float value) {
return greater_than_equal(key, add_epsilon(value));
}
inline static criterion greater_than_equal(const std::string &key, const float value) {
return criterion(key, value, std::numeric_limits<float>::infinity());
}
static criterion less_than(const std::string &key, const float value) {
return less_than_equal(key, sub_epsilon(value));
}
static criterion less_than_equal(const std::string &key, const float value) {
return criterion(key, -std::numeric_limits<float>::infinity(), value);
}
};
inline bool operator==(const criterion& lhs, const criterion& rhs){ return lhs.key == rhs.key; }
inline bool operator!=(const criterion& lhs, const criterion& rhs){ return !operator==(lhs, rhs); }
inline bool operator< (const criterion& lhs, const criterion& rhs){ return lhs.key < rhs.key; }
inline bool operator> (const criterion& lhs, const criterion& rhs){ return operator< (rhs, lhs); }
inline bool operator<=(const criterion& lhs, const criterion& rhs){ return !operator> (lhs, rhs); }
inline bool operator>=(const criterion& lhs, const criterion& rhs){ return !operator< (lhs, rhs); }
struct rule
{
std::vector<criterion> criteria;
bool sorted;
rule()
: criteria(), sorted(false) {
}
rule(const rule& other)
: criteria(other.criteria), sorted(other.sorted) {
}
rule(rule &&other) {
swap(*this, other);
}
rule& operator=(rule other) {
swap(*this, other);
return *this;
}
friend void swap(rule& first, rule& second) {
using std::swap;
swap(first.criteria, second.criteria);
swap(first.sorted, second.sorted);
}
int score() const {
return (int)criteria.size();
}
void sort() {
if (!sorted) {
std::sort(criteria.begin(), criteria.end());
sorted = true;
}
}
bool matches(std::shared_ptr<knowlege> knowlege) {
sort();
knowlege->reset();
for (auto iter = criteria.begin(); iter != criteria.end(); ++iter) {
float *value = knowlege->next(iter->key);
if (nullptr == value) { return false; }
if (!iter->matches(*value)) { return false; }
}
return true;
}
virtual void run(std::shared_ptr<knowlege> knowlege) const {
// Do something with this when you know what to do.
}
};
struct rule_set
{
std::vector<rule> rules;
bool sorted;
void sort() {
if (!sorted) {
// Sort inverse by score-- large scores go before small scores.
std::sort(rules.begin(), rules.end(), [](const rule& i, const rule& j) { return i.score() > j.score(); });
sorted = true;
}
}
const rule *find_matching_rule(std::shared_ptr<knowlege> knowlege) {
sort();
std::vector<rule*> matches;
int best_score = -1;
for (auto iter = rules.begin(); iter != rules.end(); ++iter) {
int score = iter->score();
if (score < best_score) { break; }
if (iter->matches(knowlege)) {
matches.push_back(&(*iter));
best_score = score;
}
}
if (matches.size() == 1) {
return matches[0];
}
else if (matches.size() > 0) {
// TODO: This is wrong, but I have no good way of threading a random number generator into here.
std::default_random_engine generator;
std::uniform_int_distribution<int> distribution(0, matches.size() - 1);
return matches[distribution(generator)];
}
else {
return nullptr;
}
}
bool execute_rule(std::shared_ptr<knowlege> knowlege) {
const rule *rule = find_matching_rule(knowlege);
if (rule != nullptr) {
rule->run(knowlege);
return true;
}
return false;
}
};
int main(int argc, const char *argv[])
{
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment