From ffb37e6de2934aa227c3483f8a00118e3604b5e5 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Fri, 7 Dec 2007 03:05:20 +0000 Subject: It's evolution, baby. git-svn-id: http://svn.drobilla.net/lad/machina@958 a436a847-0d15-0410-975c-d299462d15a1 --- src/engine/Engine.cpp | 3 +- src/engine/Evolver.cpp | 88 +++++++++++++++++++++++++++++++++++++++++ src/engine/JackDriver.cpp | 2 + src/engine/Machine.cpp | 88 +++++++++++++++++++++++++++++------------ src/engine/Makefile.am | 3 +- src/engine/MidiAction.cpp | 3 +- src/engine/Mutation.cpp | 14 +++++++ src/engine/Problem.cpp | 45 +++++++++++++++------ src/engine/machina/Engine.hpp | 5 ++- src/engine/machina/Evolver.hpp | 57 ++++++++++++++++++++++++++ src/engine/machina/Machine.hpp | 16 +++++--- src/engine/machina/Makefile.am | 1 + src/engine/machina/Mutation.hpp | 23 +++++++---- src/engine/machina/Problem.hpp | 24 +++++++---- src/gui/MachinaCanvas.cpp | 8 ++-- src/gui/MachinaCanvas.hpp | 2 +- src/gui/MachinaGUI.cpp | 79 ++++++++++++++++++++++-------------- src/gui/MachinaGUI.hpp | 7 ++-- 18 files changed, 367 insertions(+), 101 deletions(-) create mode 100644 src/engine/Evolver.cpp create mode 100644 src/engine/machina/Evolver.hpp diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index 1417e6e..1a2fe56 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -16,9 +16,10 @@ */ #include -#include "machina/Loader.hpp" #include "machina/Engine.hpp" #include "machina/JackDriver.hpp" +#include "machina/Loader.hpp" +#include "machina/Machine.hpp" #include "machina/SMFDriver.hpp" namespace Machina { diff --git a/src/engine/Evolver.cpp b/src/engine/Evolver.cpp new file mode 100644 index 0000000..1d4decc --- /dev/null +++ b/src/engine/Evolver.cpp @@ -0,0 +1,88 @@ +/* This file is part of Machina. + * Copyright (C) 2007 Dave Robillard + * + * Machina is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Machina 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 details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace Eugene; +using namespace boost; + +namespace Machina { + + +Evolver::Evolver(const string& target_midi, SharedPtr seed) + : _problem(new Problem(target_midi, seed)) + , _active_fitness(-FLT_MAX) +{ + SharedPtr > m(new HybridMutation()); + + m->append_mutation(1/7.0f, boost::shared_ptr< Eugene::Mutation >( + new Mutation::Compress())); + m->append_mutation(1/7.0f, boost::shared_ptr< Eugene::Mutation >( + new Mutation::AddNode())); + m->append_mutation(1/7.0f, boost::shared_ptr< Eugene::Mutation >( + new Mutation::RemoveNode())); + m->append_mutation(1/7.0f, boost::shared_ptr< Eugene::Mutation >( + new Mutation::AdjustNode())); + m->append_mutation(1/7.0f, boost::shared_ptr< Eugene::Mutation >( + new Mutation::AddEdge())); + m->append_mutation(1/7.0f, boost::shared_ptr< Eugene::Mutation >( + new Mutation::RemoveEdge())); + m->append_mutation(1/7.0f, boost::shared_ptr< Eugene::Mutation >( + new Mutation::AdjustEdge())); + + boost::shared_ptr< Selection > s(new TournamentSelection(_problem, 3, 0.8)); + boost::shared_ptr< Crossover > crossover; + _ga = SharedPtr(new MachinaGA(_problem, s, crossover, m, 10, 10, 2, 1.0, 0.0)); +} + + +void +Evolver::seed(SharedPtr parent) +{ + /*_best = SharedPtr(new Machine(*parent.get())); + _best_fitness = _problem->fitness(*_best.get());*/ + _problem->seed(parent); + _active_fitness = _problem->fitness(*parent.get()); +} + + +void +Evolver::_run() +{ + float old_best = _ga->best_fitness(); + + while (true) { + _ga->iteration(); + + if (_ga->best_fitness() > old_best) { + _improvement = true; + old_best = _ga->best_fitness(); + cout << "NEW BEST: " << old_best << endl; + } + } +} + + +} // namespace Machina + diff --git a/src/engine/JackDriver.cpp b/src/engine/JackDriver.cpp index 3517250..93145b0 100644 --- a/src/engine/JackDriver.cpp +++ b/src/engine/JackDriver.cpp @@ -100,6 +100,7 @@ JackDriver::detach() void JackDriver::set_machine(SharedPtr machine) { + cout << "DRIVER MACHINE: " << machine.get() << endl; SharedPtr last_machine = _last_machine; // Keep a reference _machine_changed.reset(0); assert(!last_machine.unique()); @@ -320,6 +321,7 @@ JackDriver::stop() finish_record(); _stop(); // waits + _machine->deactivate(); } diff --git a/src/engine/Machine.cpp b/src/engine/Machine.cpp index fbda9ad..1a9e3ae 100644 --- a/src/engine/Machine.cpp +++ b/src/engine/Machine.cpp @@ -31,7 +31,8 @@ namespace Machina { Machine::Machine() - : _is_activated(false) + : _active_nodes(MAX_ACTIVE_NODES, SharedPtr()) + , _is_activated(false) , _is_finished(false) , _time(0) { @@ -45,22 +46,60 @@ Machine::Machine() */ Machine::Machine(const Machine& copy) : Raul::Stateful() // don't copy RDF ID + , _active_nodes(MAX_ACTIVE_NODES, SharedPtr()) , _is_activated(false) , _is_finished(false) , _time(0) , _sink(copy._sink) { - for (Nodes::const_iterator i = copy._nodes.begin(); i != copy._nodes.end(); ++i) { - SharedPtr node(new Machina::Node(*i->get())); + map< SharedPtr, SharedPtr > replacements; + + for (Nodes::const_iterator n = copy._nodes.begin(); n != copy._nodes.end(); ++n) { + SharedPtr node(new Machina::Node(*n->get())); _nodes.push_back(node); + replacements[*n] = node; + } + + for (Nodes::const_iterator n = _nodes.begin(); n != _nodes.end(); ++n) { + for (Node::Edges::const_iterator e = (*n)->edges().begin(); e != (*n)->edges().end(); ++e) { + (*e)->set_tail(*n); + (*e)->set_head(replacements[(*e)->head()]); + assert((*e)->head()); + } } } - -Machine::~Machine() + +Machine& +Machine::operator=(const Machine& other) { -} + _active_nodes = std::vector< SharedPtr >(MAX_ACTIVE_NODES, SharedPtr()); + _is_activated = false; + _is_finished = false; + _time = 0; + _pending_learn = SharedPtr(); + _sink = other._sink; + _nodes.clear(); + + map< SharedPtr, SharedPtr > replacements; + + for (Nodes::const_iterator n = other._nodes.begin(); n != other._nodes.end(); ++n) { + SharedPtr node(new Machina::Node(*n->get())); + _nodes.push_back(node); + replacements[*n] = node; + } + for (Nodes::const_iterator n = _nodes.begin(); n != _nodes.end(); ++n) { + for (Node::Edges::const_iterator e = (*n)->edges().begin(); e != (*n)->edges().end(); ++e) { + (*e)->set_tail(*n); + (*e)->set_head(replacements[(*e)->head()]); + assert((*e)->head()); + } + } + + return *this; +} + /** Set the MIDI sink to be used for executing MIDI actions. * @@ -128,13 +167,12 @@ Machine::remove_node(SharedPtr node) void Machine::reset(Raul::BeatTime time) { - for (size_t i=0; i < MAX_ACTIVE_NODES; ++i) { - _active_nodes[i].reset(); - } + for (size_t i=0; i < MAX_ACTIVE_NODES; ++i) + _active_nodes.at(i).reset(); if (!_is_finished) { for (Nodes::const_iterator n = _nodes.begin(); n != _nodes.end(); ++n) { - const SharedPtr node = (*n); + SharedPtr node = (*n); if (node->is_active()) node->exit(_sink.lock(), time); @@ -156,7 +194,7 @@ Machine::earliest_node() const SharedPtr earliest; for (size_t i=0; i < MAX_ACTIVE_NODES; ++i) { - const SharedPtr node = _active_nodes[i]; + SharedPtr node = _active_nodes.at(i); if (node) { assert(node->is_active()); @@ -175,7 +213,7 @@ Machine::earliest_node() const * Returns true if node was entered, or false if the maximum active nodes has been reached. */ bool -Machine::enter_node(const SharedPtr sink, const SharedPtr node) +Machine::enter_node(SharedPtr sink, SharedPtr node) { assert(!node->is_active()); @@ -183,10 +221,10 @@ Machine::enter_node(const SharedPtr sink, const SharedPtr * while all actions are still MIDI notes... */ size_t index = (rand() % MAX_ACTIVE_NODES); for (size_t i=0; i < MAX_ACTIVE_NODES; ++i) { - if (_active_nodes[index] == NULL) { + if (_active_nodes.at(index)== NULL) { node->enter(sink, _time); assert(node->is_active()); - _active_nodes[index] = node; + _active_nodes.at(index) = node; return true; } index = (index + 1) % MAX_ACTIVE_NODES; @@ -201,16 +239,14 @@ Machine::enter_node(const SharedPtr sink, const SharedPtr /** Exit an active node at the current _time. */ void -Machine::exit_node(SharedPtr sink, const SharedPtr node) +Machine::exit_node(SharedPtr sink, SharedPtr node) { node->exit(sink, _time); assert(!node->is_active()); - for (size_t i=0; i < MAX_ACTIVE_NODES; ++i) { - if (_active_nodes[i] == node) { - _active_nodes[i].reset(); - } - } + for (size_t i=0; i < MAX_ACTIVE_NODES; ++i) + if (_active_nodes.at(i) == node) + _active_nodes.at(i).reset(); // Activate successors to this node // (that aren't aready active right now) @@ -237,13 +273,13 @@ Machine::exit_node(SharedPtr sink, const SharedPtr node) } else { - for (Node::Edges::const_iterator s = node->edges().begin(); - s != node->edges().end(); ++s) { + for (Node::Edges::const_iterator e = node->edges().begin(); + e != node->edges().end(); ++e) { const double rand_normal = rand() / (double)RAND_MAX; // [0, 1] - if (rand_normal <= (*s)->probability()) { - SharedPtr head = (*s)->head(); + if (rand_normal <= (*e)->probability()) { + SharedPtr head = (*e)->head(); if ( ! head->is_active()) enter_node(sink, head); @@ -269,7 +305,7 @@ Machine::run(const Raul::TimeSlice& time) if (_is_finished) return 0; - const SharedPtr sink = _sink.lock(); + SharedPtr sink = _sink.lock(); const TickTime cycle_end_ticks = time.start_ticks() + time.length_ticks() - 1; const BeatCount cycle_end_beats = time.ticks_to_beats(cycle_end_ticks); @@ -330,7 +366,7 @@ Machine::run(const Raul::TimeSlice& time) } - assert(this_time <= time.length_beats()); + //assert(this_time <= time.length_beats()); return this_time; } diff --git a/src/engine/Makefile.am b/src/engine/Makefile.am index c46ffed..e79502e 100644 --- a/src/engine/Makefile.am +++ b/src/engine/Makefile.am @@ -3,13 +3,14 @@ SUBDIRS = machina lib_LTLIBRARIES = libmachina.la libmachina_la_CXXFLAGS = @EUGENE_CFLAGS@ @REDLANDMM_CFLAGS@ @RAUL_CFLAGS@ @JACK_CFLAGS@ @GLIBMM_CFLAGS@ -libmachina_la_LIBADD = @EUGENE_LIBS@ @RAUL_LIBS@ @JACK_LIBS@ @GLIBMM_LIBS@ +libmachina_la_LIBADD = @EUGENE_LIBS@ @REDLANDMM_LIBS@ @RAUL_LIBS@ @JACK_LIBS@ @GLIBMM_LIBS@ libmachina_la_SOURCES = \ Action.cpp \ ActionFactory.cpp \ Edge.cpp \ Engine.cpp \ + Evolver.cpp \ LearnRequest.cpp \ Loader.cpp \ Machine.cpp \ diff --git a/src/engine/MidiAction.cpp b/src/engine/MidiAction.cpp index ac96211..46985ce 100644 --- a/src/engine/MidiAction.cpp +++ b/src/engine/MidiAction.cpp @@ -44,8 +44,7 @@ MidiAction::MidiAction(size_t size, MidiAction::~MidiAction() { - if (_event.get()) - delete _event.get(); + delete[] _event.get(); } diff --git a/src/engine/Mutation.cpp b/src/engine/Mutation.cpp index c861810..95e31a3 100644 --- a/src/engine/Mutation.cpp +++ b/src/engine/Mutation.cpp @@ -31,6 +31,8 @@ namespace Mutation { void Compress::mutate(Machine& machine) { + //cout << "COMPRESS" << endl; + // Trim disconnected nodes for (Machine::Nodes::iterator i = machine.nodes().begin(); i != machine.nodes().end() ;) { Machine::Nodes::iterator next = i; @@ -47,6 +49,8 @@ Compress::mutate(Machine& machine) void AddNode::mutate(Machine& machine) { + //cout << "ADD NODE" << endl; + // Create random node SharedPtr node(new Node(1.0)); uint8_t note = rand() % 128; @@ -64,6 +68,8 @@ AddNode::mutate(Machine& machine) void RemoveNode::mutate(Machine& machine) { + //cout << "REMOVE NODE" << endl; + SharedPtr node = machine.random_node(); machine.remove_node(node); } @@ -72,6 +78,8 @@ RemoveNode::mutate(Machine& machine) void AdjustNode::mutate(Machine& machine) { + //cout << "ADJUST NODE" << endl; + SharedPtr node = machine.random_node(); if (node) { SharedPtr enter_action = PtrCast(node->enter_action()); @@ -89,6 +97,8 @@ AdjustNode::mutate(Machine& machine) void AddEdge::mutate(Machine& machine) { + //cout << "ADJUST EDGE" << endl; + SharedPtr tail = machine.random_node(); SharedPtr head = machine.random_node(); @@ -100,6 +110,8 @@ AddEdge::mutate(Machine& machine) void RemoveEdge::mutate(Machine& machine) { + //cout << "REMOVE EDGE" << endl; + SharedPtr tail = machine.random_node(); if (tail) tail->remove_edge(tail->random_edge()); @@ -109,6 +121,8 @@ RemoveEdge::mutate(Machine& machine) void AdjustEdge::mutate(Machine& machine) { + //cout << "ADJUST EDGE" << endl; + SharedPtr edge = machine.random_edge(); if (edge) edge->set_probability(rand() / (float)RAND_MAX); diff --git a/src/engine/Problem.cpp b/src/engine/Problem.cpp index f57aa15..654d0a3 100644 --- a/src/engine/Problem.cpp +++ b/src/engine/Problem.cpp @@ -20,14 +20,16 @@ #include #include #include +#include using namespace std; namespace Machina { -Problem::Problem(const std::string& target_midi) +Problem::Problem(const std::string& target_midi, SharedPtr seed) : _target(*this) + , _seed(new Machine(*seed.get())) { Raul::SMFReader smf; const bool opened = smf.open(target_midi); @@ -51,33 +53,40 @@ Problem::Problem(const std::string& target_midi) float -Problem::fitness(Machine& machine) +Problem::fitness(const Machine& const_machine) const { + // kluuudge + Machine& machine = const_cast(const_machine); + SharedPtr eval(new Evaluator(*this)); machine.reset(0.0f); + machine.deactivate(); + machine.activate(); machine.set_sink(eval); // FIXME: timing stuff here isn't right at all... - unsigned ppqn = 19200; - Raul::TimeSlice time(ppqn, 120); + static const unsigned ppqn = 19200; + Raul::TimeSlice time(1.0/(double)ppqn, 120); time.set_start(0); - time.set_length(ppqn); + time.set_length(2*ppqn); while (time.start_ticks() < _target._n_notes * ppqn) { machine.run(time); - time.set_start(time.start_ticks() + ppqn); + time.set_start(time.start_ticks() + 2*ppqn); } eval->compute(); float f = 0; - // Punish for frequency differences. - // Optimal fitness = 0 (max) - for (uint8_t i=0; i < 128; ++i) - f -= fabs(_target._note_frequency[i] - eval->_note_frequency[i]); + for (uint8_t i=0; i < 128; ++i) { + if (eval->_note_frequency[i] <= _target._note_frequency[i]) + f += eval->_note_frequency[i]; + else + f -= _target._note_frequency[i] - eval->_note_frequency[i]; + } return f; } @@ -99,12 +108,24 @@ Problem::Evaluator::write_event(Raul::BeatTime time, void Problem::Evaluator::compute() { - for (uint8_t i=0; i < 128; ++i) { + /*for (uint8_t i=0; i < 128; ++i) { if (_note_frequency[i] > 0) { _note_frequency[i] /= (float)_n_notes; //cout << (int)i << ":\t" << _note_frequency[i] << endl; } - } + }*/ +} + + +boost::shared_ptr +Problem::initial_population(size_t gene_size, size_t pop_size) const +{ + boost::shared_ptr ret(new std::vector()); + + for (size_t i = 0; i < pop_size; ++i) + ret->push_back(Machine(*_seed.get())); + + return ret; } diff --git a/src/engine/machina/Engine.hpp b/src/engine/machina/Engine.hpp index d8d16d9..effa4be 100644 --- a/src/engine/machina/Engine.hpp +++ b/src/engine/machina/Engine.hpp @@ -35,7 +35,8 @@ public: : _driver(driver) , _rdf_world(rdf_world) , _loader(_rdf_world) - { } + { + } Redland::World& rdf_world() { return _rdf_world; } @@ -51,7 +52,7 @@ public: private: SharedPtr _driver; - Redland::World& _rdf_world; + Redland::World& _rdf_world; Loader _loader; }; diff --git a/src/engine/machina/Evolver.hpp b/src/engine/machina/Evolver.hpp new file mode 100644 index 0000000..051513b --- /dev/null +++ b/src/engine/machina/Evolver.hpp @@ -0,0 +1,57 @@ +/* This file is part of Machina. + * Copyright (C) 2007 Dave Robillard + * + * Machina is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Machina 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 details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef MACHINA_EVOLVER_HPP +#define MACHINA_EVOLVER_HPP + +#include +#include +#include +#include "Schrodinbit.hpp" + +namespace Eugene { template class HybridMutation; } + +namespace Machina { + +class Machine; +class Problem; + + +class Evolver : public Raul::Thread { +public: + Evolver(const string& target_midi, SharedPtr seed); + + void seed(SharedPtr parent); + bool improvement() { return _improvement; } + + SharedPtr best() { return _ga->best(); } + + typedef Eugene::GAImpl MachinaGA; + +private: + void _run(); + + SharedPtr _ga; + SharedPtr _problem; + float _active_fitness; + Schrodinbit _improvement; +}; + + +} // namespace Machina + +#endif // MACHINA_EVOLVER_HPP diff --git a/src/engine/machina/Machine.hpp b/src/engine/machina/Machine.hpp index 4544090..5c4df36 100644 --- a/src/engine/machina/Machine.hpp +++ b/src/engine/machina/Machine.hpp @@ -18,6 +18,7 @@ #ifndef MACHINA_MACHINE_HPP #define MACHINA_MACHINE_HPP +#include #include #include #include @@ -37,7 +38,11 @@ class Machine : public Raul::Stateful { public: Machine(); Machine(const Machine& copy); - ~Machine(); + + Machine& operator=(const Machine& other); + + // Kluge to appease Eugene + bool operator==(const Machine& other) { return false; } // Main context void activate() { _is_activated = true; } @@ -64,7 +69,8 @@ public: void clear_pending_learn() { _pending_learn.reset(); } typedef Raul::List< SharedPtr > Nodes; - Nodes& nodes() { return _nodes; } + Nodes& nodes() { return _nodes; } + const Nodes& nodes() const { return _nodes; } SharedPtr random_node(); SharedPtr random_edge(); @@ -75,11 +81,11 @@ private: // Audio context SharedPtr earliest_node() const; - bool enter_node(const SharedPtr sink, const SharedPtr node); - void exit_node(const SharedPtr sink, const SharedPtr); + bool enter_node(SharedPtr sink, SharedPtr node); + void exit_node(SharedPtr sink, SharedPtr); static const size_t MAX_ACTIVE_NODES = 128; - SharedPtr _active_nodes[MAX_ACTIVE_NODES]; + std::vector< SharedPtr > _active_nodes; bool _is_activated; bool _is_finished; diff --git a/src/engine/machina/Makefile.am b/src/engine/machina/Makefile.am index f37974c..e4f4bf0 100644 --- a/src/engine/machina/Makefile.am +++ b/src/engine/machina/Makefile.am @@ -6,6 +6,7 @@ libmachinainclude_HEADERS = \ Driver.hpp \ Edge.hpp \ Engine.hpp \ + Evolver.hpp \ JackDriver.hpp \ LearnRequest.hpp \ Loader.hpp \ diff --git a/src/engine/machina/Mutation.hpp b/src/engine/machina/Mutation.hpp index 0a9731c..52c02bc 100644 --- a/src/engine/machina/Mutation.hpp +++ b/src/engine/machina/Mutation.hpp @@ -18,6 +18,15 @@ #ifndef MACHINA_MACHINE_MUTATION_HPP #define MACHINA_MACHINE_MUTATION_HPP +#include CONFIG_H_PATH + +#if HAVE_EUGENE + #include + #define SUPER : public Eugene::Mutation +#else + #define SUPER +#endif + namespace Machina { class Machine; @@ -26,13 +35,13 @@ namespace Mutation { struct Mutation { virtual void mutate(Machine& machine) = 0; }; -struct Compress { static void mutate(Machine& machine); }; -struct AddNode { static void mutate(Machine& machine); }; -struct RemoveNode { static void mutate(Machine& machine); }; -struct AdjustNode { static void mutate(Machine& machine); }; -struct AddEdge { static void mutate(Machine& machine); }; -struct RemoveEdge { static void mutate(Machine& machine); }; -struct AdjustEdge { static void mutate(Machine& machine); }; +struct Compress SUPER { void mutate(Machine& machine); }; +struct AddNode SUPER { void mutate(Machine& machine); }; +struct RemoveNode SUPER { void mutate(Machine& machine); }; +struct AdjustNode SUPER { void mutate(Machine& machine); }; +struct AddEdge SUPER { void mutate(Machine& machine); }; +struct RemoveEdge SUPER { void mutate(Machine& machine); }; +struct AdjustEdge SUPER { void mutate(Machine& machine); }; } // namespace Mutation diff --git a/src/engine/machina/Problem.hpp b/src/engine/machina/Problem.hpp index d221e5c..abfc534 100644 --- a/src/engine/machina/Problem.hpp +++ b/src/engine/machina/Problem.hpp @@ -19,21 +19,28 @@ #define MACHINA_PROBLEM_HPP #include +#include +#include namespace Machina { -class Machine; - -class Problem { +class Problem : public Eugene::Problem { public: - Problem(const std::string& target_midi); + Problem(const std::string& target_midi, SharedPtr seed = SharedPtr()); + + void seed(SharedPtr parent) { _seed = parent; } - float fitness(Machine& machine); + float fitness(const Machine& machine) const; + + bool fitness_less_than(float a, float b) const { return a < b; } + + boost::shared_ptr + initial_population(size_t gene_size, size_t pop_size) const; private: struct Evaluator : public Raul::MIDISink { - Evaluator(Problem& problem) : _problem(problem), _n_notes(0) { + Evaluator(const Problem& problem) : _problem(problem), _n_notes(0) { for (uint8_t i=0; i < 128; ++i) _note_frequency[i] = 0; } @@ -41,13 +48,14 @@ private: size_t ev_size, const uint8_t* ev) throw (std::logic_error); void compute(); - Problem& _problem; + const Problem& _problem; float _note_frequency[128]; size_t _n_notes; }; - Evaluator _target; + Evaluator _target; + SharedPtr _seed; }; diff --git a/src/gui/MachinaCanvas.cpp b/src/gui/MachinaCanvas.cpp index 1323bb4..04053a5 100644 --- a/src/gui/MachinaCanvas.cpp +++ b/src/gui/MachinaCanvas.cpp @@ -155,10 +155,13 @@ MachinaCanvas::create_node_view(SharedPtr node) void -MachinaCanvas::build(SharedPtr machine) +MachinaCanvas::build(SharedPtr machine, bool show_labels) { + cout << "BUILDING " << machine.get() << endl; + destroy(); _last_clicked.reset(); + assert(_items.empty()); if (!machine) return; @@ -192,9 +195,6 @@ MachinaCanvas::build(SharedPtr machine) head_view->add_connection(c); add_connection(c); } - - //while (Gtk::Main::events_pending()) - // Gtk::Main::iteration(false); } arrange(); diff --git a/src/gui/MachinaCanvas.hpp b/src/gui/MachinaCanvas.hpp index 211ebd7..f5a37c4 100644 --- a/src/gui/MachinaCanvas.hpp +++ b/src/gui/MachinaCanvas.hpp @@ -40,7 +40,7 @@ public: void disconnect_node(SharedPtr port1, SharedPtr port2); - void build(SharedPtr machine); + void build(SharedPtr machine, bool show_labels); void update_edges(); ArtVpathDash* selector_dash() { return _selector_dash; } diff --git a/src/gui/MachinaGUI.cpp b/src/gui/MachinaGUI.cpp index 42cb4ce..025cd8b 100644 --- a/src/gui/MachinaGUI.cpp +++ b/src/gui/MachinaGUI.cpp @@ -15,6 +15,8 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include CONFIG_H_PATH + #include #include #include @@ -32,6 +34,10 @@ #include "NodeView.hpp" #include "EdgeView.hpp" +#ifdef HAVE_EUGENE +#include +#endif + using namespace Machina; @@ -130,8 +136,6 @@ MachinaGUI::MachinaGUI(SharedPtr engine) _quantize_spinbutton->signal_changed().connect( sigc::mem_fun(this, &MachinaGUI::quantize_changed)); - _evolve_button->signal_clicked().connect( - sigc::mem_fun(this, &MachinaGUI::evolve)); _mutate_button->signal_clicked().connect(sigc::bind( sigc::mem_fun(this, &MachinaGUI::random_mutation), SharedPtr())); _compress_button->signal_clicked().connect(sigc::hide_return(sigc::bind( @@ -169,10 +173,14 @@ MachinaGUI::MachinaGUI(SharedPtr engine) // Idle callback to update node states Glib::signal_timeout().connect(sigc::mem_fun(this, &MachinaGUI::idle_callback), 100); - // Periodic mutation callback (FIXME: temporary kludge, thread this) +#ifdef HAVE_EUGENE + _evolve_button->signal_clicked().connect(sigc::mem_fun(this, &MachinaGUI::evolve_toggled)); Glib::signal_timeout().connect(sigc::mem_fun(this, &MachinaGUI::evolve_callback), 1000); +#else + _evolve_button->hide(); +#endif - _canvas->build(engine->machine()); + _canvas->build(engine->machine(), _menu_view_labels->get_active()); } @@ -185,10 +193,10 @@ bool MachinaGUI::evolve_callback() { if (_evolve) { - // Single greedy mutation - SharedPtr copy(new Machine(*_engine->machine().get())); - random_mutation(copy); - _canvas->build(copy); + if (_evolver->improvement()) { + //_engine->driver()->set_machine(_evolver->best()); + _canvas->build(_evolver->best(), _menu_view_labels->get_active()); + } } return true; @@ -244,12 +252,22 @@ MachinaGUI::arrange() void -MachinaGUI::evolve() +MachinaGUI::evolve_toggled() { - if (_evolve_button->get_active()) + if (_evolve_button->get_active()) { + _evolver = SharedPtr(new Evolver("target.mid", _engine->machine())); _evolve = true; - else + stop_clicked(); + _evolver->start(); + } else { + _evolver->stop(); _evolve = false; + SharedPtr new_machine = SharedPtr(new Machine(*_evolver->best())); + _engine->driver()->set_machine(new_machine); + _canvas->build(new_machine, _menu_view_labels->get_active()); + new_machine->activate(); + _engine->driver()->activate(); + } } @@ -259,7 +277,7 @@ MachinaGUI::random_mutation(SharedPtr machine) if (!machine) machine = _engine->machine(); - mutate(machine, rand() % 7); + mutate(machine, machine->nodes().size() < 2 ? 1 : rand() % 7); } @@ -273,31 +291,31 @@ MachinaGUI::mutate(SharedPtr machine, unsigned mutation) switch (mutation) { case 0: - Compress::mutate(*machine.get()); - _canvas->build(machine); + Compress().mutate(*machine.get()); + _canvas->build(machine, _menu_view_labels->get_active()); break; case 1: - AddNode::mutate(*machine.get()); - _canvas->build(machine); + AddNode().mutate(*machine.get()); + _canvas->build(machine, _menu_view_labels->get_active()); break; case 2: - RemoveNode::mutate(*machine.get()); - _canvas->build(machine); + RemoveNode().mutate(*machine.get()); + _canvas->build(machine, _menu_view_labels->get_active()); break; case 3: - AdjustNode::mutate(*machine.get()); + AdjustNode().mutate(*machine.get()); idle_callback(); // update nodes break; case 4: - AddEdge::mutate(*machine.get()); - _canvas->build(machine); + AddEdge().mutate(*machine.get()); + _canvas->build(machine, _menu_view_labels->get_active()); break; case 5: - RemoveEdge::mutate(*machine.get()); - _canvas->build(machine); + RemoveEdge().mutate(*machine.get()); + _canvas->build(machine, _menu_view_labels->get_active()); break; case 6: - AdjustEdge::mutate(*machine.get()); + AdjustEdge().mutate(*machine.get()); _canvas->update_edges(); break; default: throw; @@ -372,7 +390,7 @@ MachinaGUI::menu_file_open() SharedPtr new_machine = _engine->import_machine(dialog.get_uri()); if (new_machine) { _canvas->destroy(); - _canvas->build(new_machine); + _canvas->build(new_machine, _menu_view_labels->get_active()); _save_uri = dialog.get_uri(); } } @@ -483,7 +501,7 @@ MachinaGUI::menu_import_midi() dialog.hide(); machine->activate(); machine->reset(machine->time()); - _canvas->build(machine); + _canvas->build(machine, _menu_view_labels->get_active()); _engine->driver()->set_machine(machine); } else { Gtk::MessageDialog msg_dialog(dialog, "Error loading MIDI file", @@ -588,7 +606,7 @@ MachinaGUI::record_toggled() _engine->driver()->start_record(); } else if (_engine->driver()->recording()) { _engine->driver()->finish_record(); - _canvas->build(_engine->machine()); + _canvas->build(_engine->machine(), _menu_view_labels->get_active()); update_toolbar(); } } @@ -597,12 +615,15 @@ MachinaGUI::record_toggled() void MachinaGUI::stop_clicked() { + _play_button->set_active(false); + if (_engine->driver()->recording()) { _engine->driver()->stop(); - _canvas->build(_engine->machine()); - update_toolbar(); + _engine->machine()->deactivate(); + _canvas->build(_engine->machine(), _menu_view_labels->get_active()); } else { _engine->driver()->stop(); + _engine->machine()->deactivate(); } update_toolbar(); diff --git a/src/gui/MachinaGUI.hpp b/src/gui/MachinaGUI.hpp index bbba67f..3b6c340 100644 --- a/src/gui/MachinaGUI.hpp +++ b/src/gui/MachinaGUI.hpp @@ -26,7 +26,7 @@ using namespace std; -namespace Machina { class Machine; class Engine; } +namespace Machina { class Machine; class Engine; class Evolver; } class MachinaCanvas; @@ -63,7 +63,7 @@ protected: void menu_help_about(); void menu_help_help(); void arrange(); - void evolve(); + void evolve_toggled(); void random_mutation(SharedPtr machine); void mutate(SharedPtr machine, unsigned mutation); void zoom(double z); @@ -87,7 +87,8 @@ protected: boost::shared_ptr _canvas; boost::shared_ptr _engine; - SharedPtr _maid; + SharedPtr _maid; + SharedPtr _evolver; Gtk::Main* _gtk_main; -- cgit v1.2.1