diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 6b98f92c5..a2aa807af 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -15,6 +15,8 @@ install(TARGETS solidity-upgrade DESTINATION "${CMAKE_INSTALL_BINDIR}") add_executable(yul-phaser yulPhaser/main.cpp + yulPhaser/Population.h + yulPhaser/Population.cpp yulPhaser/Chromosome.h yulPhaser/Chromosome.cpp yulPhaser/Program.h diff --git a/tools/yulPhaser/Population.cpp b/tools/yulPhaser/Population.cpp new file mode 100644 index 000000000..a5a65fa7f --- /dev/null +++ b/tools/yulPhaser/Population.cpp @@ -0,0 +1,137 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include +#include +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::phaser; + +namespace solidity::phaser +{ + +ostream& operator<<(ostream& _stream, Individual const& _individual); +ostream& operator<<(ostream& _stream, Population const& _population); + +} + +ostream& phaser::operator<<(ostream& _stream, Individual const& _individual) +{ + _stream << "Fitness: "; + if (_individual.fitness.has_value()) + _stream << _individual.fitness.value(); + else + _stream << ""; + _stream << ", optimisations: " << _individual.chromosome; + + return _stream; +} + +Population::Population(string const& _sourcePath, vector const& _chromosomes): + m_sourcePath{_sourcePath} +{ + for (auto const& chromosome: _chromosomes) + m_individuals.push_back({chromosome}); +} + +Population Population::makeRandom(string const& _sourcePath, size_t _size) +{ + vector individuals; + for (size_t i = 0; i < _size; ++i) + individuals.push_back({Chromosome::makeRandom(randomChromosomeLength())}); + + return Population(_sourcePath, individuals); +} + +size_t Population::measureFitness(Chromosome const& _chromosome, string const& _sourcePath) +{ + auto program = Program::load(_sourcePath); + program.optimise(_chromosome.optimisationSteps()); + return program.codeSize(); +} + +void Population::run(optional _numRounds, ostream& _outputStream) +{ + doEvaluation(); + for (size_t round = 0; !_numRounds.has_value() || round < _numRounds.value(); ++round) + { + doMutation(); + doSelection(); + doEvaluation(); + + _outputStream << "---------- ROUND " << round << " ----------" << endl; + _outputStream << *this; + } +} + +ostream& phaser::operator<<(ostream& _stream, Population const& _population) +{ + _stream << "Source: " << _population.m_sourcePath << endl; + + auto individual = _population.m_individuals.begin(); + for (; individual != _population.m_individuals.end(); ++individual) + _stream << *individual << endl; + + return _stream; +} + +void Population::doMutation() +{ + // TODO: Implement mutation and crossover +} + +void Population::doEvaluation() +{ + for (auto& individual: m_individuals) + if (!individual.fitness.has_value()) + individual.fitness = measureFitness(individual.chromosome, m_sourcePath); +} + +void Population::doSelection() +{ + assert(all_of(m_individuals.begin(), m_individuals.end(), [](auto& i){ return i.fitness.has_value(); })); + + sort( + m_individuals.begin(), + m_individuals.end(), + [](auto const& a, auto const& b){ return a.fitness.value() < b.fitness.value(); } + ); + + randomizeWorstChromosomes(m_individuals, m_individuals.size() / 2); +} + +void Population::randomizeWorstChromosomes( + vector& _individuals, + size_t _count +) +{ + assert(_individuals.size() >= _count); + // ASSUMPTION: _individuals is sorted in ascending order + + auto individual = _individuals.begin() + (_individuals.size() - _count); + for (; individual != _individuals.end(); ++individual) + { + *individual = {Chromosome::makeRandom(randomChromosomeLength())}; + } +} diff --git a/tools/yulPhaser/Population.h b/tools/yulPhaser/Population.h new file mode 100644 index 000000000..3c5596b03 --- /dev/null +++ b/tools/yulPhaser/Population.h @@ -0,0 +1,87 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#pragma once + +#include +#include + +#include +#include +#include + +namespace solidity::phaser +{ + +/** + * Information describing the state of an individual member of the population during the course + * of the genetic algorithm. + */ +struct Individual +{ + Chromosome chromosome; + std::optional fitness = std::nullopt; + + friend std::ostream& operator<<(std::ostream& _stream, Individual const& _individual); +}; + +/** + * Represents a changing set of individuals undergoing a genetic algorithm. + * Each round of the algorithm involves mutating existing individuals, evaluating their fitness + * and selecting the best ones for the next round. + * + * An individual is a sequence of optimiser steps represented by a @a Chromosome instance. The whole + * population is associated with a fixed Yul program. By loading the source code into a @a Program + * instance the class can compute fitness of the individual. + */ +class Population +{ +public: + static constexpr size_t MaxChromosomeLength = 30; + + explicit Population(std::string const& _sourcePath, std::vector const& _chromosomes = {}); + static Population makeRandom(std::string const& _sourcePath, size_t _size); + + void run(std::optional _numRounds, std::ostream& _outputStream); + + std::vector const& individuals() const { return m_individuals; } + + static size_t randomChromosomeLength() { return binomialRandomInt(MaxChromosomeLength, 0.5); } + static size_t measureFitness(Chromosome const& _chromosome, std::string const& _sourcePath); + + friend std::ostream& operator<<(std::ostream& _stream, Population const& _population); + +private: + explicit Population(std::string const& _sourcePath, std::vector _individuals = {}): + m_sourcePath{_sourcePath}, + m_individuals{std::move(_individuals)} {} + + void doMutation(); + void doEvaluation(); + void doSelection(); + + static void randomizeWorstChromosomes( + std::vector& _individuals, + size_t _count + ); + + std::string m_sourcePath; + + std::vector m_individuals; +}; + +} diff --git a/tools/yulPhaser/main.cpp b/tools/yulPhaser/main.cpp index 8bf32b135..280262d7c 100644 --- a/tools/yulPhaser/main.cpp +++ b/tools/yulPhaser/main.cpp @@ -15,9 +15,8 @@ along with solidity. If not, see . */ -#include #include -#include +#include #include @@ -40,8 +39,8 @@ struct CommandLineParsingResult void runAlgorithm(string const& _sourcePath) { - Program::load(_sourcePath).optimize(Chromosome::makeRandom(15).asSequence()); - cout << "Program load and optimization successful." << endl; + auto population = Population::makeRandom(_sourcePath, 10); + population.run(nullopt, cout); } CommandLineParsingResult parseCommandLine(int argc, char** argv)