diff options
author | Paul Oliver <contact@pauloliver.dev> | 2024-02-29 19:04:34 +0100 |
---|---|---|
committer | Paul Oliver <contact@pauloliver.dev> | 2024-02-29 19:16:14 +0100 |
commit | e6ab4a8ed100d5d5b7611c74cf3ccd556f1f1d71 (patch) | |
tree | 129cf13c2f9b3eae54402300db4570815789a02a |
61 files changed, 5605 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d45b26f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +**/*.a +**/*.d +**/*.o diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5b6acff --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +LIBRARY := lib/libHyperNeat.a +SOURCES := $(wildcard src/*.cpp) $(wildcard src/Utils/*.cpp) +OBJECTS := $(patsubst src/%.cpp,obj/%.o,$(SOURCES)) +DEPS := $(patsubst %.o,%.d,$(OBJECTS)) +CFLAGS := -c -O3 -Wall -std=c++11 -MMD -Iinclude -IC:/cereal/include +LFLAGS := rvs + +all: $(OBJECTS) + ar $(LFLAGS) $(LIBRARY) $(OBJECTS) + make -C plugins/CppnExplorer + +-include $(DEPS) + +$(OBJECTS): $(patsubst obj/%.o,src/%.cpp,$@) + g++ $(CFLAGS) $(patsubst obj/%.o,src/%.cpp,$@) -o $@ + +rebuild: clean all + +clean: + del lib\*.a + del obj\*.o + del obj\*.d + del obj\Utils\*.o + del obj\Utils\*.d diff --git a/README.md b/README.md new file mode 100644 index 0000000..8e68cec --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +rtES-HyperNeat (Real Time Evolving Substrate Hypercube based Neuro-Evolution of +Augmenting Topologies) is a real time neuro-evolution algorithm based on the +papers written by Kenneth Stanley and other researchers at the EPLEX group +([http://eplex.cs.ucf.edu/](http://eplex.cs.ucf.edu/)). More specifically, +rtES-HyperNeat tries to blend the existing ES-HyperNeat +([http://eplex.cs.ucf.edu/ESHyperNEAT/](http://eplex.cs.ucf.edu/ESHyperNEAT/)) +algorithm with rtNeat +([http://nn.cs.utexas.edu/?rtNEAT](http://nn.cs.utexas.edu/?rtNEAT)), a previous +version which allowed real time evolution of agents on a virtual field. + +The main problem with trying to make ES-HyperNeat real-timed was that the CPPNs +(genotypes) had to be converted into functional Neural Networks (phenotypes) on +a single time-step. This was proven to be impossible, so, as a solution, the +library handles these transformations on separate execution threads. This way, +the algorithm flows seamlessly. + +Although several implementations existed for HyperNeat, I've decided to code +mine from scratch, based only on the original papers by EPLEX. My goal is to +construct a simple, easy to use, well documented and tutorialized C++ library +that will allow any user to evolve Neural Networks for any imaginable task. I +also plan to apply this library to my Guppies experiment once its finished (see +my channel for more info on Guppies). diff --git a/include/HyperNeat/Behavior.hpp b/include/HyperNeat/Behavior.hpp new file mode 100644 index 0000000..2f474a6 --- /dev/null +++ b/include/HyperNeat/Behavior.hpp @@ -0,0 +1,40 @@ +#ifndef __HYPERNEAT_BEHAVIOR_HPP__ +#define __HYPERNEAT_BEHAVIOR_HPP__ + +#include <HyperNeat/Utils/Size.hpp> +#include <HyperNeat/Utils/Vector.hpp> + +namespace hyperneat +{ + class Organism; + class NoveltyMetric; + + class Behavior + { + public: + void clear(); + void reset(bool archive = true); + + double& at(size_t i); + double getNoveltyScore() const; + bool isToBeArchived() const; + Organism& getOrganism(); + const NoveltyMetric& getNoveltyMetric() const; + const Vector<double>& getCharacterization() const; + + bool _criteriaReached = true; + + private: + double _noveltyScore = 0.0; + bool _isReady = false; + bool _toBeArchived = false; + Organism* _organism = nullptr; + NoveltyMetric* _noveltyMetric = nullptr; + Vector<double> _characterization; + + friend class LoadFile; + friend class NoveltyMetric; + }; +} + +#endif diff --git a/include/HyperNeat/Cppn.hpp b/include/HyperNeat/Cppn.hpp new file mode 100644 index 0000000..a4511c8 --- /dev/null +++ b/include/HyperNeat/Cppn.hpp @@ -0,0 +1,58 @@ +#ifndef __HYPERNEAT_CPPN_HPP__ +#define __HYPERNEAT_CPPN_HPP__ + +#include <HyperNeat/Utils/Size.hpp> +#include <HyperNeat/Utils/ValueMap.hpp> +#include <HyperNeat/Utils/Vector2D.hpp> +#include <HyperNeat/Utils/NodeTypes.hpp> + +namespace hyperneat +{ + class Genome; + class NodeSearchPrms; + + class Cppn + { + public: + Cppn() = default; + + void create(const Genome& genome); + void clear(); + + size_t getInputsCount() const; + size_t getOutputsCount() const; + size_t getNodesCount() const; + + double& inputAt(size_t i); + double outputAt(size_t i) const; + + void cycle(); + void findNodesIn2DSection(ValueMap& valueMap, const NodeSearchPrms& qpPrms, const Point& source = Point()); + + private: + class Node + { + public: + void appendInput(); + void flushOutput(); + + class Link + { + public: + double* _input = nullptr; + double _weight = 0.0; + }; + + Vector<Link> _links; + NodeType _nodeType = NodeType::NULL_TYPE; + double _storedInput = 0.0; + double _output = 0.0; + }; + + Vector<double> _inputs; + Vector<double*> _outputs; + Vector2D<Node> _nodeLayers; + }; +} + +#endif diff --git a/include/HyperNeat/Genome.hpp b/include/HyperNeat/Genome.hpp new file mode 100644 index 0000000..d8f678f --- /dev/null +++ b/include/HyperNeat/Genome.hpp @@ -0,0 +1,42 @@ +#ifndef __HYPERNEAT_GENOME_HPP__ +#define __HYPERNEAT_GENOME_HPP__ + +#include <HyperNeat/Utils/Map.hpp> +#include <HyperNeat/Utils/Size.hpp> +#include <HyperNeat/Utils/NodeTypes.hpp> + +namespace hyperneat +{ + class Genome + { + public: + Genome() = default; + explicit Genome(size_t inputs); + + class NodeGene + { + public: + NodeGene() = default; + NodeGene(double depth, NodeType nodeType); + + class LinkGene + { + public: + LinkGene() = default; + LinkGene(double weight, bool isEnabled = true); + + double _weight = 0.0; + bool _isEnabled = true; + }; + + double _depth = 0.0; + NodeType _nodeType = NodeType::NULL_TYPE; + Map<size_t, LinkGene> _linkGenes; + }; + + size_t _inputs = 0; + Map<size_t, NodeGene> _nodeGenes; + }; +} + +#endif diff --git a/include/HyperNeat/Innovation.hpp b/include/HyperNeat/Innovation.hpp new file mode 100644 index 0000000..ac618e5 --- /dev/null +++ b/include/HyperNeat/Innovation.hpp @@ -0,0 +1,25 @@ +#ifndef __HYPERNEAT_INNOVATION_HPP__ +#define __HYPERNEAT_INNOVATION_HPP__ + +#include <Hyperneat/Utils/Size.hpp> +#include <Hyperneat/Utils/NodeTypes.hpp> + +namespace hyperneat +{ + class Innovation + { + public: + Innovation() = default; + Innovation(size_t number, size_t source, size_t target, double depth, NodeType nodeType); + + bool operator== (const Innovation& other) const; + + size_t _number = 0; + size_t _source = 0; + size_t _target = 0; + double _depth = 0.0; + NodeType _nodeType = NodeType::NULL_TYPE; + }; +} + +#endif diff --git a/include/HyperNeat/NeuralNet.hpp b/include/HyperNeat/NeuralNet.hpp new file mode 100644 index 0000000..7f4a81f --- /dev/null +++ b/include/HyperNeat/NeuralNet.hpp @@ -0,0 +1,78 @@ +#ifndef __HYPERNEAT_NEURALNET_HPP__ +#define __HYPERNEAT_NEURALNET_HPP__ + +#include <HyperNeat/Utils/Size.hpp> +#include <HyperNeat/Utils/ValueMap.hpp> + +namespace hyperneat +{ + class Cppn; + class NeuralNetPrms; + + class NeuralNet + { + public: + class Neuron; + + NeuralNet() = default; + + void create(Cppn& cppn, const NeuralNetPrms& nnPrms); + void clear(); + + size_t getInputsCount() const; + size_t getOutputsCount() const; + size_t getNeuronsCount() const; + + const Vector<double*>& getInputs() const; + const Vector<double*>& getOutputs() const; + const Vector<Neuron>& getNeurons() const; + + double getAverageActivation() const; + + double& inputAt(size_t i); + double outputAt(size_t i) const; + + void cycle(); + + class Neuron + { + public: + enum class Type { + INPUT, HIDDEN, OUTPUT + }; + + Neuron() = default; + Neuron(const Point& position, Type type, double bias); + + void appendInput(); + void flushOutput(); + + class Synapse + { + public: + Synapse() = default; + Synapse(Neuron* inputNeuron, double weight); + + double* _input = nullptr; + Neuron* _neuron = nullptr; + double _weight = 0.0; + }; + + Vector<Synapse> _synapses; + Point _position; + Type _type = Type::HIDDEN; + double _bias = 0.0; + double _storedInput = 0.0; + double _output = 0.0; + }; + + private: + Vector<double*> _inputs; + Vector<double*> _outputs; + Vector<Neuron> _neurons; + + friend class Organism; + }; +} + +#endif diff --git a/include/HyperNeat/NeuralNetPrms.hpp b/include/HyperNeat/NeuralNetPrms.hpp new file mode 100644 index 0000000..fb5c349 --- /dev/null +++ b/include/HyperNeat/NeuralNetPrms.hpp @@ -0,0 +1,25 @@ +#ifndef __HYPERNEAT_NEURALNETPRMS_HPP__ +#define __HYPERNEAT_NEURALNETPRMS_HPP__ + +#include <HyperNeat/Utils/Size.hpp> +#include <HyperNeat/Utils/Point.hpp> +#include <HyperNeat/Utils/Vector.hpp> + +namespace hyperneat +{ + class NeuralNetPrms + { + public: + Vector<Point> _inputMap; + Vector<Point> _outputMap; + size_t _testGridLevel = 3; + size_t _maxQuadTreeLevel = SIZE_MAX; + size_t _minQuadTreeLevel = 0; + double _bandPruningThreshold = 0.3; + double _varianceThreshold = 0.03; + double _divisionThreshold = 0.03; + size_t _iterations = SIZE_MAX; + }; +} + +#endif diff --git a/include/HyperNeat/NodeSearchPrms.hpp b/include/HyperNeat/NodeSearchPrms.hpp new file mode 100644 index 0000000..52d9083 --- /dev/null +++ b/include/HyperNeat/NodeSearchPrms.hpp @@ -0,0 +1,33 @@ +#ifndef __HYPERNEAT_QUERYPLANEPRMS_HPP__ +#define __HYPERNEAT_QUERYPLANEPRMS_HPP__ + +#include <HyperNeat/Utils/Size.hpp> + +namespace hyperneat +{ + class NeuralNetPrms; + + class NodeSearchPrms + { + public: + NodeSearchPrms() = default; + NodeSearchPrms(size_t o, size_t x, size_t y); + NodeSearchPrms(size_t o, size_t x, size_t y, size_t d); + + void importFrom(const NeuralNetPrms& nnPrms); + + size_t _o = 0; + size_t _x = 0; + size_t _y = 1; + bool _useDistance = true; + size_t _d = 4; + size_t _testGridLevel = 3; + size_t _maxQuadTreeLevel = -1; + size_t _minQuadTreeLevel = 0; + double _bandPruningThreshold = 0.3; + double _varianceThreshold = 0.03; + double _divisionThreshold = 0.03; + }; +} + +#endif diff --git a/include/HyperNeat/NoveltyMetric.hpp b/include/HyperNeat/NoveltyMetric.hpp new file mode 100644 index 0000000..01a6f75 --- /dev/null +++ b/include/HyperNeat/NoveltyMetric.hpp @@ -0,0 +1,37 @@ +#ifndef __HYPERNEAT_NOVELTY_METRIC_HPP__ +#define __HYPERNEAT_NOVELTY_METRIC_HPP__ + +#include <Hyperneat/Behavior.hpp> +#include <Hyperneat/Utils/Vector2D.hpp> +#include <Hyperneat/NoveltyMetricPrms.hpp> + +namespace hyperneat +{ + class Population; + + class NoveltyMetric + { + public: + const NoveltyMetricPrms& getPrms() const; + const Vector<Behavior>& getBehaviors() const; + const Vector2D<double>& getArchive() const; + + Behavior& getBehaviorOf(size_t i); + + private: + void initialize(const NoveltyMetricPrms& prms, Population* population = nullptr); + void setScores(); + + double getDistance(const Vector<double>& v1, const Vector<double>& v2) const; + + NoveltyMetricPrms _prms; + Vector<Behavior> _behaviors; + Vector2D<double> _archive; + + friend class Behavior; + friend class LoadFile; + friend class Population; + }; +} + +#endif diff --git a/include/HyperNeat/NoveltyMetricPrms.hpp b/include/HyperNeat/NoveltyMetricPrms.hpp new file mode 100644 index 0000000..305d075 --- /dev/null +++ b/include/HyperNeat/NoveltyMetricPrms.hpp @@ -0,0 +1,20 @@ +#ifndef __HYPERNEAT_NOVELTY_METRIC_PRMS_HPP__ +#define __HYPERNEAT_NOVELTY_METRIC_PRMS_HPP__ + +#include <Hyperneat/Utils/Size.hpp> + +namespace hyperneat +{ + class Population; + + class NoveltyMetricPrms + { + public: + double _noveltyThreshold = 10.0; + size_t _referenceOrganisms = 15; + size_t _characterizationSize = 3; + bool _criteriaReachedByDefault = true; + }; +} + +#endif diff --git a/include/HyperNeat/Organism.hpp b/include/HyperNeat/Organism.hpp new file mode 100644 index 0000000..d35210c --- /dev/null +++ b/include/HyperNeat/Organism.hpp @@ -0,0 +1,71 @@ +#ifndef __HYPERNEAT_ORGANISM_HPP__ +#define __HYPERNEAT_ORGANISM_HPP__ + +#include <HyperNeat/Genome.hpp> +#include <HyperNeat/NeuralNet.hpp> +#include <HyperNeat/Utils/Atomic.hpp> +#include <HyperNeat/Utils/Pointer.hpp> + +namespace hyperneat +{ + class Behavior; + class NeuralNet; + class Population; + class NeuralNetPrms; + + class Organism + { + public: + Organism(const Organism& other); + Organism& operator=(const Organism& other); + + size_t getIndex() const; + + void lock(); + void unlock(); + bool isLocked() const; + + void freeze(); + void unfreeze(); + bool isFrozen() const; + + bool isBeingGenerated() const; + size_t getSpecie() const; + bool isOld() const; + size_t getLifetime() const; + + Behavior& getBehavior(); + const Genome& getGenome() const; + + bool isChampion() const; + Population& getPopulation() const; + + void createNeuralNet(); + + Pointer<NeuralNet> _neuralNet; + double _fitness = 0.0; + + private: + Organism(Population* population); + Organism(size_t inputs, Population* population); + + void reset(bool archive = false); + + size_t _index = 0; + bool _isLocked = false; + bool _isFrozen = false; + Atomic<bool> _isBeingGenerated = {false}; + size_t _specie = 0; + size_t _lifetime = 0; + Behavior* _behavior = nullptr; + Genome _genome; + + Population* _population = nullptr; + + friend class LoadFile; + friend class Population; + friend class NoveltyMetric; + }; +} + +#endif diff --git a/include/HyperNeat/Population.hpp b/include/HyperNeat/Population.hpp new file mode 100644 index 0000000..6d79d14 --- /dev/null +++ b/include/HyperNeat/Population.hpp @@ -0,0 +1,148 @@ +#ifndef __HYPERNEAT_POPULATION_HPP__ +#define __HYPERNEAT_POPULATION_HPP__ + +#include <HyperNeat/Organism.hpp> +#include <HyperNeat/Innovation.hpp> +#include <HyperNeat/Utils/Random.hpp> +#include <HyperNeat/Utils/Vector.hpp> +#include <HyperNeat/Utils/Pointer.hpp> +#include <HyperNeat/NeuralNetPrms.hpp> +#include <HyperNeat/NoveltyMetric.hpp> +#include <HyperNeat/Utils/Vector2D.hpp> +#include <HyperNeat/Utils/Function.hpp> +#include <HyperNeat/PopulationPrms.hpp> + +namespace hyperneat +{ + class NeuralNet; + class NoveltyMetricPrms; + + class Population + { + public: + void create(const PopulationPrms& popPrms); + void create(const PopulationPrms& popPrms, const NeuralNetPrms& nnPrms); + void create(const PopulationPrms& popPrms, const NeuralNetPrms& nnPrms, const NoveltyMetricPrms& nmPrms); + void shutdown(bool resetOrganisms = false, bool archiveOrganisms = false); + ~Population(); + + void setMinimumLifetime(size_t lifetime); + + const PopulationPrms& getPopulationPrms() const; + const NeuralNetPrms& getNeuralNetPrms() const; + bool hasNeuralNets() const; + const Vector<Organism>& getAllOrganisms() const; + const Vector2D<Organism*>& getSpecies() const; + + Organism& getOrganism(size_t i); + Organism& getChampion(); + const Vector<Organism*>& getSpecie(size_t i) const; + + void lock(); + void unlock(); + bool isLocked() const; + + void lockOrganism(size_t i); + void unlockOrganism(size_t i); + bool isOrganismLocked(size_t i) const; + bool isAnyOrganismLocked() const; + size_t getLockedOrganisms() const; + + void freezeOrganism(size_t i); + void unfreezeOrganism(size_t i); + bool isOrganismFrozen(size_t i) const; + bool isAnyOrganismFrozen() const; + size_t getFrozenOrganisms() const; + + bool isOrganismBeingGenerated(size_t i) const; + bool isAnyOrganismBeingGenerated() const; + size_t getOrganismsBeingGenerated() const; + + size_t getReadyOrganisms() const; + + const Vector<Innovation>& getInnovations() const; + size_t getInnovationsCount() const; + size_t getBasicInnovationsCount() const; + Organism* getLastReplacement(); + Organism* getLastMother(); + Organism* getLastFather(); + size_t getReplacements() const; + bool recentReplacement() const; + double getDistanceThreshold() const; + size_t getOldOrganisms() const; + size_t getMinimumOldOrganisms() const; + double getAverageFitness() const; + double getAverageOldFitness() const; + + void setNoveltyMetric(const NoveltyMetricPrms& prms); + void clearNoveltyMetric(); + bool isNoveltyMetricSet() const; + const NoveltyMetric& getNoveltyMetric() const; + + size_t getUpdates() const; + + double& fitnessOf(size_t i); + bool update(Function<void(void)> beforeReplacement = []() {}, Function<void(void)> afterReplacement = []() {}); + + private: + void generateAllNeuralNets(); + + void replaceOrganism(); + Organism* killPoorOrganism(); + Vector<Organism*>* chooseParentSpecie(); + + void breedAsexually(Genome& child, const Genome& mother); + void breedSexually(Genome& child, const Genome& mother, const Genome& father); + bool mutateNodesAndLinks(Genome& child); + + void assignToSpecie(Organism& org); + void organizeSpecies(); + double computeDistance(const Genome& g1, const Genome& g2) const; + + size_t getRandSeed() const; + size_t getRandSize(size_t low, size_t hi); + double getRandReal(double low, double hi); + double getRandWeight(); + double getWeightDeviation(); + NodeType getRandNodeType(); + bool getChance(double ratio); + + PopulationPrms _prms; + Pointer<NeuralNetPrms> _nnPrms; + Vector2D<Organism*> _species; + Vector<Organism> _allOrganisms; + + bool _populationLock = false; + size_t _lockedOrganisms = 0; + size_t _frozenOrganisms = 0; + Atomic<size_t> _organismsBeingGenerated = {0}; + + Vector<Innovation> _innovations; + size_t _basicInnovs = 0; + + Organism* _lastReplacement = nullptr; + Organism* _lastMother = nullptr; + Organism* _lastFather = nullptr; + size_t _replacements = 0; + bool _recentReplacement = false; + double _distanceThreshold = 0.0; + size_t _oldOrganisms = 0; + size_t _minOldOrganisms = 0; + + Pointer<NoveltyMetric> _noveltyMetric; + + size_t _updates = 0; + + RandGen _randGen; + IntDist _nodeTypeSelector = IntDist(0, NODE_TYPES_COUNT - 1); + RealDist _weightSelector = RealDist(0.0, 1.0); + BellDist _weightDeviator; + RealDist _chanceSelector; + + friend class LoadFile; + friend class Organism; + friend class NoveltyMetric; + }; +} + +#endif diff --git a/include/HyperNeat/PopulationPrms.hpp b/include/HyperNeat/PopulationPrms.hpp new file mode 100644 index 0000000..21a6bc3 --- /dev/null +++ b/include/HyperNeat/PopulationPrms.hpp @@ -0,0 +1,63 @@ +#ifndef __HYPERNEAT_POPULATIONPRMS_HPP__ +#define __HYPERNEAT_POPULATIONPRMS_HPP__ + +// #include <cereal/cereal.hpp> +#include <HyperNeat/Utils/Size.hpp> + +namespace hyperneat +{ + class PopulationPrms + { + public: + size_t _popSize = 200; + size_t _cppnInputs = 6; + size_t _cppnOutputs = 2; + size_t _seed = 0; + double _weightRange = 1.0; + double _c1Disjoint = 1.0; + double _c3WeightDifference = 0.4; + double _initialDistanceThreshold = 4.0; + double _distanceThresholdShift = 0.3; + double _sexualReproductionRate = 0.5; + double _weightMutationRate = 0.94; + double _weightDeviation = 0.25; + double _interspeciesMatingRate = 0.001; + double _geneDisablingRatio = 0.75; + double _linkMutationRate = 0.03; + double _nodeMutationRate = 0.01; + size_t _targetSpeciesCount = 8; + double _eligibilityRatio = 0.5; + size_t _minimumLifetime = 120; + size_t _replBeforeReorganization = 5; + + // private: + // friend class cereal::access; + + // template <class Archive> + // void serialize(Archive &ar) + // { + // ar(CEREAL_NVP(_popSize)); + // ar(CEREAL_NVP(_cppnInputs)); + // ar(CEREAL_NVP(_cppnOutputs)); + // ar(CEREAL_NVP(_seed)); + // ar(CEREAL_NVP(_weightRange)); + // ar(CEREAL_NVP(_c1Disjoint)); + // ar(CEREAL_NVP(_c3WeightDifference)); + // ar(CEREAL_NVP(_initialDistanceThreshold)); + // ar(CEREAL_NVP(_distanceThresholdShift)); + // ar(CEREAL_NVP(_sexualReproductionRate)); + // ar(CEREAL_NVP(_weightMutationRate)); + // ar(CEREAL_NVP(_weightDeviation)); + // ar(CEREAL_NVP(_interspeciesMatingRate)); + // ar(CEREAL_NVP(_geneDisablingRatio)); + // ar(CEREAL_NVP(_linkMutationRate)); + // ar(CEREAL_NVP(_nodeMutationRate)); + // ar(CEREAL_NVP(_targetSpeciesCount)); + // ar(CEREAL_NVP(_eligibilityRatio)); + // ar(CEREAL_NVP(_minimumLifetime)); + // ar(CEREAL_NVP(_replBeforeReorganization)); + // } + }; +} + +#endif diff --git a/include/HyperNeat/QuadTree.hpp b/include/HyperNeat/QuadTree.hpp new file mode 100644 index 0000000..8090254 --- /dev/null +++ b/include/HyperNeat/QuadTree.hpp @@ -0,0 +1,30 @@ +#ifndef __HYPERNEAT_QUADTREE_HPP__ +#define __HYPERNEAT_QUADTREE_HPP__ + +#include <HyperNeat/Utils/Vector.hpp> +#include <HyperNeat/Utils/Function.hpp> + +namespace hyperneat +{ + class QuadTree + { + public: + QuadTree() = default; + QuadTree(double segment, double x, double y); + + double getSegment() const; + double getX() const; + double getY() const; + + void subdivide(Function<bool(QuadTree*)> subdivider); + void traverse(Function<void(const QuadTree*)> traverser) const; + + private: + Vector<QuadTree> _children; + double _segment = 0.0; + double _x = 0.0; + double _y = 0.0; + }; +} + +#endif diff --git a/include/HyperNeat/Utils/Atomic.hpp b/include/HyperNeat/Utils/Atomic.hpp new file mode 100644 index 0000000..850a06e --- /dev/null +++ b/include/HyperNeat/Utils/Atomic.hpp @@ -0,0 +1,12 @@ +#ifndef __HYPERNEAT_ATOM_HPP__ +#define __HYPERNEAT_ATOM_HPP__ + +#include <atomic> + +namespace hyperneat +{ + template <class T> + using Atomic = std::atomic<T>; +} + +#endif diff --git a/include/HyperNeat/Utils/Function.hpp b/include/HyperNeat/Utils/Function.hpp new file mode 100644 index 0000000..ebcb27b --- /dev/null +++ b/include/HyperNeat/Utils/Function.hpp @@ -0,0 +1,12 @@ +#ifndef __HYPERNEAT_FUNCTION_HPP__ +#define __HYPERNEAT_FUNCTION_HPP__ + +#include <functional> + +namespace hyperneat +{ + template <class F> + using Function = std::function<F>; +} + +#endif diff --git a/include/HyperNeat/Utils/LoadFile.hpp b/include/HyperNeat/Utils/LoadFile.hpp new file mode 100644 index 0000000..1960e90 --- /dev/null +++ b/include/HyperNeat/Utils/LoadFile.hpp @@ -0,0 +1,40 @@ +#ifndef __HYPERNEAT_LOADFILE_HPP__ +#define __HYPERNEAT_LOADFILE_HPP__ + +#include <iostream> +#include <HyperNeat/Utils/String.hpp> +#include <HyperNeat/Utils/Vector.hpp> + +namespace hyperneat +{ + using Istream = std::istream; + + class Genome; + class Organism; + class Population; + class NoveltyMetric; + class PopulationPrms; + class NoveltyMetricPrms; + + class LoadFile + { + public: + LoadFile(Istream& stream); + + void loadPopulation(Population& population); + void loadPopulationPrms(PopulationPrms& prms); + void loadNeuralNetPrms(NeuralNetPrms& prms); + void loadNoveltyMetric(NoveltyMetric& noveltyMetric); + void loadNoveltyMetricPrms(NoveltyMetricPrms& prms); + void loadOrganism(Organism& organism); + void loadGenome(Genome& genome); + + private: + Istream& nextPrm(bool arrayVal = false); + Istream& nextArrayValue(); + + Istream& _stream; + }; +} + +#endif diff --git a/include/HyperNeat/Utils/Map.hpp b/include/HyperNeat/Utils/Map.hpp new file mode 100644 index 0000000..c735ab3 --- /dev/null +++ b/include/HyperNeat/Utils/Map.hpp @@ -0,0 +1,12 @@ +#ifndef __HYPERNEAT_MAP_HPP__ +#define __HYPERNEAT_MAP_HPP__ + +#include <map> + +namespace hyperneat +{ + template <class K, class V> + using Map = std::map<K, V>; +} + +#endif diff --git a/include/HyperNeat/Utils/NodeTypes.hpp b/include/HyperNeat/Utils/NodeTypes.hpp new file mode 100644 index 0000000..815214e --- /dev/null +++ b/include/HyperNeat/Utils/NodeTypes.hpp @@ -0,0 +1,22 @@ +#ifndef __HYPERNEAT_NODETYPES_HPP__ +#define __HYPERNEAT_NODETYPES_HPP__ + +#include <HyperNeat/Utils/String.hpp> + +namespace hyperneat +{ + enum class NodeType { + NULL_TYPE = -1, + SIGMOID = 0, + GAUSSIAN = 1, + SINE = 2, + ABSOLUTE = 3, + }; + + String nodeToString(NodeType type); + NodeType stringToNode(const String& str); + + const size_t NODE_TYPES_COUNT = 4; +} + +#endif diff --git a/include/HyperNeat/Utils/Pi.hpp b/include/HyperNeat/Utils/Pi.hpp new file mode 100644 index 0000000..00a3df2 --- /dev/null +++ b/include/HyperNeat/Utils/Pi.hpp @@ -0,0 +1,9 @@ +#ifndef __HYPERNEAT_PI_HPP__ +#define __HYPERNEAT_PI_HPP__ + +namespace hyperneat +{ + const double PI = 3.14159265359; +} + +#endif diff --git a/include/HyperNeat/Utils/Point.hpp b/include/HyperNeat/Utils/Point.hpp new file mode 100644 index 0000000..ba51bac --- /dev/null +++ b/include/HyperNeat/Utils/Point.hpp @@ -0,0 +1,21 @@ +#ifndef __HYPERNEAT_POINT_HPP__ +#define __HYPERNEAT_POINT_HPP__ + +namespace hyperneat +{ + class Point + { + public: + Point() = default; + Point(double x, double y); + + double distance(const Point& other) const; + bool operator== (const Point& other) const; + bool operator< (const Point& other) const; + + double _x = 0.0; + double _y = 0.0; + }; +} + +#endif diff --git a/include/HyperNeat/Utils/Pointer.hpp b/include/HyperNeat/Utils/Pointer.hpp new file mode 100644 index 0000000..f02e853 --- /dev/null +++ b/include/HyperNeat/Utils/Pointer.hpp @@ -0,0 +1,12 @@ +#ifndef __HYPERNEAT_POINTER_HPP__ +#define __HYPERNEAT_POINTER_HPP__ + +#include <memory> + +namespace hyperneat +{ + template <class T> + using Pointer = std::unique_ptr<T>; +} + +#endif diff --git a/include/HyperNeat/Utils/Random.hpp b/include/HyperNeat/Utils/Random.hpp new file mode 100644 index 0000000..4a4d979 --- /dev/null +++ b/include/HyperNeat/Utils/Random.hpp @@ -0,0 +1,14 @@ +#ifndef __HYPERNEAT_RANDOM_HPP__ +#define __HYPERNEAT_RANDOM_HPP__ + +#include <random> + +namespace hyperneat +{ + using RandGen = std::mt19937_64; + using IntDist = std::uniform_int_distribution<size_t>; + using RealDist = std::uniform_real_distribution<double>; + using BellDist = std::normal_distribution<double>; +} + +#endif diff --git a/include/HyperNeat/Utils/SaveFile.hpp b/include/HyperNeat/Utils/SaveFile.hpp new file mode 100644 index 0000000..18cad38 --- /dev/null +++ b/include/HyperNeat/Utils/SaveFile.hpp @@ -0,0 +1,45 @@ +#ifndef __HYPERNEAT_SAVEFILE_HPP__ +#define __HYPERNEAT_SAVEFILE_HPP__ + +#include <iostream> +#include <HyperNeat/Utils/String.hpp> +#include <HyperNeat/Utils/Vector.hpp> + +namespace hyperneat +{ + using Ostream = std::ostream; + + class Genome; + class Organism; + class Population; + class NoveltyMetric; + class NeuralNetPrms; + class PopulationPrms; + class NoveltyMetricPrms; + + class SaveFile + { + public: + SaveFile(Ostream& stream); + + void savePopulation(Population& population, bool shuttedDown = false, size_t tabs = 0, + const String& prefix = ""); + void savePopulationPrms(const PopulationPrms& prms, size_t tabs = 0, const String& prefix = ""); + void saveNeuralNetPrms(const NeuralNetPrms& prms, size_t tabs = 0, const String& prefix = ""); + void saveOrganism(const Organism& organism, bool shuttedDown = false, size_t tabs = 0, + const String& prefix = ""); + void saveGenome(const Genome& genome, size_t tabs = 0, const String& prefix = ""); + void saveNoveltyMetric(const NoveltyMetric& noveltyMetric, bool shuttedDown = false, size_t tabs = 0, + const String& prefix = ""); + void saveNoveltyMetricPrms(const NoveltyMetricPrms& noveltyMetricPrms, size_t tabs = 0, + const String& prefix = ""); + + private: + Ostream& print(size_t tabs = 0); + String newl(size_t lines = 1); + + Ostream& _stream; + }; +} + +#endif diff --git a/include/HyperNeat/Utils/Set.hpp b/include/HyperNeat/Utils/Set.hpp new file mode 100644 index 0000000..d403e6b --- /dev/null +++ b/include/HyperNeat/Utils/Set.hpp @@ -0,0 +1,12 @@ +#ifndef __HYPERNEAT_SET_HPP__ +#define __HYPERNEAT_SET_HPP__ + +#include <set> + +namespace hyperneat +{ + template <class V> + using Set = std::set<V>; +} + +#endif diff --git a/include/HyperNeat/Utils/Size.hpp b/include/HyperNeat/Utils/Size.hpp new file mode 100644 index 0000000..0ef5616 --- /dev/null +++ b/include/HyperNeat/Utils/Size.hpp @@ -0,0 +1,12 @@ +#ifndef __HYPERNEAT_SIZE_HPP__ +#define __HYPERNEAT_SIZE_HPP__ + +#include <cstddef> +#include <climits> + +namespace hyperneat +{ + using std::size_t; +} + +#endif diff --git a/include/HyperNeat/Utils/String.hpp b/include/HyperNeat/Utils/String.hpp new file mode 100644 index 0000000..ab74d45 --- /dev/null +++ b/include/HyperNeat/Utils/String.hpp @@ -0,0 +1,11 @@ +#ifndef HYPERNEAT_STRING_HPP +#define HYPERNEAT_STRING_HPP + +#include <string> + +namespace hyperneat +{ + using String = std::string; +} + +#endif diff --git a/include/HyperNeat/Utils/Thread.hpp b/include/HyperNeat/Utils/Thread.hpp new file mode 100644 index 0000000..4b861e3 --- /dev/null +++ b/include/HyperNeat/Utils/Thread.hpp @@ -0,0 +1,11 @@ +#ifndef __HYPERNEAT_THREAD_HPP__ +#define __HYPERNEAT_THREAD_HPP__ + +#include <thread> + +namespace hyperneat +{ + using Thread = std::thread; +} + +#endif diff --git a/include/HyperNeat/Utils/ValueMap.hpp b/include/HyperNeat/Utils/ValueMap.hpp new file mode 100644 index 0000000..c2910b5 --- /dev/null +++ b/include/HyperNeat/Utils/ValueMap.hpp @@ -0,0 +1,12 @@ +#ifndef __HYPERNEAT_VALUEMAP_HPP__ +#define __HYPERNEAT_VALUEMAP_HPP__ + +#include <HyperNeat/Utils/Vector.hpp> +#include <HyperNeat/Utils/ValuePoint.hpp> + +namespace hyperneat +{ + using ValueMap = Vector<ValuePoint>; +} + +#endif diff --git a/include/HyperNeat/Utils/ValuePoint.hpp b/include/HyperNeat/Utils/ValuePoint.hpp new file mode 100644 index 0000000..b9e15ab --- /dev/null +++ b/include/HyperNeat/Utils/ValuePoint.hpp @@ -0,0 +1,19 @@ +#ifndef __HYPERNEAT_VALUEPOINT_HPP__ +#define __HYPERNEAT_VALUEPOINT_HPP__ + +#include <HyperNeat/Utils/Point.hpp> + +namespace hyperneat +{ + class ValuePoint : public Point + { + public: + ValuePoint() = default; + ValuePoint(double x, double y, double value, double segment); + + double _value = 0.0; + double _segment = 0.0; + }; +} + +#endif diff --git a/include/HyperNeat/Utils/Vector.hpp b/include/HyperNeat/Utils/Vector.hpp new file mode 100644 index 0000000..f7352e4 --- /dev/null +++ b/include/HyperNeat/Utils/Vector.hpp @@ -0,0 +1,12 @@ +#ifndef __HYPERNEAT_VECTOR_HPP__ +#define __HYPERNEAT_VECTOR_HPP__ + +#include <vector> + +namespace hyperneat +{ + template <class V> + using Vector = std::vector<V>; +} + +#endif diff --git a/include/HyperNeat/Utils/Vector2D.hpp b/include/HyperNeat/Utils/Vector2D.hpp new file mode 100644 index 0000000..257dbe6 --- /dev/null +++ b/include/HyperNeat/Utils/Vector2D.hpp @@ -0,0 +1,12 @@ +#ifndef __HYPERNEAT_VECTOR2D_HPP__ +#define __HYPERNEAT_VECTOR2D_HPP__ + +#include <Hyperneat/Utils/Vector.hpp> + +namespace hyperneat +{ + template <class V> + using Vector2D = Vector<Vector<V>>; +} + +#endif diff --git a/lib/.keep b/lib/.keep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/lib/.keep diff --git a/obj/.keep b/obj/.keep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/obj/.keep diff --git a/obj/Utils/.keep b/obj/Utils/.keep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/obj/Utils/.keep diff --git a/plugins/CppnExplorer/CppnExplorer.sublime-project b/plugins/CppnExplorer/CppnExplorer.sublime-project new file mode 100644 index 0000000..dee3d7f --- /dev/null +++ b/plugins/CppnExplorer/CppnExplorer.sublime-project @@ -0,0 +1,41 @@ +{ + "build_systems": + [ + { + "cmd": + [ + "make", + "-C", + "${project_path}" + ], + "name": "Build" + }, + { + "cmd": + [ + "make", + "clean", + "-C", + "${project_path}" + ], + "name": "Clean" + } + ], + "folders": + [ + { + "file_exclude_patterns": + [ + "*.sublime-project", + "*.sublime-workspace" + ], + "folder_exclude_patterns": + [ + "obj", + "lib", + "Algorithm Documentation" + ], + "path": "." + } + ] +} diff --git a/plugins/CppnExplorer/CppnExplorer.sublime-workspace b/plugins/CppnExplorer/CppnExplorer.sublime-workspace new file mode 100644 index 0000000..cf691cc --- /dev/null +++ b/plugins/CppnExplorer/CppnExplorer.sublime-workspace @@ -0,0 +1,1199 @@ +{ + "auto_complete": + { + "selected_items": + [ + [ + "fiel", + "fieldGenerator" + ], + [ + "_ne", + "_neuralNetReady" + ], + [ + "_neu", + "_neuralNetReady" + ], + [ + "_neur", + "_neuralNet" + ], + [ + "setOu", + "setOutlineColor" + ], + [ + "PIX", + "PIXEL_SIZE" + ], + [ + "Neu", + "Neuron" + ], + [ + "IN", + "INPUT" + ], + [ + "setO", + "setOrigin" + ], + [ + "Uin", + "Uint8" + ], + [ + "PI", + "PIXEL_SIZE" + ], + [ + "Neur", + "Neuron" + ], + [ + "Bl", + "BlendAdd" + ], + [ + "_wi", + "_withNeuralNet" + ], + [ + "neura", + "neuralNetGenerator" + ], + [ + "neural", + "_neuralNetGenerator" + ], + [ + "_win", + "_windowOpen" + ], + [ + "eve", + "event" + ], + [ + "joi", + "joinable" + ], + [ + "win", + "_windowThread" + ], + [ + "grid", + "gridSpacing" + ], + [ + "Rende", + "RenderTarget" + ], + [ + "Render", + "RenderWindow" + ], + [ + "sr", + "srcNeuron" + ], + [ + "colo", + "colorMult" + ], + [ + "setOutl", + "setOutlineThickness" + ], + [ + "gri", + "gridClr" + ], + [ + "_sh", + "_showGrid" + ], + [ + "res", + "resolution" + ], + [ + "fie", + "field" + ], + [ + "fi", + "_field" + ], + [ + "out", + "outputAt" + ], + [ + "cpp", + "_cppn" + ], + [ + "_bia", + "_biasTexture" + ], + [ + "in", + "inputAt" + ], + [ + "INV", + "INV_WEIGHTS" + ], + [ + "col", + "colorInt" + ], + [ + "C", + "CircleShape" + ], + [ + "Neural", + "NeuralNet" + ], + [ + "_cur", + "_cursorPos" + ], + [ + "_cu", + "_cursorPos" + ], + [ + "prin", + "printWeights" + ], + [ + "Key", + "Keyboard" + ], + [ + "Cpp", + "CppnExplorer" + ], + [ + "co", + "Color" + ], + [ + "_is", + "_isLocked" + ], + [ + "Ne", + "Neuron" + ], + [ + "_re", + "_recentReplacement" + ], + [ + "re", + "relateIO" + ], + [ + "_in", + "_inputMap" + ], + [ + "en", + "endl" + ], + [ + "_ou", + "_output" + ], + [ + "_", + "_position" + ], + [ + "_out", + "_outputMap" + ], + [ + "pu", + "previousNeurons" + ], + [ + "tem", + "tempNeurons" + ], + [ + "def", + "default" + ], + [ + "gene", + "generateNeuralNet" + ], + [ + "c", + "const" + ], + [ + "ato", + "atomic_size_t" + ], + [ + "db", + "dbgFileName" + ], + [ + "dbg", + "dbgFileName" + ], + [ + "th", + "threadID" + ], + [ + "Pop", + "PopulationDefaults" + ], + [ + "Vec", + "Vector2D" + ], + [ + "wor", + "worldFile" + ], + [ + "ste", + "onStep" + ], + [ + "pr", + "print" + ], + [ + "str", + "struct struct" + ], + [ + "SE", + "SELECT_LOAD" + ], + [ + "SELE", + "SELECT_NEW" + ], + [ + "pri", + "printCommand" + ], + [ + "com", + "command" + ], + [ + "arg", + "argv" + ], + [ + "org", + "organismIdx" + ], + [ + "spe", + "specieSize" + ], + [ + "nod", + "nodeType" + ], + [ + "loa", + "loadGenome" + ], + [ + "load", + "loadPopulationPrms" + ], + [ + "getCHa", + "getChampionIdx" + ], + [ + "cham", + "champIdx" + ], + [ + "size", + "size_t" + ], + [ + "file", + "fileName" + ], + [ + "fil", + "fileName" + ], + [ + "Po", + "Population" + ], + [ + "File", + "FileStream" + ], + [ + "Fil", + "FileStream" + ], + [ + "Inp", + "InputFile" + ], + [ + "pop", + "population" + ], + [ + "isO", + "isOrganismBeingGenerated" + ], + [ + "isA", + "isAnyOrganismBeingGenerated" + ], + [ + "organ", + "organismIsBeingGenerated" + ], + [ + "_orga", + "_organismsBeingGenerated" + ], + [ + "is", + "isBeingGenerated" + ], + [ + "_or", + "_organismsBeingGenerated" + ], + [ + "_org", + "_organismIsLocked" + ], + [ + "_isL", + "_isLocked" + ], + [ + "organi", + "_organismsBeingGenerated" + ], + [ + "_lock", + "_lockedOrganisms" + ], + [ + "isL", + "isLockedByReplacement" + ], + [ + "si", + "size_t" + ], + [ + "_pop", + "_populationLock" + ], + [ + "leve", + "levelToSegment" + ], + [ + "inc", + "include" + ], + [ + "beg", + "begin" + ], + [ + "dis", + "distance" + ], + [ + "emp", + "emplace_back" + ], + [ + "mu", + "mutex" + ], + [ + "incl", + "inclIter" + ], + [ + "siz", + "size_t" + ], + [ + "cr", + "crntInclusions" + ], + [ + "sour", + "sourceNeuron" + ], + [ + "neuro", + "_neurons" + ], + [ + "syn", + "synTot" + ], + [ + "thre", + "threadVec" + ], + [ + "_organi", + "_allOrganisms" + ], + [ + "poo", + "poorOrganism" + ], + [ + "fta", + "fatherIdx" + ], + [ + "fath", + "fatherIdx" + ], + [ + "fat", + "fatherIdx" + ], + [ + "orga", + "organismReplaced" + ], + [ + "repla", + "replaceOrganism" + ], + [ + "ran", + "randWeight" + ], + [ + "_cpp", + "_cppnInputs" + ], + [ + "_i", + "_innovationNo" + ], + [ + "gen", + "geneIdx" + ], + [ + "ra", + "randSeed" + ], + [ + "_o", + "_output" + ] + ] + }, + "buffers": + [ + { + "file": "todolist.txt", + "settings": + { + "buffer_size": 1115, + "line_ending": "Windows" + } + }, + { + "file": "include/CppnExplorer.hpp", + "settings": + { + "buffer_size": 4318, + "line_ending": "Windows" + } + }, + { + "file": "src/CppnExplorer.cpp", + "settings": + { + "buffer_size": 17141, + "line_ending": "Windows" + } + } + ], + "build_system": "Build", + "command_palette": + { + "height": 375.0, + "selected_items": + [ + [ + "tas", + "Tasks: New document" + ], + [ + "TAS", + "Tasks: New document" + ], + [ + "", + "Build: Build" + ], + [ + "ops", + "View: Toggle Open Files in Side Bar" + ] + ], + "width": 378.0 + }, + "console": + { + "height": 118.0, + "history": + [ + ] + }, + "distraction_free": + { + "menu_visible": true, + "show_minimap": false, + "show_open_files": false, + "show_tabs": false, + "side_bar_visible": false, + "status_bar_visible": false + }, + "expanded_folders": + [ + "/C/Projects/Libraries/HyperNeat/plugins/CppnExplorer" + ], + "file_history": + [ + "/C/Projects/Applications/NeuroRacers/bin/current.population", + "/C/Projects/Libraries/HyperNeat/plugins/CppnExplorer/Makefile", + "/C/Users/Paul/Downloads/ID/Adobe Indesign CS6/DLL FILE/IMPORTANT.txt", + "/C/Users/Paul/Downloads/ID/Adobe Indesign CS6/INSTALLING INSTRUCTIONS.txt", + "/C/Projects/Libraries/HyperNeat/plugins/CppnExplorer/CppnExplorer.sublime-project", + "/C/Projects/Libraries/HyperNeat/plugins/CppnExplorer/src/CppnExplorer.cpp", + "/C/Projects/Libraries/HyperNeat/plugins/CppnExplorer/include/CppnExplorer.hpp", + "/C/TBB/include/tbb/atomic.h", + "/C/Program Files (x86)/Orbiter/Modules/D3D11Shaders/Planet_Clouds.fx", + "/C/Program Files (x86)/Orbiter/Modules/D3D11Shaders/Planet.fx", + "/C/Program Files (x86)/Orbiter/Config/NGPlanetConfig/Earth_ng.cfg", + "/C/Program Files (x86)/Orbiter/Config/Jupiter.cfg", + "/C/Program Files (x86)/Orbiter/Config/NGPlanetConfig/Titan_ng.cfg", + "/C/Program Files (x86)/Orbiter/D3D11Client.cfg", + "/C/Projects/.git", + "/C/Projects/New Text Document.txt", + "/C/Users/Paul/Downloads/orbiter100830/Config/Mars.cfg", + "/C/Projects/Libraries/HyperNeat/plugins/CppnExplorer/todo", + "/C/Users/Paul/Downloads/orbiter100830/Config/Earth.cfg", + "/C/Users/Paul/Downloads/asmi-ovp-1f2ee85a8579/asmi-ovp-1f2ee85a8579/D3D11CLIENT README.txt", + "/C/Users/Paul/Downloads/orbiter100830/readme.txt", + "/C/Users/Paul/Downloads/orbiter100830/patch.txt", + "/C/Sublime Text 3/Data/Packages/User/Preferences.sublime-settings", + "/C/Users/Paul/Documents/Trabajo/Will Bourdreau/Laurieta Rose/NextStepsBoat.txt", + "/C/Projects/Libraries/HyperNeat/plugins/cppn.explorer/src/CppnExplorer.cpp", + "/C/Projects/Libraries/HyperNeat/plugins/cppn.explorer/Makefile", + "/C/Projects/Libraries/HyperNeat/plugins/cppn.explorer/include/CppnExplorer.hpp", + "/C/Projects/Libraries/HyperNeat/plugins/cppn.explorer/cppn.explorer.sublime-project", + "/C/Projects/Libraries/HyperNeat/HyperNeat.sublime-project", + "/C/Projects/Libraries/HyperNeat/src/Organism.cpp", + "/C/Projects/Libraries/HyperNeat/include/HyperNeat/Population.hpp", + "/C/Projects/Libraries/HyperNeat/src/Population.cpp", + "/C/Projects/Libraries/HyperNeat/src/NeuralNet.cpp", + "/C/Projects/Libraries/HyperNeat/include/HyperNeat/NeuralNet.hpp", + "/C/Projects/Libraries/HyperNeat/include/HyperNeat/Organism.hpp", + "/C/Projects/Libraries/HyperNeat/include/HyperNeat/Utils/Atomic.hpp", + "/C/Projects/Libraries/HyperNeat/Makefile", + "/C/Projects/Libraries/HyperNeat/include/HyperNeat/Utils/Future.hpp", + "/C/Projects/Libraries/HyperNeat/todo", + "/C/Users/Paul/Downloads/tbb42_20130725oss_win/tbb42_20130725oss/include/tbb/tbb.h", + "/C/Users/Paul/Downloads/tbb41_20130516oss_src/tbb41_20130516oss_src/tbb41_20130516oss/README.txt", + "/C/Projects/Libraries/HyperNeat/include/HyperNeat/Utils/Vector.hpp", + "/C/Projects/Libraries/HyperNeat/src/Utils/Point.cpp", + "/C/Projects/Libraries/HyperNeat/include/HyperNeat/Utils/Point.hpp", + "/C/Projects/Libraries/HyperNeat/src/QuadTree.cpp", + "/C/Projects/Libraries/HyperNeat/src/Utils/LoadFile.cpp", + "/C/Projects/Libraries/HyperNeat/include/HyperNeat/Utils/Atom.hpp", + "/C/Projects/Libraries/HyperNeat/include/HyperNeat/Utils/Vector2D.hpp", + "/C/Projects/Libraries/HyperNeat/include/HyperNeat/Debugger.hpp", + "/C/Projects/Libraries/HyperNeat/include/HyperNeat/Utils/Thread.hpp", + "/C/Projects/Libraries/HyperNeat/include/HyperNeat/Utils/Mutex.hpp", + "/C/Projects/Libraries/HyperNeat/src/Cppn.cpp", + "/C/Projects/Libraries/HyperNeat/src/Cppn.s", + "/C/Users/Paul/Downloads/FitVids.js-master/FitVids.js-master/tests.html", + "/C/Users/Paul/Downloads/FitVids.js-master/FitVids.js-master/CONTRIBUTING.md", + "/C/Projects/Libraries/HyperNeat/include/HyperNeat/StackMacros.hpp", + "/C/Projects/Libraries/HyperNeat/include/HyperNeat/PopulationPrms.hpp", + "/C/Users/Paul/Downloads/Ida Pro v6.1/READ ME !!.txt", + "/C/Projects/Libraries/HyperNeat/src/QueryPlanePrms.cpp", + "/C/Projects/Libraries/HyperNeat/src/Utils/SaveFile.cpp", + "/C/SFML/build-debug/CMakeCache.txt", + "/C/Box2D/build-debug/CMakeCache.txt", + "/C/Projects/Libraries/HyperNeat/include/HyperNeat/Genome.hpp", + "/C/Projects/Applications/NeuroRacers/bin/population", + "/C/Projects/Libraries/HyperNeat/include/HyperNeat/Cppn.hpp", + "/C/SFML/build-dynamic/CMakeCache.txt", + "/C/Box2D/build-dynamic/CMakeCache.txt", + "/C/Projects/Libraries/HyperNeat/src/Utils/TEMP_Vector.cpp", + "/C/Projects/Applications/HyperGuppies/Makefile", + "/C/Projects/Applications/Guppies SFML/SimBase_Execute.cpp", + "/C/Projects/Applications/HyperGuppies/include/Utils/String.hpp", + "/C/Projects/Applications/HyperGuppies/todo", + "/C/Programming/Applications/HyperGuppies/include/Utils/RLUtils.hpp", + "/C/Programming/Applications/HyperGuppies/include/WorldParams.hpp", + "/C/Users/Paul/Downloads/pdc25_vc_w32/README", + "/C/Programming/Applications/HyperGuppies/bin/worlds/default.world", + "/C/Programming/Applications/HyperGuppies/include/Utils/File.hpp", + "/C/Programming/Applications/HyperGuppies/Makefile", + "/C/Programming/Applications/HyperGuppies/HyperGuppies.sublime-project", + "/C/Programming/Applications/Guppies SFML/Params.hpp", + "/C/Users/Paul/Downloads/SFML/SFML-master/CMakeLists.txt", + "/C/Programming/Applications/HyperGuppies/include/Utils/Function.hpp", + "/C/Users/Paul/Downloads/RBTray-4_3/readme.txt", + "/C/Programming/Applications/HyperGuppies/src/Main.cpp", + "/C/Programming/Applications/HyperGuppies/todo", + "/C/RLUtil/RLUtil.hpp", + "/C/Users/Paul/Downloads/PlainTasks-master/PlainTasks-master/Readme.md", + "/C/Programming/Applications/HyperGuppies/include/App.hpp", + "/C/Programming/Applications/HyperGuppies/src/App.cpp", + "/C/Users/Paul/Downloads/rlutil-master/rlutil-master/rlutil.hpp", + "/C/Users/Paul/Downloads/rlutil-master/rlutil-master/example.c", + "/C/Users/Paul/Downloads/rlutil-master/rlutil-master/test.cpp", + "/C/Users/Paul/Downloads/rlutil-master/rlutil-master/rlutil.h", + "/C/Box2D/include/Common/b2Settings.h", + "/C/Users/Paul/Desktop/Box2D/Box2D/Common/b2Settings.h", + "/C/Users/Paul/Desktop/Box2D/CMakeLists.txt", + "/C/Users/Paul/Desktop/Box2D/Box2D/CMakeLists.txt", + "/C/Programming/Libraries/HyperNeat/Makefile", + "/C/Users/Paul/Downloads/neuro.3.0.3/neuro/style.css", + "/C/Users/Paul/Downloads/tapio-rlutil-c49f5ae/tapio-rlutil-c49f5ae/rlutil.h", + "/C/Programming/Libraries/HyperNeat/src/Utils/LoadFile.cpp", + "/C/Programming/Applications/NeuroRacers/bin/population", + "/C/Programming/Libraries/HyperNeat/todo", + "/C/Programming/Libraries/HyperNeat/include/Population.hpp", + "/C/Programming/Libraries/HyperNeat/include/Utils/LoadFile.hpp", + "/C/Programming/Libraries/HyperNeat/src/Utils/SaveFile.cpp", + "/C/Programming/Libraries/HyperNeat/include/Utils/SaveFile.hpp", + "/C/Programming/Libraries/HyperNeat/include/Organism.hpp", + "/C/Users/Paul/Desktop/Box2D_v2.2.1/Box2D/Common/b2Settings.h", + "/C/Users/Paul/Desktop/Box2D_v2.2.1/Building.txt", + "/C/Users/Paul/Desktop/Box2D_v2.2.1/Box2D/Box2D.h", + "/C/Users/Paul/Desktop/Box2D_v2.2.1/Readme.txt", + "/C/Programming/Libraries/HyperNeat/src/Genome.cpp", + "/C/Programming/Libraries/HyperNeat/include/Genome.hpp", + "/C/Programming/Libraries/HyperNeat/src/Cppn.cpp", + "/C/Programming/Libraries/HyperNeat/include/Cppn.hpp", + "/C/Programming/Libraries/HyperNeat/src/Organism.cpp", + "/C/Programming/Libraries/HyperNeat/src/Population.cpp", + "/C/Box2D/src/Dynamics/b2World.cpp", + "/C/Box2D/src/Dynamics/b2World.h", + "/C/Box2D/src/Dynamics/b2WorldCallbacks.h", + "/C/Programming/Libraries/HyperNeat/include/Utils/NodeTypes.hpp", + "/C/Programming/Libraries/HyperNeat/include/Utils/String.hpp", + "/C/Programming/Libraries/HyperNeat/src/LoadSaveSystem.cpp", + "/C/Programming/Libraries/HyperNeat/include/LoadSaveSystem.hpp", + "/C/Programming/Libraries/HyperNeat/include/Saver.hpp", + "/C/Programming/Libraries/HyperNeat/src/Utils/File.cpp", + "/C/Programming/Libraries/HyperNeat/include/Utils/File.hpp" + ], + "find": + { + "height": 36.0 + }, + "find_in_files": + { + "height": 92.0, + "where_history": + [ + "" + ] + }, + "find_state": + { + "case_sensitive": true, + "find_history": + [ + "BlendMode", + "fieldGenerator", + "value", + "_grid", + "223", + "refNeuron", + "0", + "PIXEL_SIZE", + "cout", + "_windowOpen", + "64", + "bias", + "pos", + "bias", + "PIXEL", + "CPPN_EX_WIDTH", + "_withNeuralNet", + "600", + "512", + "_window", + "System", + "Graphics", + "RenderWindow", + "WIN", + "sf::", + "gridClr", + "64", + "sf::", + " = nullptr", + "16", + "128", + "=", + "_resolution", + "SunDir", + "16", + "128", + "32", + "sf::Uint8", + "Alpha", + "sf::Color::White", + "{ 0, 0, 0, 0 }", + "sf::Color::Black", + "Add", + "2", + "1.0f", + "Thickness", + "setOrigin", + "0", + "_uiColor", + "0.0", + "resolution", + "Neurons", + "\\", + "{ 0, 0, 0 }", + "_uiColor", + "6", + "8", + "10", + "3", + "_uiColor", + "{ 0, 0, 0 }", + ", 128", + "PIXEL * 1.0f", + "6", + "4", + "0", + "_cppn.outputAt(0)", + "_valueImage.", + "bias", + "mage", + "val", + "*", + "{ 0, 0, 0, 128 }", + "_uiColor", + "4.", + "3.0", + "2", + "1.5", + "colorInt / 2", + "radius", + "_body", + "64", + "0", + "20", + "128", + "20.0", + "16", + "10.0", + "0.025f", + "2", + "4", + "x1", + "1", + "x", + "_weight", + "64", + "PIXEL", + "0.008f", + "0", + "_body", + "128", + "0.1", + "Scale", + "H", + ", 2, sf::Lines", + ")", + "sf::Vertex(", + "sf::Vector2f", + "Color", + "cursor", + "cVal", + "weight", + "_cppn", + "weight", + "_cppn", + "cVal", + " - cBias", + "abs(", + "cVal", + "val", + "static_cast<double>(", + "0", + "int", + "int(255)", + "sf::Uint8", + "float", + "x", + "cppn" + ], + "highlight": true, + "in_selection": false, + "preserve_case": false, + "regex": true, + "replace_history": + [ + "_" + ], + "reverse": false, + "show_context": true, + "use_buffer2": true, + "whole_word": false, + "wrap": true + }, + "groups": + [ + { + "selected": 2, + "sheets": + [ + { + "buffer": 0, + "file": "todolist.txt", + "semi_transient": false, + "settings": + { + "buffer_size": 1115, + "regions": + { + }, + "selection": + [ + [ + 363, + 363 + ] + ], + "settings": + { + "BracketHighlighterBusy": false, + "bh_regions": + [ + "bh_double_quote", + "bh_double_quote_center", + "bh_double_quote_open", + "bh_double_quote_close", + "bh_square", + "bh_square_center", + "bh_square_open", + "bh_square_close", + "bh_regex", + "bh_regex_center", + "bh_regex_open", + "bh_regex_close", + "bh_curly", + "bh_curly_center", + "bh_curly_open", + "bh_curly_close", + "bh_angle", + "bh_angle_center", + "bh_angle_open", + "bh_angle_close", + "bh_tag", + "bh_tag_center", + "bh_tag_open", + "bh_tag_close", + "bh_default", + "bh_default_center", + "bh_default_open", + "bh_default_close", + "bh_round", + "bh_round_center", + "bh_round_open", + "bh_round_close", + "bh_unmatched", + "bh_unmatched_center", + "bh_unmatched_open", + "bh_unmatched_close", + "bh_single_quote", + "bh_single_quote_center", + "bh_single_quote_open", + "bh_single_quote_close" + ], + "syntax": "Packages/PlainTasks/PlainTasks.tmLanguage", + "tab_size": 2, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 0.0, + "zoom_level": 1.0 + }, + "stack_index": 2, + "type": "text" + }, + { + "buffer": 1, + "file": "include/CppnExplorer.hpp", + "semi_transient": false, + "settings": + { + "buffer_size": 4318, + "regions": + { + }, + "selection": + [ + [ + 391, + 391 + ] + ], + "settings": + { + "BracketHighlighterBusy": false, + "bh_regions": + [ + "bh_double_quote", + "bh_double_quote_center", + "bh_double_quote_open", + "bh_double_quote_close", + "bh_square", + "bh_square_center", + "bh_square_open", + "bh_square_close", + "bh_regex", + "bh_regex_center", + "bh_regex_open", + "bh_regex_close", + "bh_curly", + "bh_curly_center", + "bh_curly_open", + "bh_curly_close", + "bh_angle", + "bh_angle_center", + "bh_angle_open", + "bh_angle_close", + "bh_tag", + "bh_tag_center", + "bh_tag_open", + "bh_tag_close", + "bh_default", + "bh_default_center", + "bh_default_open", + "bh_default_close", + "bh_round", + "bh_round_center", + "bh_round_open", + "bh_round_close", + "bh_unmatched", + "bh_unmatched_center", + "bh_unmatched_open", + "bh_unmatched_close", + "bh_single_quote", + "bh_single_quote_center", + "bh_single_quote_open", + "bh_single_quote_close" + ], + "syntax": "Packages/C++ Starting Kit/CCpp.tmLanguage", + "tab_size": 4, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 0.0, + "zoom_level": 1.0 + }, + "stack_index": 1, + "type": "text" + }, + { + "buffer": 2, + "file": "src/CppnExplorer.cpp", + "semi_transient": false, + "settings": + { + "buffer_size": 17141, + "regions": + { + }, + "selection": + [ + [ + 1778, + 1778 + ] + ], + "settings": + { + "BracketHighlighterBusy": false, + "bh_regions": + [ + "bh_single_quote", + "bh_single_quote_center", + "bh_single_quote_open", + "bh_single_quote_close", + "bh_unmatched", + "bh_unmatched_center", + "bh_unmatched_open", + "bh_unmatched_close", + "bh_round", + "bh_round_center", + "bh_round_open", + "bh_round_close", + "bh_default", + "bh_default_center", + "bh_default_open", + "bh_default_close", + "bh_square", + "bh_square_center", + "bh_square_open", + "bh_square_close", + "bh_regex", + "bh_regex_center", + "bh_regex_open", + "bh_regex_close", + "bh_curly", + "bh_curly_center", + "bh_curly_open", + "bh_curly_close", + "bh_angle", + "bh_angle_center", + "bh_angle_open", + "bh_angle_close", + "bh_tag", + "bh_tag_center", + "bh_tag_open", + "bh_tag_close", + "bh_double_quote", + "bh_double_quote_center", + "bh_double_quote_open", + "bh_double_quote_close" + ], + "syntax": "Packages/C++ Starting Kit/CCpp.tmLanguage", + "tab_size": 4, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 828.0, + "zoom_level": 1.0 + }, + "stack_index": 0, + "type": "text" + } + ] + } + ], + "incremental_find": + { + "height": 0.0 + }, + "input": + { + "height": 30.0 + }, + "layout": + { + "cells": + [ + [ + 0, + 0, + 1, + 1 + ] + ], + "cols": + [ + 0.0, + 1.0 + ], + "rows": + [ + 0.0, + 1.0 + ] + }, + "menu_visible": false, + "output.exec": + { + "height": 96.0 + }, + "output.find_results": + { + "height": 0.0 + }, + "project": "CppnExplorer.sublime-project", + "replace": + { + "height": 64.0 + }, + "save_all_on_build": true, + "select_file": + { + "height": 0.0, + "selected_items": + [ + ], + "width": 0.0 + }, + "select_project": + { + "height": 500.0, + "selected_items": + [ + [ + "", + "C:\\Projects\\Libraries\\HyperNeat\\HyperNeat.sublime-project" + ] + ], + "width": 805.0 + }, + "select_symbol": + { + "height": 0.0, + "selected_items": + [ + ], + "width": 0.0 + }, + "settings": + { + }, + "show_minimap": true, + "show_open_files": false, + "show_tabs": true, + "side_bar_visible": true, + "side_bar_width": 160.0, + "status_bar_visible": true, + "template_settings": + { + } +} diff --git a/plugins/CppnExplorer/Makefile b/plugins/CppnExplorer/Makefile new file mode 100644 index 0000000..0d31499 --- /dev/null +++ b/plugins/CppnExplorer/Makefile @@ -0,0 +1,19 @@ +LIBRARY := lib/libCppnExplorer.a +SOURCES := $(wildcard src/*.cpp) +OBJECTS := $(patsubst src/%.cpp,obj/%.o,$(SOURCES)) +DEPS := $(patsubst %.o,%.d,$(OBJECTS)) +CFLAGS := -c -O3 -Wall -std=c++11 -MMD -Iinclude -I../../include -IC:/SFML/include +LFLAGS := rvs + +all: $(OBJECTS) + ar $(LFLAGS) $(LIBRARY) $(OBJECTS) + +-include $(DEPS) + +$(OBJECTS): $(patsubst obj/%.o,src/%.cpp,$@) + g++ $(CFLAGS) $(patsubst obj/%.o,src/%.cpp,$@) -o $@ + +clean: + del lib\*.a + del obj\*.o + del obj\*.d diff --git a/plugins/CppnExplorer/include/CppnExplorer.hpp b/plugins/CppnExplorer/include/CppnExplorer.hpp new file mode 100644 index 0000000..5ca20c5 --- /dev/null +++ b/plugins/CppnExplorer/include/CppnExplorer.hpp @@ -0,0 +1,129 @@ +#ifndef __CPPN_EXPLORER_HPP__ +#define __CPPN_EXPLORER_HPP__ + +#include <Hyperneat/Cppn.hpp> +#include <Hyperneat/Genome.hpp> +#include <Hyperneat/NeuralNet.hpp> +#include <SFML/Graphics/Texture.hpp> +#include <HyperNeat/Utils/Atomic.hpp> +#include <HyperNeat/Utils/Thread.hpp> +#include <Hyperneat/NeuralNetPrms.hpp> +#include <SFML/Graphics/VertexArray.hpp> +#include <SFML/Graphics/CircleShape.hpp> +#include <SFML/Graphics/RenderWindow.hpp> +#include <SFML/Graphics/RectangleShape.hpp> + +namespace hyperneat +{ + class CppnExplorer + { + public: + CppnExplorer(); + ~CppnExplorer(); + + void run(const Genome& genome, const NeuralNetPrms* nnPrms = nullptr, sf::RenderTarget* target = nullptr); + void shutdown(); + void draw(); + + // void run(const Genome& genome, const NeuralNetPrms& nnPrms); + // void shutdown(); + + private: + void windowHandler(); + void eventHandler(); + void neuralNetGenerator(); + void fieldGenerator(); + + class Neuron + { + public: + Neuron(const NeuralNet::Neuron& refNeuron); + + sf::VertexArray _synapses = sf::VertexArray(sf::Lines); + sf::CircleShape _nucleus = sf::CircleShape(4.0f * PIXEL_SIZE, 12); + }; + + Cppn _cppn; + Genome _genome; + NeuralNetPrms _nnPrms; + Vector<Neuron> _neurons; + NeuralNet _neuralNet; + bool _withNeuralNet = false; + Atomic<bool> _neuralNetReady = {false}; + + sf::RenderWindow _window; + Atomic<bool> _windowOpen = {false}; + sf::RenderTarget* _target = nullptr; + const sf::Color _uiColor = {255, 255, 255, 8}; + + sf::Image _fieldImage; + sf::RectangleShape _fieldSprite; + sf::Texture _fieldTexture; + size_t _currentField = 0; + Atomic<bool> _fieldReady = {false}; + + Thread _windowHandler; + Thread _fieldGenerator; + Thread _neuralNetGenerator; + + static constexpr unsigned RESOLUTION = 512; + static constexpr float PIXEL_SIZE = 1.0f / static_cast<float>(RESOLUTION); + + // class Neuron + // { + // public: + // enum class Type { + // INPUT, + // HIDDEN, + // OUTPUT + // }; + + // Type _type; + // sf::CircleShape _body = sf::CircleShape(0.0f, 30); + // sf::CircleShape _nucleus = sf::CircleShape(0.0f, 30); + // sf::VertexArray _synapses = sf::VertexArray(sf::Lines); + // }; + + // void print(const sf::Color* field); + // void placeCursorAt(const sf::Vector2f& newPos); + // void createNeuron(Neuron& repNeuron, const NeuralNet::Neuron& srcNeuron); + + // Cppn _cppn; + // Genome _genome; + // NeuralNet _neuralNet; + // NeuralNetPrms _nnPrms; + + // const unsigned RESOLUTION = 512; + // sf::RenderWindow _window; + // Atomic<bool> _running; + // Thread _executor; + + // const sf::Color WEIGHTS = { 128, 0, 255 }; + // const sf::Color INV_WEIGHTS = { 255, 0, 128 }; + // const sf::Color BIAS = { 0, 255, 255 }; + // const sf::Color* _field = &WEIGHTS; + + // sf::Image _valueImage; + // sf::Texture _valueTexture; + // sf::RectangleShape _valueSprite; + // sf::Image _biasImage; + // sf::Texture _biasTexture; + // sf::RectangleShape _biasSprite; + // bool _showBias = false; + + // const sf::Color _uiColor = { 64, 64, 64 }; + // const float PIXEL = 1.0f / static_cast<float>(RESOLUTION); + + // sf::Vector2f _cursorPos = { 0.0f, 0.0f }; + // sf::RectangleShape _cursorH = sf::RectangleShape(sf::Vector2f(0.2f, PIXEL * 3.0f)); + // sf::RectangleShape _cursorV = sf::RectangleShape(sf::Vector2f(PIXEL * 3.0f, 0.2f)); + // sf::VertexArray _grid = sf::VertexArray(sf::Lines); + + // bool _showField = true; + // bool _showNeurons = true; + // bool _showGrid = true; + // Vector<Neuron> _neurons; + }; +} + +#endif diff --git a/plugins/CppnExplorer/lib/.keep b/plugins/CppnExplorer/lib/.keep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/plugins/CppnExplorer/lib/.keep diff --git a/plugins/CppnExplorer/obj/.keep b/plugins/CppnExplorer/obj/.keep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/plugins/CppnExplorer/obj/.keep diff --git a/plugins/CppnExplorer/src/CppnExplorer.cpp b/plugins/CppnExplorer/src/CppnExplorer.cpp new file mode 100644 index 0000000..555922a --- /dev/null +++ b/plugins/CppnExplorer/src/CppnExplorer.cpp @@ -0,0 +1,549 @@ +#include <CppnExplorer.hpp> +#include <SFML/Window/Event.hpp> +#include <HyperNeat/Utils/Point.hpp> + +using namespace sf; +using namespace hyperneat; + +CppnExplorer::CppnExplorer() +{ + _fieldSprite.setOrigin(1.0f, 1.0f); + _fieldSprite.setPosition(0.0f, 0.0f); + _fieldSprite.setTexture(&_fieldTexture); + _fieldSprite.setSize(Vector2f(2.0f, 2.0f)); + _fieldImage.create(RESOLUTION, RESOLUTION); +} + +CppnExplorer::~CppnExplorer() +{ + shutdown(); +} + +void +CppnExplorer::run(const Genome& genome, const NeuralNetPrms* nnPrms, RenderTarget* target) +{ + shutdown(); + _target = target; + _genome = genome; + + _cppn.clear(); + _cppn.create(_genome); + + if (nnPrms) { + _withNeuralNet = true; + _nnPrms = *nnPrms; + _neuralNetGenerator = Thread(&CppnExplorer::neuralNetGenerator, this); + } + + if (!_target) { + _windowOpen = true; + _target = &_window; + _windowHandler = Thread(&CppnExplorer::windowHandler, this); + } + + _fieldGenerator = Thread(&CppnExplorer::fieldGenerator, this); +} + +void +CppnExplorer::shutdown() +{ + if (_neuralNetGenerator.joinable()) { + _neuralNetGenerator.join(); + } + + if (_windowHandler.joinable()) { + _windowOpen = false; + _windowHandler.join(); + } + + _neurons.clear(); + _withNeuralNet = false; + _neuralNetReady = false; + _windowOpen = false; + _target = nullptr; +} + +void +CppnExplorer::draw() +{ + _target->clear({8, 8, 16}); + + if (_neuralNetReady) { + for (auto& i : _neurons) { + _target->draw(i._synapses, BlendAdd); + } + + for (auto& i : _neurons) { + _target->draw(i._nucleus, BlendAdd); + } + } + + if (_windowOpen) { + _window.display(); + } +} + +void +CppnExplorer::windowHandler() +{ + ContextSettings settings(0, 0, 8); + String title("Cppn Explorer"); + View view({-1.0f, -1.0f, 2.0f, 2.0f}); + VideoMode videoMode(RESOLUTION, RESOLUTION); + Uint32 style(Style::Titlebar | Style::Close); + + _window.create(videoMode, title, style, settings); + _window.setVerticalSyncEnabled(true); + _window.setView(view); + + while (_windowOpen) { + eventHandler(); + draw(); + } +} + +void +CppnExplorer::eventHandler() +{ + Event event; + + while (_window.pollEvent(event)) { + switch (event.type) { + case Event::Closed: + _windowOpen = false; + break; + + default:; + } + } +} + +void +CppnExplorer::neuralNetGenerator() +{ + _neuralNet.clear(); + _neuralNet.create(_cppn, _nnPrms); + + _neurons.reserve(_neuralNet.getNeurons().size()); + + for (auto& i : _neuralNet.getNeurons()) { + _neurons.emplace_back(i); + } + + _neuralNetReady = true; +} + +void +CppnExplorer::fieldGenerator() +{ + +} + +CppnExplorer::Neuron::Neuron(const NeuralNet::Neuron& refNeuron) +{ + _nucleus.setPosition(refNeuron._position._x, refNeuron._position._y); + _nucleus.setOrigin(_nucleus.getRadius(), _nucleus.getRadius()); + + Color nColor = (refNeuron._bias > 0.0 ? Color::White : Color::Red); + nColor.a = static_cast<Uint8>(fabs((refNeuron._bias / 3.0) * 112.0) + 16.0); + _nucleus.setFillColor(nColor); + + switch (refNeuron._type) { + case NeuralNet::Neuron::Type::INPUT: + _nucleus.setOutlineThickness(4.0f * PIXEL_SIZE); + _nucleus.setOutlineColor({255, 255, 255, nColor.a}); + break; + + case NeuralNet::Neuron::Type::OUTPUT: + _nucleus.setOutlineThickness(4.0f * PIXEL_SIZE); + _nucleus.setOutlineColor({255, 0, 0, nColor.a}); + break; + + default:; + } + + for (auto& i : refNeuron._synapses) { + auto iNeuron = *i._neuron; + Color sColor = (i._weight > 0.0 ? Color::White : Color::Red); + sColor.a = static_cast<Uint8>(fabs((i._weight / 3.0) * 112.0) + 16.0); + Vector2f iPos = {static_cast<float>(iNeuron._position._x), static_cast<float>(iNeuron._position._y)}; + Vector2f tPos = {static_cast<float>(refNeuron._position._x), static_cast<float>(refNeuron._position._y)}; + + _synapses.append({iPos, sColor}); + _synapses.append({tPos, sColor}); + } +} + +// void +// CppnExplorer::run(const Genome& genome, const NeuralNetPrms& nnPrms) +// { +// shutdown(); + +// _genome = genome; +// _nnPrms = nnPrms; + +// _cppn.create(_genome); +// _neuralNet.create(_cppn, _nnPrms); + +// { +// float gridStep = 2.0f / 16.0f; +// Color gridClr = _uiColor; +// gridClr.a = 64; + +// for (float i = -1.0f; i <= 1.0f; i += gridStep) { +// _grid.append({{ -1.0f, i }, gridClr }); +// _grid.append({{ 1.0f, i }, gridClr }); +// _grid.append({{ i, -1.0f }, gridClr }); +// _grid.append({{ i, 1.0f }, gridClr }); +// } +// } + +// _executor = Thread([&]() { +// _running = true; + +// { +// VideoMode videoMode(RESOLUTION, RESOLUTION); +// String title("Cppn Explorer"); +// Uint32 style(Style::Titlebar | Style::Close); +// ContextSettings settings(0, 0, 8); +// View view(FloatRect(-1.0f, -1.0f, 2.0f, 2.0f)); + +// _window.create(videoMode, title, style, settings); +// _window.setVerticalSyncEnabled(true); +// _window.setView(view); +// } + +// _cppn.inputAt(5) = 1.0; + +// _valueImage.create(RESOLUTION, RESOLUTION); +// _biasImage.create(RESOLUTION, RESOLUTION); +// print(&WEIGHTS); +// print(&BIAS); + +// _valueSprite.setTexture(&_valueTexture); +// _valueSprite.setSize(Vector2f(2.0f, 2.0f)); +// _valueSprite.setOrigin(1.0f, 1.0f); +// _valueSprite.setPosition(0.0f, 0.0f); + +// _biasSprite.setTexture(&_biasTexture); +// _biasSprite.setSize(Vector2f(2.0f, 2.0f)); +// _biasSprite.setOrigin(1.0f, 1.0f); +// _biasSprite.setPosition(0.0f, 0.0f); + +// _cursorH.setFillColor(_uiColor); +// _cursorV.setFillColor(_uiColor); +// _cursorH.setOrigin(_cursorH.getSize() / 2.0f); +// _cursorV.setOrigin(_cursorV.getSize() / 2.0f); +// _cursorH.setOutlineThickness(PIXEL); +// _cursorV.setOutlineThickness(PIXEL); +// _cursorH.setOutlineColor({ 128, 128, 128 }); +// _cursorV.setOutlineColor({ 128, 128, 128 }); + +// _neurons.reserve(_neuralNet.getNeuronsCount()); + +// for (auto& i : _neuralNet.getNeurons()) { +// _neurons.emplace_back(); +// createNeuron(_neurons.back(), i); +// } + +// while (_running) { +// Event event; + +// while (_window.pollEvent(event)) { +// switch (event.type) { +// case Event::KeyPressed: +// switch (event.key.code) { +// case Keyboard::B: +// _showBias = !_showBias; +// break; + +// case Keyboard::C: +// placeCursorAt({ 0.0f, 0.0f }); +// print(_field); +// break; + +// case Keyboard::F: +// _field == &WEIGHTS ? ++_field : --_field; +// print(_field); +// break; + +// case Keyboard::V: +// _showField = !_showField; +// break; + +// case Keyboard::N: +// _showNeurons = !_showNeurons; +// break; + +// case Keyboard::G: +// _showGrid = !_showGrid; +// break; + +// default:; +// } + +// break; + +// case Event::MouseButtonPressed: +// if (event.mouseButton.button == Mouse::Left) { +// placeCursorAt(_window.mapPixelToCoords(Mouse::getPosition(_window))); +// print(_field); +// } + +// break; + +// case Event::Closed: +// _running = false; +// break; + +// default:; +// } +// } + +// _window.clear({ 8, 8, 16 }); + +// if (_showField) { +// if (_showBias) { +// _window.draw(_biasSprite); +// } else { +// _window.draw(_valueSprite); +// } +// } + +// if (_showGrid) { +// _window.draw(_grid, { BlendMode::BlendAdd }); +// } + +// if (_showNeurons) { +// for (auto& i : _neurons) { +// _window.draw(i._synapses); +// } + +// for (auto& i : _neurons) { +// if (i._type != Neuron::Type::HIDDEN) { +// continue; +// } + +// _window.draw(i._body); +// _window.draw(i._nucleus); +// } + +// for (auto& i : _neurons) { +// if (i._type == Neuron::Type::HIDDEN) { +// continue; +// } + +// _window.draw(i._body); +// _window.draw(i._nucleus); +// } +// } + +// _window.draw(_cursorH); +// _window.draw(_cursorV); + +// _window.display(); +// } +// }); +// } + +// void +// CppnExplorer::shutdown() +// { +// if (_executor.joinable()) { +// _running = false; +// _executor.join(); +// } + +// this->~CppnExplorer(); +// new (this) CppnExplorer(); +// } + +// void +// CppnExplorer::print(const Color* field) +// { +// size_t out = 0; +// double* x1 = nullptr; +// double* y1 = nullptr; +// double* x2 = nullptr; +// double* y2 = nullptr; +// Image* image = nullptr; +// Texture* texture = nullptr; + +// if (field == &WEIGHTS) { +// x1 = &_cppn.inputAt(0); +// y1 = &_cppn.inputAt(1); +// x2 = &_cppn.inputAt(2); +// y2 = &_cppn.inputAt(3); +// } else { +// x1 = &_cppn.inputAt(2); +// y1 = &_cppn.inputAt(3); +// x2 = &_cppn.inputAt(0); +// y2 = &_cppn.inputAt(1); +// } + +// *x1 = _cursorPos.x; +// *y1 = _cursorPos.y; + +// if (field == &BIAS) { +// out = 1; +// image = &_biasImage; +// texture = &_biasTexture; +// } else { +// image = &_valueImage; +// texture = &_valueTexture; +// } + +// double half = static_cast<double>(RESOLUTION / 2); + +// for (unsigned y = 0; y < RESOLUTION; ++y) { +// for (unsigned x = 0; x < RESOLUTION; ++x) { +// *x2 = (static_cast<double>(x) - half) / half; +// *y2 = (static_cast<double>(y) - half) / half; +// _cppn.inputAt(4) = Point(*x1, *y1).distance(Point(*x2, *y2)); +// _cppn.cycle(); + +// auto multColor = [](const Color& base, double factor) { +// Color result; + +// result.r = static_cast<Uint8>(static_cast<double>(base.r) * factor); +// result.g = static_cast<Uint8>(static_cast<double>(base.g) * factor); +// result.b = static_cast<Uint8>(static_cast<double>(base.b) * factor); + +// return result; +// }; + +// if (_cppn.outputAt(out) > 0) { +// image->setPixel(x, y, multColor(Color::White, _cppn.outputAt(out))); +// } else { +// image->setPixel(x, y, multColor(*field, fabs(_cppn.outputAt(out)))); +// } +// } +// } + +// texture->loadFromImage(*image); +// } + +// void +// CppnExplorer::placeCursorAt(const Vector2f& newPos) +// { +// _cursorPos = newPos; +// _cursorH.setPosition(_cursorPos); +// _cursorV.setPosition(_cursorPos); +// } + +// void +// CppnExplorer::createNeuron(Neuron& repNeuron, const NeuralNet::Neuron& srcNeuron) +// { +// repNeuron._body.setOutlineThickness(PIXEL * 2.0f); +// repNeuron._nucleus.setOutlineThickness(PIXEL * 2.0f); + +// float radius = 0.0f; +// Color inputColor = BIAS; +// Color hiddenColor = INV_WEIGHTS; +// Color outputColor = WEIGHTS; + +// inputColor.a = 128; +// hiddenColor.a = 128; +// outputColor.a = 128; + +// switch (srcNeuron._type) { +// case NeuralNet::Neuron::Type::INPUT: +// repNeuron._type = Neuron::Type::INPUT; +// repNeuron._body.setOutlineColor(inputColor); +// repNeuron._body.setFillColor({ 0, 0, 0, 200 }); +// radius = PIXEL * 24.0f; +// break; + +// case NeuralNet::Neuron::Type::HIDDEN: +// repNeuron._type = Neuron::Type::HIDDEN; +// repNeuron._body.setOutlineColor(hiddenColor); +// repNeuron._body.setFillColor({ 0, 0, 0, 200 }); + +// { +// float level = 1.0f; +// for (; srcNeuron._position._x * level != floor(srcNeuron._position._x * level); level *= 2.0f); +// radius = 1.0f / (level * 4.0f); + +// if (radius > PIXEL * 16.0f) { +// radius = PIXEL * 16.0f; +// } +// } + +// break; + +// case NeuralNet::Neuron::Type::OUTPUT: +// repNeuron._type = Neuron::Type::OUTPUT; +// repNeuron._body.setOutlineColor(outputColor); +// repNeuron._body.setFillColor({ 0, 0, 0, 200 }); +// radius = PIXEL * 24.0f; +// break; +// } + +// repNeuron._body.setRadius(radius); +// repNeuron._body.setOrigin(radius, radius); +// repNeuron._body.setPosition(srcNeuron._position._x, srcNeuron._position._y); + +// repNeuron._nucleus.setRadius(radius / 2.0f); +// repNeuron._nucleus.setOrigin(radius / 2.0f, radius / 2.0f); +// repNeuron._nucleus.setPosition(srcNeuron._position._x, srcNeuron._position._y); +// repNeuron._nucleus.setOutlineColor(repNeuron._body.getOutlineColor()); + +// double multInt = static_cast<Uint8>(fabs(srcNeuron._bias / 3.0) * 255.0); +// Color colorMult(multInt, multInt, multInt, 128); + +// if (srcNeuron._bias > 0) { +// repNeuron._nucleus.setFillColor(Color::White * colorMult); +// } else { +// repNeuron._nucleus.setFillColor(BIAS * colorMult); +// } + +// Color synColor = WEIGHTS; + +// for (auto& i : srcNeuron._synapses) { +// for (auto& j : _neuralNet.getNeurons()) { +// if (&j._output == i._input) { +// Uint8 weight = static_cast<Uint8>(i._weight * 20.0); +// Vector2f srcPos(j._position._x, j._position._y); + +// if (i._input == &srcNeuron._output) { +// Color selfSynColor; + +// if (i._weight > 0.0) { +// selfSynColor = Color(255, 255, 255, 20 + weight); +// } else { +// selfSynColor = synColor; +// } + +// float rad2 = repNeuron._body.getRadius() * 2.0f; +// Vector2f p1 = repNeuron._body.getPosition() + Vector2f(-rad2, -rad2); +// Vector2f p2 = repNeuron._body.getPosition() + Vector2f(-rad2, rad2); +// Vector2f p3 = repNeuron._body.getPosition() + Vector2f( rad2, rad2); +// Vector2f p4 = repNeuron._body.getPosition() + Vector2f( rad2, -rad2); + +// repNeuron._synapses.append({ p1, selfSynColor }); +// repNeuron._synapses.append({ p2, selfSynColor }); + +// repNeuron._synapses.append({ p2, selfSynColor }); +// repNeuron._synapses.append({ p3, selfSynColor }); + +// repNeuron._synapses.append({ p3, selfSynColor }); +// repNeuron._synapses.append({ p4, selfSynColor }); + +// repNeuron._synapses.append({ p4, selfSynColor }); +// repNeuron._synapses.append({ p1, selfSynColor }); +// } else { +// repNeuron._synapses.append({ repNeuron._body.getPosition(), Color::Black }); + +// if (i._weight > 0.0) { +// repNeuron._synapses.append({{ srcPos }, Color(255, 255, 255, 20 + weight) }); +// } else { +// synColor.a = 20 + weight; +// repNeuron._synapses.append({{ srcPos }, synColor }); +// } +// } + +// break; +// } +// } +// } +// } diff --git a/plugins/CppnExplorer/todolist.txt b/plugins/CppnExplorer/todolist.txt new file mode 100644 index 0000000..5677680 --- /dev/null +++ b/plugins/CppnExplorer/todolist.txt @@ -0,0 +1,21 @@ +Cppn Explorer: + ☐ Render to any sf::RenderTarget + ☐ Calculate field on separate thread (keep 2 textures) + ☐ Render Neurons as crossmarks + ☐ (When Rendering to Window) Cycle fields with F key + ☐ (When Rendering to Window) Cycle between Neurons and Synapses using arrow keys + ☐ Display text data + ☐ (When Rendering to Window) Embed icon + ☐ Show Cppn Network + + +___________________ +Archive: + ✔ Display connection to same neuron as square around neuron @done (13-12-10 00:29) @project(Cppn Explorer) + ✔ Use better color and blending for neurons @done (13-11-25 00:24) @project(Cppn Explorer) + ✔ Display Synapses as blended lines with Transparency representing Weights @done (13-11-25 00:24) @project(Cppn Explorer) + ✔ Display cursor beatifully @done (13-11-23 20:31) @project(Cppn Explorer) + ✔ Display grid @done (13-11-23 01:58) @project(Cppn Explorer) + ✔ Fix cursor @done (13-11-21 02:18) @project(Cppn Explorer) + ✔ Print and switch between fields @done (13-11-06 04:35) @project(Cppn Explorer) + ✔ Display Neurons beautifully (with bias represented as a nucleus) @done (13-11-04 20:52) @project(Cppn Explorer) diff --git a/src/Behavior.cpp b/src/Behavior.cpp new file mode 100644 index 0000000..15b2ad9 --- /dev/null +++ b/src/Behavior.cpp @@ -0,0 +1,59 @@ +#include <HyperNeat/Behavior.hpp> +#include <HyperNeat/NoveltyMetric.hpp> + +using namespace hyperneat; + +void +Behavior::clear() +{ + _characterization.assign(_characterization.size(), 0.0); +} + +void +Behavior::reset(bool archive) +{ + if (_toBeArchived && archive) { + _noveltyMetric->_archive.emplace_back(_characterization); + } + + _noveltyScore = 0.0; + _toBeArchived = false; + _criteriaReached = _noveltyMetric->_prms._criteriaReachedByDefault; + _characterization.assign(_characterization.size(), 0.0); +} + +double& +Behavior::at(size_t i) +{ + return _characterization[i]; +} + +double +Behavior::getNoveltyScore() const +{ + return _noveltyScore; +} + +bool +Behavior::isToBeArchived() const +{ + return _toBeArchived; +} + +Organism& +Behavior::getOrganism() +{ + return *_organism; +} + +const NoveltyMetric& +Behavior::getNoveltyMetric() const +{ + return *_noveltyMetric; +} + +const Vector<double>& +Behavior::getCharacterization() const +{ + return _characterization; +} diff --git a/src/Cppn.cpp b/src/Cppn.cpp new file mode 100644 index 0000000..fb1d19d --- /dev/null +++ b/src/Cppn.cpp @@ -0,0 +1,292 @@ +#include <cmath> +#include <HyperNeat/Cppn.hpp> +#include <HyperNeat/Genome.hpp> +#include <HyperNeat/QuadTree.hpp> +#include <HyperNeat/Utils/Pi.hpp> +#include <HyperNeat/NodeSearchPrms.hpp> + +using namespace std; +using namespace hyperneat; + +void +Cppn::create(const Genome& genome) +{ + using DepthSorter = Map<double, Map<size_t, const Genome::NodeGene*>>; + DepthSorter dSorter; + + for (auto& i : genome._nodeGenes) { + dSorter[i.second._depth][i.first] = &i.second; + } + + _inputs.resize(genome._inputs, 0.0); + _nodeLayers.resize(dSorter.size()); + + { + auto layerIter = _nodeLayers.begin(); + + for (auto& i : dSorter) { + layerIter->resize(i.second.size()); + auto nodeIter = layerIter->begin(); + + for (auto& j : i.second) { + size_t enabledLinks = 0; + + for (auto& k : j.second->_linkGenes) { + if (k.second._isEnabled) { + ++enabledLinks; + } + } + + nodeIter->_links.resize(enabledLinks); + ++nodeIter; + } + + ++layerIter; + } + } + + size_t layerIdx = 0; + + for (auto& i : dSorter) { + size_t nodeIdx = 0; + + for (auto& j : i.second) { + Node& crntNode = _nodeLayers[layerIdx][nodeIdx]; + size_t linkIdx = 0; + + for (auto& k : j.second->_linkGenes) { + if (k.second._isEnabled) { + Node::Link& crntLink = crntNode._links[linkIdx]; + + if (k.first < _inputs.size()) { + crntLink._input = &_inputs[k.first]; + } else { + double depth = genome._nodeGenes.at(k.first)._depth; + size_t layer = distance(dSorter.begin(), dSorter.find(depth)); + size_t node = distance(dSorter[depth].begin(), dSorter[depth].find(k.first)); + + crntLink._input = &_nodeLayers[layer][node]._output; + } + + crntLink._weight = k.second._weight; + ++linkIdx; + } + } + + crntNode._nodeType = j.second->_nodeType; + ++nodeIdx; + + if (i.first == 1.0) { + _outputs.push_back(&crntNode._output); + } + } + + ++layerIdx; + } +} + +void +Cppn::clear() +{ + _inputs.clear(); + _outputs.clear(); + _nodeLayers.clear(); +} + +size_t +Cppn::getInputsCount() const +{ + return _inputs.size(); +} + +size_t +Cppn::getOutputsCount() const +{ + return _outputs.size(); +} + +size_t +Cppn::getNodesCount() const +{ + size_t nodesCount = 0; + + for (auto& i : _nodeLayers) { + nodesCount += i.size(); + } + + return nodesCount; +} + +double& +Cppn::inputAt(size_t i) +{ + return _inputs[i]; +} + +double +Cppn::outputAt(size_t i) const +{ + return *_outputs[i]; +} + +void +Cppn::cycle() +{ + for (auto& i : _nodeLayers) { + for (auto& j : i) { + j.appendInput(); + } + + for (auto& j : i) { + j.flushOutput(); + } + } +} + +void +Cppn::findNodesIn2DSection(ValueMap& valueMap, const NodeSearchPrms& nsPrms, const Point& source) +{ + QuadTree qTree(1.0, 0.0, 0.0); + + qTree.subdivide([&](QuadTree* qt) { + auto levelToSegment = [](size_t level) { + double dlevel = static_cast<double>(level); + return 1.0 / pow(2.0, dlevel); + }; + + double minQuadTreeSegment = levelToSegment(nsPrms._maxQuadTreeLevel); + double maxQuadTreeSegment = levelToSegment(nsPrms._minQuadTreeLevel); + double testGridResolution = levelToSegment(nsPrms._testGridLevel); + + if (qt->getSegment() <= minQuadTreeSegment) { + return false; + } + + if (qt->getSegment() > maxQuadTreeSegment) { + return true; + } + + double gridStep = qt->getSegment() * 2.0 * testGridResolution; + double gridHalf = gridStep / 2.0; + double initX = qt->getX() - qt->getSegment() + gridHalf; + double initY = qt->getY() - qt->getSegment() + gridHalf; + double endX = qt->getX() + qt->getSegment(); + double endY = qt->getY() + qt->getSegment(); + double cellsNo = (1.0 / testGridResolution) * (1.0 / testGridResolution); + + Vector<double> w; + w.reserve(static_cast<size_t>(cellsNo)); + + for (double y = initY; y < endY; y += gridStep) { + for (double x = initX; x < endX; x += gridStep) { + _inputs[nsPrms._x] = x; + _inputs[nsPrms._y] = y; + + if (nsPrms._useDistance) { + _inputs[nsPrms._d] = Point(x, y).distance(source); + } + + cycle(); + w.emplace_back(*_outputs[nsPrms._o]); + } + } + + double wMean = 0.0; + double variance = 0.0; + + for (auto& i : w) { + wMean += i; + } + + wMean /= cellsNo; + + for (auto& i : w) { + variance += (wMean - i) * (wMean - i); + } + + variance /= cellsNo; + + if (variance > nsPrms._varianceThreshold) { + return true; + } + + return false; + }); + + qTree.traverse([&](const QuadTree* qt) { + auto measureDistAndIO = [&]() { + if (nsPrms._useDistance) { + _inputs[nsPrms._d] = Point(_inputs[nsPrms._x], _inputs[nsPrms._y]).distance(source); + } + + cycle(); + return *_outputs[nsPrms._o]; + }; + + double gridSize = qt->getSegment() * 2.0; + + _inputs[nsPrms._y] = qt->getY(); + _inputs[nsPrms._x] = qt->getX() - gridSize; + double valLeft = measureDistAndIO(); + + _inputs[nsPrms._x] = qt->getX() + gridSize; + double valRight = measureDistAndIO(); + + _inputs[nsPrms._x] = qt->getX(); + _inputs[nsPrms._y] = qt->getY() - gridSize; + double valBottom = measureDistAndIO(); + + _inputs[nsPrms._y] = qt->getY() + gridSize; + double valTop = measureDistAndIO(); + + _inputs[nsPrms._y] = qt->getY(); + double valCenter = measureDistAndIO(); + + valLeft = fabs(valCenter - valLeft); + valRight = fabs(valCenter - valRight); + valBottom = fabs(valCenter - valBottom); + valTop = fabs(valCenter - valTop); + + double bandValue = max(min(valLeft, valRight), min(valBottom, valTop)); + + if (bandValue > nsPrms._bandPruningThreshold) { + valueMap.emplace_back(qt->getX(), qt->getY(), valCenter, qt->getSegment()); + } + }); +} + +void +Cppn::Node::appendInput() +{ + for (auto& i : _links) { + _storedInput += *i._input * i._weight; + } +} + +void +Cppn::Node::flushOutput() +{ + switch (_nodeType) { + case NodeType::SIGMOID: + _output = 1.0 / (1.0 + exp(-_storedInput * 2.0)); + _output = _output * 2.0 - 1.0; + break; + + case NodeType::GAUSSIAN: + _output = exp(-_storedInput * _storedInput); + _output = _output * 2.0 - 1.0; + break; + + case NodeType::SINE: + _output = sin((_storedInput / 2.0) * PI); + break; + + case NodeType::ABSOLUTE: + _output = max(min(fabs(_storedInput), 0.0), 1.0); + break; + + default:; + } + + _storedInput = 0.0; +} diff --git a/src/Genome.cpp b/src/Genome.cpp new file mode 100644 index 0000000..ce5fc11 --- /dev/null +++ b/src/Genome.cpp @@ -0,0 +1,15 @@ +#include <HyperNeat/Genome.hpp> + +using namespace hyperneat; + +Genome::Genome(size_t inputs) + : _inputs(inputs) +{} + +Genome::NodeGene::NodeGene(double depth, NodeType nodeType) + : _depth(depth), _nodeType(nodeType) +{} + +Genome::NodeGene::LinkGene::LinkGene(double weight, bool isEnabled) + : _weight(weight), _isEnabled(isEnabled) +{} diff --git a/src/Innovation.cpp b/src/Innovation.cpp new file mode 100644 index 0000000..0332616 --- /dev/null +++ b/src/Innovation.cpp @@ -0,0 +1,16 @@ +#include <Hyperneat/Innovation.hpp> + +using namespace hyperneat; + +Innovation::Innovation(size_t number, size_t source, size_t target, double depth, NodeType nodeType) + : _number(number), _source(source), _target(target), _depth(depth), _nodeType(nodeType) +{} + +bool +Innovation::operator==(const Innovation& other) const +{ + return (_source == other._source ) && + (_target == other._target ) && + (_depth == other._depth ) && + (_nodeType == other._nodeType); +} diff --git a/src/NeuralNet.cpp b/src/NeuralNet.cpp new file mode 100644 index 0000000..abed262 --- /dev/null +++ b/src/NeuralNet.cpp @@ -0,0 +1,294 @@ +#include <cmath> +#include <algorithm> +#include <HyperNeat/Cppn.hpp> +#include <HyperNeat/NeuralNet.hpp> +#include <HyperNeat/Utils/Map.hpp> +#include <HyperNeat/Utils/Set.hpp> +#include <HyperNeat/NeuralNetPrms.hpp> +#include <HyperNeat/Utils/Function.hpp> +#include <HyperNeat/NodeSearchPrms.hpp> + +using namespace std; +using namespace hyperneat; + +void +NeuralNet::create(Cppn& cppn, const NeuralNetPrms& nnPrms) +{ + class TempNeuronSynapse { + public: + TempNeuronSynapse() = default; + TempNeuronSynapse(double weight, Point neuronSource) + : _weight(weight), _neuronSource(neuronSource) + {} + + double _weight = 0.0; + Point _neuronSource; + }; + + class TempNeuron { + public: + bool _isIncluded = false; + double _bias = 0.0; + Vector<TempNeuronSynapse> _neuronSynapses; + }; + + _inputs.reserve(nnPrms._inputMap.size()); + _outputs.reserve(nnPrms._outputMap.size()); + + NodeSearchPrms nsPrms(0, 2, 3, 4); + nsPrms.importFrom(nnPrms); + cppn.inputAt(5) = 1.0; + + Map<Point, TempNeuron> tempNeurons; + + auto findConnections = [&](const Point& source, size_t x1, size_t y1, size_t x2, size_t y2, size_t d, + bool checkExist, Function<void(double, const Point&)> storeConn) { + cppn.inputAt(x1) = source._x; + cppn.inputAt(y1) = source._y; + cppn.inputAt(x2) = 0.0; + cppn.inputAt(y2) = 0.0; + cppn.inputAt(d) = 0.0; + + ValueMap newConnections; + cppn.findNodesIn2DSection(newConnections, nsPrms, source); + + for (auto& i : newConnections) { + if (checkExist && !tempNeurons.count(i)) { + continue; + } + + if (fabs(i._value) > 0.2) { + storeConn((i._value + (i._value > 0 ? -0.2 : 0.2)) * 3.75, i); + } + } + }; + + { + Set<Point> neuronSet1; + Set<Point> neuronSet2; + Set<Point>* previousNeurons = &neuronSet1; + Set<Point>* nextNeurons = &neuronSet2; + + for (auto& i : nnPrms._inputMap) { + tempNeurons[i]; + previousNeurons->insert(i); + } + + for (size_t i = 0; i < nnPrms._iterations && !previousNeurons->empty(); ++i) { + Map<Point, TempNeuron> newNeurons; + + for (auto& j : *previousNeurons) { + findConnections(j, 0, 1, 2, 3, 4, false, [&](double weight, const Point& target) { + if (tempNeurons.count(target)) { + auto& synapses = tempNeurons[target]._neuronSynapses; + synapses.emplace_back(weight, j); + } else { + auto& synapses = newNeurons[target]._neuronSynapses; + synapses.emplace_back(weight, j); + nextNeurons->insert(target); + } + }); + } + + previousNeurons->clear(); + swap(nextNeurons, previousNeurons); + tempNeurons.insert(newNeurons.begin(), newNeurons.end()); + } + } + + nsPrms._x = 0; + nsPrms._y = 1; + + { + Vector<TempNeuron*> inclusionSet1; + Vector<TempNeuron*> inclusionSet2; + Vector<TempNeuron*>* crntInclusions = &inclusionSet1; + Vector<TempNeuron*>* nextInclusions = &inclusionSet2; + + for (auto& i : nnPrms._outputMap) { + tempNeurons[i]._isIncluded = true; + nextInclusions->push_back(&tempNeurons[i]); + + findConnections(i, 2, 3, 0, 1, 4, true, [&](double weight, const Point& target) { + auto& synapses = tempNeurons[i]._neuronSynapses; + synapses.emplace_back(weight, target); + }); + } + + while (!nextInclusions->empty()) { + crntInclusions->clear(); + swap(crntInclusions, nextInclusions); + + for (auto& i : *crntInclusions) { + for (auto& j : i->_neuronSynapses) { + auto& sourceNeuron = tempNeurons.at(j._neuronSource); + + if (!sourceNeuron._isIncluded) { + nextInclusions->push_back(&sourceNeuron); + sourceNeuron._isIncluded = true; + } + } + } + } + } + + for (auto& i : nnPrms._inputMap) { + tempNeurons[i]._isIncluded = true; + } + + cppn.inputAt(2) = 0.0; + cppn.inputAt(3) = 0.0; + cppn.inputAt(4) = 0.0; + + for (auto i = tempNeurons.begin(), end = tempNeurons.end(); i != end;) { + if (i->second._isIncluded) { + cppn.inputAt(0) = i->first._x; + cppn.inputAt(1) = i->first._y; + cppn.inputAt(4) = i->first.distance(Point()); + cppn.cycle(); + i->second._bias = cppn.outputAt(1) * 3.0; + ++i; + } else { + i = tempNeurons.erase(i); + } + } + + _neurons.resize(tempNeurons.size()); + + { + auto nIter = _neurons.begin(); + + for (auto& i : tempNeurons) { + nIter->_bias = i.second._bias; + nIter->_position = i.first; + auto& crntNrnSyns = nIter->_synapses; + auto& neuronSynapses = i.second._neuronSynapses; + crntNrnSyns.reserve(neuronSynapses.size()); + + for (auto& j : neuronSynapses) { + auto src = tempNeurons.find(j._neuronSource); + size_t sIdx = distance(tempNeurons.begin(), src); + crntNrnSyns.emplace_back(&_neurons[sIdx], j._weight); + } + + ++nIter; + } + } + + auto relateIO = [&](Vector<double*>& ptrVec, const Vector<Point>& map, Neuron::Type type) { + for (auto& i : map) { + auto neuron = tempNeurons.find(i); + size_t nIdx = distance(tempNeurons.begin(), neuron); + _neurons[nIdx]._type = type; + ptrVec.push_back(&_neurons[nIdx]._output); + } + }; + + relateIO(_inputs, nnPrms._inputMap, Neuron::Type::INPUT); + relateIO(_outputs, nnPrms._outputMap, Neuron::Type::OUTPUT); +} + +void +NeuralNet::clear() +{ + _inputs.clear(); + _outputs.clear(); + _neurons.clear(); +} + +size_t +NeuralNet::getInputsCount() const +{ + return _inputs.size(); +} + +size_t +NeuralNet::getOutputsCount() const +{ + return _outputs.size(); +} + +size_t +NeuralNet::getNeuronsCount() const +{ + return _neurons.size(); +} + +double +NeuralNet::getAverageActivation() const +{ + double totalActivation = 0.0; + + for (auto& i : _neurons) { + totalActivation += i._output; + } + + return totalActivation / static_cast<double>(_neurons.size()); +} + +double& +NeuralNet::inputAt(size_t i) +{ + return *_inputs[i]; +} + +double +NeuralNet::outputAt(size_t i) const +{ + return *_outputs[i]; +} + +const Vector<double*>& +NeuralNet::getInputs() const +{ + return _inputs; +} + +const Vector<double*>& +NeuralNet::getOutputs() const +{ + return _outputs; +} + +const Vector<NeuralNet::Neuron>& +NeuralNet::getNeurons() const +{ + return _neurons; +} + +void +NeuralNet::cycle() +{ + for (auto& i : _neurons) { + i.appendInput(); + } + + for (auto& i : _neurons) { + i.flushOutput(); + } +} + +NeuralNet::Neuron::Neuron(const Point& position, Type type, double bias) + : _position(position), _type(type), _bias(bias) +{} + +void +NeuralNet::Neuron::appendInput() +{ + for (auto& i : _synapses) { + _storedInput += *i._input * i._weight; + } + + _storedInput += _bias; +} + +void +NeuralNet::Neuron::flushOutput() +{ + _output = 1.0 / (1.0 + exp(-_storedInput * 4.9)); + _storedInput = 0.0; +} + +NeuralNet::Neuron::Synapse::Synapse(Neuron* inputNeuron, double weight) + : _input(&inputNeuron->_output), _neuron(inputNeuron), _weight(weight) +{} diff --git a/src/NodeSearchPrms.cpp b/src/NodeSearchPrms.cpp new file mode 100644 index 0000000..db2421a --- /dev/null +++ b/src/NodeSearchPrms.cpp @@ -0,0 +1,23 @@ +#include <HyperNeat/NeuralNetPrms.hpp> +#include <HyperNeat/NodeSearchPrms.hpp> + +using namespace hyperneat; + +NodeSearchPrms::NodeSearchPrms(size_t o, size_t x, size_t y) + : _o(o), _x(x), _y(y), _useDistance(false) +{} + +NodeSearchPrms::NodeSearchPrms(size_t o, size_t x, size_t y, size_t d) + : _o(o), _x(x), _y(y), _d(d) +{} + +void +NodeSearchPrms::importFrom(const NeuralNetPrms& nnPrms) +{ + _testGridLevel = nnPrms._testGridLevel; + _maxQuadTreeLevel = nnPrms._maxQuadTreeLevel; + _minQuadTreeLevel = nnPrms._minQuadTreeLevel; + _bandPruningThreshold = nnPrms._bandPruningThreshold; + _varianceThreshold = nnPrms._varianceThreshold; + _divisionThreshold = nnPrms._divisionThreshold; +} diff --git a/src/NoveltyMetric.cpp b/src/NoveltyMetric.cpp new file mode 100644 index 0000000..dd40118 --- /dev/null +++ b/src/NoveltyMetric.cpp @@ -0,0 +1,101 @@ +#include <algorithm> +#include <Hyperneat/Population.hpp> +#include <Hyperneat/NoveltyMetric.hpp> + +using namespace std; +using namespace hyperneat; + +const NoveltyMetricPrms& +NoveltyMetric::getPrms() const +{ + return _prms; +} + +const Vector<Behavior>& +NoveltyMetric::getBehaviors() const +{ + return _behaviors; +} + +const Vector2D<double>& +NoveltyMetric::getArchive() const +{ + return _archive; +} + +Behavior& +NoveltyMetric::getBehaviorOf(size_t i) +{ + return _behaviors[i]; +} + +void +NoveltyMetric::initialize(const NoveltyMetricPrms& prms, Population* population) +{ + _prms = prms; + _behaviors.resize(population->getAllOrganisms().size()); + + for (size_t i = 0; i < _behaviors.size(); ++i) { + population->getOrganism(i)._behavior = &_behaviors[i]; + _behaviors[i]._organism = &population->getOrganism(i); + _behaviors[i]._noveltyMetric = this; + _behaviors[i]._criteriaReached = prms._criteriaReachedByDefault; + _behaviors[i]._characterization.resize(prms._characterizationSize, 0.0); + } +} + +void +NoveltyMetric::setScores() +{ + size_t readyOrganisms = 0; + + for (auto& i : _behaviors) { + if ((i._isReady = (i._organism->isOld() && i._criteriaReached))) { + ++readyOrganisms; + } + } + + for (auto& i : _behaviors) { + if (!i._isReady) { + i._toBeArchived = false; + i._organism->_fitness = i._noveltyScore = 0.0; + continue; + } + + Vector<double> distances; + distances.reserve(readyOrganisms + _archive.size() - 1); + + for (auto& j : _behaviors) { + if (j._isReady && (&i != &j)) { + distances.emplace_back(getDistance(i._characterization, j._characterization)); + } + } + + for (auto& j : _archive) { + distances.emplace_back(getDistance(i._characterization, j)); + } + + auto dEnd = distances.end(); + auto dBeg = distances.begin(); + auto dRef = dBeg + _prms._referenceOrganisms; + + partial_sort(dBeg, dRef, dEnd); + + double totalDistance = accumulate(dBeg, dRef, 0.0); + i._organism->_fitness = i._noveltyScore = (totalDistance / static_cast<double>(_prms._referenceOrganisms)); + i._toBeArchived = (i._noveltyScore > _prms._noveltyThreshold); + } +} + +double +NoveltyMetric::getDistance(const Vector<double>& v1, const Vector<double>& v2) const +{ + double result = 0.0; + + for (size_t i = 0; i < v1.size(); ++i) { + double diff = v1[i] - v2[i]; + result += diff * diff; + } + + return sqrt(result); +} diff --git a/src/Organism.cpp b/src/Organism.cpp new file mode 100644 index 0000000..0c2c1bc --- /dev/null +++ b/src/Organism.cpp @@ -0,0 +1,180 @@ +#include <HyperNeat/Cppn.hpp> +#include <HyperNeat/Behavior.hpp> +#include <HyperNeat/NeuralNet.hpp> +#include <HyperNeat/Population.hpp> +#include <HyperNeat/Utils/Thread.hpp> +#include <HyperNeat/NeuralNetPrms.hpp> + +using namespace std; +using namespace hyperneat; + +Organism::Organism(const Organism& other) +{ + *this = other; +} + +Organism& +Organism::operator=(const Organism& other) +{ + _fitness = other._fitness; + _index = other._index; + _isLocked = other._isLocked; + _isFrozen = other._isFrozen; + _specie = other._specie; + _lifetime = other._lifetime; + _genome = other._genome; + _population = other._population; + + return *this; +} + +size_t +Organism::getIndex() const +{ + return _index; +} + +void +Organism::lock() +{ + if (!_isLocked) { + _isLocked = true; + ++_population->_lockedOrganisms; + } +} + +void +Organism::unlock() +{ + if (_isLocked) { + _isLocked = false; + --_population->_lockedOrganisms; + } +} + +bool +Organism::isLocked() const +{ + return _isLocked; +} + +void +Organism::freeze() +{ + if (!_isFrozen) { + _isFrozen = true; + ++_population->_frozenOrganisms; + } +} + +void +Organism::unfreeze() +{ + if (_isFrozen) { + _isFrozen = false; + --_population->_frozenOrganisms; + } +} + +bool +Organism::isFrozen() const +{ + return _isFrozen; +} + +bool +Organism::isBeingGenerated() const +{ + return _isBeingGenerated; +} + +size_t +Organism::getSpecie() const +{ + return _specie; +} + +bool +Organism::isOld() const { + return _lifetime >= _population->_prms._minimumLifetime; +} + +size_t +Organism::getLifetime() const +{ + return _lifetime; +} + +Behavior& +Organism::getBehavior() +{ + return *_behavior; +} + +const Genome& +Organism::getGenome() const +{ + return _genome; +} + +bool +Organism::isChampion() const +{ + return &_population->getChampion() == this; +} + +Population& +Organism::getPopulation() const +{ + return *_population; +} + +void +Organism::createNeuralNet() +{ + if (_isBeingGenerated) { + return; + } + + _isBeingGenerated = true; + ++_population->_organismsBeingGenerated; + + Thread generator([&]() { + Cppn cppn; + cppn.create(_genome); + + _neuralNet->clear(); + _neuralNet->create(cppn, _population->getNeuralNetPrms()); + + _isBeingGenerated = false; + --_population->_organismsBeingGenerated; + }); + + generator.detach(); +} + +Organism::Organism(Population* population) + : _population(population) +{} + +Organism::Organism(size_t inputs, Population* population) + : _genome(inputs), _population(population) +{} + +void +Organism::reset(bool archive) +{ + unlock(); + unfreeze(); + + if (isOld()) { + --_population->_oldOrganisms; + } + + _lifetime = 0; + _fitness = 0.0; + + if (_behavior) { + _behavior->reset(archive); + } +} diff --git a/src/Population.cpp b/src/Population.cpp new file mode 100644 index 0000000..f81887e --- /dev/null +++ b/src/Population.cpp @@ -0,0 +1,939 @@ +#include <chrono> +#include <algorithm> +#include <HyperNeat/Cppn.hpp> +#include <HyperNeat/NeuralNet.hpp> +#include <HyperNeat/Population.hpp> +#include <HyperNeat/Utils/Thread.hpp> + +using namespace std; +using namespace hyperneat; + +void +Population::create(const PopulationPrms& popPrms) +{ + if (_prms._seed != 0) { + _randGen.seed(_prms._seed); + } else { + _randGen.seed(getRandSeed()); + } + + _prms = popPrms; + _weightDeviator = BellDist(0.0, _prms._weightDeviation); + _weightSelector = RealDist(-_prms._weightRange, _prms._weightRange); + _distanceThreshold = _prms._initialDistanceThreshold; + _minOldOrganisms = _prms._popSize * _prms._eligibilityRatio; + _basicInnovs = _prms._cppnOutputs * NODE_TYPES_COUNT + _prms._cppnInputs; + + _allOrganisms.resize(_prms._popSize, Organism(_prms._cppnInputs, this)); + + size_t orgIndex = 0; + + for (auto& i : _allOrganisms) { + i._index = orgIndex++; + auto& nodeGenes = i._genome._nodeGenes; + + for (size_t j = 0; j < _prms._cppnOutputs; ++j) { + size_t nodeType = _nodeTypeSelector(_randGen); + size_t geneIdx = _prms._cppnInputs + (j * NODE_TYPES_COUNT) + nodeType; + nodeGenes[geneIdx] = {1.0, static_cast<NodeType>(nodeType)}; + + for (size_t k = 0; k < _prms._cppnInputs; ++k) { + nodeGenes[geneIdx]._linkGenes[k] = {getRandWeight()}; + } + } + } + + organizeSpecies(); +} + +void +Population::create(const PopulationPrms& popPrms, const NeuralNetPrms& nnPrms) +{ + create(popPrms); + _nnPrms = Pointer<NeuralNetPrms>(new NeuralNetPrms(nnPrms)); + generateAllNeuralNets(); +} + +void +Population::create(const PopulationPrms& popPrms, const NeuralNetPrms& nnPrms, const NoveltyMetricPrms& nmPrms) +{ + create(popPrms, nnPrms); + setNoveltyMetric(nmPrms); +} + +void +Population::shutdown(bool resetOrganisms, bool archiveOrganisms) +{ + while (isAnyOrganismBeingGenerated()); + + if (resetOrganisms) { + for (auto& i : _allOrganisms) { + i.reset(archiveOrganisms); + } + } +} + +Population::~Population() +{ + while (isAnyOrganismBeingGenerated()); +} + +void +Population::setMinimumLifetime(size_t lifetime) +{ + _oldOrganisms = 0; + _prms._minimumLifetime = lifetime; + + for (auto& i : _allOrganisms) { + if (i._lifetime >= lifetime) { + ++_oldOrganisms; + } + } +} + +const PopulationPrms& +Population::getPopulationPrms() const +{ + return _prms; +} + +const NeuralNetPrms& +Population::getNeuralNetPrms() const +{ + return *_nnPrms; +} + +bool +Population::hasNeuralNets() const +{ + return (bool)_nnPrms; +} + +const Vector<Organism>& +Population::getAllOrganisms() const +{ + return _allOrganisms; +} + +const Vector2D<Organism*>& +Population::getSpecies() const +{ + return _species; +} + +Organism& +Population::getOrganism(size_t i) +{ + return _allOrganisms[i]; +} + +const Vector<Organism*>& +Population::getSpecie(size_t i) const +{ + return _species[i]; +} + +Organism& +Population::getChampion() +{ + auto comFitness = [](Organism& a, Organism& b) { + return a._fitness < b._fitness; + }; + + auto comNovelty = [](Organism& a, Organism& b) { + return a.getBehavior().getNoveltyScore() < b.getBehavior().getNoveltyScore(); + }; + + return *max_element(_allOrganisms.begin(), _allOrganisms.end(), isNoveltyMetricSet() ? comNovelty : comFitness); +} + +void +Population::lock() +{ + _populationLock = true; +} + +void +Population::unlock() +{ + _populationLock = false; +} + +bool +Population::isLocked() const +{ + return _populationLock; +} + +void +Population::lockOrganism(size_t i) +{ + _allOrganisms[i].lock(); +} + +void +Population::unlockOrganism(size_t i) +{ + _allOrganisms[i].unlock(); +} + +bool +Population::isOrganismLocked(size_t i) const +{ + return _allOrganisms[i]._isLocked; +} + +bool +Population::isAnyOrganismLocked() const +{ + return _lockedOrganisms != 0 ? true : false; +} + +size_t +Population::getLockedOrganisms() const +{ + return _lockedOrganisms; +} + +void +Population::freezeOrganism(size_t i) +{ + _allOrganisms[i].freeze(); +} + +void +Population::unfreezeOrganism(size_t i) +{ + _allOrganisms[i].unfreeze(); +} + +bool +Population::isOrganismFrozen(size_t i) const +{ + return _allOrganisms[i]._isFrozen; +} + +bool +Population::isAnyOrganismFrozen() const +{ + return _frozenOrganisms != 0 ? true : false; +} + +size_t +Population::getFrozenOrganisms() const +{ + return _frozenOrganisms; +} + +bool +Population::isOrganismBeingGenerated(size_t i) const +{ + return _allOrganisms[i].isBeingGenerated(); +} + +bool +Population::isAnyOrganismBeingGenerated() const +{ + return _organismsBeingGenerated != 0 ? true : false; +} + +size_t +Population::getOrganismsBeingGenerated() const +{ + return _organismsBeingGenerated; +} + +size_t +Population::getReadyOrganisms() const +{ + size_t result = 0; + + for (auto& i : _allOrganisms) { + if (i.isOld() && !i.isLocked()) { + ++result; + } + } + + return result; +} + +const Vector<Innovation>& +Population::getInnovations() const +{ + return _innovations; +} + +size_t +Population::getInnovationsCount() const +{ + return _innovations.size(); +} + +size_t +Population::getBasicInnovationsCount() const +{ + return _basicInnovs; +} + +Organism* +Population::getLastReplacement() +{ + return _lastReplacement; +} + +Organism* +Population::getLastMother() +{ + return _lastMother; +} + +Organism* +Population::getLastFather() +{ + return _lastFather; +} + +size_t +Population::getReplacements() const +{ + return _replacements; +} + +bool +Population::recentReplacement() const +{ + return _recentReplacement; +} + +double +Population::getDistanceThreshold() const +{ + return _distanceThreshold; +} + +size_t +Population::getOldOrganisms() const +{ + return _oldOrganisms; +} + +size_t +Population::getMinimumOldOrganisms() const +{ + return _minOldOrganisms; +} + +double +Population::getAverageFitness() const +{ + double total = 0.0; + + for (auto& i : _allOrganisms) { + total += i._fitness; + } + + return total / static_cast<double>(_allOrganisms.size()); +} + +double +Population::getAverageOldFitness() const +{ + double total = 0.0; + + for (auto& i : _allOrganisms) { + if (i.isOld()) { + total += i._fitness; + } + } + + return total / static_cast<double>(getOldOrganisms()); +} + +void +Population::setNoveltyMetric(const NoveltyMetricPrms& prms) { + _noveltyMetric = Pointer<NoveltyMetric>(new NoveltyMetric); + _noveltyMetric->initialize(prms, this); +} + +void +Population::clearNoveltyMetric() +{ + _noveltyMetric.reset(); + + for (auto& i : _allOrganisms) { + i._behavior = nullptr; + } +} + +bool +Population::isNoveltyMetricSet() const +{ + return _noveltyMetric.get(); +} + +const NoveltyMetric& +Population::getNoveltyMetric() const +{ + return *_noveltyMetric; +} + +size_t +Population::getUpdates() const +{ + return _updates; +} + +double& +Population::fitnessOf(size_t i) +{ + return _allOrganisms[i]._fitness; +} + +bool +Population::update(Function<void(void)> beforeReplacement, Function<void(void)> afterReplacement) +{ + ++_updates; + + for (auto& i : _allOrganisms) { + if (!i._isFrozen && !i.isBeingGenerated()) { + ++i._lifetime; + + if (i._lifetime == _prms._minimumLifetime) { + ++_oldOrganisms; + } + } + } + + if (_populationLock) { + return false; + } + + if ((getReadyOrganisms() >= _minOldOrganisms) && !isAnyOrganismBeingGenerated()) { + beforeReplacement(); + + if (isNoveltyMetricSet()) { + _noveltyMetric->setScores(); + } + + _recentReplacement = true; + replaceOrganism(); + afterReplacement(); + } else { + _recentReplacement = false; + } + + return _recentReplacement; +} + +void +Population::generateAllNeuralNets() +{ + auto generateChunk = [&](size_t i, size_t end) { + for (; i < end; ++i) { + Cppn cppn; + cppn.create(_allOrganisms[i].getGenome()); + + _allOrganisms[i]._neuralNet = Pointer<NeuralNet>(new NeuralNet); + _allOrganisms[i]._neuralNet->create(cppn, *_nnPrms); + } + }; + + size_t threadCount = max(1u, Thread::hardware_concurrency()); + size_t chunkSize = _allOrganisms.size() / threadCount; + + Vector<Thread> chunks(threadCount); + + for (size_t i = 0; i < threadCount - 1; ++i) { + chunks[i] = Thread(generateChunk, i * chunkSize, (i + 1) * chunkSize); + } + + chunks.back() = Thread(generateChunk, (chunks.size() - 1) * chunkSize, _allOrganisms.size()); + + for (auto& i : chunks) { + i.join(); + } +} + +void +Population::replaceOrganism() +{ + _lastReplacement = killPoorOrganism(); + _lastMother = nullptr; + _lastFather = nullptr; + auto parentSpecie = chooseParentSpecie(); + Vector<Organism*> eligibleMothers; + + for (auto& i : *parentSpecie) { + if (i->_lifetime >= _prms._minimumLifetime) { + eligibleMothers.emplace_back(i); + } + } + + size_t motherIdx = getRandSize(0, eligibleMothers.size() - 1); + _lastMother = eligibleMothers[motherIdx]; + + if (getChance(_prms._sexualReproductionRate)) { + if (getChance(_prms._interspeciesMatingRate)) { + Vector<Organism*> eligibleFathers; + + for (auto& i : _species) { + if (&i == parentSpecie) { + continue; + } + + for (auto& j : i) { + if (j->_lifetime >= _prms._minimumLifetime) { + eligibleFathers.emplace_back(j); + } + } + } + + if (eligibleFathers.size() > 0) { + size_t fatherIdx = getRandSize(0, eligibleFathers.size() - 1); + _lastFather = eligibleFathers[fatherIdx]; + } + } else { + if (eligibleMothers.size() > 1) { + size_t fatherIdx = getRandSize(0, eligibleMothers.size() - 2); + + if (fatherIdx >= motherIdx) { + ++fatherIdx; + } + + _lastFather = eligibleMothers[fatherIdx]; + } + } + + if (!_lastFather) { + breedAsexually(_lastReplacement->_genome, _lastMother->_genome); + } else { + if (_lastFather->_fitness > _lastMother->_fitness) { + swap(_lastFather, _lastMother); + } + + breedSexually(_lastReplacement->_genome, _lastMother->_genome, _lastFather->_genome); + } + } else { + breedAsexually(_lastReplacement->_genome, _lastMother->_genome); + } + + assignToSpecie(*_lastReplacement); + ++_replacements; + --_oldOrganisms; + + if ((_replacements % _prms._replBeforeReorganization) == 0) { + organizeSpecies(); + } +} + +Organism* +Population::killPoorOrganism() +{ + Organism* poorOrganism = nullptr; + double minAdjFitness = -1.0; + + for (auto& i : _allOrganisms) { + if (i._lifetime >= _prms._minimumLifetime && !i._isLocked) { + double orgAdjFitness = i._fitness / static_cast<double>(_species[i._specie].size()); + + if (orgAdjFitness < minAdjFitness || minAdjFitness == -1.0) { + minAdjFitness = orgAdjFitness; + poorOrganism = &i; + } + } + } + + if (poorOrganism->_isFrozen) { + poorOrganism->_isFrozen = false; + --_frozenOrganisms; + } + + poorOrganism->_fitness = 0.0; + poorOrganism->_lifetime = 0; + poorOrganism->_genome._nodeGenes.clear(); + + if (poorOrganism->_behavior) { + poorOrganism->_behavior->reset(); + } + + auto& childSpec = _species[poorOrganism->_specie]; + auto childIdx = find(childSpec.begin(), childSpec.end(), poorOrganism); + childSpec.erase(childIdx); + + return poorOrganism; +} + +Vector<Organism*>* +Population::chooseParentSpecie() +{ + double totalAverageFitnesses = 0.0; + Vector<double> averageFitnesses; + averageFitnesses.reserve(_species.size()); + + for (auto &i : _species) { + double specieFitness = 0.0; + size_t specieSize = 0; + + for (auto &j : i) { + if (j->_lifetime >= _prms._minimumLifetime) { + specieFitness += j->_fitness; + ++specieSize; + } + } + + if (specieSize == 0) { + averageFitnesses.emplace_back(0.0); + } else { + specieFitness /= static_cast<double>(specieSize); + averageFitnesses.emplace_back(specieFitness); + totalAverageFitnesses += specieFitness; + } + } + + size_t specieIdx = 0; + double selector = getRandReal(0.0, totalAverageFitnesses) - averageFitnesses.front(); + + while (selector > 0.0) { + ++specieIdx; + selector -= averageFitnesses[specieIdx]; + } + + return &_species[specieIdx]; +} + +void +Population::breedAsexually(Genome& child, const Genome& mother) +{ + child = mother; + + if (mutateNodesAndLinks(child)) { + return; + } + + for (auto& i : child._nodeGenes) { + for (auto& j : i.second._linkGenes) { + if (getChance(_prms._weightMutationRate)) { + j.second._weight += getWeightDeviation(); + + if (j.second._weight > _prms._weightRange) { + j.second._weight = _prms._weightRange; + } else if (j.second._weight < -_prms._weightRange) { + j.second._weight = -_prms._weightRange; + } + } + } + } +} + +void +Population::breedSexually(Genome& child, const Genome& mother, const Genome& father) +{ + child = mother; + + for (auto& i : child._nodeGenes) { + if (father._nodeGenes.count(i.first)) { + auto& fatherNode = father._nodeGenes.at(i.first); + + for (auto& j : i.second._linkGenes) { + if (fatherNode._linkGenes.count(j.first)) { + auto& childLink = j.second; + auto& fatherLink = fatherNode._linkGenes.at(j.first); + + childLink._weight += fatherLink._weight; + childLink._weight /= 2.0; + + if (!childLink._isEnabled || !fatherLink._isEnabled) { + if (getChance(_prms._geneDisablingRatio)) { + childLink._isEnabled = false; + } else { + childLink._isEnabled = true; + } + } + } + } + } + } +} + +bool +Population::mutateNodesAndLinks(Genome& child) +{ + class LinkMutation { + public: + LinkMutation() = default; + LinkMutation(size_t source, size_t target) + : _source(source), _target(target) + {} + + size_t _source = 0; + size_t _target = 0; + }; + + class NodeMutation { + public: + NodeMutation() = default; + NodeMutation(size_t source, size_t target, double weight) + : _source(source), _target(target), _weight(weight) + {} + + size_t _source = 0; + size_t _target = 0; + double _weight = 0.0; + }; + + if (getChance(_prms._linkMutationRate)) { + Vector<LinkMutation> linkMutations; + + for (auto& i : child._nodeGenes) { + auto& childNode = i.first; + auto& childLinks = i.second._linkGenes; + auto& childDepth = i.second._depth; + + for (size_t j = 0; j < child._inputs; ++j) { + if (!childLinks.count(j)) { + linkMutations.emplace_back(j, childNode); + } + } + + for (auto& j : child._nodeGenes) { + auto& sourceNode = j.first; + auto& sourceDepth = j.second._depth; + + if (!childLinks.count(sourceNode) && childDepth > sourceDepth) { + linkMutations.emplace_back(sourceNode, childNode); + } + } + } + + if (!linkMutations.empty()) { + size_t mutationIdx = getRandSize(0, linkMutations.size() - 1); + size_t target = linkMutations[mutationIdx]._target; + size_t source = linkMutations[mutationIdx]._source; + + child._nodeGenes[target]._linkGenes[source] = {0.0}; + } + + return true; + } + + if (getChance(_prms._nodeMutationRate)) { + Vector<NodeMutation> nodeMutations; + + for (auto& i : child._nodeGenes) { + for (auto& j : i.second._linkGenes) { + auto& childLink = j.second; + + if (childLink._isEnabled) { + nodeMutations.emplace_back(j.first, i.first, childLink._weight); + } + } + } + + size_t mutationIdx = getRandSize(0, nodeMutations.size() - 1); + size_t target = nodeMutations[mutationIdx]._target; + size_t source = nodeMutations[mutationIdx]._source; + double targetDepth = child._nodeGenes[target]._depth; + double sourceDepth = 0.0; + + if (source >= child._inputs) { + sourceDepth = child._nodeGenes[source]._depth; + } + + double depth = (targetDepth + sourceDepth) / 2.0; + NodeType function = getRandNodeType(); + + Innovation innov = {0, source, target, depth, function}; + auto iter = find(_innovations.begin(), _innovations.end(), innov); + size_t newID = 0; + + if (iter != _innovations.end()) { + newID = iter->_number; + } else { + newID = _innovations.size() + _basicInnovs; + _innovations.emplace_back(newID, source, target, depth, function); + } + + child._nodeGenes[newID] = {depth, function}; + child._nodeGenes[newID]._linkGenes[source] = {0.0}; + child._nodeGenes[target]._linkGenes[newID] = {0.0}; + child._nodeGenes[target]._linkGenes[source]._isEnabled = false; + + return true; + } + + return false; +} + +void +Population::assignToSpecie(Organism& org) +{ + bool included = false; + + for (auto i = _species.begin(), end = _species.end(); i != end; ++i) { + if (i->empty()) { + continue; + } + + double genDistance = computeDistance((*i)[0]->_genome, org._genome); + + if (genDistance < _distanceThreshold) { + included = true; + org._specie = distance(_species.begin(), i); + i->emplace_back(&org); + break; + } + } + + if (!included) { + org._specie = _species.size(); + _species.emplace_back(1, &org); + } +} + +void +Population::organizeSpecies() +{ + _species.clear(); + + for (auto& i : _allOrganisms) { + assignToSpecie(i); + } + + if (_species.size() < _prms._targetSpeciesCount && _distanceThreshold > _prms._distanceThresholdShift) { + _distanceThreshold -= _prms._distanceThresholdShift; + } else if (_species.size() > _prms._targetSpeciesCount) { + _distanceThreshold += _prms._distanceThresholdShift; + } +} + + +double +Population::computeDistance(const Genome& g1, const Genome& g2) const +{ + class LinkPair { + public: + const Genome::NodeGene::LinkGene* _l1 = nullptr; + const Genome::NodeGene::LinkGene* _l2 = nullptr; + }; + + using LinkPairs = Map<size_t, LinkPair>; + + class GenePair { + public: + const Genome::NodeGene* _g1 = nullptr; + const Genome::NodeGene* _g2 = nullptr; + LinkPairs _links; + }; + + using GenePairs = Map<size_t, GenePair>; + + auto alignGenes = [](GenePairs& gPairs, const Genome& g1, const Genome& g2) { + for (auto& i : g1._nodeGenes) { + gPairs[i.first]._g1 = &i.second; + + for (auto& j : i.second._linkGenes) { + gPairs[i.first]._links[j.first]._l1 = &j.second; + } + } + + for (auto& i : g2._nodeGenes) { + gPairs[i.first]._g2 = &i.second; + + for (auto& j : i.second._linkGenes) { + gPairs[i.first]._links[j.first]._l2 = &j.second; + } + } + }; + + GenePairs gPairs; + alignGenes(gPairs, g1, g2); + + double g1Size = 0.0; + double g2Size = 0.0; + double disjointGenes = 0.0; + double linkGenePairs = 0.0; + double weightDifference = 0.0; + + for (auto& i : gPairs) { + if (i.second._g1 && i.second._g2) { + ++g1Size; + ++g2Size; + + for (auto& j : i.second._links) { + if (j.second._l1 && j.second._l2) { + ++g1Size; + ++g2Size; + + ++linkGenePairs; + weightDifference += fabs(j.second._l1->_weight - j.second._l2->_weight); + } else if (j.second._l1) { + ++g1Size; + ++disjointGenes; + } else { + ++g2Size; + ++disjointGenes; + } + } + } else if (i.second._g1) { + double geneSize = static_cast<double>(1 + i.second._links.size()); + g1Size += geneSize; + disjointGenes += geneSize; + } else { + double geneSize = static_cast<double>(1 + i.second._links.size()); + g2Size += geneSize; + disjointGenes += geneSize; + } + } + + double normalize = g1Size > g2Size ? g1Size : g2Size; + double disjointFactor = (_prms._c1Disjoint * disjointGenes) / normalize; + double weightFactor = 0.0; + + if (linkGenePairs != 0.0) { + weightFactor = (_prms._c3WeightDifference * weightDifference) / linkGenePairs; + } + + return disjointFactor + weightFactor; +} + +size_t +Population::getRandSeed() const +{ + return chrono::system_clock::now().time_since_epoch().count(); +} + +size_t +Population::getRandSize(size_t low, size_t hi) +{ + return IntDist(low, hi)(_randGen); +} + +double +Population::getRandReal(double low, double hi) +{ + return RealDist(low, hi)(_randGen); +} + +double +Population::getRandWeight() +{ + return _weightSelector(_randGen); +} + +double +Population::getWeightDeviation() +{ + return _weightDeviator(_randGen); +} + +NodeType +Population::getRandNodeType() +{ + return static_cast<NodeType>(_nodeTypeSelector(_randGen)); +} + +bool +Population::getChance(double ratio) +{ + return _chanceSelector(_randGen) < ratio; +} diff --git a/src/QuadTree.cpp b/src/QuadTree.cpp new file mode 100644 index 0000000..d6686ff --- /dev/null +++ b/src/QuadTree.cpp @@ -0,0 +1,55 @@ +#include <HyperNeat/QuadTree.hpp> + +using namespace std; +using namespace hyperneat; + +QuadTree::QuadTree(double segment, double x, double y) + : _segment(segment), _x(x), _y(y) +{} + +double +QuadTree::getSegment() const +{ + return _segment; +} + +double +QuadTree::getX() const +{ + return _x; +} + +double +QuadTree::getY() const +{ + return _y; +} + +void +QuadTree::subdivide(Function<bool(QuadTree*)> subdivider) +{ + if (subdivider(this)) { + double newSeg = _segment / 2.0; + _children.resize(4); + _children[0] = {newSeg, _x - newSeg, _y - newSeg}; + _children[1] = {newSeg, _x + newSeg, _y - newSeg}; + _children[2] = {newSeg, _x - newSeg, _y + newSeg}; + _children[3] = {newSeg, _x + newSeg, _y + newSeg}; + + for (auto &i : _children) { + i.subdivide(subdivider); + } + } +} + +void +QuadTree::traverse(Function<void(const QuadTree*)> traverser) const +{ + if (!_children.empty()) { + for (auto &i : _children) { + i.traverse(traverser); + } + } else { + traverser(this); + } +} diff --git a/src/Utils/LoadFile.cpp b/src/Utils/LoadFile.cpp new file mode 100644 index 0000000..3849f76 --- /dev/null +++ b/src/Utils/LoadFile.cpp @@ -0,0 +1,280 @@ +#include <HyperNeat/Population.hpp> +#include <HyperNeat/NoveltyMetric.hpp> +#include <HyperNeat/NeuralNetPrms.hpp> +#include <HyperNeat/Utils/LoadFile.hpp> + +using namespace std; +using namespace hyperneat; + +LoadFile::LoadFile(Istream& stream) + : _stream(stream) +{ + _stream.setf(ios::boolalpha); +} + +void +LoadFile::loadPopulation(Population& population) +{ + size_t speciesCnt = 0; + size_t innovationsCount = 0; + ssize_t lastReplacement = 0; + ssize_t lastMother = 0; + ssize_t lastFather = 0; + bool hasNoveltyMetric = false; + bool hasNeuralNets = false; + auto& prms = population._prms; + + nextPrm() >> population._populationLock; + nextPrm() >> population._lockedOrganisms; + nextPrm() >> population._frozenOrganisms; + nextPrm() >> innovationsCount; + nextPrm() >> population._basicInnovs; + nextPrm() >> lastReplacement; + nextPrm() >> lastMother; + nextPrm() >> lastFather; + nextPrm() >> population._replacements; + nextPrm() >> population._distanceThreshold; + nextPrm() >> population._oldOrganisms; + nextPrm() >> population._minOldOrganisms; + nextPrm() >> hasNoveltyMetric; + nextPrm() >> population._updates; + nextPrm() >> speciesCnt; + nextPrm() >> hasNeuralNets; + + loadPopulationPrms(prms); + + if (hasNeuralNets) { + population._nnPrms = Pointer<NeuralNetPrms>(new NeuralNetPrms); + loadNeuralNetPrms(*population._nnPrms); + } + + population._innovations.resize(innovationsCount); + + for (auto& i : population._innovations) { + String nodeStr; + + nextPrm() >> i._number; + nextPrm() >> i._source; + nextPrm() >> i._target; + nextPrm() >> i._depth; + nextPrm() >> nodeStr; + + i._nodeType = stringToNode(nodeStr); + } + + population._allOrganisms.resize(prms._popSize, Organism(&population)); + population._species.resize(speciesCnt); + + for (auto& i : population._allOrganisms) { + loadOrganism(i); + } + + population._lastReplacement = (lastReplacement == -1 ? nullptr: &population._allOrganisms[lastReplacement]); + population._lastMother = (lastMother == -1 ? nullptr: &population._allOrganisms[lastMother]); + population._lastFather = (lastFather == -1 ? nullptr: &population._allOrganisms[lastFather]); + + for (auto& i : population._species) { + size_t specieSize = 0; + nextPrm() >> specieSize; + + while (specieSize--) { + size_t organismIdx = 0; + nextArrayValue() >> organismIdx; + + i.emplace_back(&population._allOrganisms[organismIdx]); + } + } + + if (hasNoveltyMetric) { + NoveltyMetricPrms nmPrms; + loadNoveltyMetricPrms(nmPrms); + + population.setNoveltyMetric(nmPrms); + loadNoveltyMetric(*population._noveltyMetric); + } + + if (hasNeuralNets) { + population.generateAllNeuralNets(); + } + + if (prms._seed != 0) { + population._randGen.seed(prms._seed); + } else { + population._randGen.seed(population.getRandSeed()); + } + + population._weightDeviator = BellDist(0.0, prms._weightDeviation); + population._weightSelector = RealDist(-prms._weightRange, prms._weightRange); + + population._organismsBeingGenerated = 0; +} + +void +LoadFile::loadPopulationPrms(PopulationPrms& prms) +{ + nextPrm() >> prms._popSize; + nextPrm() >> prms._cppnInputs; + nextPrm() >> prms._cppnOutputs; + nextPrm() >> prms._seed; + nextPrm() >> prms._weightRange; + nextPrm() >> prms._c1Disjoint; + nextPrm() >> prms._c3WeightDifference; + nextPrm() >> prms._initialDistanceThreshold; + nextPrm() >> prms._distanceThresholdShift; + nextPrm() >> prms._sexualReproductionRate; + nextPrm() >> prms._weightMutationRate; + nextPrm() >> prms._weightDeviation; + nextPrm() >> prms._interspeciesMatingRate; + nextPrm() >> prms._geneDisablingRatio; + nextPrm() >> prms._linkMutationRate; + nextPrm() >> prms._nodeMutationRate; + nextPrm() >> prms._targetSpeciesCount; + nextPrm() >> prms._eligibilityRatio; + nextPrm() >> prms._minimumLifetime; + nextPrm() >> prms._replBeforeReorganization; +} + +void +LoadFile::loadNeuralNetPrms(NeuralNetPrms& prms) +{ + size_t inputs = 0; + size_t outputs = 0; + + nextPrm() >> prms._testGridLevel; + nextPrm() >> prms._maxQuadTreeLevel; + nextPrm() >> prms._minQuadTreeLevel; + nextPrm() >> prms._bandPruningThreshold; + nextPrm() >> prms._varianceThreshold; + nextPrm() >> prms._divisionThreshold; + nextPrm() >> prms._iterations; + nextPrm() >> inputs; + nextPrm() >> outputs; + + prms._inputMap.resize(inputs); + prms._outputMap.resize(outputs); + + for (auto& i : prms._inputMap) { + nextArrayValue() >> i._x; + nextArrayValue() >> i._y; + } + + for (auto& i : prms._outputMap) { + nextArrayValue() >> i._x; + nextArrayValue() >> i._y; + } +} + +void +LoadFile::loadNoveltyMetric(NoveltyMetric& noveltyMetric) +{ + for (auto& i : noveltyMetric._behaviors) { + nextPrm() >> i._criteriaReached; + nextPrm() >> i._noveltyScore; + nextPrm() >> i._toBeArchived; + + for (auto& j : i._characterization) { + nextArrayValue() >> j; + } + } + + size_t archiveSize = 0; + nextPrm() >> archiveSize; + noveltyMetric._archive.resize(archiveSize); + + for (auto& i : noveltyMetric._archive) { + i.resize(noveltyMetric._prms._characterizationSize, 0.0); + + for (auto& j : i) { + nextArrayValue() >> j; + } + } +} + +void +LoadFile::loadNoveltyMetricPrms(NoveltyMetricPrms& noveltyMetricPrms) +{ + nextPrm() >> noveltyMetricPrms._noveltyThreshold; + nextPrm() >> noveltyMetricPrms._referenceOrganisms; + nextPrm() >> noveltyMetricPrms._characterizationSize; + nextPrm() >> noveltyMetricPrms._criteriaReachedByDefault; +} + +void +LoadFile::loadOrganism(Organism& organism) +{ + nextPrm() >> organism._index; + nextPrm() >> organism._fitness; + nextPrm() >> organism._isLocked; + nextPrm() >> organism._isFrozen; + nextPrm() >> organism._specie; + nextPrm() >> organism._lifetime; + + loadGenome(organism._genome); +} + +void +LoadFile::loadGenome(Genome& genome) +{ + size_t nodes = 0; + + nextPrm() >> genome._inputs; + nextPrm() >> nodes; + + while (nodes--) { + String nodeType; + size_t links = 0; + size_t innov = 0; + + nextPrm() >> innov; + auto& nodeGene = genome._nodeGenes[innov]; + + nextPrm() >> nodeGene._depth; + nextPrm() >> nodeType; + nodeGene._nodeType = stringToNode(nodeType); + + nextPrm() >> links; + + while (links--) { + size_t source = 0; + + nextPrm() >> source; + auto& linkGene = nodeGene._linkGenes[source]; + + nextPrm() >> linkGene._weight; + nextPrm() >> linkGene._isEnabled; + } + } +} + +Istream& +LoadFile::nextPrm(bool arrayVal) +{ + for (;;) { + char ch = static_cast<char>(_stream.peek()); + + if (ch == '#') { + _stream.ignore(numeric_limits<streamsize>::max(), '\n'); + } else { + if (!arrayVal) { + _stream.ignore(); + + if (ch == '=') { + return _stream; + } + } else { + if (string("-0123456789").find(ch) != string::npos) { + return _stream; + } + + _stream.ignore(); + } + } + } +} + + +Istream& +LoadFile::nextArrayValue() +{ + return nextPrm(true); +} diff --git a/src/Utils/NodeTypes.cpp b/src/Utils/NodeTypes.cpp new file mode 100644 index 0000000..b11b079 --- /dev/null +++ b/src/Utils/NodeTypes.cpp @@ -0,0 +1,41 @@ +#include <HyperNeat/Utils/NodeTypes.hpp> + +namespace hyperneat +{ + String + nodeToString(NodeType type) + { + switch (type) { + case NodeType::SIGMOID: + return "\"sigmoid\""; + + case NodeType::GAUSSIAN: + return "\"gaussian\""; + + case NodeType::SINE: + return "\"sine\""; + + case NodeType::ABSOLUTE: + return "\"absolute\""; + + default: + return "\"nullType\""; + } + } + + NodeType + stringToNode(const String& str) + { + if (str == "\"sigmoid\"") { + return NodeType::SIGMOID; + } else if (str == "\"gaussian\"") { + return NodeType::GAUSSIAN; + } else if (str == "\"sine\"") { + return NodeType::SINE; + } else if (str == "\"absolute\"") { + return NodeType::ABSOLUTE; + } else { + return NodeType::NULL_TYPE; + } + } +} diff --git a/src/Utils/Point.cpp b/src/Utils/Point.cpp new file mode 100644 index 0000000..7dd9610 --- /dev/null +++ b/src/Utils/Point.cpp @@ -0,0 +1,33 @@ +#include <cmath> +#include <HyperNeat/Utils/Point.hpp> + +using namespace std; +using namespace hyperneat; + +Point::Point(double x, double y) + : _x(x), _y(y) +{} + +double +Point::distance(const Point& other) const +{ + double x = _x - other._x; + double y = _y - other._y; + + return sqrt(x * x + y * y); +} + +bool +Point::operator==(const Point& other) const{ + return (_x == other._x) && (_y == other._y); +} + +bool +Point::operator<(const Point& other) const +{ + if (_x == other._x) { + return _y < other._y; + } else { + return _x < other._x; + } +} diff --git a/src/Utils/SaveFile.cpp b/src/Utils/SaveFile.cpp new file mode 100644 index 0000000..8c471f6 --- /dev/null +++ b/src/Utils/SaveFile.cpp @@ -0,0 +1,249 @@ +#include <HyperNeat/Population.hpp> +#include <HyperNeat/NoveltyMetric.hpp> +#include <HyperNeat/NeuralNetPrms.hpp> +#include <HyperNeat/Utils/SaveFile.hpp> + +using namespace std; +using namespace hyperneat; + +SaveFile::SaveFile(Ostream& stream) + : _stream(stream) +{ + _stream.setf(ios::boolalpha); + _stream.precision(numeric_limits<double>::digits10); +} + +void +SaveFile::savePopulation(Population& population, bool shuttedDown, size_t tabs, const String& prefix) +{ + print(tabs) << "# hyperneat::Population data:" << newl(); + print(tabs) << "# -------------------------------------------------------------------------------------" << newl(); + print(tabs) << "# DO NOT erase or change the order of any entry! Data is read back ENTRY by ENTRY." << newl(2); + + ssize_t lastReplacement = (population.getLastReplacement() ? population.getLastReplacement()->getIndex() : -1); + ssize_t lastMother = (population.getLastMother() ? population.getLastMother()->getIndex() : -1); + ssize_t lastFather = (population.getLastFather() ? population.getLastFather()->getIndex() : -1); + + print(tabs) << "[" << prefix << "population]" << newl(); + print(tabs + 1) << "populationLock = " << (shuttedDown ? false : population.isLocked()) << newl(); + print(tabs + 1) << "lockedOrganisms = " << (shuttedDown ? 0 : population.getLockedOrganisms()) << newl(); + print(tabs + 1) << "frozenOrganisms = " << (shuttedDown ? 0 : population.getFrozenOrganisms()) << newl(); + print(tabs + 1) << "innovationsCount = " << population.getInnovationsCount() << newl(); + print(tabs + 1) << "basicInnovations = " << population.getBasicInnovationsCount() << newl(); + print(tabs + 1) << "lastReplacement = " << lastReplacement << newl(); + print(tabs + 1) << "lastMother = " << lastMother << newl(); + print(tabs + 1) << "lastFather = " << lastFather << newl(); + print(tabs + 1) << "replacements = " << population.getReplacements() << newl(); + print(tabs + 1) << "distanceThreshold = " << population.getDistanceThreshold() << newl(); + print(tabs + 1) << "oldOrganisms = " << (shuttedDown ? 0 : population.getOldOrganisms()) << newl(); + print(tabs + 1) << "minOldOrganisms = " << population.getMinimumOldOrganisms() << newl(); + print(tabs + 1) << "noveltyMetric = " << population.isNoveltyMetricSet() << newl(); + print(tabs + 1) << "updates = " << population.getUpdates() << newl(); + print(tabs + 1) << "species = " << population.getSpecies().size() << newl(); + print(tabs + 1) << "withNeuralNets = " << population.hasNeuralNets() << newl(2); + + savePopulationPrms(population.getPopulationPrms(), tabs + 1, prefix + "population."); + + if (population.hasNeuralNets()) { + print() << newl(); + saveNeuralNetPrms(population.getNeuralNetPrms(), tabs + 1, prefix + "population."); + } + + for (auto& i : population.getInnovations()) { + print() << newl(); + print(tabs + 1) << "[[" + prefix + "population.innovation]]" << newl(); + print(tabs + 2) << "number = " << i._number << newl(); + print(tabs + 2) << "source = " << i._source << newl(); + print(tabs + 2) << "target = " << i._target << newl(); + print(tabs + 2) << "depth = " << i._depth << newl(); + print(tabs + 2) << "function = " << nodeToString(i._nodeType) << newl(); + } + + for (auto& i : population.getAllOrganisms()) { + print() << newl(); + saveOrganism(i, shuttedDown, tabs + 1, prefix + "population."); + } + + for (auto& i : population.getSpecies()) { + print() << newl(); + print(tabs + 1) << "[[" + prefix + "population.specie]]" << newl(); + print(tabs + 2) << "size = " << i.size() << newl(); + print(tabs + 2) << "members = [" << newl(); + + for (auto& j : i) { + print(tabs + 3) << j->getIndex() << "," << newl(); + } + + print(tabs + 2) << "]" << newl(); + } + + if (population.isNoveltyMetricSet()) { + print() << newl(); + saveNoveltyMetric(population.getNoveltyMetric(), shuttedDown, tabs + 1, prefix + "population."); + } +} + +void +SaveFile::savePopulationPrms(const PopulationPrms& prms, size_t tabs, const String& prefix) +{ + print(tabs) << "[" << prefix << "parameters]" << newl(); + print(tabs + 1) << "popSize = " << prms._popSize << newl(); + print(tabs + 1) << "cppnInputs = " << prms._cppnInputs << newl(); + print(tabs + 1) << "cppnOutputs = " << prms._cppnOutputs << newl(); + print(tabs + 1) << "seed = " << prms._seed << newl(); + print(tabs + 1) << "weightRange = " << prms._weightRange << newl(); + print(tabs + 1) << "disjointCoeff = " << prms._c1Disjoint << newl(); + print(tabs + 1) << "weightDifferenceCoeff = " << prms._c3WeightDifference << newl(); + print(tabs + 1) << "initialDistanceThreshold = " << prms._initialDistanceThreshold << newl(); + print(tabs + 1) << "distanceThresholdShift = " << prms._distanceThresholdShift << newl(); + print(tabs + 1) << "sexualReproductionRate = " << prms._sexualReproductionRate << newl(); + print(tabs + 1) << "weightMutationRate = " << prms._weightMutationRate << newl(); + print(tabs + 1) << "weightDeviation = " << prms._weightDeviation << newl(); + print(tabs + 1) << "interspeciesMatingRate = " << prms._interspeciesMatingRate << newl(); + print(tabs + 1) << "geneDisablingRatio = " << prms._geneDisablingRatio << newl(); + print(tabs + 1) << "linkMutationRate = " << prms._linkMutationRate << newl(); + print(tabs + 1) << "nodeMutationRate = " << prms._nodeMutationRate << newl(); + print(tabs + 1) << "targetSpeciesCount = " << prms._targetSpeciesCount << newl(); + print(tabs + 1) << "eligibilityRatio = " << prms._eligibilityRatio << newl(); + print(tabs + 1) << "minimumLifetime = " << prms._minimumLifetime << newl(); + print(tabs + 1) << "replBeforeReorganization = " << prms._replBeforeReorganization << newl(); +} + +void +SaveFile::saveNeuralNetPrms(const NeuralNetPrms& prms, size_t tabs, const String& prefix) +{ + print(tabs) << "[" << prefix << "neuralNetParameters]" << newl(); + print(tabs + 1) << "testGridLevel = " << prms._testGridLevel << newl(); + print(tabs + 1) << "maxQuadTreeLevel = " << prms._maxQuadTreeLevel << newl(); + print(tabs + 1) << "minQuadTreeLevel = " << prms._minQuadTreeLevel << newl(); + print(tabs + 1) << "bandPruningThreshold = " << prms._bandPruningThreshold << newl(); + print(tabs + 1) << "varianceThreshold = " << prms._varianceThreshold << newl(); + print(tabs + 1) << "divisionThreshold = " << prms._divisionThreshold << newl(); + print(tabs + 1) << "iterations = " << prms._iterations << newl(); + print(tabs + 1) << "inputs = " << prms._inputMap.size() << newl(); + print(tabs + 1) << "outputs = " << prms._outputMap.size() << newl(2); + print(tabs + 1) << "inputMap = [" << newl(); + + for (auto& i : prms._inputMap) { + print(tabs + 2) << "[" << i._x << ", " << i._y << "]," << newl(); + } + + print(tabs + 1) << "]" << newl(2); + print(tabs + 1) << "outputMap = [" << newl(); + + for (auto& i : prms._outputMap) { + print(tabs + 2) << "[" << i._x << ", " << i._y << "]," << newl(); + } + + print(tabs + 1) << "]" << newl(); +} + +void +SaveFile::saveOrganism(const Organism& organism, bool shuttedDown, size_t tabs, const String& prefix) +{ + print(tabs) << "[[" << prefix << "organism]]" << newl(); + + if (organism.isChampion()) { + print(tabs + 1) << "# Current champion." << newl(2); + } + + print(tabs + 1) << "index = " << organism.getIndex() << newl(); + print(tabs + 1) << "fitness = " << (shuttedDown ? 0 : organism._fitness) << newl(); + print(tabs + 1) << "isLocked = " << (shuttedDown ? false : organism.isLocked()) << newl(); + print(tabs + 1) << "isFrozen = " << (shuttedDown ? false : organism.isFrozen()) << newl(); + print(tabs + 1) << "specie = " << organism.getSpecie() << newl(); + print(tabs + 1) << "lifetime = " << (shuttedDown ? 0 : organism.getLifetime()) << newl(2); + + saveGenome(organism.getGenome(), tabs + 1, prefix + "organism."); + + // If shutted down, save Neural Net +} + +void +SaveFile::saveGenome(const Genome& genome, size_t tabs, const String& prefix) +{ + print(tabs) << "[" << prefix << "genome]" << newl(); + print(tabs + 1) << "inputs = " << genome._inputs << newl(); + print(tabs + 1) << "nodes = " << genome._nodeGenes.size() << newl(); + + for (auto& i : genome._nodeGenes) { + print() << newl(); + print(tabs + 1) << "[[" << prefix << "genome.nodeGene]]" << newl(); + print(tabs + 2) << "innovation = " << i.first << newl(); + print(tabs + 2) << "depth = " << i.second._depth << newl(); + print(tabs + 2) << "function = " << nodeToString(i.second._nodeType) << newl(); + print(tabs + 2) << "links = " << i.second._linkGenes.size() << newl(); + + for (auto& j : i.second._linkGenes) { + print() << newl(); + print(tabs + 2) << "[[" << prefix << "genome.nodeGene.linkGene]]" << newl(); + print(tabs + 3) << "source = " << j.first << newl(); + print(tabs + 3) << "weight = " << j.second._weight << newl(); + print(tabs + 3) << "isEnabled = " << j.second._isEnabled << newl(); + } + } +} + +void +SaveFile::saveNoveltyMetric(const NoveltyMetric& noveltyMetric, bool shuttedDown, size_t tabs, const String& prefix) +{ + print(tabs) << "[" << prefix << "noveltyMetric]" << newl(); + saveNoveltyMetricPrms(noveltyMetric.getPrms(), tabs + 1, prefix + "noveltyMetric."); + + bool critDef = noveltyMetric.getPrms()._criteriaReachedByDefault; + + for (auto& i : noveltyMetric.getBehaviors()) { + print() << newl(); + print(tabs + 1) << "[[" << prefix << "noveltyMetric.behavior]]" << newl(); + print(tabs + 2) << "criteriaReached = " << (shuttedDown ? critDef : i._criteriaReached) << newl(); + print(tabs + 2) << "noveltyScore = " << (shuttedDown ? 0 : i.getNoveltyScore()) << newl(); + print(tabs + 2) << "toBeArchived = " << (shuttedDown ? false : i.isToBeArchived()) << newl(); + print(tabs + 2) << "characterization = [" << newl(); + + for (auto& j : i.getCharacterization()) { + print(tabs + 3) << j << "," << newl(); + } + + print(tabs + 2) << "]" << newl(); + } + + print() << newl(); + print(tabs + 1) << "[" << prefix << "noveltyMetric.archive]" << newl(); + print(tabs + 2) << "size = " << noveltyMetric.getArchive().size() << newl(); + print(tabs + 2) << "characterizations = [" << newl(); + + for (auto& i : noveltyMetric.getArchive()) { + print(tabs + 3) << "[" << newl(); + + for (auto& j : i) { + print(tabs + 4) << j << "," << newl(); + } + + print(tabs + 3) << "]," << newl(); + } + + print(tabs + 2) << "]" << newl(); +} + +void +SaveFile::saveNoveltyMetricPrms(const NoveltyMetricPrms& noveltyMetricPrms, size_t tabs, const String& prefix) +{ + print(tabs) << "[" << prefix << "parameters]" << newl(); + print(tabs + 1) << "noveltyThreshold = " << noveltyMetricPrms._noveltyThreshold << newl(); + print(tabs + 1) << "referenceOrganisms = " << noveltyMetricPrms._referenceOrganisms << newl(); + print(tabs + 1) << "characterizationSize = " << noveltyMetricPrms._characterizationSize << newl(); + print(tabs + 1) << "criteriaReachedByDefault = " << noveltyMetricPrms._criteriaReachedByDefault << newl(); +} + +Ostream& +SaveFile::print(size_t tabs) +{ + _stream << String(tabs * 4, ' '); + return _stream; +} + +String +SaveFile::newl(size_t lines) +{ + return String(lines, '\n'); +} diff --git a/src/Utils/ValuePoint.cpp b/src/Utils/ValuePoint.cpp new file mode 100644 index 0000000..d9cdc64 --- /dev/null +++ b/src/Utils/ValuePoint.cpp @@ -0,0 +1,7 @@ +#include <HyperNeat/Utils/ValuePoint.hpp> + +using namespace hyperneat; + +ValuePoint::ValuePoint(double x, double y, double value, double segment) + : Point(x, y), _value(value), _segment(segment) +{} diff --git a/todolist.txt b/todolist.txt new file mode 100644 index 0000000..2071772 --- /dev/null +++ b/todolist.txt @@ -0,0 +1,44 @@ +FOR VERSION 0.1 Beta: + ✔ Test [minimal criteria = above average fitness] on NeuroRacers @done (14-04-05 03:47) + ✔ Serialize loaded and saved files in TOML @done (14-04-05 03:48) + ✔ Cppn functions should be Sine, Gaussian, Sigmoid and Absolute @done (14-04-05 18:44) + ✔ Add Population::setMinimumLifetime() @done (14-04-05 19:18) + ✔ Add NeuralNet::getAverageActivation() @done (14-04-05 19:53) + ✔ Place NeuralNet inside Organism class @done (14-04-11 03:08) + ✔ Load/Save system links to a iostream object instead of fstream @done (14-05-09 19:14) + ☐ Test cereal library (branch) + ☐ Load/Save system for NNets (Add Save Sim to Guppies) + ☐ Plugins: + ☐ CPPN explorer + ☐ NeuralNet Monitor + ☐ Population Monitor + ☐ Create HyperGuppies + ☐ Code Doxygen documentation + ☐ Make tutorial + ☐ Release hyperneat:: 0.1 Beta + +FOR VERSION 1.0: + ☐ Test recursive CPPNs + ☐ addOrganism() / removeOrganism() / Change some parameters (Add Feature to Guppies) + ☐ Create functional copy & move operators (for CPPNs, NNets and Populations) + ☐ Create simple exception handling (for all public methods) + ☐ Create Brain class (apply on Guppies) + ☐ Allow non-realtime updating (NEAT instead of rtNEAT) + ☐ Test Population::update() as separate thread + ☐ Update documentation and tutorial + + +___________________ +Archive: + ✔ Find nice way to organize #includes @done (13-09-05 15:03) @project(FOR VERSION 0.1 Beta) + ✔ DEBUG!!! - Done ??? @done (13-10-06 23:53) @project(FOR VERSION 0.1 Beta) + ✔ Fix NeuralNet::create() @done (13-10-09 17:38) @project(FOR VERSION 0.1 Beta) + ✔ Make everything HyperThreaded again @done (13-10-11 04:41) @project(FOR VERSION 0.1 Beta) + ✔ Eliminate Population::getAllNeuralNets() @done (13-10-10 23:02) @project(FOR VERSION 0.1 Beta) + ✔ Recent update flag on Population @done (13-10-11 04:00) @project(FOR VERSION 0.1 Beta) + ✔ Make Neuron::Type @done (13-10-11 05:22) @project(FOR VERSION 0.1 Beta) + ✔ Test Genome::NodeGene matching system (Save & Load) @done (13-10-28 01:37) @project(FOR VERSION 0.1 Beta) + ✔ Include distance on Node Search @done (13-11-06 04:22) @project(FOR VERSION 0.1 Beta) + ✔ Revise Integer data types @done (13-11-09 23:52) @project(FOR VERSION 0.1 Beta) + ✔ Include a pointSetter() function as parameter to Population::update() function @done (13-11-10 00:20) @project(FOR VERSION 0.1 Beta) + ✔ Implement "Minimal Criteria Novelty Search" (test on NeuroRacers) @done (13-11-21 01:54) @project(FOR VERSION 0.1 Beta) |