aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2007-12-07 03:05:20 +0000
committerDavid Robillard <d@drobilla.net>2007-12-07 03:05:20 +0000
commitffb37e6de2934aa227c3483f8a00118e3604b5e5 (patch)
tree8c40493f8de562677e1e54cc4b26a1fbbb493d26
parent33e9991326a1cd90a4956f3221f5a48d03d5af89 (diff)
downloadmachina-ffb37e6de2934aa227c3483f8a00118e3604b5e5.tar.gz
machina-ffb37e6de2934aa227c3483f8a00118e3604b5e5.tar.bz2
machina-ffb37e6de2934aa227c3483f8a00118e3604b5e5.zip
It's evolution, baby.
git-svn-id: http://svn.drobilla.net/lad/machina@958 a436a847-0d15-0410-975c-d299462d15a1
-rw-r--r--src/engine/Engine.cpp3
-rw-r--r--src/engine/Evolver.cpp88
-rw-r--r--src/engine/JackDriver.cpp2
-rw-r--r--src/engine/Machine.cpp88
-rw-r--r--src/engine/Makefile.am3
-rw-r--r--src/engine/MidiAction.cpp3
-rw-r--r--src/engine/Mutation.cpp14
-rw-r--r--src/engine/Problem.cpp45
-rw-r--r--src/engine/machina/Engine.hpp5
-rw-r--r--src/engine/machina/Evolver.hpp57
-rw-r--r--src/engine/machina/Machine.hpp16
-rw-r--r--src/engine/machina/Makefile.am1
-rw-r--r--src/engine/machina/Mutation.hpp23
-rw-r--r--src/engine/machina/Problem.hpp24
-rw-r--r--src/gui/MachinaCanvas.cpp8
-rw-r--r--src/gui/MachinaCanvas.hpp2
-rw-r--r--src/gui/MachinaGUI.cpp79
-rw-r--r--src/gui/MachinaGUI.hpp7
18 files changed, 367 insertions, 101 deletions
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 <glibmm/ustring.h>
-#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 <http://drobilla.net>
+ *
+ * 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 <iostream>
+#include <eugene/core/Mutation.hpp>
+#include <eugene/core/HybridMutation.hpp>
+#include <eugene/core/TournamentSelection.hpp>
+#include <machina/Evolver.hpp>
+#include <machina/Mutation.hpp>
+#include <machina/Problem.hpp>
+
+using namespace std;
+using namespace Eugene;
+using namespace boost;
+
+namespace Machina {
+
+
+Evolver::Evolver(const string& target_midi, SharedPtr<Machine> seed)
+ : _problem(new Problem(target_midi, seed))
+ , _active_fitness(-FLT_MAX)
+{
+ SharedPtr<Eugene::HybridMutation<Machine> > m(new HybridMutation<Machine>());
+
+ m->append_mutation(1/7.0f, boost::shared_ptr< Eugene::Mutation<Machine> >(
+ new Mutation::Compress()));
+ m->append_mutation(1/7.0f, boost::shared_ptr< Eugene::Mutation<Machine> >(
+ new Mutation::AddNode()));
+ m->append_mutation(1/7.0f, boost::shared_ptr< Eugene::Mutation<Machine> >(
+ new Mutation::RemoveNode()));
+ m->append_mutation(1/7.0f, boost::shared_ptr< Eugene::Mutation<Machine> >(
+ new Mutation::AdjustNode()));
+ m->append_mutation(1/7.0f, boost::shared_ptr< Eugene::Mutation<Machine> >(
+ new Mutation::AddEdge()));
+ m->append_mutation(1/7.0f, boost::shared_ptr< Eugene::Mutation<Machine> >(
+ new Mutation::RemoveEdge()));
+ m->append_mutation(1/7.0f, boost::shared_ptr< Eugene::Mutation<Machine> >(
+ new Mutation::AdjustEdge()));
+
+ boost::shared_ptr< Selection<Machine> > s(new TournamentSelection<Machine>(_problem, 3, 0.8));
+ boost::shared_ptr< Crossover<Machine> > crossover;
+ _ga = SharedPtr<MachinaGA>(new MachinaGA(_problem, s, crossover, m, 10, 10, 2, 1.0, 0.0));
+}
+
+
+void
+Evolver::seed(SharedPtr<Machine> parent)
+{
+ /*_best = SharedPtr<Machine>(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> machine)
{
+ cout << "DRIVER MACHINE: " << machine.get() << endl;
SharedPtr<Machine> 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<Node>())
+ , _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<Node>())
, _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<Machina::Node> node(new Machina::Node(*i->get()));
+ map< SharedPtr<Node>, SharedPtr<Node> > replacements;
+
+ for (Nodes::const_iterator n = copy._nodes.begin(); n != copy._nodes.end(); ++n) {
+ SharedPtr<Machina::Node> 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<Node> >(MAX_ACTIVE_NODES, SharedPtr<Node>());
+ _is_activated = false;
+ _is_finished = false;
+ _time = 0;
+ _pending_learn = SharedPtr<LearnRequest>();
+ _sink = other._sink;
+ _nodes.clear();
+
+ map< SharedPtr<Node>, SharedPtr<Node> > replacements;
+
+ for (Nodes::const_iterator n = other._nodes.begin(); n != other._nodes.end(); ++n) {
+ SharedPtr<Machina::Node> 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> 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> node = (*n);
+ SharedPtr<Node> node = (*n);
if (node->is_active())
node->exit(_sink.lock(), time);
@@ -156,7 +194,7 @@ Machine::earliest_node() const
SharedPtr<Node> earliest;
for (size_t i=0; i < MAX_ACTIVE_NODES; ++i) {
- const SharedPtr<Node> node = _active_nodes[i];
+ SharedPtr<Node> 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<Raul::MIDISink> sink, const SharedPtr<Node> node)
+Machine::enter_node(SharedPtr<Raul::MIDISink> sink, SharedPtr<Node> node)
{
assert(!node->is_active());
@@ -183,10 +221,10 @@ Machine::enter_node(const SharedPtr<Raul::MIDISink> sink, const SharedPtr<Node>
* 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<Raul::MIDISink> sink, const SharedPtr<Node>
/** Exit an active node at the current _time.
*/
void
-Machine::exit_node(SharedPtr<Raul::MIDISink> sink, const SharedPtr<Node> node)
+Machine::exit_node(SharedPtr<Raul::MIDISink> sink, SharedPtr<Node> 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<Raul::MIDISink> sink, const SharedPtr<Node> 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<Node> head = (*s)->head();
+ if (rand_normal <= (*e)->probability()) {
+ SharedPtr<Node> 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<Raul::MIDISink> sink = _sink.lock();
+ SharedPtr<Raul::MIDISink> 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> 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> 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> node = machine.random_node();
if (node) {
SharedPtr<MidiAction> enter_action = PtrCast<MidiAction>(node->enter_action());
@@ -89,6 +97,8 @@ AdjustNode::mutate(Machine& machine)
void
AddEdge::mutate(Machine& machine)
{
+ //cout << "ADJUST EDGE" << endl;
+
SharedPtr<Node> tail = machine.random_node();
SharedPtr<Node> head = machine.random_node();
@@ -100,6 +110,8 @@ AddEdge::mutate(Machine& machine)
void
RemoveEdge::mutate(Machine& machine)
{
+ //cout << "REMOVE EDGE" << endl;
+
SharedPtr<Node> 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> 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 <machina/Machine.hpp>
#include <raul/SMFReader.hpp>
#include <raul/midi_events.h>
+#include <eugene/core/Problem.hpp>
using namespace std;
namespace Machina {
-Problem::Problem(const std::string& target_midi)
+Problem::Problem(const std::string& target_midi, SharedPtr<Machine> 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<Machine&>(const_machine);
+
SharedPtr<Evaluator> 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::Population>
+Problem::initial_population(size_t gene_size, size_t pop_size) const
+{
+ boost::shared_ptr<Population> ret(new std::vector<Machine>());
+
+ 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> _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 <http://drobilla.net>
+ *
+ * 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 <raul/SharedPtr.hpp>
+#include <raul/Thread.hpp>
+#include <eugene/core/GAImpl.hpp>
+#include "Schrodinbit.hpp"
+
+namespace Eugene { template <typename G> class HybridMutation; }
+
+namespace Machina {
+
+class Machine;
+class Problem;
+
+
+class Evolver : public Raul::Thread {
+public:
+ Evolver(const string& target_midi, SharedPtr<Machine> seed);
+
+ void seed(SharedPtr<Machine> parent);
+ bool improvement() { return _improvement; }
+
+ SharedPtr<const Machine> best() { return _ga->best(); }
+
+ typedef Eugene::GAImpl<Machine> MachinaGA;
+
+private:
+ void _run();
+
+ SharedPtr<MachinaGA> _ga;
+ SharedPtr<Problem> _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 <vector>
#include <boost/utility.hpp>
#include <raul/SharedPtr.hpp>
#include <raul/WeakPtr.hpp>
@@ -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<Node> > Nodes;
- Nodes& nodes() { return _nodes; }
+ Nodes& nodes() { return _nodes; }
+ const Nodes& nodes() const { return _nodes; }
SharedPtr<Node> random_node();
SharedPtr<Edge> random_edge();
@@ -75,11 +81,11 @@ private:
// Audio context
SharedPtr<Node> earliest_node() const;
- bool enter_node(const SharedPtr<Raul::MIDISink> sink, const SharedPtr<Node> node);
- void exit_node(const SharedPtr<Raul::MIDISink> sink, const SharedPtr<Node>);
+ bool enter_node(SharedPtr<Raul::MIDISink> sink, SharedPtr<Node> node);
+ void exit_node(SharedPtr<Raul::MIDISink> sink, SharedPtr<Node>);
static const size_t MAX_ACTIVE_NODES = 128;
- SharedPtr<Node> _active_nodes[MAX_ACTIVE_NODES];
+ std::vector< SharedPtr<Node> > _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 <eugene/core/Mutation.hpp>
+ #define SUPER : public Eugene::Mutation<Machine>
+#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 <raul/MIDISink.hpp>
+#include <eugene/core/Problem.hpp>
+#include <machina/Machine.hpp>
namespace Machina {
-class Machine;
-
-class Problem {
+class Problem : public Eugene::Problem<Machine> {
public:
- Problem(const std::string& target_midi);
+ Problem(const std::string& target_midi, SharedPtr<Machine> seed = SharedPtr<Machine>());
+
+ void seed(SharedPtr<Machine> 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<Population>
+ 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<Machine> _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<Machina::Node> node)
void
-MachinaCanvas::build(SharedPtr<Machina::Machine> machine)
+MachinaCanvas::build(SharedPtr<const Machina::Machine> 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<Machina::Machine> 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<NodeView> port1,
SharedPtr<NodeView> port2);
- void build(SharedPtr<Machina::Machine> machine);
+ void build(SharedPtr<const Machina::Machine> 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 <cmath>
#include <sstream>
#include <fstream>
@@ -32,6 +34,10 @@
#include "NodeView.hpp"
#include "EdgeView.hpp"
+#ifdef HAVE_EUGENE
+#include <machina/Evolver.hpp>
+#endif
+
using namespace Machina;
@@ -130,8 +136,6 @@ MachinaGUI::MachinaGUI(SharedPtr<Machina::Engine> 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<Machine>()));
_compress_button->signal_clicked().connect(sigc::hide_return(sigc::bind(
@@ -169,10 +173,14 @@ MachinaGUI::MachinaGUI(SharedPtr<Machina::Engine> 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<Machine> 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<Evolver>(new Evolver("target.mid", _engine->machine()));
_evolve = true;
- else
+ stop_clicked();
+ _evolver->start();
+ } else {
+ _evolver->stop();
_evolve = false;
+ SharedPtr<Machine> new_machine = SharedPtr<Machine>(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> 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> 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<Machina::Machine> 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<Machina::Machine> machine);
void mutate(SharedPtr<Machina::Machine> machine, unsigned mutation);
void zoom(double z);
@@ -87,7 +87,8 @@ protected:
boost::shared_ptr<MachinaCanvas> _canvas;
boost::shared_ptr<Machina::Engine> _engine;
- SharedPtr<Raul::Maid> _maid;
+ SharedPtr<Raul::Maid> _maid;
+ SharedPtr<Machina::Evolver> _evolver;
Gtk::Main* _gtk_main;