Added mathsym+tcc and boost against all advice
This commit is contained in:
parent
58ae49dd99
commit
90702a435d
136 changed files with 14409 additions and 0 deletions
364
eo/contrib/mathsym/sym/README.cpp
Normal file
364
eo/contrib/mathsym/sym/README.cpp
Normal file
|
|
@ -0,0 +1,364 @@
|
|||
|
||||
/*
|
||||
DESCRIPTION:
|
||||
|
||||
|
||||
The class 'Sym' in this package provides a reference counted, hashed tree structure that can be used in genetic programming.
|
||||
The hash table behind the scenes makes sure that every subtree in the application is stored only once.
|
||||
This has a couple of advantages:
|
||||
|
||||
o Memory: all subtrees are stored only once
|
||||
o Comparison: comparison for equality for two subtrees boils down to a pointer comparison
|
||||
o Overview: by accessing the hashtable, you get an instant overview of the state of the population
|
||||
|
||||
|
||||
The disadvantage of this method is the constant time overhead for computing hashes. In practice,
|
||||
it seems to be fast enough.
|
||||
|
||||
|
||||
===== How to Use this =========
|
||||
|
||||
In essence, the Sym data structure contains two important pieces of data,
|
||||
the 'token' (of type token_t = int) and the children, a vector of Sym (called SymVec).
|
||||
The token should contain all information to be able to figure out which
|
||||
function/terminal is represented by the node in the tree. By retrieving this token value
|
||||
and the SymVec it is possible to write recursive traversal routines for evaluation, printing,
|
||||
etc.
|
||||
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include "Sym.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
/*
|
||||
* Suppose token value '0' designates our terminal, and token value '1' designates a binary function.
|
||||
* Later on a ternary function will be used as well, designated with token value '2'
|
||||
* The function below will create a tree of size three
|
||||
*/
|
||||
Sym test1() {
|
||||
|
||||
SymVec children;
|
||||
children.push_back( Sym(0) ); // push_back is a member from std::vector, SymVec is derived from std::vector
|
||||
children.push_back( Sym(0) );
|
||||
|
||||
Sym tree = Sym(token_t(1), children); // creates the tree
|
||||
|
||||
/* Done, now print some information about the node */
|
||||
|
||||
cout << "Size = " << tree.size() << endl; // prints 3
|
||||
cout << "Depth = " << tree.depth() << endl; // prints 2
|
||||
cout << "Refcount = " << tree.refcount() << endl; // prints 1
|
||||
|
||||
Sym tree2 = tree; // make a copy (this only changes refcount)
|
||||
|
||||
cout << "Refcount now = " << tree.refcount() << endl; // print 2
|
||||
|
||||
return tree; // tree2 will be deleted and reference count returns to 1
|
||||
}
|
||||
|
||||
/* To actually use the tree, evaluate it, the following simple recursive function
|
||||
* can be used
|
||||
*/
|
||||
|
||||
int eval(const Sym& sym) {
|
||||
if (sym.token() == 0) { // it's a terminal in this example
|
||||
return 1;
|
||||
}
|
||||
// else it's the function
|
||||
const SymVec& children = sym.args(); // get the children out, children.size() is the arity
|
||||
|
||||
// let's assume that we've also got a ternary function designated by token '2'
|
||||
|
||||
if (sym.token() == token_t(1))
|
||||
return eval(children[0]) + eval(children[1]); // evaluate
|
||||
|
||||
return eval(children[0]) + eval(children[1]) * eval(children[2]); // a ternary function
|
||||
}
|
||||
|
||||
/* Note that you simply use the stored token that was defined above. Simply checking the size of SymVec in
|
||||
* this particular example could have sufficed, but it's instructive to use the tokens.
|
||||
*
|
||||
* And to test this:
|
||||
*/
|
||||
|
||||
void test_eval() {
|
||||
|
||||
Sym tree = test1();
|
||||
|
||||
cout << "Evaluating tree1 returns " << eval(tree) << endl;
|
||||
}
|
||||
|
||||
/* Writing initialization functions.
|
||||
*
|
||||
* As the Sym class is recursive in nature, initialization can simply be done using
|
||||
* recursive routines as above. As an example, the following code does 'full' initialization.
|
||||
*/
|
||||
|
||||
Sym init_full(int depth_left) {
|
||||
if (depth_left == 0) return Sym(0); // create terminal
|
||||
// else create either a binary or a ternary function
|
||||
|
||||
depth_left--;
|
||||
|
||||
if (rand() % 2 == 0) { // create binary
|
||||
SymVec vec(2);
|
||||
vec[0] = init_full(depth_left);
|
||||
vec[1] = init_full(depth_left);
|
||||
|
||||
return Sym(token_t(1), vec);
|
||||
|
||||
} else { // create ternary tree
|
||||
SymVec vec(3);
|
||||
vec[0] = init_full(depth_left);
|
||||
vec[1] = init_full(depth_left);
|
||||
vec[2] = init_full(depth_left);
|
||||
|
||||
return Sym(token_t(2), vec); // token value 2 designates a ternary now, even though the arity can simply be read from the size of the 'SymVec'
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Examining the hash table.
|
||||
*
|
||||
* The hash table is a static member of the Sym class, but can be obtained and inspected
|
||||
* at any point during the run. The hash table follows the SGI implementation of hashmap (and effectively
|
||||
* uses it in gcc). An example:
|
||||
*/
|
||||
|
||||
void inspect_hashtable() {
|
||||
SymMap& dag = Sym::get_dag(); // get the hashmap
|
||||
unsigned i = 0;
|
||||
for (SymMap::iterator it = dag.begin(); it != dag.end(); ++it) {
|
||||
Sym node(it); // initialize a 'sym' with the iterator
|
||||
|
||||
cout << "Node " << i++ << " size " << node.size();
|
||||
cout << " refcount " << node.refcount()-1; // -1: note that by creating the Sym above the refcount is increased
|
||||
cout << " depth " << node.depth();
|
||||
cout << '\n';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* The above code effectively examines all distinct subtrees in use in the application and prints some stats for the node */
|
||||
|
||||
/* Manipulating trees
|
||||
*
|
||||
* The Sym class is set up in such a way that you cannot change a Sym, so how do you perform crossover and mutation?
|
||||
*
|
||||
* Simple, you create new syms. The Sym class supports two functions to make this easier: 'get_subtree' and 'insert_subtree'.
|
||||
* These traverse the tree by index, where 0 designates the root and other values are indexed depth first.
|
||||
*/
|
||||
|
||||
Sym subtree_xover(Sym a, Sym b) {
|
||||
|
||||
Sym to_insert = get_subtree(a, rand() % a.size() ); // select random subtree, will crash if too high a value is given
|
||||
|
||||
/* 'insert' it into b. This will not really insert, it will however create a new sym,
|
||||
* equal to 'b' but with a's subtree inserted at the designated spot. */
|
||||
return insert_subtree(b, rand() % b.size(), to_insert);
|
||||
|
||||
}
|
||||
|
||||
/* Tying it together, we can create a simple genetic programming system. Mutation is not implemented here,
|
||||
* but would be easy enough to add by using recursion and/or 'set'. */
|
||||
|
||||
void run_gp() {
|
||||
|
||||
int ngens = 50;
|
||||
int popsize = 1000;
|
||||
|
||||
cout << "Starting running " << popsize << " individuals for " << ngens << " generations." << endl;
|
||||
|
||||
vector<Sym> pop(popsize);
|
||||
|
||||
// init population
|
||||
for (unsigned i = 0; i < pop.size(); ++i) {
|
||||
pop[i] = init_full(5);
|
||||
}
|
||||
|
||||
double best = 0.0;
|
||||
|
||||
// do a very simple steady state tournament
|
||||
for (unsigned gen = 0; gen < ngens * pop.size(); ++gen) {
|
||||
int sel1 = rand()% pop.size();
|
||||
int sel2 = rand() % pop.size();
|
||||
int sel3 = rand() % pop.size();
|
||||
|
||||
double ev1 = eval(pop[sel1]);
|
||||
double ev3 = eval(pop[sel3]);
|
||||
|
||||
double bst = max(ev1,ev3);
|
||||
if (bst > best) {
|
||||
best = bst;
|
||||
}
|
||||
|
||||
if (ev3 > ev1) {
|
||||
sel1 = sel3; // selection pressure
|
||||
}
|
||||
|
||||
Sym child = subtree_xover(pop[sel1], pop[sel2]);
|
||||
|
||||
// Check for uniqueness
|
||||
if (child.refcount() == 1) pop[ rand() % pop.size() ] = child;
|
||||
}
|
||||
|
||||
// and at the end:
|
||||
|
||||
inspect_hashtable();
|
||||
|
||||
// and also count number of nodes in the population
|
||||
int sz = 0;
|
||||
for (unsigned i = 0; i < pop.size(); ++i) { sz += pop[i].size(); }
|
||||
cout << "Number of distinct nodes " << Sym::get_dag().size() << endl;
|
||||
cout << "Nodes in population " << sz << endl;
|
||||
cout << "ratio " << double(Sym::get_dag().size())/sz << endl;
|
||||
cout << "Best fitness " << best << endl;
|
||||
|
||||
}
|
||||
|
||||
/* One extra mechanism is supported to add annotations to nodes. Something derived from
|
||||
* 'UniqueNodeStats' can be used to attach new information to nodes. For this to function,
|
||||
* we need to supply a 'factory' function that creates these node-stats; attach this function to the
|
||||
* Sym class, so that it gets called whenever a new node is created. The constructors of the Sym class
|
||||
* take care of this.
|
||||
*
|
||||
* IMPORTANT:
|
||||
* in a realistic application, the factory function needs to be set BEFORE any Syms are created
|
||||
* Mixing Syms creating with and without the factory can lead to unexpected results
|
||||
*
|
||||
* First we derive some structure from UniqueNodeStats: */
|
||||
|
||||
struct MyNodeStats : public UniqueNodeStats {
|
||||
|
||||
int sumsize;
|
||||
|
||||
~MyNodeStats() { cout << "MyNodeStats::~MyNodeStats, sumsize = " << sumsize << endl; }
|
||||
};
|
||||
|
||||
/* then define the factory function. It will get a Sym, which is just created. */
|
||||
UniqueNodeStats* create_stats(const Sym& sym) {
|
||||
MyNodeStats* stats = new MyNodeStats; // Sym will take care of memory management
|
||||
|
||||
int sumsize = sym.size();
|
||||
for (unsigned i = 0; i < sym.args().size(); ++i) {
|
||||
// retrieve the extra node stats of the child
|
||||
UniqueNodeStats* unique_stats = sym.args()[i].extra_stats(); // extra_stats retrieves the stats
|
||||
MyNodeStats* child_stats = static_cast<MyNodeStats*>(unique_stats); // cast it to the right struct
|
||||
sumsize += child_stats->sumsize;
|
||||
}
|
||||
|
||||
stats->sumsize = sumsize;
|
||||
return stats; // now it will get attached to the node and deleted when its reference count goes to zero
|
||||
}
|
||||
|
||||
void test_node_stats() {
|
||||
|
||||
if (Sym::get_dag().size() != 0) {
|
||||
cerr << "Cannot mix nodes with and without factory functions" << endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Very Important: attach the factory function to the Sym class */
|
||||
Sym::set_factory_function(create_stats);
|
||||
|
||||
Sym tree = init_full(5); // create a tree
|
||||
|
||||
// get extra node stats out
|
||||
MyNodeStats* stats = static_cast<MyNodeStats*>( tree.extra_stats() );
|
||||
|
||||
cout << "Size = " << tree.size() << " SumSize = " << stats->sumsize << endl;
|
||||
|
||||
Sym::clear_factory_function(); // reset
|
||||
}
|
||||
|
||||
|
||||
/* And run the code above */
|
||||
|
||||
int main() {
|
||||
srand(time(0));
|
||||
cout << "********** TEST EVALUATION **************\n";
|
||||
test_eval();
|
||||
cout << "********** TEST ALGORITHM ***************\n";
|
||||
run_gp();
|
||||
|
||||
cout << "********** TEST FACTORY ****************\n";
|
||||
test_node_stats(); // can work because there are no live nodes
|
||||
|
||||
}
|
||||
|
||||
/* ********** Member function reference: ********************
|
||||
*
|
||||
* Sym() The default constructor will create an undefined node (no token and no children), check for empty() to see if a node is undefined
|
||||
*
|
||||
* Sym(token_t) Create a terminal
|
||||
*
|
||||
* Sym(token_t, const SymVec&)
|
||||
* Create a node with token and SymVec as the children
|
||||
*
|
||||
* Sym(SymIterator it)
|
||||
* Create a sym from an iterator (taken from the hashtable directly, or from Sym::iterator)
|
||||
*
|
||||
* dtor, copy-ctor and assignment
|
||||
*
|
||||
* UniqueNodeStats* extra_stats()
|
||||
* Returns an UniqueNodeStats pointer (= 0 if no factory is defined)
|
||||
*
|
||||
*
|
||||
* int hashcode() returns the hashcode for the node
|
||||
*
|
||||
* int refcount() returns the reference count for the node
|
||||
*
|
||||
* bool operator== checks for equality (note that this is a pointer compare, really really fast)
|
||||
*
|
||||
* bool empty() returns whether the node is undefined, i.e. created through the default ctor
|
||||
*
|
||||
* int arity() shorthand for sym.args().size()
|
||||
*
|
||||
* token_t token() return identifying token for the node
|
||||
*
|
||||
* const SymVec& args()
|
||||
* returns the children of the node (in a vector<Sym>)
|
||||
*
|
||||
* unsigned size() returns the size, i.e., number of nodes
|
||||
*
|
||||
* unsigned depth() returns the depth
|
||||
*
|
||||
* iterator() returns the pointer to the node in the hashtable
|
||||
*
|
||||
*
|
||||
********** Static functions: ********************
|
||||
*
|
||||
*
|
||||
*
|
||||
* SymMap& get_dag() returns the hash table containing all nodes. This should only be used for inspection,
|
||||
* even though the dag itself is not const. This to enable the use of the ctor Sym(SymIterator) to inspect
|
||||
* using the Sym interface (rather than the hash table interface). This does allow you to make destructive
|
||||
* changes to the class, so use with care
|
||||
*
|
||||
* set_factory_function( UniqueNodeStats (*)(const Sym&) )
|
||||
* Set the factory function
|
||||
*
|
||||
* clear_factory_function()
|
||||
* Clears the factory function, allocated UniqueNodeStats will still be deleted, but no new ones will be created.
|
||||
*
|
||||
********** Utility Functions ********************
|
||||
*
|
||||
* Sym get_subtree(const Sym& org, int i)
|
||||
* Retreive the i-th subtree from the Sym. Standard depth first ordering, where root has index 0 and the
|
||||
* rightmost terminal has index sym.size()-1
|
||||
*
|
||||
* Sym insert_subtree(const Sym& org, int i, const Sym& subtree)
|
||||
* Returns a Sym that is equal to 'org', for which the i-th subtree (same ordering as get_subtree) is replaced
|
||||
* by the third argument subtree.
|
||||
*
|
||||
* Sym next(const Sym&)
|
||||
* Returns the successor of the argument sym from the hashtable with wrap around. This is implemented just because
|
||||
* it can be done. It may be an interesting way to mutate...
|
||||
*
|
||||
* */
|
||||
|
||||
|
||||
135
eo/contrib/mathsym/sym/Sym.cpp
Normal file
135
eo/contrib/mathsym/sym/Sym.cpp
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright (C) 2005 Maarten Keijzer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "Sym.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef UniqueNodeStats* (*NodeStatFunc)(Sym&);
|
||||
|
||||
UniqueNodeStats* (*Sym::factory)(const Sym&) = 0;
|
||||
|
||||
void (*Sym::extra_dtor)(token_t) = 0;
|
||||
|
||||
SymMap Sym::dag(100000); // reserve space for so many nodes
|
||||
|
||||
size_t get_size(const SymVec& vec) {
|
||||
size_t sz = 0;
|
||||
for (unsigned i = 0; i < vec.size(); ++i) {
|
||||
sz += vec[i].size();
|
||||
}
|
||||
return sz;
|
||||
}
|
||||
|
||||
size_t get_depth(const SymVec& vec) {
|
||||
size_t dp = 1;
|
||||
for (unsigned i = 0; i < vec.size(); ++i) {
|
||||
dp = std::max(dp, vec[i].depth());
|
||||
}
|
||||
return dp;
|
||||
}
|
||||
|
||||
Sym::Sym(token_t tok, const SymVec& args_)
|
||||
{
|
||||
detail::SymKey key(tok, detail::SymArgs(args_));
|
||||
detail::SymValue val;
|
||||
|
||||
node = dag.insert(pair<const detail::SymKey, detail::SymValue>(key, val)).first;
|
||||
|
||||
if (__unchecked_refcount() == 0) { // new node, set some stats
|
||||
node->second.size = 1 + get_size(args_);
|
||||
node->second.depth = 1 + get_depth(args_);
|
||||
incref();
|
||||
node->first.fixate();
|
||||
// call the factory function if available
|
||||
if (factory) node->second.uniqueNodeStats = factory(*this);
|
||||
}
|
||||
else incref();
|
||||
}
|
||||
|
||||
Sym::Sym(token_t tok, const Sym& a) {
|
||||
SymVec args_; args_.push_back(a);
|
||||
detail::SymKey key(tok, detail::SymArgs(args_));
|
||||
detail::SymValue val;
|
||||
|
||||
node = dag.insert(pair<const detail::SymKey, detail::SymValue>(key, val)).first;
|
||||
|
||||
if (__unchecked_refcount() == 0) { // new node, set some stats
|
||||
node->second.size = 1 + get_size(args_);
|
||||
node->second.depth = 1 + get_depth(args_);
|
||||
incref();
|
||||
node->first.fixate();
|
||||
// call the factory function if available
|
||||
if (factory) node->second.uniqueNodeStats = factory(*this);
|
||||
}
|
||||
else incref();
|
||||
}
|
||||
|
||||
Sym::Sym(token_t tok) {
|
||||
detail::SymKey key(tok);
|
||||
detail::SymValue val;
|
||||
node = dag.insert(pair<const detail::SymKey, detail::SymValue>(key, val)).first;
|
||||
|
||||
if (__unchecked_refcount() == 0) { // new node, set some stats
|
||||
node->second.size = 1;
|
||||
node->second.depth = 1;
|
||||
incref();
|
||||
|
||||
// call the factory function if available
|
||||
if (factory) node->second.uniqueNodeStats = factory(*this);
|
||||
}
|
||||
else incref();
|
||||
}
|
||||
|
||||
std::pair<Sym,bool> insert_subtree_impl(const Sym& cur, size_t w, const Sym& nw) {
|
||||
if (w-- == 0) return make_pair(nw, !(nw == cur));
|
||||
|
||||
const SymVec& vec = cur.args();
|
||||
std::pair<Sym,bool> result;
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < vec.size(); ++i) {
|
||||
if (w < vec[i].size()) {
|
||||
result = insert_subtree_impl(vec[i], w, nw);
|
||||
if (result.second == false) return std::make_pair(cur, false); // unchanged
|
||||
break;
|
||||
}
|
||||
w -= vec[i].size();
|
||||
}
|
||||
SymVec newvec = cur.args();
|
||||
newvec[i] = result.first;
|
||||
return make_pair(Sym(cur.token(), newvec), true);
|
||||
}
|
||||
|
||||
Sym insert_subtree(const Sym& cur, size_t w, const Sym& nw) {
|
||||
return insert_subtree_impl(cur,w,nw).first;
|
||||
}
|
||||
Sym get_subtree(const Sym& cur, size_t w) {
|
||||
if (w-- == 0) return cur;
|
||||
|
||||
const SymVec& vec = cur.args();
|
||||
for (unsigned i = 0; i < vec.size(); ++i) {
|
||||
if (w < vec[i].size()) return get_subtree(vec[i], w);
|
||||
w-=vec[i].size();
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
|
||||
|
||||
164
eo/contrib/mathsym/sym/Sym.h
Normal file
164
eo/contrib/mathsym/sym/Sym.h
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Copyright (C) 2005 Maarten Keijzer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef SYMNODE_H_
|
||||
#define SYMNODE_H_
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#if __GNUC__ == 3
|
||||
#include <backward/hash_map.h>
|
||||
#else
|
||||
#include <hash_map.h>
|
||||
using std::hash_map;
|
||||
#endif
|
||||
|
||||
/* Empty 'extra statistics' structure, derive from this to keep other characteristics of nodes */
|
||||
struct UniqueNodeStats { virtual ~UniqueNodeStats(){} };
|
||||
|
||||
#include "SymImpl.h"
|
||||
#include "token.h"
|
||||
|
||||
typedef hash_map<detail::SymKey, detail::SymValue, detail::SymKey::Hash> SymMap;
|
||||
typedef SymMap::iterator SymIterator;
|
||||
class Sym;
|
||||
|
||||
typedef std::vector<Sym> SymVec;
|
||||
|
||||
/* Sym is the tree, for which all the nodes are stored in a hash table.
|
||||
* This makes checking for equality O(1) */
|
||||
|
||||
class Sym
|
||||
{
|
||||
public:
|
||||
|
||||
Sym() : node(dag.end()) {}
|
||||
explicit Sym(token_t token, const SymVec& args);
|
||||
explicit Sym(token_t token, const Sym& args);
|
||||
explicit Sym(token_t var);
|
||||
|
||||
explicit Sym(SymIterator it) : node(it) { incref(); }
|
||||
|
||||
Sym(const Sym& oth) : node(oth.node) { incref(); }
|
||||
~Sym() { decref(); }
|
||||
|
||||
const Sym& operator=(const Sym& oth) {
|
||||
if (oth.node == node) return *this;
|
||||
decref();
|
||||
node = oth.node;
|
||||
incref();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* Unique Stats are user defined */
|
||||
UniqueNodeStats* extra_stats() const { return empty()? 0 : node->second.uniqueNodeStats; }
|
||||
|
||||
int hashcode() const { detail::SymKey::Hash hash; return hash(node->first); }
|
||||
|
||||
// Friends, need to touch the node
|
||||
friend struct detail::SymKey::Hash;
|
||||
friend struct detail::SymKey;
|
||||
|
||||
unsigned refcount() const { return empty()? 0: node->second.refcount; }
|
||||
|
||||
bool operator==(const Sym& other) const {
|
||||
return node == other.node;
|
||||
}
|
||||
bool operator!=(const Sym& other) const { return !(*this == other); }
|
||||
|
||||
bool empty() const { return node == dag.end(); }
|
||||
|
||||
/* Support for traversing trees */
|
||||
unsigned arity() const { return node->first.arity(); }
|
||||
token_t token() const { assert(!empty()); return node->first.token; }
|
||||
|
||||
const SymVec& args() const { return node->first.vec(); }
|
||||
|
||||
/* size() - depth */
|
||||
unsigned size() const { return empty()? 0 : node->second.size; }
|
||||
unsigned depth() const { return empty()? 0 : node->second.depth; }
|
||||
|
||||
SymMap::iterator iterator() const { return node; }
|
||||
|
||||
/* Statics accessing some static members */
|
||||
static SymMap& get_dag() { return dag; }
|
||||
|
||||
/* This function can be set to create some UniqueNodeStats derivative that can contain extra stats for a node,
|
||||
* it can for instance be used to create ERC's and what not. */
|
||||
static void set_factory_function(UniqueNodeStats* (*f)(const Sym&)) { factory=f; }
|
||||
static void clear_factory_function() { factory = 0; }
|
||||
|
||||
static void set_extra_dtor( void (*extra)(token_t) ) { extra_dtor = extra; }
|
||||
|
||||
unsigned address() const { return reinterpret_cast<unsigned>(&*node); }
|
||||
|
||||
private :
|
||||
|
||||
// implements getting subtrees
|
||||
Sym private_get(size_t w) const;
|
||||
|
||||
unsigned __unchecked_refcount() const { return node->second.refcount; }
|
||||
|
||||
void incref() {
|
||||
if (!empty())
|
||||
++(node->second.refcount);
|
||||
}
|
||||
void decref() {
|
||||
if (!empty() && --(node->second.refcount) == 0) {
|
||||
if (extra_dtor) {
|
||||
extra_dtor(token());
|
||||
}
|
||||
dag.erase(node);
|
||||
}
|
||||
}
|
||||
|
||||
// The one and only data member, an iterator into the static map below
|
||||
SymIterator node;
|
||||
|
||||
// A static hash_map that contains all live nodes..
|
||||
static SymMap dag;
|
||||
|
||||
// Factory function for creating extra node stats, default will be 0
|
||||
static UniqueNodeStats* (*factory)(const Sym&);
|
||||
|
||||
static void (*extra_dtor)(token_t);
|
||||
|
||||
};
|
||||
|
||||
/* Utility hash functor for syms */
|
||||
class HashSym {
|
||||
public:
|
||||
int operator()(const Sym& sym) const { return sym.hashcode(); }
|
||||
};
|
||||
|
||||
/* Utility Functions */
|
||||
|
||||
// get_subtree retrieves a subtree by standard ordering (0=root, and then depth first)
|
||||
Sym get_subtree(const Sym& org, size_t w);
|
||||
|
||||
// insert_subtree uses the same ordering as get and inserts the second argument, returning a new tree
|
||||
Sym insert_subtree(const Sym& org, size_t w, const Sym& nw);
|
||||
|
||||
/* Get the successor from the hashtable, no particular purpose other than an interesting way to mutate */
|
||||
inline Sym next(const Sym& sym) {
|
||||
SymIterator it = sym.iterator();
|
||||
++it;
|
||||
if (it == Sym::get_dag().end()) it = Sym::get_dag().begin();
|
||||
return Sym(it);
|
||||
}
|
||||
|
||||
#endif
|
||||
109
eo/contrib/mathsym/sym/SymImpl.cpp
Normal file
109
eo/contrib/mathsym/sym/SymImpl.cpp
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright (C) 2005 Maarten Keijzer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "Sym.h"
|
||||
|
||||
using namespace std;
|
||||
namespace detail {
|
||||
|
||||
class SymArgsImpl {
|
||||
public:
|
||||
vector<Sym> owned_args;
|
||||
};
|
||||
|
||||
size_t SymArgs::len() const {
|
||||
return vec().size();
|
||||
}
|
||||
|
||||
SymArgs::SymArgs() : pimpl( new SymArgsImpl ) {
|
||||
args_ptr = &pimpl->owned_args;
|
||||
}
|
||||
|
||||
SymArgs::SymArgs(const std::vector<Sym>& v) : pimpl(0) {
|
||||
args_ptr = &v;
|
||||
}
|
||||
|
||||
SymArgs::~SymArgs() {
|
||||
delete pimpl;
|
||||
}
|
||||
|
||||
SymArgs::SymArgs(const SymArgs& args) : pimpl(0), args_ptr(args.args_ptr) {
|
||||
if (args.pimpl && args.args_ptr == &args.pimpl->owned_args) {
|
||||
pimpl = new SymArgsImpl(*args.pimpl);
|
||||
args_ptr = &pimpl->owned_args;
|
||||
}
|
||||
}
|
||||
|
||||
const SymArgs& SymArgs::operator=(const SymArgs& args) {
|
||||
if (args.pimpl && args.args_ptr == &args.pimpl->owned_args) {
|
||||
pimpl = new SymArgsImpl(*args.pimpl);
|
||||
args_ptr = &pimpl->owned_args;
|
||||
} else {
|
||||
args_ptr = args.args_ptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void SymArgs::fixate() const {
|
||||
assert(pimpl == 0);
|
||||
pimpl = new SymArgsImpl;
|
||||
pimpl->owned_args = *args_ptr;
|
||||
args_ptr = &pimpl->owned_args;
|
||||
}
|
||||
|
||||
// For Tackett's hashcode
|
||||
#define PRIMET 21523
|
||||
#define HASHMOD 277218551
|
||||
|
||||
const int nprimes = 4;
|
||||
const unsigned long primes[] = {3221225473ul, 201326611ul, 1610612741ul, 805306457ul};
|
||||
|
||||
int SymKey::calc_hash() const {
|
||||
unsigned long hash = unsigned(token);
|
||||
hash *= PRIMET;
|
||||
|
||||
const SymVec& v = args.vec();
|
||||
for (unsigned i = 0; i < v.size(); ++i) {
|
||||
hash += ( (v[i].address() >> 3) * primes[i%nprimes]) % HASHMOD;
|
||||
}
|
||||
|
||||
return hash;// % HASHMOD;
|
||||
}
|
||||
|
||||
bool SymKey::operator==(const SymKey& other) const {
|
||||
if (token != other.token) return false;
|
||||
return args.vec() == other.args.vec();
|
||||
}
|
||||
|
||||
/* Just to store this info somewhere:
|
||||
*
|
||||
* Address Based Hash Function Implementation
|
||||
* uint32 address_hash(char* addr)
|
||||
* {
|
||||
* register uint32 key;
|
||||
* key = (uint32) addr;
|
||||
* return (key >> 3) * 2654435761;
|
||||
* }
|
||||
*/
|
||||
|
||||
SymValue::SymValue() : refcount(0), size(0), depth(0), uniqueNodeStats(0) {}
|
||||
|
||||
SymValue::~SymValue() { delete uniqueNodeStats; }
|
||||
|
||||
|
||||
|
||||
} // namespace detail
|
||||
103
eo/contrib/mathsym/sym/SymImpl.h
Normal file
103
eo/contrib/mathsym/sym/SymImpl.h
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright (C) 2005 Maarten Keijzer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef __SYM_IMPL_H__
|
||||
#define __SYM_IMPL_H__
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "token.h"
|
||||
|
||||
class Sym;
|
||||
namespace detail {
|
||||
|
||||
class SymArgsImpl;
|
||||
class SymArgs {
|
||||
|
||||
mutable SymArgsImpl* pimpl; // contains circular reference to vector<Sym>
|
||||
mutable const std::vector<Sym>* args_ptr;
|
||||
|
||||
public:
|
||||
|
||||
SymArgs();
|
||||
SymArgs(const std::vector<Sym>& v);
|
||||
~SymArgs();
|
||||
|
||||
SymArgs(const SymArgs& args);
|
||||
const SymArgs& SymArgs::operator=(const SymArgs& other);
|
||||
|
||||
size_t len() const;
|
||||
const std::vector<Sym>& vec() const { return *args_ptr; }
|
||||
void fixate() const;
|
||||
};
|
||||
|
||||
class SymKey
|
||||
{
|
||||
public:
|
||||
SymKey(token_t _token) : args(), token(_token) {}
|
||||
SymKey(token_t _token, const detail::SymArgs& _args) : args(_args), token(_token) {}
|
||||
|
||||
|
||||
private:
|
||||
detail::SymArgs args;
|
||||
public:
|
||||
bool operator==(const SymKey& other) const;
|
||||
|
||||
struct Hash
|
||||
{
|
||||
int operator()(const SymKey& k) const { return k.calc_hash(); };
|
||||
};
|
||||
|
||||
unsigned arity() const { return args.len(); }
|
||||
const std::vector<Sym>& vec() const { return args.vec(); }
|
||||
|
||||
token_t token; // identifies the function
|
||||
|
||||
// fixates (i.e. claims memory) for the embedded vector of Syms
|
||||
void fixate() const { args.fixate(); }
|
||||
|
||||
private:
|
||||
int calc_hash() const;
|
||||
};
|
||||
|
||||
struct SymValue
|
||||
{
|
||||
friend class Sym;
|
||||
|
||||
SymValue();
|
||||
~SymValue();
|
||||
|
||||
unsigned getRefCount() const { return refcount; }
|
||||
unsigned getSize() const { return size; }
|
||||
unsigned getDepth() const { return depth; }
|
||||
|
||||
private :
|
||||
|
||||
// for reference counting
|
||||
unsigned refcount;
|
||||
|
||||
// some simple stats
|
||||
unsigned size;
|
||||
unsigned depth;
|
||||
|
||||
UniqueNodeStats* uniqueNodeStats;
|
||||
};
|
||||
|
||||
|
||||
} // namespace detail
|
||||
|
||||
#endif
|
||||
|
||||
28
eo/contrib/mathsym/sym/token.h
Normal file
28
eo/contrib/mathsym/sym/token.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (C) 2005 Maarten Keijzer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef TOKEN_H
|
||||
#define TOKEN_H
|
||||
|
||||
|
||||
typedef unsigned token_t;
|
||||
|
||||
struct functor_t {
|
||||
token_t token;
|
||||
unsigned arity;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in a new issue