Added mathsym+tcc and boost against all advice

This commit is contained in:
maartenkeijzer 2005-10-06 12:13:53 +00:00
commit 90702a435d
51 changed files with 14409 additions and 0 deletions

340
eo/contrib/mathsym/COPYING Normal file
View file

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

View file

@ -0,0 +1,87 @@
COMPILEFLAGS=-Wno-deprecated -g -Wall -Wshadow #-DINTERVAL_DEBUG
OPTFLAGS= -O2 -DNDEBUG
PROFILE_FLAGS=#-pg
INCLUDES=-I. -Isym -Ifun -Igen -Ieval -Iregression -I../../src -Ieo_interface -I..
CPPFLAGS=$(COMPILEFLAGS) $(OPTFLAGS) $(INCLUDES) $(PROFILE_FLAGS) -D__I386__ -DSIZEOF_UNSIGNED_LONG=4
EXTLIBS=tcc/libtcc.a tcc/libtcc1.a ../../src/libeo.a ../../src/utils/libeoutils.a
LIBS=${EXTLIBS} -ldl
SYMLIB=libsym.a
VPATH=sym fun gen eval regression eo_interface
CXXSOURCES=FunDef.cpp Sym.cpp SymImpl.cpp SymOps.cpp sym_compile.cpp TreeBuilder.cpp LanguageTable.cpp\
Dataset.cpp ErrorMeasure.cpp Scaling.cpp TargetInfo.cpp BoundsCheck.cpp util.cpp NodeSelector.cpp\
eoSymCrossover.cpp
PROGRAMS=test/test test/test_compile test/testeo
OBJS= $(CXXSOURCES:.cpp=.o) c_compile.o
all: tcc/ symreg
include $(CXXSOURCES:.cpp=.d) symreg.d
clean:
rm *.o *.d $(PROGRAMS) $(SYMLIB) symreg || true
distclean: clean
rm -rf tcc
symreg: libsym.a symreg.o $(EXTLIBS)
$(CXX) -o symreg symreg.o libsym.a $(LIBS) $(PROFILE_FLAGS)
libsym.a: $(OBJS)
rm libsym.a; ar cq $(SYMLIB) $(OBJS)
check: $(PROGRAMS)
test/test && test/test_compile && test/testeo && echo "all tests succeeded"
test/test: test/test.o ${SYMLIB}
$(CXX) -o test/test test/test.o ${SYMLIB} ${LIBS}
test/test_compile: test/test_compile.o ${SYMLIB}
$(CXX) -o test/test_compile test/test_compile.o $(SYMLIB) ${LIBS}
test/testeo: test/testeo.o ${SYMLIB}
$(CXX) -o test/testeo test/testeo.o $(SYMLIB) ${LIBS}
# eo
../../src/libeo.a:
make -C ../../src
../../src/utils/libeoutils.a:
make -C ../../src/utils
# tiny cc
tcc/: tcc.tar.gz
tar xvfz tcc.tar.gz && cd tcc && ./configure && make
tcc/Makefile: tcc/
cd tcc && ./configure
tcc/libtcc.a: tcc/Makefile
make -Ctcc
tcc/libtcc1.a: tcc/Makefile
make -Ctcc
#rules
c_compile.o: eval/c_compile.c
gcc -c eval/c_compile.c -I./tcc $(COMPILEFLAGS) $(OPTFLAGS)
%.o:%.cpp
$(CXX) -o $@ -c $< $(CPPFLAGS) $(INCLUDE)
%.d: %.cpp
$(SHELL) -ec '$(CXX) -M $(CPPFLAGS) $< | sed "s/$*.o/& $@/g" > $@ '
%.d: %.c
$(SHELL) -ec '$(CXX) -M $(CPPFLAGS) $< | sed "s/$*.o/& $@/g" > $@ '

172
eo/contrib/mathsym/README Normal file
View file

