From 58475802e8fa308e8f1802f3c3119c5e990fa9b9 Mon Sep 17 00:00:00 2001 From: DaMatrix Date: Mon, 19 Aug 2019 12:33:09 +0200 Subject: [PATCH 1/6] begin implementing ai again it's been a long time --- ai/build.gradle | 2 +- .../main/java/net/daporkchop/lib/ai/AI.java | 33 +++++++ .../java/net/daporkchop/lib/ai/Evaluator.java | 88 +++++++++++++++++++ .../net/daporkchop/lib/ai/NeuralNetwork.java | 26 ++++++ .../java/net/daporkchop/lib/ai/Trainer.java | 41 +++++++++ .../lib/ai/alg/MachineLearning.java | 27 ++++++ .../lib/ai/alg/TrainingOptions.java | 49 +++++++++++ .../net/daporkchop/lib/ai/alg/neat/NEAT.java | 22 +++++ 8 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 ai/src/main/java/net/daporkchop/lib/ai/AI.java create mode 100644 ai/src/main/java/net/daporkchop/lib/ai/Evaluator.java create mode 100644 ai/src/main/java/net/daporkchop/lib/ai/NeuralNetwork.java create mode 100644 ai/src/main/java/net/daporkchop/lib/ai/Trainer.java create mode 100644 ai/src/main/java/net/daporkchop/lib/ai/alg/MachineLearning.java create mode 100644 ai/src/main/java/net/daporkchop/lib/ai/alg/TrainingOptions.java create mode 100644 ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEAT.java diff --git a/ai/build.gradle b/ai/build.gradle index 520d56bdc..d9653766d 100644 --- a/ai/build.gradle +++ b/ai/build.gradle @@ -14,5 +14,5 @@ */ dependencies { - //compile "nl.sandergielisse:mythan:1.0-SNAPSHOT" + compile "nl.sandergielisse:mythan:1.0-SNAPSHOT" } diff --git a/ai/src/main/java/net/daporkchop/lib/ai/AI.java b/ai/src/main/java/net/daporkchop/lib/ai/AI.java new file mode 100644 index 000000000..0cfd7d665 --- /dev/null +++ b/ai/src/main/java/net/daporkchop/lib/ai/AI.java @@ -0,0 +1,33 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.ai; + +/** + * Abstract representation of an AI. + * + * @author DaPorkchop_ + * @see NeuralNetwork + */ +public interface AI { + /** + * Gets this AIs fitness level (aka skill). + * + * The meaning of this value varies depending on the {@link Evaluator} used to train this AI. + * + * @return this AIs fitness + */ + double fitness(); +} diff --git a/ai/src/main/java/net/daporkchop/lib/ai/Evaluator.java b/ai/src/main/java/net/daporkchop/lib/ai/Evaluator.java new file mode 100644 index 000000000..ecb6eee75 --- /dev/null +++ b/ai/src/main/java/net/daporkchop/lib/ai/Evaluator.java @@ -0,0 +1,88 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.ai; + +import lombok.NonNull; + +/** + * A type that can train an AI. + * + * @author DaPorkchop_ + */ +public interface Evaluator { + /** + * Initializes the trainer in preparation for training. + *

+ * This method is only called once for the beginning of training. + *

+ * This may be used to e.g. load training data from disk. + * + * @param totalWorkers the total number of workers that will be used + * @throws Exception if an exception occurs while initializing + */ + default void init(int totalWorkers) throws Exception { + } + + /** + * Initializes a single worker in preparation for training. + *

+ * This method is only called once per worker, and will always be called from the same thread that the worker + * will later be training on. + * + * @param worker the ordinal of the current worker + * @param totalWorkers the total number of workers + * @throws Exception if an exception occurs while initializing + */ + default void initWorker(int worker, int totalWorkers) throws Exception { + } + + /** + * Evaluates a single specimen. + *

+ * This method should evaluate the fitness of the given specimen by feeding it a large number of test + * data samples and seeing what the results are, and return a {@code double} value that represents the fitness + * of the specimen. The network's fitness is not on any sort of scale, higher values simply indicate that the + * specimen performed better (although the fitness value should be directly proportional to the specimen's + * performance, for example it could be simply the number of tests that were passed successfully). However, the + * fitness must be at least {@code 0.0d} and may not be {@link Double#NaN}. + * + * @param specimen the specimen to evaluate + * @return the specimen's skill + */ + double evaluate(@NonNull A specimen); + + /** + * De-initializes a worker after training is complete. + *

+ * This method is only called once per worker, and will always be called from the same thread that the worker + * will later be training on. + * + * @param worker the ordinal of the current worker + * @param totalWorkers the total number of workers + * @throws Exception if an exception occurs while de-initializing + */ + default void deinitWorker(int worker, int totalWorkers) throws Exception { + } + + /** + * De-initializes the trainer after training is complete. + * + * @param totalWorkers the total number of workers + * @throws Exception if an exception occurs while initializing + */ + default void deinit(int totalWorkers) throws Exception { + } +} diff --git a/ai/src/main/java/net/daporkchop/lib/ai/NeuralNetwork.java b/ai/src/main/java/net/daporkchop/lib/ai/NeuralNetwork.java new file mode 100644 index 000000000..79b6b3bd4 --- /dev/null +++ b/ai/src/main/java/net/daporkchop/lib/ai/NeuralNetwork.java @@ -0,0 +1,26 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.ai; + +/** + * An {@link AI} based on a network of interconnected nodes. + * + * A neural network typically has a fixed number of inputs and outputs. + * + * @author DaPorkchop_ + */ +public interface NeuralNetwork extends AI { +} diff --git a/ai/src/main/java/net/daporkchop/lib/ai/Trainer.java b/ai/src/main/java/net/daporkchop/lib/ai/Trainer.java new file mode 100644 index 000000000..6c7de1d7e --- /dev/null +++ b/ai/src/main/java/net/daporkchop/lib/ai/Trainer.java @@ -0,0 +1,41 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.ai; + +/** + * Helper class to manage training of AIs. + * + * @author DaPorkchop_ + */ +//TODO: this should be made using futures and stuff, so that training may be paused, etc. +public interface Trainer /*extends Serializable*/ { + /** + * @return the specimen with the highest fitness + */ + A fittestSpecimen(); + + /** + * @return the {@link Evaluator} instance being used by this trainer + */ + Evaluator evaluator(); + + /** + * Initiates the training cycle, continuing until a specimen reaches at least the given fitness. + * + * @param fitness the minimum fitness to train to + */ + void trainToFitness(double fitness); +} diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/MachineLearning.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/MachineLearning.java new file mode 100644 index 000000000..1f428f736 --- /dev/null +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/MachineLearning.java @@ -0,0 +1,27 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.ai.alg; + +import net.daporkchop.lib.ai.AI; +import net.daporkchop.lib.ai.Evaluator; + +/** + * Abstract representation of a machine learning algorithm. + * + * @author DaPorkchop_ + */ +public interface MachineLearning> { +} diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/TrainingOptions.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/TrainingOptions.java new file mode 100644 index 000000000..5d5543881 --- /dev/null +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/TrainingOptions.java @@ -0,0 +1,49 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.ai.alg; + +import lombok.Getter; +import lombok.experimental.Accessors; +import net.daporkchop.lib.ai.AI; +import net.daporkchop.lib.common.util.PorkUtil; + +/** + * Options used when training an AI. + * + * @author DaPorkchop_ + */ +@Accessors(fluent = true, chain = true) +@Getter +public abstract class TrainingOptions> { + /** + * The number of worker threads to use for training. + *

