diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3b2fef6f0..70d547a0b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -144,6 +144,7 @@ set(yul_phaser_sources yulPhaser/CommonTest.cpp yulPhaser/Chromosome.cpp yulPhaser/FitnessMetrics.cpp + yulPhaser/GeneticAlgorithms.cpp yulPhaser/Population.cpp yulPhaser/Program.cpp yulPhaser/SimulationRNG.cpp @@ -153,6 +154,7 @@ set(yul_phaser_sources # unnecessary duplication. Create a library or find a way to reuse the list in both places. ../tools/yulPhaser/Chromosome.cpp ../tools/yulPhaser/FitnessMetrics.cpp + ../tools/yulPhaser/GeneticAlgorithms.cpp ../tools/yulPhaser/Population.cpp ../tools/yulPhaser/Program.cpp ../tools/yulPhaser/SimulationRNG.cpp diff --git a/test/yulPhaser/GeneticAlgorithms.cpp b/test/yulPhaser/GeneticAlgorithms.cpp new file mode 100644 index 000000000..3528f2be0 --- /dev/null +++ b/test/yulPhaser/GeneticAlgorithms.cpp @@ -0,0 +1,96 @@ +/* + 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 + +#include + +#include +#include + +#include + +using namespace std; +using namespace boost::unit_test::framework; +using namespace boost::test_tools; +using namespace solidity::langutil; +using namespace solidity::util; + +namespace solidity::phaser::test +{ + +class DummyAlgorithm: public GeneticAlgorithm +{ +public: + using GeneticAlgorithm::GeneticAlgorithm; + void runNextRound() override { ++m_currentRound; } + + size_t m_currentRound = 0; +}; + +class GeneticAlgorithmFixture +{ +protected: + shared_ptr m_fitnessMetric = make_shared(); + output_test_stream m_output; +}; + +BOOST_AUTO_TEST_SUITE(Phaser) +BOOST_AUTO_TEST_SUITE(GeneticAlgorithmsTest) +BOOST_AUTO_TEST_SUITE(GeneticAlgorithmTest) + +BOOST_FIXTURE_TEST_CASE(run_should_call_runNextRound_once_per_round, GeneticAlgorithmFixture) +{ + DummyAlgorithm algorithm(Population(m_fitnessMetric), m_output); + + BOOST_TEST(algorithm.m_currentRound == 0); + algorithm.run(10); + BOOST_TEST(algorithm.m_currentRound == 10); + algorithm.run(3); + BOOST_TEST(algorithm.m_currentRound == 13); +} + +BOOST_FIXTURE_TEST_CASE(run_should_print_the_top_chromosome, GeneticAlgorithmFixture) +{ + // run() is allowed to print more but should at least print the first one + + DummyAlgorithm algorithm( + // NOTE: Chromosomes chosen so that they're not substrings of each other and are not + // words likely to appear in the output in normal circumstances. + Population(m_fitnessMetric, {Chromosome("fcCUnDve"), Chromosome("jsxIOo"), Chromosome("ighTLM")}), + m_output + ); + + BOOST_TEST(m_output.is_empty()); + algorithm.run(1); + BOOST_TEST(countSubstringOccurrences(m_output.str(), toString(algorithm.population().individuals()[0].chromosome)) == 1); + algorithm.run(3); + BOOST_TEST(countSubstringOccurrences(m_output.str(), toString(algorithm.population().individuals()[0].chromosome)) == 4); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() + +} diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index d3101ad7b..601b249e6 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/GeneticAlgorithms.h + yulPhaser/GeneticAlgorithms.cpp yulPhaser/Population.h yulPhaser/Population.cpp yulPhaser/FitnessMetrics.h diff --git a/tools/yulPhaser/GeneticAlgorithms.cpp b/tools/yulPhaser/GeneticAlgorithms.cpp new file mode 100644 index 000000000..6e69565f4 --- /dev/null +++ b/tools/yulPhaser/GeneticAlgorithms.cpp @@ -0,0 +1,32 @@ +/* + 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 + +using namespace std; +using namespace solidity::phaser; + +void GeneticAlgorithm::run(optional _numRounds) +{ + for (size_t round = 0; !_numRounds.has_value() || round < _numRounds.value(); ++round) + { + runNextRound(); + + m_outputStream << "---------- ROUND " << round << " ----------" << endl; + m_outputStream << m_population; + } +} diff --git a/tools/yulPhaser/GeneticAlgorithms.h b/tools/yulPhaser/GeneticAlgorithms.h new file mode 100644 index 000000000..c2700416b --- /dev/null +++ b/tools/yulPhaser/GeneticAlgorithms.h @@ -0,0 +1,66 @@ +/* + 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 . +*/ +/** + * Contains an abstract base class representing a genetic algorithm and its concrete implementations. + */ + +#pragma once + +#include + +#include +#include + +namespace solidity::phaser +{ + +/** + * Abstract base class for genetic algorithms. + * + * The main feature is the @a run() method that executes the algorithm, updating the internal + * population during each round and printing the results to the stream provided to the constructor. + * + * Derived classes can provide specific methods for updating the population by implementing + * the @a runNextRound() method. + */ +class GeneticAlgorithm +{ +public: + GeneticAlgorithm(Population _initialPopulation, std::ostream& _outputStream): + m_population(std::move(_initialPopulation)), + m_outputStream(_outputStream) {} + + GeneticAlgorithm(GeneticAlgorithm const&) = delete; + GeneticAlgorithm& operator=(GeneticAlgorithm const&) = delete; + virtual ~GeneticAlgorithm() = default; + + Population const& population() const { return m_population; } + + void run(std::optional _numRounds = std::nullopt); + + /// The method that actually implements the algorithm. Should use @a m_population as input and + /// replace it with the updated state after the round. + virtual void runNextRound() = 0; + +protected: + Population m_population; + +private: + std::ostream& m_outputStream; +}; + +}