@ -0,0 +1,172 @@
This is not yet another gp system (nyagp). For one, it is not general.
It does one thing, find mathematical functions, and tries to do that well.
So, if you're trying to steer ants on various New Mexico trails, or build your
own tiny block world, you're in the wrong place. However, if you're interested
in finding mathematical functions either through direct application on data or
running it through a simulator, you might find what you're looking for here.
=== Representation (sym/ + gen/) ========
Mathsym has a few interesting characteristics. First and foremost is the
basic representation. It uses trees, but these trees are stored in a
reference counted hashtable. This means that every subtree that is alive
is stored once and only once. The reference counting mechanism takes care
of memory management.
The idea of using a hashtable (for offline analysis) comes from Walter Tackett, in his
1994 dissertation. The current system is just a real-time implementation of this
idea, adding the reference counting for ease of use.
The hashtable brings overhead. It's still pretty fast, but a string based representation
would run rounds around it. However, by virtue of it storing every subtree only once, it
is fairly tight on memory. This helps tremendously when confronted with growing populations, bloat.
The hashtable implementation can not stop bloat, but does make it more manageable. In a typical
GP run, the number of distinct subtrees is only 10-20% of the total number of subtrees.
Other advantages of the hashtable are in the ability to examine the run more thoroughly. It is easy
to check how many subtrees are present in the system, and for each subtree you can check the reference
count.
The basic tree is called a Sym. A Sym is simply a tree, and has children, accessible through args().
A Sym simply contains an iterator (== decorated pointer) to an entry in the hashtable.
Every time you create a Sym, it is either looked up in the hashtable or added to the hashtable.
A Sym has several members: size, depth, args, etc. One interesting member is the refcount().
This returns the reference count of the Sym in the hashtable, and thus returns the number
of distinct contexts in which the Sym is used.
Another nice thing of these hashtable Syms is that a check for equality reduces to a pointer comparison.
The Sym nodes are identified by a simple token, of type token_t (usually an unsigned int). It
is completely generic and could conceivably be adapted to steer ants. The rest of the library
is however targeted at mathematical functions purely.
sym/Sym.h is the file to look into for the functionality provided by Sym. The sym/ directory
is where the source files are stored that are relevant for the generic Sym functionality. The
'gen/' directory contains some generic functionality to build and traverse trees, independent of
the function and terminal set.
The file sym/README.cpp documents the use of the sym library for general GP use.
=== Function Set (fun/) ===
The standard GP function set of binary functions: addition, multiplication, subtraction and
division is NOT supported.
What is however supported are the functions of:
summation: arbitrary arity, arity zero meaning 0.0. Arity 2 is standard addition
product: arbitrary arity, arity zero meaning 1.0. Arity 2 is standard multiplication
inversion: 1.0 / x. Only arity 1
unary minus: -x. Only arity 1
Plus a whole bunch of other functions (see "fun/FunDef.h")
The reason for this is the observation (actually from a friend of mine, thanks Luuk),
that this set of functions is complete and slightly more orthogonal than a binary set.
The directory 'fun' contains the functionality for the function and terminal set, together
with ERC's etc. fun/FunDef.cpp contains the definition of the functionality. Stuff can be
added here, but best to contact me if you miss particular functions.
=== Evaluation (eval/) ===
The second important thing is evaluation. Although Syms can be evaluated through an interpreter,
this is not the fastest way to go about with it. The standard way of evaluating a Sym is to
first *compile* it to a function, and then run it in your favourite environment. Compilation
is done through the use of the excellent tinycc compiler, which is blazingly fast and produces
pretty good functions.
Compilation comes in several flavours: compile a single function and retrieve a pointer to a function
of signature:
double func(const double* x);
where x is the input array. Another option is to compile a bunch of functions in one go, and retrieve an array
of such function pointers. The Syms are simply printed and compiled. An example:
double func(const double* x) { return x*x + x * 1./x; }
The batch version proceeds significantly more quickly than calling compile every time. The function pointers
can be given to a simulation for extremely quick evaluation.
A third option is to compile a complete population in one go, and return a single pointer of signature
void func(const double* x, double* y);
Where 'y' is the (preallocated) output array. This allows to evaluate a complete population in one function
call, storing the results in 'y'. It uses the hashtable to store every calculation only once. An example
for the two function x*x + x*1./x and x + sin(x*x) is:
void func(const double* x, double* y) {
double a0 = x;
double a1 = a0 * a0;
double a2 = 1.0;
double a3 = a2 / a0;
double a4 = a2 * a3;
y[0] = a4;
double a5 = sin(a1);
double a6 = a0 + a5;
y[1] = a6;
}
This is the fastest way to evaluate even humongous populations quickly. You might be surprised at
the amount of code re-use in a GP population.
The three compilation functions can be found in eval/sym_compile.h
A limiting factor in tinycc is that the struct TCCState that is used to hold the compilation context,
is not really self-contained. This unfortunately means that with every call to 'compile' ALL previous
pointers that have been produced are invalidated. I'm still looking at ways to circumvent this.
To work with mathsym, a few small changes in tccelf.c were necessary, check README.TCC for details.
=== Interval Arithmetic (eval/) ===
GP is pretty good at finding mathematical expressions that are numerically unsound. Take for instance
the function '1 / x'. This is well defined only when x is strictly positive, but will lead to problems
when x equals 0. The standard answer is to define some pseudo-arithmetical function called 'protected
division' that will return some value (usually 1) when a division by zero occurs. This leads to a number
of protected functions (sqrt, log, tan, etc.) which all need to be protected. Interpreting results from
GP using such functions is in general hard.
Interval arithmetic (through another excellent library boost/numeric/interval) is used to calculate
if particular functions can conceivably produce problems. This completely annihilates the use for Koza-style
protected operators and is a more safe and sound method. For interval arithmetic to function, the bounds
on the input variables need to be known. As for every function we can calculate a guarenteed,
though not necessarily tight, output interval given the input intervals, we can check arbitrary functions
for possible problems. If, for example for division, the input interval contains 0, we know that a division
by zero is theoretically possible. It's then best to throw away the entire function.
Interval Arithmetic is accessible through the class IntervalBoundsCheck (eval/BoundsCheck.h)
=== More generic support (gen/) ===
The gen subdirectory contains some general utility classes for defining function sets and for
creating trees. The idea is that these functions are generic and only append on the sym/ part
of the library. Unfortunately, the language table currently needs an ERC function, a default
implementation is hidden inside fun/FunDef.cpp. Will fix at some point.
gen/LanguageTable.cpp -> defines the functions/terminals that can be used
gen/TreeBuilder.cpp -> can create trees based on a LanguageTable
=== Data and Errors (regression/) ===
The above classes are generic and apply for any type of problem where a mathematical function can be
used to steer some process, run a simulation, whatever. First check the intervals, then compile the
Sym(s) to a (set of) function pointer(s), and use the pointers in some way to evaluate for fitness.
One particular type of problem for which support is built in is 'symbolic regression'. This type of
problem involves finding an mathematical input/output relationship based on some data.
To enable this, regression/ introduces the class Dataset to contain the data and ErrorMeasure to calculate
error. Currently supported: mean squared error, mean absolute error and mean squared error scaled (proportional
to correlation squared). They use some helper classes such as Scaling and TargetInfo.
=== EO interface (eo_interface/) ===
Contains the classes to make it all work with EO. Check the root application 'symreg' for ways to use this

View file

@ -0,0 +1,5 @@
To refrain tcc from searching for libtcc1.a in the path, uncomment
out the lines looking for that in 'tccelf.c'. Search for libtcc1 and uncomment
these two lines. All should be well.

View file