+ * Defaults to the CPU count. + */ + protected int workers = PorkUtil.CPU_COUNT; + + /** + * @see #workers + */ + @SuppressWarnings("unchecked") + public O workers(int workers) { + if (workers <= 0) { + throw new IllegalStateException("Must have at least 1 worker!"); + } + this.workers = workers; + return (O) this; + } +} diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEAT.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEAT.java new file mode 100644 index 000000000..e9b76e1b7 --- /dev/null +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEAT.java @@ -0,0 +1,22 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.ai.alg.neat; + +/** + * @author DaPorkchop_ + */ +public class NEAT { +} From 5920eb684449246c6fd91d1b283051f12348dc0e Mon Sep 17 00:00:00 2001 From: DaMatrix Date: Mon, 19 Aug 2019 12:52:50 +0200 Subject: [PATCH 2/6] add some infrastructure for implementing NEAT --- .../java/net/daporkchop/lib/ai/Trainer.java | 17 +++++- .../lib/ai/alg/MachineLearning.java | 12 ++++- .../net/daporkchop/lib/ai/alg/neat/NEAT.java | 15 +++++- .../lib/ai/alg/neat/NEATNetwork.java | 34 ++++++++++++ .../lib/ai/alg/neat/NEATOptions.java | 46 ++++++++++++++++ .../lib/ai/alg/neat/NEATTrainer.java | 54 +++++++++++++++++++ 6 files changed, 174 insertions(+), 4 deletions(-) create mode 100644 ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATNetwork.java create mode 100644 ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATOptions.java create mode 100644 ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATTrainer.java diff --git a/ai/src/main/java/net/daporkchop/lib/ai/Trainer.java b/ai/src/main/java/net/daporkchop/lib/ai/Trainer.java index 6c7de1d7e..0d113f110 100644 --- a/ai/src/main/java/net/daporkchop/lib/ai/Trainer.java +++ b/ai/src/main/java/net/daporkchop/lib/ai/Trainer.java @@ -15,22 +15,35 @@ package net.daporkchop.lib.ai; +import net.daporkchop.lib.ai.alg.MachineLearning; +import net.daporkchop.lib.ai.alg.TrainingOptions; + /** * Helper class to manage training of AIs. * * @author DaPorkchop_ */ //TODO: this should be made using futures and stuff, so that training may be paused, etc. -public interface Trainer /*extends Serializable*/ { +public interface Trainer> /*extends Serializable*/ { /** * @return the specimen with the highest fitness */ A fittestSpecimen(); + /** + * @return the options used by this trainer + */ + O options(); + + /** + * @return the machine learning algorithm employed by this trainer + */ + MachineLearning algorithm(); + /** * @return the {@link Evaluator} instance being used by this trainer */ - Evaluator evaluator(); + Evaluator evaluator(); /** * Initiates the training cycle, continuing until a specimen reaches at least the given fitness. diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/MachineLearning.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/MachineLearning.java index 1f428f736..bdc603abd 100644 --- a/ai/src/main/java/net/daporkchop/lib/ai/alg/MachineLearning.java +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/MachineLearning.java @@ -15,13 +15,23 @@ package net.daporkchop.lib.ai.alg; +import lombok.NonNull; import net.daporkchop.lib.ai.AI; import net.daporkchop.lib.ai.Evaluator; +import net.daporkchop.lib.ai.Trainer; /** * Abstract representation of a machine learning algorithm. * * @author DaPorkchop_ */ -public interface MachineLearning> { +public interface MachineLearning> { + /** + * Prepares to begin training an AI. + * + * @param evaluator the {@link Evaluator} that will evaluate the AI's performance + * @param options options used for training + * @return a {@link Trainer} that will be used to control the training process + */ + Trainer beginTraining(@NonNull Evaluator evaluator, @NonNull O options); } diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEAT.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEAT.java index e9b76e1b7..63885bc51 100644 --- a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEAT.java +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEAT.java @@ -15,8 +15,21 @@ package net.daporkchop.lib.ai.alg.neat; +import lombok.NonNull; +import net.daporkchop.lib.ai.Evaluator; +import net.daporkchop.lib.ai.NeuralNetwork; +import net.daporkchop.lib.ai.Trainer; +import net.daporkchop.lib.ai.alg.MachineLearning; + /** + * Implementation of the Neuroevolution of Augmenting Topologies (NEAT) algorithm, made by Kenneth O. Stanley + * and Risto Miikkulainen (see http://nn.cs.utexas.edu/downloads/papers/stanley.ec02.pdf). + * * @author DaPorkchop_ */ -public class NEAT { +public class NEAT implements MachineLearning { + @Override + public NEATTrainer beginTraining(@NonNull Evaluator evaluator, @NonNull NEATOptions options) { + return null; + } } diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATNetwork.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATNetwork.java new file mode 100644 index 000000000..82d8ceb94 --- /dev/null +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATNetwork.java @@ -0,0 +1,34 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.ai.alg.neat; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import net.daporkchop.lib.ai.NeuralNetwork; + +/** + * An implementation of {@link NeuralNetwork} for use in NEAT training. + * + * @author DaPorkchop_ + */ +@Accessors(fluent = true, chain = true) +@Getter +public class NEATNetwork implements NeuralNetwork { + @Setter(AccessLevel.PACKAGE) + protected double fitness = Double.NaN; +} diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATOptions.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATOptions.java new file mode 100644 index 000000000..7247c3d30 --- /dev/null +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATOptions.java @@ -0,0 +1,46 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.ai.alg.neat; + +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import net.daporkchop.lib.ai.NeuralNetwork; +import net.daporkchop.lib.ai.alg.TrainingOptions; + +/** + * Options for training a neural network using the NEAT algorithm. + * + * @author DaPorkchop_ + */ +@Accessors(chain = true, fluent = true) +@Getter +@Setter +public class NEATOptions extends TrainingOptions { + protected double geneDisableChance = 0.75D; + protected double mutationWeightChance = 0.8D; + protected double mutationWeightRandomChance = 0.1D; + protected double mutationWeightMaxDisturbance = 0.25D; + protected double mutationNewConnectionChance = 0.05D; + protected double mutationNewNodeChance = 0.03D; + protected double distanceExcessWeight = 1.0D; + protected double distanceDisjointWeight = 1.0D; + protected double distanceWeightsWeight = 0.4D; + protected double speciesCompatibilityDistance = 0.8D; + protected double generationEliminationPercentage = 0.9D; + protected double breedCrossChance = 0.75D; + protected double mutationWeightChanceRandomRange = 5.0D; +} diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATTrainer.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATTrainer.java new file mode 100644 index 000000000..fad9a000c --- /dev/null +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATTrainer.java @@ -0,0 +1,54 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.ai.alg.neat; + +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import net.daporkchop.lib.ai.Evaluator; +import net.daporkchop.lib.ai.NeuralNetwork; +import net.daporkchop.lib.ai.Trainer; +import net.daporkchop.lib.ai.alg.MachineLearning; + +/** + * Implementation of {@link Trainer} for the NEAT algorithm. + * + * @author DaPorkchop_ + * @see NEAT + */ +@Accessors(chain = true, fluent = true) +@RequiredArgsConstructor +public class NEATTrainer implements Trainer { + @NonNull + @Getter + protected final NEATOptions options; + @NonNull + @Getter + protected final Evaluator evaluator; + @NonNull + @Getter + protected final NEAT algorithm; + + @Override + public NEATNetwork fittestSpecimen() { + return null; + } + + @Override + public synchronized void trainToFitness(double fitness) { + } +} From 2640737f1450f3027c9a540071ea6b2f4b34408f Mon Sep 17 00:00:00 2001 From: DaMatrix Date: Mon, 19 Aug 2019 13:26:50 +0200 Subject: [PATCH 3/6] temp commit --- .../net/daporkchop/lib/ai/NeuralNetwork.java | 37 ++++++++++++++- .../ai/alg/abst/AbstractNeuralNetwork.java | 46 +++++++++++++++++++ .../net/daporkchop/lib/ai/alg/neat/NEAT.java | 8 +++- .../lib/ai/alg/neat/NEATNetwork.java | 34 +++++++++++++- .../lib/ai/alg/neat/NEATOptions.java | 9 ++++ .../lib/ai/alg/neat/NEATTrainer.java | 7 +-- .../lib/ai/alg/neat/package-info.java | 21 +++++++++ 7 files changed, 156 insertions(+), 6 deletions(-) create mode 100644 ai/src/main/java/net/daporkchop/lib/ai/alg/abst/AbstractNeuralNetwork.java create mode 100644 ai/src/main/java/net/daporkchop/lib/ai/alg/neat/package-info.java diff --git a/ai/src/main/java/net/daporkchop/lib/ai/NeuralNetwork.java b/ai/src/main/java/net/daporkchop/lib/ai/NeuralNetwork.java index 79b6b3bd4..5ffea6da2 100644 --- a/ai/src/main/java/net/daporkchop/lib/ai/NeuralNetwork.java +++ b/ai/src/main/java/net/daporkchop/lib/ai/NeuralNetwork.java @@ -15,12 +15,47 @@ package net.daporkchop.lib.ai; +import lombok.NonNull; + /** * An {@link AI} based on a network of interconnected nodes. - * + *