@ -0,0 +1,62 @@
/*
* 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 EOSYM_H_
#define EOSYM_H_
#include <EO.h>
#include <Sym.h>
#include <FunDef.h>
template <class Fitness>
class EoSym : public EO<Fitness>, public Sym {
public:
void set(const Sym& sym) {
invalidate();
static_cast<Sym*>(this)->operator=(sym);
}
virtual void printOn(ostream& os) const;
virtual void readFrom(istream& is);
};
template <class Fitness>
void EoSym<Fitness>::printOn(ostream& os) const {
EO<Fitness>::printOn(os);
os << ' ';
write_raw(os, *this);
}
template <class Fitness>
void EoSym<Fitness>::readFrom(istream& is) {
EO<Fitness>::readFrom(is);
read_raw(is, *this);
}
template <class Fitness>
inline std::ostream& operator<<(std::ostream& os, const EoSym<Fitness>& f) { f.printOn(os); return os; }
template <class Fitness>
inline istream& operator>>(std::istream& is, EoSym<Fitness>& f) { f.readFrom(is); return os; }
#endif

View file

@ -0,0 +1,76 @@
/*
* 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>
#include <NodeSelector.h>
#include <eoSymCrossover.h>
#include <utils/eoRNG.h>
bool subtree_quad(Sym& a, Sym& b, NodeSelector& select) {
unsigned i = select.select_node(a);
unsigned j = select.select_node(b);
Sym aprime = insert_subtree(a, i, get_subtree(b, j));
Sym bprime = insert_subtree(b, j, get_subtree(a, i));
a = aprime;
b = bprime;
return true;
}
bool subtree_bin(Sym& a, const Sym& b, NodeSelector& select) {
unsigned i = select.select_node(a);
unsigned j = select.select_node(b);
a = insert_subtree(a, i, get_subtree(b,j));
return true;
}
Sym homologous_binimpl(Sym a, Sym b) {
bool use_a = rng.random(2);
token_t head = (use_a? a : b).token();
SymVec args = use_a?a.args() : b.args();
const SymVec& a_args = a.args();
const SymVec& b_args = b.args();
unsigned mn = std::min(a_args.size(), b_args.size());
bool changed = !use_a;
for (unsigned i = 0; i < mn; ++i) {
args[i] = homologous_binimpl(a_args[i], b_args[i]);
if (args[i] != a_args[i]) {
changed = true;
}
}
return changed? Sym(head, args) : a;
}
bool homologous_bin(Sym& a, const Sym& b) {
if (a==b) return false;
Sym org = a;
a = homologous_binimpl(a,b);
return org != a;
}

View file

@ -0,0 +1,64 @@
/*
* 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 EOSYMCROSSOVER_H
#define EOSYMCROSSOVER_H
class NodeSelector;
class Sym;
#include <eoOp.h>
extern bool subtree_quad(Sym& a, Sym& b, NodeSelector& select);
template <class EoType>
class eoQuadSubtreeCrossover : public eoQuadOp<EoType> {
NodeSelector& node_selector;
public:
eoQuadSubtreeCrossover(NodeSelector& _node_selector) : node_selector(_node_selector) {}
bool operator()(EoType& a, EoType& b) { return subtree_quad(a,b, node_selector); }
};
extern bool subtree_bin(Sym& a, const Sym& b, NodeSelector& select);
template <class EoType>
class eoBinSubtreeCrossover : public eoBinOp<EoType> {
NodeSelector& node_selector;
public :
eoBinSubtreeCrossover(NodeSelector& _node_selector) : node_selector(_node_selector) {}
bool operator()(EoType& a, const EoType& b) { return subtree_bin(a, b, node_selector); }
};
/** Yet another homologous crossover, afaik not particularly
* defined in the literature
*/
extern bool homologous_bin(Sym& a, const Sym& b);
template <class EoType>
class eoBinHomologousCrossover : public eoBinOp<EoType> {
public:
bool operator()(EoType& a, const EoType& b) {
return homologous_bin(a,b);
}
};
#endif

View file

@ -0,0 +1,94 @@
/*
* 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 SYMEVAL_H
#define SYMEVAL_H
#include <Sym.h>
#include <ErrorMeasure.h>
#include <BoundsCheck.h>
#include <eoPopEvalFunc.h>
template <class EoType>
class eoSymPopEval : public eoPopEvalFunc<EoType> {
BoundsCheck& check;
ErrorMeasure& measure;
public:
eoSymPopEval(BoundsCheck& _check, ErrorMeasure& _measure) :
check(_check), measure(_measure) {}
/** apparently this thing works on two populations, why?
*
* In any case, currently only implemented the population wide
* evaluation version, as that one is much faster. This because the
* compile going on behind the scenes is much faster when done in one
* go (and using subtree similarity) then when done on a case by case
* basis.
*/
void operator()(eoPop<EoType>& p1, eoPop<EoType>& p2) {
std::vector<unsigned> unevaluated;
std::vector<Sym> tmppop;
for (unsigned i = 0; i < p1.size(); ++i) {
if (p1[i].invalid()) {
if (check.in_bounds(p1[i])) {
unevaluated.push_back(i);
tmppop.push_back( static_cast<Sym>(p1[i]) );
} else {
p1[i].fitness( measure.worst_performance() );
}
}
}
for (unsigned i = 0; i < p2.size(); ++i) {
if (p2[i].invalid()) {
if (check.in_bounds(p2[i])) {
unevaluated.push_back(p1.size() + i);
tmppop.push_back( static_cast<Sym>(p2[i]) );
} else {
p2[i].fitness( measure.worst_performance() ); // pretty bad error
}
}
}
std::vector<ErrorMeasure::result> result = measure.calc_error(tmppop);
for (unsigned i = 0; i < result.size(); ++i) {
unsigned idx = unevaluated[i];
if (idx < p1.size()) {
p1[idx].fitness(result[i].error);
} else {
idx -= p1.size();
p2[idx].fitness(result[i].error);
}
}
}
};
#endif

View file

@ -0,0 +1,64 @@
/*
* 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 EOSYMINIT_H
#define EOSYMINIT_H
#include <eoInit.h>
#include <TreeBuilder.h>
/** Default initializer, Koza style */
template <class EoType>
class eoSymInit : public eoInit<EoType> {
TreeBuilder& builder;
double own_grow_prob;
unsigned own_max_depth;
double& grow_prob;
unsigned& max_depth;
public:
/** By default build ramped half and half with max depth 6 */
eoSymInit(TreeBuilder& _builder)
: builder(_builder),
own_grow_prob(0.5),
own_max_depth(6),
grow_prob(own_grow_prob),
max_depth(own_max_depth)
{}
/** Control the grow_prob and max_depth externally */
eoSymInit(TreeBuilder& _builder, double& _grow_prob, unsigned& _max_depth)
: builder(_builder),
grow_prob(_grow_prob),
max_depth(_max_depth)
{}
/** build the tree */
void operator()(EoType& tree) {
int depth_to_use = rng.random(max_depth-2) + 2; // two levels minimum
builder.build_tree(tree, depth_to_use, rng.flip(grow_prob));
}
};
#endif

View file

@ -0,0 +1,122 @@
/*
* 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 SYMMUTATE_H
#define SYMMUTATE_H
#include <TreeBuilder.h>
#include <NodeSelector.h>
#include <eoSym.h>
#include <eoOp.h>
template <class EoType>
class eoSymSubtreeMutate : public eoMonOp<EoType> {
TreeBuilder& subtree_builder;
NodeSelector& node_selector;
public :
eoSymSubtreeMutate(TreeBuilder& _subtree_builder, NodeSelector& _node_selector)
: subtree_builder(_subtree_builder), node_selector(_node_selector) {}
bool operator()(EoType& tomutate) {
unsigned xover_point = node_selector.select_node(tomutate);
// create subtree
Sym newtree = subtree_builder.build_tree(6, true); // TODO, parameterize
static_cast<Sym&>(tomutate) = insert_subtree(tomutate, xover_point, newtree);
return true;
}
};
/** Class for doing node mutation
* Two parameters:
*
* mutation_rate (the rate at which to do mutation)
* is_rate_absolute : don't rescale the rate to the size of the tree
*/
template <class EoType>
class eoSymNodeMutate : public eoMonOp<EoType> {
LanguageTable& table;
double own_mutation_rate;
bool own_is_rate_absolute;
// these two can (should?) move to an impl file
bool mutate(Sym& sym, double p) {
std::pair<Sym, bool> r = do_mutate(sym, p);
sym = r.first;
return r.second;
}
std::pair<Sym, bool> do_mutate(Sym sym, double p) {
bool changed = false;
SymVec args = sym.args();
if (rng.flip(p)) {
token_t new_token = table.get_random_function( args.size());
if (new_token != sym.token()) changed = true;
sym = Sym(new_token, args);
}
for (unsigned i = 0; i < args.size(); ++i) {
std::pair<Sym,bool> r = do_mutate(args[i], p);
changed |= r.second;
if (r.second)
args[i] = r.first;
}
if (changed)
return std::make_pair(Sym(sym.token(), args), true);
// else
return std::make_pair(sym, false);
}
public:
double& mutation_rate;
bool& is_rate_absolute;
eoSymNodeMutate(LanguageTable& _table)
: table(_table),
own_mutation_rate(1.0),
own_is_rate_absolute(false), // this means a probability of node mutation of 1/sym.size()
mutation_rate(own_mutation_rate),
is_rate_absolute(own_is_rate_absolute)
{}
eoSymNodeMutate(LanguageTable& _table, double& _mutation_rate, bool& _is_rate_absolute)
: table(_table),
mutation_rate(_mutation_rate),
is_rate_absolute(_is_rate_absolute)
{}
bool operator()(EoType& _eo) {
double p = mutation_rate;
if (!is_rate_absolute) p /= _eo.size();
return mutate(_eo, p);
}
};
#endif

View file