* A neural network typically has a fixed number of inputs and outputs. * * @author DaPorkchop_ */ public interface NeuralNetwork extends AI { + /** + * Runs the network on the given input data, storing the output values in the given array. + * + * @param inputs an array containing the input values + * @param outputs an array that the output values will be stored in + * @throws IllegalArgumentException if either parameters are not exactly the correct size (see {@link #inputs()} and {@link #outputs()}, respectively) + */ + void compute(@NonNull double[] inputs, @NonNull double[] outputs) throws IllegalArgumentException; + + /** + * Runs the network on the given input data, storing the output values in the returned array. + *

+ * This method should be avoided in favor of {@link #compute(double[], double[])} if possible. + * + * @param inputs an array containing the input values + * @return an array containing the output values + * @throws IllegalArgumentException if the input array is not exactly the same size as {@link #inputs()} + */ + default double[] compute(@NonNull double[] inputs) throws IllegalArgumentException { + double[] outputs = new double[this.outputs()]; + this.compute(inputs, outputs); + return outputs; + } + + /** + * @return the network's input count + */ + int inputs(); + + /** + * @return the network's output count + */ + int outputs(); } diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/abst/AbstractNeuralNetwork.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/abst/AbstractNeuralNetwork.java new file mode 100644 index 000000000..db2bccd81 --- /dev/null +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/abst/AbstractNeuralNetwork.java @@ -0,0 +1,46 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.ai.alg.abst; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import net.daporkchop.lib.ai.NeuralNetwork; + +/** + * A basic implementation of {@link NeuralNetwork}. + * + * @author DaPorkchop_ + */ +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Accessors(fluent = true) +public abstract class AbstractNeuralNetwork implements NeuralNetwork { + protected int inputs = -1; + protected int outputs = -1; + + protected void validateParameters(@NonNull double[] inputs, @NonNull double[] outputs) throws IllegalArgumentException { + if (inputs.length != this.inputs) { + throw new IllegalArgumentException(String.format("Invalid input count! Expected %d, found %d", this.inputs, inputs.length)); + } else if (outputs.length != this.outputs) { + throw new IllegalArgumentException(String.format("Invalid output count! Expected %d, found %d", this.outputs, outputs.length)); + } + } +} diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEAT.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEAT.java index 63885bc51..53491b529 100644 --- a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEAT.java +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEAT.java @@ -30,6 +30,12 @@ public class NEAT implements MachineLearning { @Override public NEATTrainer beginTraining(@NonNull Evaluator evaluator, @NonNull NEATOptions options) { - return null; + if (options.inputs <= 0) { + throw new IllegalArgumentException("Number of inputs must be set!"); + } else if (options.outputs <= 0) { + throw new IllegalArgumentException("Number of outputs must be set!"); + } else { + return new NEATTrainer(evaluator, options, this); + } } } diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATNetwork.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATNetwork.java index 82d8ceb94..fdb3c7f8b 100644 --- a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATNetwork.java +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATNetwork.java @@ -17,9 +17,11 @@ import lombok.AccessLevel; import lombok.Getter; +import lombok.NonNull; import lombok.Setter; import lombok.experimental.Accessors; import net.daporkchop.lib.ai.NeuralNetwork; +import net.daporkchop.lib.ai.alg.abst.AbstractNeuralNetwork; /** * An implementation of {@link NeuralNetwork} for use in NEAT training. @@ -28,7 +30,37 @@ */ @Accessors(fluent = true, chain = true) @Getter -public class NEATNetwork implements NeuralNetwork { +public class NEATNetwork extends AbstractNeuralNetwork { + + + @Override + public void compute(@NonNull double[] inputs, @NonNull double[] outputs) throws IllegalArgumentException { + } + @Setter(AccessLevel.PACKAGE) protected double fitness = Double.NaN; + + @Accessors(fluent = true) + @Getter + public static abstract class BaseNode { + protected double weight; + protected int innovationNumber; //not quite sure what this is yet... + protected boolean enabled; + } + + public static abstract class DependantNode extends BaseNode { + + } + + public static final class InputNode extends BaseNode { + protected int inputIndex; + } + + public static final class HiddenNode extends DependantNode { + protected int cacheIndex; + } + + public static final class OutputNode extends DependantNode { + protected int outputIndex; + } } diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATOptions.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATOptions.java index 7247c3d30..cf52c9f84 100644 --- a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATOptions.java +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATOptions.java @@ -16,11 +16,14 @@ package net.daporkchop.lib.ai.alg.neat; import lombok.Getter; +import lombok.NonNull; import lombok.Setter; import lombok.experimental.Accessors; import net.daporkchop.lib.ai.NeuralNetwork; import net.daporkchop.lib.ai.alg.TrainingOptions; +import java.util.function.DoubleUnaryOperator; + /** * Options for training a neural network using the NEAT algorithm. * @@ -30,6 +33,9 @@ @Getter @Setter public class NEATOptions extends TrainingOptions { + protected int inputs = 0; + protected int outputs = 0; + protected double geneDisableChance = 0.75D; protected double mutationWeightChance = 0.8D; protected double mutationWeightRandomChance = 0.1D; @@ -43,4 +49,7 @@ public class NEATOptions extends TrainingOptions { protected double generationEliminationPercentage = 0.9D; protected double breedCrossChance = 0.75D; protected double mutationWeightChanceRandomRange = 5.0D; + + @NonNull + protected DoubleUnaryOperator activationFunction = x -> 1.0D / (1.0D + Math.exp(-4.9D * x)); //Mythan's "CustomizedSigmoidActivation" } diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATTrainer.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATTrainer.java index fad9a000c..67f2a368c 100644 --- a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATTrainer.java +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATTrainer.java @@ -15,6 +15,7 @@ package net.daporkchop.lib.ai.alg.neat; +import lombok.AccessLevel; import lombok.Getter; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -31,14 +32,14 @@ * @see NEAT */ @Accessors(chain = true, fluent = true) -@RequiredArgsConstructor +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) public class NEATTrainer implements Trainer { @NonNull @Getter - protected final NEATOptions options; + protected final Evaluator evaluator; @NonNull @Getter - protected final Evaluator evaluator; + protected final NEATOptions options; @NonNull @Getter protected final NEAT algorithm; diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/package-info.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/package-info.java new file mode 100644 index 000000000..d7328f4de --- /dev/null +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/package-info.java @@ -0,0 +1,21 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/** + * This implementation of NEAT is based heavily off Mythan: https://github.com/SanderGielisse/Mythan + * + * @author DaPorkchop_ + */ +package net.daporkchop.lib.ai.alg.neat; \ No newline at end of file From b37fe84852fa2e06e8fa4df5ec0ec3b61d05122b Mon Sep 17 00:00:00 2001 From: DaMatrix Date: Tue, 20 Aug 2019 12:01:24 +0200 Subject: [PATCH 4/6] yuck --- .../ai/alg/abst/AbstractNeuralNetwork.java | 5 +- .../lib/ai/alg/neat/NEATNetwork.java | 112 ++++++++++++++++-- 2 files changed, 102 insertions(+), 15 deletions(-) diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/abst/AbstractNeuralNetwork.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/abst/AbstractNeuralNetwork.java index db2bccd81..a07e6c3d0 100644 --- a/ai/src/main/java/net/daporkchop/lib/ai/alg/abst/AbstractNeuralNetwork.java +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/abst/AbstractNeuralNetwork.java @@ -29,12 +29,11 @@ * @author DaPorkchop_ */ @AllArgsConstructor -@NoArgsConstructor @Getter @Accessors(fluent = true) public abstract class AbstractNeuralNetwork implements NeuralNetwork { - protected int inputs = -1; - protected int outputs = -1; + protected final int inputs; + protected final int outputs; protected void validateParameters(@NonNull double[] inputs, @NonNull double[] outputs) throws IllegalArgumentException { if (inputs.length != this.inputs) { diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATNetwork.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATNetwork.java index fdb3c7f8b..a7576bf83 100644 --- a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATNetwork.java +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATNetwork.java @@ -18,49 +18,137 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.NonNull; +import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; import net.daporkchop.lib.ai.NeuralNetwork; import net.daporkchop.lib.ai.alg.abst.AbstractNeuralNetwork; +import java.util.function.DoubleUnaryOperator; +import java.util.function.UnaryOperator; + /** * An implementation of {@link NeuralNetwork} for use in NEAT training. * + * This is not a particularly efficient implementation, it needs a lot of optimization both in terms of memory and + * CPU resources, and doesn't allow multithreaded access, but it's basically just a proof-of-concept. + * * @author DaPorkchop_ */ -@Accessors(fluent = true, chain = true) +@RequiredArgsConstructor @Getter +@Accessors(fluent = true, chain = true) public class NEATNetwork extends AbstractNeuralNetwork { + protected final InputNode[] inputNodes; + protected final OutputNode[] outputNodes; + protected final HiddenNode[] hiddenNodes; + protected final DoubleUnaryOperator activationFunction; + public NEATNetwork(@NonNull InputNode[] inputNodes, @NonNull OutputNode[] outputNodes, @NonNull HiddenNode[] hiddenNodes, @NonNull DoubleUnaryOperator activationFunction) { + super(inputNodes.length, outputNodes.length); + + this.inputNodes = inputNodes; + this.outputNodes = outputNodes; + this.hiddenNodes = hiddenNodes; + this.activationFunction = activationFunction; + } @Override - public void compute(@NonNull double[] inputs, @NonNull double[] outputs) throws IllegalArgumentException { + public synchronized void compute(@NonNull double[] inputs, @NonNull double[] outputs) throws IllegalArgumentException { + this.validateParameters(inputs, outputs); + + //copy values into input nodes + for (int i = this.inputs - 1; i >= 0; i--) { + this.inputNodes[i].value = inputs[i]; + } + + //reset hidden nodes + for (int i = this.hiddenNodes.length - 1; i >= 0; i--) { + this.hiddenNodes[i].reset(); + } + + //compute output values + for (int i = this.outputs - 1; i >= 0; i--) { + outputs[i] = this.outputNodes[i].getValue(this.activationFunction); + } } @Setter(AccessLevel.PACKAGE) protected double fitness = Double.NaN; - @Accessors(fluent = true) + @RequiredArgsConstructor @Getter - public static abstract class BaseNode { - protected double weight; - protected int innovationNumber; //not quite sure what this is yet... - protected boolean enabled; + @Accessors(fluent = true, chain = true) + public static final class Connection { + @NonNull + private final Node from; + @NonNull + private final DependantNode to; + + private final double weight; + + private final int innovationNumber; + private final boolean enabled; } - public static abstract class DependantNode extends BaseNode { + @Getter + @Accessors(fluent = true) + public static abstract class Node { + @Getter(AccessLevel.NONE) + protected double value = Double.NaN; + + public double getValue(DoubleUnaryOperator activationFunction) { + return this.value; + } + + public final void reset() { + this.value = Double.NaN; + } + } + + @RequiredArgsConstructor + @Getter + @Accessors(fluent = true) + public static abstract class DependantNode extends Node { + @NonNull + protected final Connection[] connections; + + public final double getValue(DoubleUnaryOperator activationFunction) { + double value = this.value; + if (value == Double.NaN) { + value = 0.0d; + + for (int i = this.connections.length; i >= 0; i--) { + Connection connection = this.connections[i]; + if (connection.enabled) { + value += connection.from.getValue(activationFunction) * connection.weight; + } + } + this.value = value = activationFunction.applyAsDouble(value); + } + return value; + } } - public static final class InputNode extends BaseNode { - protected int inputIndex; + @Getter + @Accessors(fluent = true, chain = true) + public static final class InputNode extends Node { } + @Getter + @Accessors(fluent = true, chain = true) public static final class HiddenNode extends DependantNode { - protected int cacheIndex; + public HiddenNode(Connection[] connections) { + super(connections); + } } + @Getter + @Accessors(fluent = true, chain = true) public static final class OutputNode extends DependantNode { - protected int outputIndex; + public OutputNode(Connection[] connections) { + super(connections); + } } } From 997c593e3e7fd9f54f3be6f9ae9f2455344b7ef2 Mon Sep 17 00:00:00 2001 From: DaMatrix Date: Tue, 20 Aug 2019 12:37:17 +0200 Subject: [PATCH 5/6] ok i won't bother myself with NEAT, let's make something myself this is a horrible idea, i don't know a thing about machine learning --- .../ai/alg/{neat/NEAT.java => pgen/PGen.java} | 13 ++--- .../PGenNetwork.java} | 10 ++-- .../PGenOptions.java} | 33 ++++++----- .../PGenTrainer.java} | 17 +++--- .../lib/ai/alg/pgen/evolution/Population.java | 48 ++++++++++++++++ .../lib/ai/alg/pgen/evolution/Species.java | 56 +++++++++++++++++++ .../evolution/Specimen.java} | 22 +++++++- 7 files changed, 159 insertions(+), 40 deletions(-) rename ai/src/main/java/net/daporkchop/lib/ai/alg/{neat/NEAT.java => pgen/PGen.java} (78%) rename ai/src/main/java/net/daporkchop/lib/ai/alg/{neat/NEATNetwork.java => pgen/PGenNetwork.java} (94%) rename ai/src/main/java/net/daporkchop/lib/ai/alg/{neat/NEATOptions.java => pgen/PGenOptions.java} (67%) rename ai/src/main/java/net/daporkchop/lib/ai/alg/{neat/NEATTrainer.java => pgen/PGenTrainer.java} (85%) create mode 100644 ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/Population.java create mode 100644 ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/Species.java rename ai/src/main/java/net/daporkchop/lib/ai/alg/{neat/package-info.java => pgen/evolution/Specimen.java} (72%) diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEAT.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/PGen.java similarity index 78% rename from ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEAT.java rename to ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/PGen.java index 53491b529..414d6768d 100644 --- a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEAT.java +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/PGen.java @@ -13,29 +13,28 @@ * */ -package net.daporkchop.lib.ai.alg.neat; +package net.daporkchop.lib.ai.alg.pgen; import lombok.NonNull; import net.daporkchop.lib.ai.Evaluator; import net.daporkchop.lib.ai.NeuralNetwork; -import net.daporkchop.lib.ai.Trainer; import net.daporkchop.lib.ai.alg.MachineLearning; /** - * Implementation of the Neuroevolution of Augmenting Topologies (NEAT) algorithm, made by Kenneth O. Stanley - * and Risto Miikkulainen (see http://nn.cs.utexas.edu/downloads/papers/stanley.ec02.pdf). + * Implementation of the PGen (PorkGenetic) neural network training algorithm, kinda inspired by NEAT but not really + * because I don't really feel like actually reading up on it (i'm lazy lol) * * @author DaPorkchop_ */ -public class NEAT implements MachineLearning { +public class PGen implements MachineLearning { @Override - public NEATTrainer beginTraining(@NonNull Evaluator evaluator, @NonNull NEATOptions options) { + public PGenTrainer beginTraining(@NonNull Evaluator evaluator, @NonNull PGenOptions options) { if (options.inputs <= 0) { throw new IllegalArgumentException("Number of inputs must be set!"); } else if (options.outputs <= 0) { throw new IllegalArgumentException("Number of outputs must be set!"); } else { - return new NEATTrainer(evaluator, options, this); + return new PGenTrainer(evaluator, options, this); } } } diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATNetwork.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/PGenNetwork.java similarity index 94% rename from ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATNetwork.java rename to ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/PGenNetwork.java index a7576bf83..281b961da 100644 --- a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATNetwork.java +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/PGenNetwork.java @@ -13,7 +13,7 @@ * */ -package net.daporkchop.lib.ai.alg.neat; +package net.daporkchop.lib.ai.alg.pgen; import lombok.AccessLevel; import lombok.Getter; @@ -25,26 +25,24 @@ import net.daporkchop.lib.ai.alg.abst.AbstractNeuralNetwork; import java.util.function.DoubleUnaryOperator; -import java.util.function.UnaryOperator; /** - * An implementation of {@link NeuralNetwork} for use in NEAT training. + * An implementation of {@link NeuralNetwork} for use in PGen training. * * This is not a particularly efficient implementation, it needs a lot of optimization both in terms of memory and * CPU resources, and doesn't allow multithreaded access, but it's basically just a proof-of-concept. * * @author DaPorkchop_ */ -@RequiredArgsConstructor @Getter @Accessors(fluent = true, chain = true) -public class NEATNetwork extends AbstractNeuralNetwork { +public class PGenNetwork extends AbstractNeuralNetwork { protected final InputNode[] inputNodes; protected final OutputNode[] outputNodes; protected final HiddenNode[] hiddenNodes; protected final DoubleUnaryOperator activationFunction; - public NEATNetwork(@NonNull InputNode[] inputNodes, @NonNull OutputNode[] outputNodes, @NonNull HiddenNode[] hiddenNodes, @NonNull DoubleUnaryOperator activationFunction) { + public PGenNetwork(@NonNull InputNode[] inputNodes, @NonNull OutputNode[] outputNodes, @NonNull HiddenNode[] hiddenNodes, @NonNull DoubleUnaryOperator activationFunction) { super(inputNodes.length, outputNodes.length); this.inputNodes = inputNodes; diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATOptions.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/PGenOptions.java similarity index 67% rename from ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATOptions.java rename to ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/PGenOptions.java index cf52c9f84..790c39049 100644 --- a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATOptions.java +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/PGenOptions.java @@ -13,42 +13,41 @@ * */ -package net.daporkchop.lib.ai.alg.neat; +package net.daporkchop.lib.ai.alg.pgen; import lombok.Getter; import lombok.NonNull; import lombok.Setter; import lombok.experimental.Accessors; -import net.daporkchop.lib.ai.NeuralNetwork; import net.daporkchop.lib.ai.alg.TrainingOptions; import java.util.function.DoubleUnaryOperator; /** - * Options for training a neural network using the NEAT algorithm. + * Options for training a neural network using the PGen algorithm. * * @author DaPorkchop_ */ @Accessors(chain = true, fluent = true) @Getter @Setter -public class NEATOptions extends TrainingOptions { +public class PGenOptions extends TrainingOptions { protected int inputs = 0; protected int outputs = 0; - protected double geneDisableChance = 0.75D; - protected double mutationWeightChance = 0.8D; - protected double mutationWeightRandomChance = 0.1D; - protected double mutationWeightMaxDisturbance = 0.25D; - protected double mutationNewConnectionChance = 0.05D; - protected double mutationNewNodeChance = 0.03D; - protected double distanceExcessWeight = 1.0D; - protected double distanceDisjointWeight = 1.0D; - protected double distanceWeightsWeight = 0.4D; - protected double speciesCompatibilityDistance = 0.8D; - protected double generationEliminationPercentage = 0.9D; - protected double breedCrossChance = 0.75D; - protected double mutationWeightChanceRandomRange = 5.0D; + //these default values are somewhat very arbitrary + protected double mutationNewNodeChance = 0.1d; + protected double mutationNewConnectionChance = 0.2d; + protected double mutationDeleteConnectionChance = 0.1d; + protected double mutationWeightMaxDeviation = 0.06d; + protected double speciesCrossBreedChance = 0.1d; + + protected int generationSurvivors = 64; + + protected int maxSpeciesSize = 1024; + protected int maxSpeciesCount = 64; + + protected int baseSpeciesSize = 512; @NonNull protected DoubleUnaryOperator activationFunction = x -> 1.0D / (1.0D + Math.exp(-4.9D * x)); //Mythan's "CustomizedSigmoidActivation" diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATTrainer.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/PGenTrainer.java similarity index 85% rename from ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATTrainer.java rename to ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/PGenTrainer.java index 67f2a368c..06ad37af7 100644 --- a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/NEATTrainer.java +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/PGenTrainer.java @@ -13,7 +13,7 @@ * */ -package net.daporkchop.lib.ai.alg.neat; +package net.daporkchop.lib.ai.alg.pgen; import lombok.AccessLevel; import lombok.Getter; @@ -23,29 +23,30 @@ import net.daporkchop.lib.ai.Evaluator; import net.daporkchop.lib.ai.NeuralNetwork; import net.daporkchop.lib.ai.Trainer; -import net.daporkchop.lib.ai.alg.MachineLearning; /** - * Implementation of {@link Trainer} for the NEAT algorithm. + * Implementation of {@link Trainer} for the PGen algorithm. * * @author DaPorkchop_ - * @see NEAT + * @see PGen */ @Accessors(chain = true, fluent = true) @RequiredArgsConstructor(access = AccessLevel.PACKAGE) -public class NEATTrainer implements Trainer { +public class PGenTrainer implements Trainer { @NonNull @Getter protected final Evaluator evaluator; @NonNull @Getter - protected final NEATOptions options; + protected final PGenOptions options; @NonNull @Getter - protected final NEAT algorithm; + protected final PGen algorithm; + + protected final @Override - public NEATNetwork fittestSpecimen() { + public PGenNetwork fittestSpecimen() { return null; } diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/Population.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/Population.java new file mode 100644 index 000000000..8d3080640 --- /dev/null +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/Population.java @@ -0,0 +1,48 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.ai.alg.pgen.evolution; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.experimental.Accessors; + +/** + * A sample of all members of a single species during a specific generation. + * + * @author DaPorkchop_ + */ +@AllArgsConstructor +@RequiredArgsConstructor +@Getter +@Accessors(fluent = true) +public class Population { + @NonNull + protected Specimen[] members; + @NonNull + protected final Species species; + protected final int generation; + + /** + * @return the population that came before this one, or {@code null} if this is the base population + */ + public Population previous() { + return this.generation > 0 ? this.species.populations.get(this.generation - 1) : null; + } +} diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/Species.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/Species.java new file mode 100644 index 000000000..095612e0b --- /dev/null +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/Species.java @@ -0,0 +1,56 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.ai.alg.pgen.evolution; + +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; + +import java.util.ArrayList; +import java.util.List; + +/** + * A group of specimens with unique traits inherited over multiple generations. + * + * A species will split in half once the previous species' survivors of a single generation are considered too "different" + * from one another to produce offspring. + * + * However, there is still a low chance that survivors will breed with a member of another distinct species, bringing + * traits from a totally different gene pool into the species (assuming the offspring survives). + * + * @author DaPorkchop_ + */ +@Getter +@Accessors(fluent = true) +public class Species { + protected final List populations; + + protected final int branchedOnGeneration; + protected int generation; + + public Species() { + this.populations = new ArrayList<>(); + + this.branchedOnGeneration = this.generation = 0; + } + + public Species(@NonNull Species parent) { + this.populations = new ArrayList<>(parent.populations); + + this.branchedOnGeneration = this.generation = parent.generation; + } +} diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/package-info.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/Specimen.java similarity index 72% rename from ai/src/main/java/net/daporkchop/lib/ai/alg/neat/package-info.java rename to ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/Specimen.java index d7328f4de..3ea248cd1 100644 --- a/ai/src/main/java/net/daporkchop/lib/ai/alg/neat/package-info.java +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/Specimen.java @@ -13,9 +13,27 @@ * */ +package net.daporkchop.lib.ai.alg.pgen.evolution; + +import lombok.Getter; +import lombok.NonNull; +import lombok.experimental.Accessors; +import net.daporkchop.lib.ai.NeuralNetwork; + /** - * This implementation of NEAT is based heavily off Mythan: https://github.com/SanderGielisse/Mythan + * A single member of a species. * * @author DaPorkchop_ */ -package net.daporkchop.lib.ai.alg.neat; \ No newline at end of file +@Accessors(fluent = true) +public class Specimen implements NeuralNetwork { + @Getter + protected final Species species; + @Getter + protected final Population population; + + public Specimen(@NonNull Population population) { + this.species = population.species; + this.population = population; + } +} From e25b73cfad0943369a6c6926ef94b9e48ab5939a Mon Sep 17 00:00:00 2001 From: DaMatrix Date: Tue, 20 Aug 2019 13:25:11 +0200 Subject: [PATCH 6/6] gtg now lol --- .../java/net/daporkchop/lib/ai/Trainer.java | 5 -- .../ai/alg/abst/AbstractNeuralNetwork.java | 5 +- .../net/daporkchop/lib/ai/alg/pgen/PGen.java | 3 +- .../lib/ai/alg/pgen/PGenOptions.java | 3 +- .../alg/pgen/{ => evolution}/PGenTrainer.java | 41 +++++++-- .../lib/ai/alg/pgen/evolution/Population.java | 8 +- .../lib/ai/alg/pgen/evolution/Species.java | 22 +++-- .../lib/ai/alg/pgen/evolution/Specimen.java | 83 ++++++++++++++++++- 8 files changed, 144 insertions(+), 26 deletions(-) rename ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/{ => evolution}/PGenTrainer.java (61%) diff --git a/ai/src/main/java/net/daporkchop/lib/ai/Trainer.java b/ai/src/main/java/net/daporkchop/lib/ai/Trainer.java index 0d113f110..18cb30285 100644 --- a/ai/src/main/java/net/daporkchop/lib/ai/Trainer.java +++ b/ai/src/main/java/net/daporkchop/lib/ai/Trainer.java @@ -35,11 +35,6 @@ public interface Trainer */ O options(); - /** - * @return the machine learning algorithm employed by this trainer - */ - MachineLearning algorithm(); - /** * @return the {@link Evaluator} instance being used by this trainer */ diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/abst/AbstractNeuralNetwork.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/abst/AbstractNeuralNetwork.java index a07e6c3d0..c60bee8ca 100644 --- a/ai/src/main/java/net/daporkchop/lib/ai/alg/abst/AbstractNeuralNetwork.java +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/abst/AbstractNeuralNetwork.java @@ -22,19 +22,22 @@ import lombok.RequiredArgsConstructor; import lombok.experimental.Accessors; import net.daporkchop.lib.ai.NeuralNetwork; +import net.daporkchop.lib.ai.alg.TrainingOptions; /** * A basic implementation of {@link NeuralNetwork}. * * @author DaPorkchop_ */ -@AllArgsConstructor +@RequiredArgsConstructor @Getter @Accessors(fluent = true) public abstract class AbstractNeuralNetwork implements NeuralNetwork { protected final int inputs; protected final int outputs; + protected double fitness = Double.NaN; + protected void validateParameters(@NonNull double[] inputs, @NonNull double[] outputs) throws IllegalArgumentException { if (inputs.length != this.inputs) { throw new IllegalArgumentException(String.format("Invalid input count! Expected %d, found %d", this.inputs, inputs.length)); diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/PGen.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/PGen.java index 414d6768d..9444be4c1 100644 --- a/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/PGen.java +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/PGen.java @@ -19,6 +19,7 @@ import net.daporkchop.lib.ai.Evaluator; import net.daporkchop.lib.ai.NeuralNetwork; import net.daporkchop.lib.ai.alg.MachineLearning; +import net.daporkchop.lib.ai.alg.pgen.evolution.PGenTrainer; /** * Implementation of the PGen (PorkGenetic) neural network training algorithm, kinda inspired by NEAT but not really @@ -34,7 +35,7 @@ public PGenTrainer beginTraining(@NonNull Evaluator evaluator, @N } else if (options.outputs <= 0) { throw new IllegalArgumentException("Number of outputs must be set!"); } else { - return new PGenTrainer(evaluator, options, this); + return new PGenTrainer(evaluator, options); } } } diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/PGenOptions.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/PGenOptions.java index 790c39049..6cb018818 100644 --- a/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/PGenOptions.java +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/PGenOptions.java @@ -36,8 +36,7 @@ public class PGenOptions extends TrainingOptions { protected int outputs = 0; //these default values are somewhat very arbitrary - protected double mutationNewNodeChance = 0.1d; - protected double mutationNewConnectionChance = 0.2d; + protected double mutationNewConnectionChance = 0.3d; protected double mutationDeleteConnectionChance = 0.1d; protected double mutationWeightMaxDeviation = 0.06d; protected double speciesCrossBreedChance = 0.1d; diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/PGenTrainer.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/PGenTrainer.java similarity index 61% rename from ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/PGenTrainer.java rename to ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/PGenTrainer.java index 06ad37af7..06816ec77 100644 --- a/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/PGenTrainer.java +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/PGenTrainer.java @@ -13,7 +13,7 @@ * */ -package net.daporkchop.lib.ai.alg.pgen; +package net.daporkchop.lib.ai.alg.pgen.evolution; import lombok.AccessLevel; import lombok.Getter; @@ -23,6 +23,15 @@ import net.daporkchop.lib.ai.Evaluator; import net.daporkchop.lib.ai.NeuralNetwork; import net.daporkchop.lib.ai.Trainer; +import net.daporkchop.lib.ai.alg.pgen.PGen; +import net.daporkchop.lib.ai.alg.pgen.PGenNetwork; +import net.daporkchop.lib.ai.alg.pgen.PGenOptions; +import net.daporkchop.lib.ai.alg.pgen.evolution.Species; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; /** * Implementation of {@link Trainer} for the PGen algorithm. @@ -31,19 +40,22 @@ * @see PGen */ @Accessors(chain = true, fluent = true) -@RequiredArgsConstructor(access = AccessLevel.PACKAGE) public class PGenTrainer implements Trainer { - @NonNull @Getter protected final Evaluator evaluator; - @NonNull @Getter protected final PGenOptions options; - @NonNull - @Getter - protected final PGen algorithm; - protected final + protected final List species = new ArrayList<>(); + protected final int[] activeSpecies; + protected int generation = 0; + + public PGenTrainer(@NonNull Evaluator evaluator, @NonNull PGenOptions options) { + this.evaluator = evaluator; + this.options = options; + + Arrays.fill(this.activeSpecies = new int[options.maxSpeciesCount()], -1); + } @Override public PGenNetwork fittestSpecimen() { @@ -52,5 +64,18 @@ public PGenNetwork fittestSpecimen() { @Override public synchronized void trainToFitness(double fitness) { + while (this.fittestSpecimen() == null || this.fittestSpecimen().fitness() < fitness) { + } + } + + protected void buildNextGeneration() { + ThreadLocalRandom random = ThreadLocalRandom.current(); + if (this.generation > 0) { + //build generation based on previous one + } else { + //training has not started yet, generate random base population + Species species = new Species(this); + Population population = new Population(new Specimen[this.options.baseSpeciesSize()], species); + } } } diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/Population.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/Population.java index 8d3080640..a8a5bef8f 100644 --- a/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/Population.java +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/Population.java @@ -28,8 +28,6 @@ * * @author DaPorkchop_ */ -@AllArgsConstructor -@RequiredArgsConstructor @Getter @Accessors(fluent = true) public class Population { @@ -39,6 +37,12 @@ public class Population { protected final Species species; protected final int generation; + public Population(@NonNull Specimen[] members, @NonNull Species species) { + this.members = members; + this.species = species; + this.generation = species.generation; + } + /** * @return the population that came before this one, or {@code null} if this is the base population */ diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/Species.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/Species.java index 095612e0b..e5fb663be 100644 --- a/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/Species.java +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/Species.java @@ -17,7 +17,6 @@ import lombok.Getter; import lombok.NonNull; -import lombok.RequiredArgsConstructor; import lombok.experimental.Accessors; import java.util.ArrayList; @@ -26,8 +25,8 @@ /** * A group of specimens with unique traits inherited over multiple generations. * - * A species will split in half once the previous species' survivors of a single generation are considered too "different" - * from one another to produce offspring. + * A species will split off from the base once the base species' survivors of a single generation are considered too + * "different" from one another to produce offspring. * * However, there is still a low chance that survivors will breed with a member of another distinct species, bringing * traits from a totally different gene pool into the species (assuming the offspring survives). @@ -37,20 +36,33 @@ @Getter @Accessors(fluent = true) public class Species { + protected final PGenTrainer trainer; protected final List populations; + protected final List children = new ArrayList<>(); + + protected final int id; + protected final int parent; protected final int branchedOnGeneration; protected int generation; - public Species() { + public Species(@NonNull PGenTrainer trainer) { + this.trainer = trainer; this.populations = new ArrayList<>(); + this.id = 0; + this.parent = -1; + trainer.species.add(this); this.branchedOnGeneration = this.generation = 0; } - public Species(@NonNull Species parent) { + public Species(@NonNull PGenTrainer trainer, @NonNull Species parent) { + this.trainer = trainer; this.populations = new ArrayList<>(parent.populations); + this.id = trainer.species.size(); + this.parent = parent.id; + trainer.species.add(this); this.branchedOnGeneration = this.generation = parent.generation; } } diff --git a/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/Specimen.java b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/Specimen.java index 3ea248cd1..4126d04fc 100644 --- a/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/Specimen.java +++ b/ai/src/main/java/net/daporkchop/lib/ai/alg/pgen/evolution/Specimen.java @@ -17,23 +17,102 @@ import lombok.Getter; import lombok.NonNull; +import lombok.RequiredArgsConstructor; import lombok.experimental.Accessors; -import net.daporkchop.lib.ai.NeuralNetwork; +import net.daporkchop.lib.ai.alg.abst.AbstractNeuralNetwork; + +import java.util.LinkedList; +import java.util.List; +import java.util.function.DoubleUnaryOperator; /** * A single member of a species. + *

+ * This is a rather inefficient implementation by itself, and is really only intended to be used directly by the + * trainer for evaluation purposes. A specialized implementation without all the trait information is provided to + * the evaluator to speed up execution. * * @author DaPorkchop_ */ @Accessors(fluent = true) -public class Specimen implements NeuralNetwork { +public class Specimen extends AbstractNeuralNetwork { + protected static final int INPUT_MASK = 0x80000000; + protected static final int OUTPUT_MASK = 0x40000000; + protected static final int NONE_MASK = ~(INPUT_MASK | OUTPUT_MASK); + @Getter protected final Species species; @Getter protected final Population population; + protected final DoubleUnaryOperator activationFunction; + + protected final Output public Specimen(@NonNull Population population) { + super(population.species.trainer.options().inputs(), population.species.trainer.options().outputs()); + this.species = population.species; this.population = population; + this.activationFunction = this.species.trainer.options().activationFunction(); + } + + @Override + public void compute(@NonNull double[] inputs, @NonNull double[] outputs) throws IllegalArgumentException { + } + + protected interface Node { + int id(); + + double value(Specimen specimen, double[] inputs); + } + + @RequiredArgsConstructor + @Getter + @Accessors(fluent = true) + protected static final class Input implements Node { + protected final int sourceIndex; + + @Override + public int id() { + return this.sourceIndex | INPUT_MASK; + } + + @Override + public double value(Specimen specimen, double[] inputs) { + return inputs[this.sourceIndex]; + } + } + + @Getter + @Accessors(fluent = true) + protected static abstract class ConsumerNode implements Node { + protected final List sources = new LinkedList<>(); + + @Override + public double value(Specimen specimen, double[] inputs) { + double val = 0.0d; + for (Node node : this.sources) { + val += node.value(specimen, inputs); + } + return specimen.activationFunction.applyAsDouble(val); + } + } + + @Getter + @Accessors(fluent = true) + protected static final class HiddenNode extends ConsumerNode { + protected double weight; + protected final List sources = new LinkedList<>(); + protected Node dst; + protected int id; + + @Override + public double value(Specimen specimen, double[] inputs) { + return super.value(specimen, inputs) * this.weight; + } + } + + protected static final class OutputNode extends ConsumerNode { + } }