@ -0,0 +1,80 @@
/*
* 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 <vector>
#include "BoundsCheck.h"
#include <Sym.h>
#include <FunDef.h>
#include <sstream>
using namespace std;
class IntervalBoundsCheckImpl {
public :
vector<Interval> bounds;
};
IntervalBoundsCheck::IntervalBoundsCheck(const vector<double>& mins, const vector<double>& maxes) {
pimpl = new IntervalBoundsCheckImpl;
vector<Interval>& b = pimpl->bounds;
b.resize( mins.size());
for (unsigned i = 0; i < b.size(); ++i) {
b[i] = Interval(mins[i], maxes[i]);
}
}
IntervalBoundsCheck::~IntervalBoundsCheck() { delete pimpl; }
IntervalBoundsCheck::IntervalBoundsCheck(const IntervalBoundsCheck& that) { pimpl = new IntervalBoundsCheckImpl(*that.pimpl); }
IntervalBoundsCheck& IntervalBoundsCheck::operator=(const IntervalBoundsCheck& that) { *pimpl = *that.pimpl; return *this; }
bool IntervalBoundsCheck::in_bounds(const Sym& sym) const {
Interval bounds;
try {
bounds = eval(sym, pimpl->bounds);
if (!valid(bounds)) return false;
} catch (interval_error) {
return false;
}
return true;
}
std::string IntervalBoundsCheck::get_bounds(const Sym& sym) const {
try {
Interval bounds = eval(sym, pimpl->bounds);
if (!valid(bounds)) return "err";
ostringstream os;
os << bounds;
return os.str();
} catch (interval_error) {
return "err";
}
}
std::pair<double, double> IntervalBoundsCheck::calc_bounds(const Sym& sym) const {
Interval bounds = eval(sym, pimpl->bounds);
return make_pair(bounds.lower(), bounds.upper());
}

View file

@ -0,0 +1,58 @@
/*
* 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 BOUNDS_CHECK_H_
#define BOUNDS_CHECK_H_
#include <string>
class IntervalBoundsCheckImpl;
class Sym;
class BoundsCheck {
public :
virtual ~BoundsCheck() {};
virtual bool in_bounds(const Sym&) const = 0;
virtual std::string get_bounds(const Sym&) const = 0;
};
// checks if a formula keeps within bounds using interval arithmetic
class IntervalBoundsCheck : public BoundsCheck {
IntervalBoundsCheckImpl* pimpl;
public:
IntervalBoundsCheck(const std::vector<double>& minima, const std::vector<double>& maxima);
~IntervalBoundsCheck();
IntervalBoundsCheck(const IntervalBoundsCheck&);
IntervalBoundsCheck& operator=(const IntervalBoundsCheck&);
bool in_bounds(const Sym&) const;
std::string get_bounds(const Sym&) const;
std::pair<double, double> calc_bounds(const Sym&) const;
};
class NoBoundsCheck : public BoundsCheck {
bool in_bounds(const Sym&) const { return false; }
std::string get_bounds(const Sym&) const { return ""; }
};
#endif

View file

@ -0,0 +1,115 @@
/*
* 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 INTERVAL__H__
#define INTERVAL__H__
#include <boost/numeric/interval.hpp>
#include <iostream>
#include <limits>
typedef boost::numeric::interval_lib::rounded_transc_exact<double> RoundingTransc;
typedef boost::numeric::interval_lib::save_state<RoundingTransc> Rounding;
typedef boost::numeric::interval_lib::checking_base<double> Checking;
typedef boost::numeric::interval_lib::policies<Rounding,Checking> Policy;
typedef boost::numeric::interval<double, Policy> Interval;
struct interval_error{};
inline bool valid(const Interval& val) {
if (!finite(val.lower()) || !finite(val.upper())) return false;
return val.lower() > -1e10 && val.upper() < 1e10;
}
inline Interval sqrt(const Interval& val) {
if (val.lower() < 0.0) {
return Interval::whole();
}
return boost::numeric::sqrt(val);
}
inline Interval sqr(const Interval& val) {
return square(val);
}
inline Interval acos(const Interval& val) {
if (val.lower() < 1.0 || val.upper() > 1.0) {
return Interval::whole();
}
return boost::numeric::acos(val);
}
inline Interval asin(const Interval& val) {
if (val.lower() < 1.0 || val.upper() > 1.0) {
return Interval::whole();
}
return boost::numeric::asin(val);
}
inline Interval acosh(const Interval& val) {
if (val.lower() < 1.0) return Interval::whole();
return boost::numeric::acosh(val);
}
inline
std::ostream& operator<<(std::ostream& os, const Interval& val) {
os << '[' << val.lower() << ", " << val.upper() << ']';
return os;
}
#ifdef TEST_INTERVAL
using namespace std;
using namespace boost::numeric;
int main() {
Interval a(0, 10);
Interval b(-1.5, 2);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "a + b = " << a + b << endl;
cout << "a - b = " << a - b << endl;
cout << "b - a = " << b - a << endl;
cout << "-a = " << -a << endl;
cout << "a * b = " << a * b << endl;
cout << "b/(a+1) = " << b / (a + 1.0) << endl;
cout << "b * a = " << b * a << endl;
cout << "b / a = " << b/a << endl;
cout << "cos(a) = " << cos(a) << endl;
cout << "cos(b) = " << cos(b) << endl;
cout << "log(b) = " << log(b) << endl;
cout << "sqrt(b) = " << sqrt(b) << endl;
cout << "sqrt(a) = " << sqrt(a) << endl;
cout << "sqr(b) = " << sqr(b) << endl;
cout << "exp(b*a)= " << exp(b*a) << endl;
cout << "atan(a) = " << atan(a) << endl;
cout << "cosh(b) = " << cosh(b) << endl;
}
#endif
#endif

View file

@ -0,0 +1,40 @@
#include <stdio.h>
#include <libtcc.h>
#include <math.h>
static TCCState* s = 0;
extern void symc_init() {
if (s != 0) {
tcc_delete(s);
}
s = tcc_new();
if (s == 0) {
fprintf(stderr, "Tiny cc doesn't function properly");
exit(1);
}
}
extern void symc_compile(const char* func_str) {
//printf("Compiling %s\n", func_str);
tcc_compile_string(s, func_str);
}
extern void symc_link() {
tcc_relocate(s);
}
extern void* symc_get_fun(const char* func_name) {
unsigned long val;
tcc_get_symbol(s, &val, func_name);
return (void*) val;
}
extern void* symc_make(const char* func_str, const char* func_name) {
symc_init();
symc_compile(func_str);
symc_link();
return symc_get_fun(func_name);
}

View file

@ -0,0 +1,181 @@
/*
* 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
*/
#if __GNUC__ == 3
#include <backward/hash_map.h>
#else
#include <hash_map.h>
using std::hash_map;
#endif
#include "Sym.h"
#include "FunDef.h"
#include "sym_compile.h"
#include <sstream>
using namespace std;
extern "C" {
void symc_init();
void symc_compile(const char* func_str);
void symc_link();
void* symc_get_fun(const char* func_name);
void* symc_make(const char* func_str, const char* func_name);
}
string make_prototypes() {
string prot = get_prototypes();
prot += "double sqr(double x) { return x*x; }";
return prot;
}
// contains variable names, like 'a0', 'a1', etc.
typedef hash_map<Sym, string, HashSym> HashMap;
HashMap::iterator find_entry(Sym sym, ostream& os, HashMap& map) {
HashMap::iterator result = map.find(sym);
if (result == map.end()) { // new entry
const SymVec& args = sym.args();
vector<string> argstr(args.size());
for (unsigned i = 0; i < args.size(); ++i) {
argstr[i] = find_entry(args[i], os, map)->second;
}
unsigned current_entry = map.size(); // current number of variables defined
// write out the code
const FunDef& fun = get_element(sym.token());
string code = fun.c_print(argstr, vector<string>());
os << "double a" << current_entry << "=" << code << ";\n";
// insert variable ref in map
ostringstream str;
str << 'a' << current_entry;
result = map.insert( make_pair(sym, str.str()) ).first; // only want iterator
}
return result;
}
void write_entry(Sym sym, ostream& os, HashMap& map, unsigned out) {
HashMap::iterator it = find_entry(sym, os, map);
os << "y[" << out << "]=" << it->second << ";\n";
}
multi_function compile(const std::vector<Sym>& syms) {
ostringstream os;
os << make_prototypes();
os << "double func(const double* x, double* y) { \n ";
HashMap map;
for (unsigned i = 0; i < syms.size(); ++i) {
write_entry(syms[i], os, map, i);
}
os << ";}";
string func_str = os.str();
//cout << "compiling " << func_str << endl;
return (multi_function) symc_make(func_str.c_str(), "func");
}
single_function compile(Sym sym) {
ostringstream os;
os << make_prototypes();
os << "double func(const double* x) { return ";
string code = c_print(sym);
os << code;
os << ";}";
string func_str = os.str();
//cout << "compiling " << func_str << endl;
return (single_function) symc_make(func_str.c_str(), "func");
}
/* finds and inserts the full code in a hashmap */
HashMap::iterator find_code(Sym sym, HashMap& map) {
HashMap::iterator result = map.find(sym);
if (result == map.end()) { // new entry
const SymVec& args = sym.args();
vector<string> argstr(args.size());
for (unsigned i = 0; i < args.size(); ++i) {
argstr[i] = find_code(args[i], map)->second;
}
// write out the code
const FunDef& fun = get_element(sym.token());
string code = fun.c_print(argstr, vector<string>());
result = map.insert( make_pair(sym, code) ).first; // only want iterator
}
return result;
}
string print_code(Sym sym, HashMap& map) {
HashMap::iterator it = find_code(sym, map);
return it->second;
}
void compile(const std::vector<Sym>& syms, std::vector<single_function>& functions) {
symc_init();
ostringstream os;
os << make_prototypes();
HashMap map;
for (unsigned i = 0; i < syms.size(); ++i) {
os << "double func" << i << "(const double* x) { return ";
os << print_code(syms[i], map); //c_print(syms[i]);
os << ";}\n";
//symc_compile(os.str().c_str());
//cout << "compiling " << os.str() << endl;
}
#ifdef INTERVAL_DEBUG
//cout << "Compiling " << os.str() << endl;
#endif
symc_compile(os.str().c_str());
symc_link();
functions.resize(syms.size());
for (unsigned i = 0; i < syms.size(); ++i) {
ostringstream os2;
os2 << "func" << i;
functions[i] = (single_function) symc_get_fun(os2.str().c_str());
}
}

View file

@ -0,0 +1,36 @@
/*
* 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 SYMCOMPILE_H_
#define SYMCOMPILE_H_
#include <vector>
typedef double (*single_function)(const double *);
typedef void (*multi_function)(const double*, double*); // last argument is output
/*
* Important, after every call of the functions below, the function pointers of the previous
* call are invalidated. Sorry, but that's the way the cookie crumbles (in tcc)
* */
single_function compile(Sym sym);
multi_function compile(const std::vector<Sym>& sym);
void compile(const std::vector<Sym>& sym, std::vector<single_function>& functions);
#endif

View file

@ -0,0 +1,569 @@
/*
* 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>
#if __GNUC__ == 3
#include <backward/hash_map.h>
#else
#include <hash_map.h>
using std::hash_map;
#endif
#include "Sym.h"
#include "FunDef.h"
#include <LanguageTable.h>
using namespace std;
using namespace boost::numeric;
vector<const FunDef*> language;
token_t add_function(FunDef* function) {
language.push_back(function);
return token_t(language.size()-1);
}
const FunDef& get_element(token_t token) { return *language[token]; }
/* Printing */
string c_print(const Sym& sym) {
return c_print(sym, vector<string>());
}
string c_print(const Sym& sym, const vector<string>& vars) {
const SymVec& args = sym.args();
vector<string> names(args.size());
for (unsigned i = 0; i < args.size(); ++i) {
names[i] = c_print(args[i], vars);
}
return language[sym.token()]->c_print(names, vars);
}
/* Evaluation */
double eval(const Sym& sym, const std::vector<double>& inputs) {
return language[sym.token()]->eval(sym.args(), inputs);
}
/* Interval Logic */
Interval eval(const Sym& sym, const vector<Interval>& inputs) {
const SymVec& args = sym.args();
vector<Interval> interv(args.size());
for (unsigned i = 0; i < args.size(); ++i) {
interv[i] = eval(args[i], inputs);
if (!valid(interv[i])) throw interval_error();
}
return language[sym.token()]->eval(interv, inputs);
}
/* */
void add_function_to_table(LanguageTable& table, token_t token) {
const FunDef& fundef = *language[token];
if (fundef.has_varargs() == false) {
table.add_function(token, fundef.min_arity());
} else { // sum or prod (or min or max)
table.add_function(token, 2);
}
}
// by default it is eager
double FunDef::eval(const SymVec& args, const vector<double>& inputs) const {
vector<double> values(args.size());
for (unsigned i = 0; i < args.size(); ++i) {
values[i] = ::eval(args[i], inputs);
}
return eval(values, inputs);
}
/* Variable Handling */
FunDef* make_var(int idx); // defined in FunDefs.h
static vector<token_t> var_token;
Sym SymVar(unsigned idx) {
if (var_token.size() <= idx) {
// it is new
var_token.resize(idx+1, token_t(-1));
var_token[idx] = add_function( make_var(idx) );
} else if (var_token[idx] == token_t(-1)) {
var_token[idx] = add_function( make_var(idx) );
}
return Sym(var_token[idx]);
}
/* Constant Handling */
struct HashDouble{
size_t operator()(double val) const {
unsigned long h = 0;
char* s = (char*)&val;
for (unsigned i=0 ; i<sizeof(double); ++i)
h = 5*h + s[i];
return size_t(h);
}
};
typedef hash_map<double, token_t, HashDouble> DoubleSet;
static DoubleSet doubleSet; // for quick checking if a constant already exists
static vector<bool> is_constant;
static vector<double> token_value;
static std::vector<token_t> free_list;
void delete_val(token_t token) { // clean up the information about this constant
if (is_constant[token]) {
double value = token_value[token];
delete language[token];
language[token] = 0;
doubleSet.erase(value);
free_list.push_back(token);
}
}
FunDef* make_const(double value);
Sym SymConst(double value) {
Sym::set_extra_dtor(delete_val);
DoubleSet::iterator it = doubleSet.find(value);
if (it != doubleSet.end()) {
return Sym(it->second);
}
if (free_list.empty()) { // make space for tokens;
unsigned sz = language.size();
language.resize(sz + sz+1); // double
is_constant.resize(language.size(), false);
token_value.resize(language.size(), 0.0);
for (unsigned i = sz; i < language.size(); ++i) {
free_list.push_back(i);
}
}
token_t token = free_list.back();
free_list.pop_back();
assert(language[token] == 0);
language[token] = make_const(value);
doubleSet[value] = token;
is_constant[token] = true;
token_value[token] = value;
return Sym(token);
}
/* LanguageTable depends on this one, XXX move somewhere safe.*/
#include <utils/eoRNG.h>
extern Sym default_const() { return SymConst(rng.normal()); }
/* The functions */
class Var : public FunDef {
public :
int idx;
string default_str;
Var(int _idx) : idx(_idx) {
ostringstream os;
os << "x[" << idx << ']'; // CompiledCode expects this form
default_str = os.str();
}
double eval(const vector<double>& _, const vector<double>& inputs) const { return inputs[idx]; }
double eval(const SymVec& _, const vector<double>& inputs) const { return inputs[idx]; }
string c_print(const vector<string>& _, const vector<string>& names) const {
if (names.empty()) {
return default_str;
}
return names[idx];
}
Interval eval(const vector<Interval>& _, const vector<Interval>& inputs) const {
return inputs[idx];
}
unsigned min_arity() const { return 0; }
string name() const { return "var"; }
};
class Const : public FunDef {
private:
double value;
string value_str;
public:
Const(double _value) : value(_value) {
ostringstream os;
os.precision(17);
os.setf(ios::showpoint);
os << '(' << value << ')';
value_str = os.str();
}
double eval(const vector<double>& _, const vector<double>& inputs) const { return value; }
double eval(const SymVec& _, const vector<double>& inputs) const { return value; }
string c_print(const vector<string>& _, const vector<string>& names) const {
return value_str;
}
Interval eval(const vector<Interval>& _, const vector<Interval>& inputs) const {
// Profil/Bias seems to have a problem with 0 * inf when the Interval is exact zero (fpe)
//if (value == 0.0) return Interval(-BiasEpsilon,BiasEpsilon);
return Interval(value);
}
unsigned min_arity() const { return 0; }
string name() const { return "parameter"; }
};
// Get functions out, excluding Const and Var
vector<const FunDef*> get_defined_functions() {
vector<const FunDef*> res;
for (unsigned i = 0; i < language.size(); ++i) {
res.push_back(language[i]);
if (dynamic_cast<const Const*>(language[i]) != 0 || (dynamic_cast<const Var*>(language[i]) != 0) ) {
res.back() = 0; // erase
}
}
return res;
}
class Sum : public FunDef {
public :
double eval(const vector<double>& vals, const vector<double>& _) const {
double res = 0;
for (unsigned i = 0; i < vals.size(); ++i) res += vals[i];
return res;
}
string c_print(const vector<string>& args, const vector<string>& _) const {
if (args.empty()) { return "0.0"; }
ostringstream os;
os << "(" << args[0];
for (unsigned i = 1; i < args.size(); ++i) {
os << "+" << args[i];
}
os << ")";
return os.str();
}
Interval eval(const vector<Interval>& args, const vector<Interval>& inputs) const {
Interval interv(0.0); //(0.0-BiasEpsilon, 0.0+BiasEpsilon); // Profil/Bias seems to have a problem with 0 * inf when the Interval is exact zero (fpe)
for (unsigned i = 0; i < args.size(); ++i) {
Interval a = args[i]; // Profil doesn't know much about const correctness
interv += a;
}
return interv;
}
unsigned min_arity() const { return 0; }
bool has_varargs() const { return true; }
string name() const { return "sum"; }
};
class Prod : public FunDef {
public :
double eval(const vector<double>& vals, const vector<double>& _) const {
double res = 1;
for (unsigned i = 0; i < vals.size(); ++i) res *= vals[i];
return res;
}
string c_print(const vector<string>& args, const vector<string>& _) const {
if (args.empty()) { return "1.0"; }
ostringstream os;
os << "(" << args[0];
for (unsigned i = 1; i < args.size(); ++i) {
os << "*" << args[i];
}
os << ")";
return os.str();
}
Interval eval(const vector<Interval>& args, const vector<Interval>& inputs) const {
Interval interv(1.0);
for (unsigned i = 0; i < args.size(); ++i) {
Interval a = args[i]; // Profil doesn't know much about const correctness
interv *= a;
}
return interv;
}
unsigned min_arity() const { return 0; }
bool has_varargs() const { return true; }
string name() const { return "prod"; }
};
class Power : public FunDef {
public :
double eval(const vector<double>& vals, const vector<double>& _) const {
return pow(vals[0], vals[1]);
}
string c_print(const vector<string>& args, const vector<string>& _) const {
return "pow(" + args[0] + ',' + args[1] + ')';
}
Interval eval(const vector<Interval>& args, const vector<Interval>& _) const {
Interval first = args[0];
Interval second = args[1];
Interval lg = log(first);
if (!valid(lg)) throw interval_error();
return exp(second * lg);
}
unsigned min_arity() const { return 2; }
string name() const { return "pow"; }
};
class IsNeg : public FunDef {
public:
double eval(const vector<double>& vals, const vector<double>& _) const {
if (vals[0] < 0.0) return vals[1];
return vals[2];
}
double eval(const Sym& sym, const vector<double>& inputs) const {
const SymVec& args = sym.args();
double arg0 = ::eval(args[0], inputs);
if (arg0 < 0.0) {
return ::eval(args[1], inputs);
}
return ::eval(args[2], inputs);
}
string c_print(const vector<string>& args, const vector<string>& _) const {
return "((" + args[0] + "<0.0)?" + args[1] + ":" + args[2]+")";
}
Interval eval(const vector<Interval>& args, const vector<Interval>& _) const {
Interval a0 = args[0];
if (a0.upper() < 0.0) return args[1];
if (a0.lower() >= 0.0) return args[2];
return Interval( std::min(args[1].lower(), args[2].lower()), std::max(args[1].upper(), args[2].upper()));
}
unsigned min_arity() const { return 3; }
string name() const { return "ifltz"; }
};
template <typename Func>
class Unary : public FunDef {
Func un;
double eval(const vector<double>& vals, const vector<double>& _) const {
return un(vals[0]);
}
string c_print(const vector<string>& args, const vector<string>& _) const {
return un(args[0]);
}
Interval eval(const vector<Interval>& args, const vector<Interval>& _) const {
return un(args[0]);
}
unsigned min_arity() const { return 1; }
string name() const { return un.name(); }
};
struct Inv {
double operator()(double val) const { return 1.0 / val; }
string operator()(string v) const { return "(1./" + v + ")"; }
Interval operator()(Interval v) const { return 1.0 / v; }
string name() const { return "inv"; }
};
struct Min {
double operator()(double val) const { return -val; }
string operator()(string v) const { return "(-" + v + ")"; }
Interval operator()(Interval v) const { return -v; }
string name() const { return "min"; }
};
string prototypes = "double pow(double, double);";
string get_prototypes() { return prototypes; }
unsigned add_prototype(string str) { prototypes += string("double ") + str + "(double);"; return prototypes.size(); }
#define FUNCDEF(funcname) struct funcname##_struct { \
double operator()(double val) const { return funcname(val); }\
string operator()(string val) const { return string(#funcname) + '(' + val + ')'; }\
Interval operator()(Interval val) const { return funcname(val); }\
string name() const { return string(#funcname); }\
};\
const token_t funcname##_token = add_function( new Unary<funcname##_struct>);\
unsigned funcname##_size = add_prototype(#funcname);
FunDef* make_var(int idx) { return new Var(idx); }
FunDef* make_const(double value) { return new Const(value); }
const token_t sum_token = add_function( new Sum );
const token_t prod_token = add_function( new Prod);
const token_t inv_token = add_function( new Unary<Inv>);
const token_t min_token = add_function( new Unary<Min>);
const token_t pow_token = add_function( new Power);
const token_t ifltz_token = add_function( new IsNeg);
FUNCDEF(sin);
FUNCDEF(cos);
FUNCDEF(tan);
FUNCDEF(asin);
FUNCDEF(acos);
FUNCDEF(atan);
FUNCDEF(sinh);
FUNCDEF(cosh);
FUNCDEF(tanh);
FUNCDEF(asinh);
FUNCDEF(acosh);
FUNCDEF(atanh);
FUNCDEF(exp);
FUNCDEF(log);
double sqr(double x) { return x*x; }
FUNCDEF(sqr);
FUNCDEF(sqrt);
/* Serialization */
void write_raw(ostream& os, const Sym& sym) {
token_t token = sym.token();
const SymVec& args = sym.args();
if (is_constant.size() > token && is_constant[token]) {
os << "c" << language[token]->c_print(vector<string>(), vector<string>());
} else {
const Var* var = dynamic_cast<const Var*>( language[token] );
if (var != 0) {
os << "v" << var->idx;
} else {
os << "f" << token << ' ' << args.size();
}
}
for (unsigned i = 0; i < args.size(); ++i) {
write_raw(os, args[i]);
}
}
string write_raw(const Sym& sym) {
ostringstream os;
write_raw(os, sym);
return os.str();
}
Sym read_raw(istream& is) {
char id = is.get();
switch (id) {
case 'c' :
{
double val;
is.get(); // skip '('
is >> val;
is.get(); // skip ')'
return SymConst(val);
}
case 'v' :
{
unsigned idx;
is >> idx;
return SymVar(idx);
}
case 'f' :
{
token_t token;
unsigned arity;
is >> token;
is >> arity;
SymVec args(arity);
for (unsigned i = 0; i < arity; ++i) {
args[i] = read_raw(is);
}
return Sym(token, args);
}
default : {
cerr << "Character = " << id << " Could not read formula from stream" << endl;
exit(1);
}
}
return Sym();
}
Sym read_raw(string str) {
istringstream is(str);
return read_raw(is);
}
void read_raw(istream& is, Sym& sym) {
sym = read_raw(is);
}

View file

@ -0,0 +1,124 @@
/*
* 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 FUNCTION_DEF_H_
#define FUNCTION_DEF_H_
#include <string>
#include <vector>
#include <memory>
#include "Sym.h"
#include "Interval.h"
class FunDef {
public:
virtual ~FunDef() {}
// (possibly) lazy evaluation function, default implementation calls 'eager' eval
virtual double eval(const SymVec& args, const std::vector<double>& inputs) const;
// eager evaluation function
virtual double eval(const std::vector<double>& args, const std::vector<double>& inputs) const = 0;
// interval evaluation
virtual Interval eval(const std::vector<Interval>& args, const std::vector<Interval>& inputs) const = 0;
// prints 'c' like code
virtual std::string c_print(const std::vector<std::string>& names, const std::vector<std::string>& names) const = 0;
virtual unsigned min_arity() const = 0;
virtual bool has_varargs() const { return false; } // sum, prod, min, max are variable arity
virtual std::string name() const = 0;
protected:
};
extern std::vector<const FunDef*> get_defined_functions();
extern const FunDef& get_element(token_t token);
/* Single case evaluation */
extern double eval(const Sym& sym, const std::vector<double>& inputs);
/* Static analysis through interval arithmetic */
extern Interval eval(const Sym& sym, const std::vector<Interval>& inputs);
/* Pretty printers, second version allows setting of variable names */
extern std::string c_print(const Sym& sym);
extern std::string c_print(const Sym& sym, const std::vector<std::string>& var_names);
/* Pretty printer streamer */
inline std::ostream& operator<<(std::ostream& os, Sym sym) { return os << c_print(sym); }
/* Create constant */
extern Sym SymConst(double value);
/* Create variable */
extern Sym SymVar(unsigned idx);
/* Add function to the language table (and take a guess at the arity) */
class LanguageTable;
extern void add_function_to_table(LanguageTable& table, token_t token);
// token names
extern const token_t sum_token;
extern const token_t prod_token;
extern const token_t inv_token;
extern const token_t min_token;
extern const token_t pow_token;
extern const token_t ifltz_token;
#define HEADERFUNC(name) extern const token_t name##_token;\
inline Sym name(Sym arg) { return Sym(name##_token, arg); }
/* This defines the tokens: sin_token, cos_token, etc. */
HEADERFUNC(sin);
HEADERFUNC(cos);
HEADERFUNC(tan);
HEADERFUNC(asin);
HEADERFUNC(acos);
HEADERFUNC(atan);
HEADERFUNC(sinh);
HEADERFUNC(cosh);
HEADERFUNC(tanh);
HEADERFUNC(asinh);
HEADERFUNC(acosh);
HEADERFUNC(atanh);
HEADERFUNC(exp);
HEADERFUNC(log);
HEADERFUNC(sqr);
HEADERFUNC(sqrt);
extern std::string get_prototypes();
// reading and writing in internal format
extern std::string write_raw(const Sym& sym);
extern void write_raw(ostream& os, const Sym& sym);
extern Sym read_raw(std::string str);
extern Sym read_raw(std::istream& is);
extern void read_raw(std::istream& is, Sym& sym);
#include "SymOps.h"
#endif

View 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 "FunDef.h"
#include "SymOps.h"
#include "Sym.h"
using namespace std;
void collect(token_t t, Sym a, SymVec& args) {
if (a.token() == t) {
const SymVec& a_args = a.args();
for (unsigned i = 0; i < a_args.size(); ++i) {
collect(t, a_args[i], args);
}
return;
}
args.push_back(a);
}
Sym operator+(Sym a, Sym b) {
SymVec args;
collect(sum_token, a, args);
collect(sum_token, b, args);
return Sym(sum_token, args);
}
Sym operator*(Sym a, Sym b) {
SymVec args;
collect(prod_token, a, args);
collect(prod_token, b, args);
return Sym(prod_token, args);
}
Sym operator/(Sym a, Sym b) {
SymVec args;
collect(prod_token, a, args);
SymVec args2;
collect(prod_token, b, args2);
SymVec inv;
inv.push_back(Sym(prod_token, args2));
args.push_back( Sym(inv_token, inv) );
return Sym(prod_token, args);
}
Sym operator-(Sym a, Sym b) {
SymVec args;
collect(sum_token, a, args);
SymVec args2;
collect(sum_token, b, args2);
SymVec min;
min.push_back(Sym(sum_token, args2));
args.push_back( Sym(min_token, min) );
return Sym(sum_token, args);
}
Sym operator-(Sym a) {
return Sym(min_token, a);
}
Sym pow(Sym a, Sym b) {
SymVec args;
args.push_back(a);
args.push_back(b);
return Sym(pow_token, args);
}
Sym ifltz(Sym a, Sym b, Sym c) {
SymVec args;
args.push_back(a);
args.push_back(b);
args.push_back(c);
return Sym(ifltz_token, args);
}

View file

@ -0,0 +1,31 @@
/*
* 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 SYMOPS_H
#define SYMOPS_H
#include "Sym.h"
extern Sym operator+(Sym a, Sym b);
extern Sym operator*(Sym a, Sym b);
extern Sym operator/(Sym a, Sym b);
extern Sym operator-(Sym a, Sym b);
extern Sym pow(Sym a, Sym b);
extern Sym ifltz(Sym a, Sym b, Sym c);
extern Sym operator-(Sym a);
#endif

Some files were not shown because too many files have changed in this diff Show more