comparison src/sc_grammar.cpp @ 0:add35537fdbb tip

Initial import
author irh <ian.r.hobson@gmail.com>
date Thu, 25 Aug 2011 11:05:55 +0100
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:add35537fdbb
1 // Copyright 2011, Ian Hobson.
2 //
3 // This file is part of gpsynth.
4 //
5 // gpsynth is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // gpsynth is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with gpsynth in the file COPYING.
17 // If not, see http://www.gnu.org/licenses/.
18
19 #include "sc_grammar.hpp"
20
21 #include "boost_ex.hpp"
22 #include "graph_helpers.hpp"
23 #include "logger.hpp"
24 #include "statistics.hpp"
25 #include "std_ex.hpp"
26
27 #include "json/json.h"
28
29 #include "boost/graph/random.hpp"
30 #include "boost/graph/topological_sort.hpp"
31
32 #include <iostream>
33 #include <iterator>
34 #include <fstream>
35 #include <set>
36 #include <sstream>
37
38 const int g_maximum_mixer_channels = 3;
39
40 const double g_probability_command_node_command = 0.85;
41 const double g_probability_command_node_special = 0.15;
42
43 const double g_probability_mod_node_command = 0.25;
44 const double g_probability_mod_node_special = 0.15;
45 const double g_probability_mod_node_constant = 0.4;
46
47 const double g_probability_argument_affected = 1.0;
48
49 const double g_probability_input_mutation = 1.0;
50 const double g_probability_connection_mutation = 1.0;
51
52 const double g_minimum_connection_weight = 0.001;
53
54 namespace {
55
56 double MapCoefficientToRange(double c, const stdx::Range<double>& range) {
57 return range.minimum_ + (range.Size() * c);
58 }
59
60 // Returns argument connection weight and offset, scaled to argument input
61 void GenerateWeightAndOffset(const sc::Argument& argument,
62 sg::CommandRate rate,
63 sg::Connection* connection) {
64 const stdx::Range<double>& scale_range = (rate == sg::kControlRate)
65 ? argument.range_control_
66 : argument.range_;
67 if (argument.fixed_range_) {
68 connection->weight_ = scale_range.Size();
69 connection->offset_ = scale_range.Minimum();
70 } else if (argument.name_ == "in") {
71 // input connections don't have an offset
72 double weight = stdx::RandomCoefficient();
73 if (rate == sg::kAudioRate) {
74 // square audio weightings
75 connection->weight_ = weight * weight;
76 }
77 } else {
78 // generate random weight and offset
79 double weight;
80 do {
81 weight = stdx::RandomCoefficient();
82 } while (weight < g_minimum_connection_weight);
83 double offset = stdx::RandomRange(0.0, 1.0 - weight);
84 switch (argument.scaling_mode_) {
85 case sc::Argument::kScalingLinear:
86 connection->weight_ = MapCoefficientToRange(weight, scale_range);
87 connection->offset_ = MapCoefficientToRange(offset, scale_range);
88 break;
89
90 case sc::Argument::kScalingLog:
91 connection->weight_ = MapCoefficientToRange(weight * weight * weight,
92 scale_range);
93 connection->offset_ = MapCoefficientToRange(offset * offset * offset,
94 scale_range);
95 break;
96
97 case sc::Argument::kConstant:
98 throw std::runtime_error("ApplyRandomWeightAndOffset - preset argument provided");
99 }
100 }
101 }
102
103 sc::Argument ParseArgument(const Json::Value& json) {
104 sc::Argument arg;
105 arg.name_ = json.get("name", "").asString();
106 if (json.isMember("constant")) {
107 arg.scaling_mode_ = sc::Argument::kConstant;
108 arg.constant_value_ = json["constant"].asDouble();
109 return arg;
110 }
111 if (json.isMember("range")) {
112 const Json::Value range = json["range"];
113 arg.range_.SetMinimum(range[0].asDouble());
114 arg.range_.SetMaximum(range[1].asDouble());
115 }
116 if (json.isMember("range_control")) {
117 const Json::Value range = json["range_control"];
118 arg.range_control_.SetMinimum(range[0].asDouble());
119 arg.range_control_.SetMaximum(range[1].asDouble());
120 } else {
121 arg.range_control_ = arg.range_;
122 }
123 if (json.isMember("fixed_range")) {
124 arg.fixed_range_ = json["fixed_range"].asBool();
125 }
126 std::string scaling = json.get("scaling", "linear").asString();
127 if (scaling == "linear") {
128 arg.scaling_mode_ = sc::Argument::kScalingLinear;
129 } else if (scaling == "log") {
130 arg.scaling_mode_ = sc::Argument::kScalingLog;
131 }
132 return arg;
133 }
134
135 sc::Command ParseCommand(const Json::Value& json,
136 const std::map<std::string, sc::Argument>& types,
137 sc::Command::CommandMode command_mode) {
138 if (json.type() == Json::stringValue) {
139 return sc::Command(json.asString(), command_mode);
140 }
141 // name
142 std::string name = json["name"].asString();
143 // output type
144 sc::Command::OutputType output_type = sc::Command::kAll;
145 if (json.isMember("output")) {
146 std::string output = json["output"].asString();
147 if (output == "control") {
148 output_type = sc::Command::kControl;
149 } else if (output == "audio") {
150 output_type = sc::Command::kAudio;
151 }
152 }
153 // arguments
154 std::vector<sc::Argument> args;
155 // [in] argument for modifiers
156 if (command_mode == sc::Command::kModifier) {
157 args.push_back(sc::Argument("in"));
158 }
159 Json::Value json_args = json["args"];
160 Json::ValueType arg_type = json_args.type();
161 if (arg_type == Json::stringValue) {
162 // single argument as string
163 args.push_back(sc::Argument(json_args.asString()));
164 } else if (arg_type == Json::objectValue) {
165 // single argument object
166 if (json_args.isMember("type")) {
167 args.push_back(stdx::GetFromMap(types, json_args["type"].asString()));
168 args.back().name_ = json_args["name"].asString();
169 } else {
170 args.push_back(ParseArgument(json_args));
171 }
172 } else {
173 // array of arguments
174 for (int i = 0; i < json_args.size(); i++) {
175 Json::Value arg = json_args[i];
176 if (arg.type() == Json::stringValue) {
177 args.push_back(sc::Argument(arg.asString()));
178 } else {
179 if (arg.isMember("type")) {
180 args.push_back(stdx::GetFromMap(types, arg["type"].asString()));
181 args.back().name_ = arg["name"].asString();
182 } else {
183 args.push_back(ParseArgument(arg));
184 }
185 }
186 }
187 }
188 // success
189 return sc::Command(name, command_mode, output_type, args);
190 }
191
192
193 sc::Command ParseSource(const Json::Value& json,
194 const std::map<std::string, sc::Argument>& types) {
195 return ParseCommand(json, types, sc::Command::kSource);
196 }
197
198 sc::Command ParseModifier(const Json::Value& json,
199 const std::map<std::string, sc::Argument>& types) {
200 return ParseCommand(json, types, sc::Command::kModifier);
201 }
202
203 void MutateValue(double* value, bool* mutation_occurred,
204 double range_min = 0.0, double range_max = 1.0) {
205 // add gaussian noise to values, low variance
206 static bx::GaussianGenerator generator = bx::MakeGaussianGenerator(0, 0.125);
207 double new_value = stdx::Clamp(*value + generator(), range_min, range_max);
208 if (*value != new_value) {
209 *value = new_value;
210 *mutation_occurred = true;
211 }
212 }
213
214 } // namespace
215
216
217
218 namespace sc {
219
220 Grammar::Grammar(const std::string& json_data)
221 : max_depth_(3)
222 {
223 ParseJSON(json_data);
224 }
225
226 void Grammar::ParseJSON(const std::string& json_data) {
227 commands_.clear();
228 Json::Reader reader;
229 Json::Value root;
230 if (!reader.parse(json_data, root)) {
231 std::stringstream message;
232 message << "Grammar::Parse: Failed to parse data\n"
233 << reader.getFormattedErrorMessages();
234 throw std::runtime_error(message.str());
235 }
236 try {
237 // argument types
238 // stores predefined argument types while parsing grammar
239 std::map<std::string, Argument> argument_types;
240 const Json::Value& args = root["arg-types"];
241 for (int i = 0; i < args.size(); i++) {
242 const Json::Value& arg = args[i];
243 argument_types[arg["type"].asString()] = ParseArgument(arg);
244 }
245 // sound sources
246 const Json::Value& sources = root["sources"];
247 for (int i = 0; i < sources.size(); i++) {
248 commands_.push_back(ParseSource(sources[i], argument_types));
249 }
250 // modifiers
251 const Json::Value& modifiers = root["modifiers"];
252 for (int i = 0; i < modifiers.size(); i++) {
253 commands_.push_back(ParseModifier(modifiers[i], argument_types));
254 }
255 } catch (const std::exception& e) {
256 std::stringstream message;
257 message << "Grammar::Parse: " << e.what();
258 throw std::runtime_error(message.str());
259 }
260 }
261
262 int Grammar::RandomCommand(sg::CommandRate rate,
263 int tree_depth,
264 bool must_be_modifier /* = false */) const {
265 Command::OutputType output;
266 Command::CommandMode mode;
267 CommandID command = -1;
268 CommandID number_of_commands = commands_.size();
269 // if we're at or one away from maximum depth then the command needs to
270 // be a source, headroom of 1 for some parameter control
271 bool must_be_source = (tree_depth >= (max_depth_ - 1));
272 do {
273 command = stdx::Random(number_of_commands);
274 output = commands_[command].Output();
275 if (rate == sg::kAudioRate) {
276 do {
277 command = stdx::Random(number_of_commands);
278 output = commands_[command].Output();
279 } while (!(output == Command::kAll || output == Command::kAudio));
280 } else {
281 do {
282 command = stdx::Random(number_of_commands);
283 output = commands_[command].Output();
284 } while (!(output == Command::kAll || output == Command::kControl));
285 }
286 mode = commands_[command].Mode();
287 } while ((must_be_source && (mode != Command::kSource))
288 || (must_be_modifier && (mode != Command::kModifier)));
289 return static_cast<int>(command);
290 }
291
292 sg::Vertex Grammar::CreateCommand(sg::Graph &graph,
293 sg::CommandRate rate,
294 int depth,
295 bool must_be_modifier /* = false */,
296 bool make_modifier_input /* = true */) const {
297 // select a random command for the node
298 int command_id = RandomCommand(rate, depth, must_be_modifier);
299 // create the node and add it to the graph
300 sg::Node command_info(command_id, sg::kCommand, rate);
301 sg::Vertex vertex = boost::add_vertex(command_info, graph);
302 // a modifier command has to have further nodes added to its main input
303 const Command& command = commands_[command_id];
304 for (int i = 0; i < command.Arguments().size(); i++) {
305 const Argument& argument = command.GetArgument(i);
306 if (argument.name_ == "in") {
307 if (make_modifier_input) {
308 // modifier input must be given a command tree
309 sg::Vertex tree = RandomTree(graph, rate, kTreeMode_Command, depth + 1);
310 sg::Connection connection(i);
311 GenerateWeightAndOffset(argument, rate, &connection);
312 boost::add_edge(tree, vertex, connection, graph);
313 }
314 } else if (argument.scaling_mode_ == sc::Argument::kConstant) {
315 // don't attach inputs to predefined arguments
316 continue;
317 } else if (stdx::Chance(g_probability_argument_affected)) {
318 // randomly create input for command arguments
319 sg::Vertex tree = RandomTree(graph,
320 sg::kControlRate,
321 kTreeMode_Mod,
322 depth + 1);
323 sg::Connection connection(i);
324 GenerateWeightAndOffset(argument, rate, &connection);
325 boost::add_edge(tree, vertex, connection, graph);
326 }
327 }
328 return vertex;
329 }
330
331 sg::Vertex Grammar::RandomTree(sg::Graph &graph,
332 sg::CommandRate rate,
333 TreeMode tree_mode,
334 int depth) const {
335 enum NodeType {
336 kCommand,
337 kSpecial,
338 kConstant,
339 kParameter
340 };
341 static stats::ProbabilitySelector node_type_selector;
342 if (!node_type_selector.Initialized()) {
343 std::vector<double> probabilities(3);
344 probabilities[kCommand] = g_probability_mod_node_command;
345 probabilities[kSpecial] = g_probability_mod_node_special;
346 probabilities[kConstant] = g_probability_mod_node_constant;
347 node_type_selector.SetProbabilities(probabilities);
348 }
349 // establish the type of node to create
350 int node_type;
351 if (depth >= max_depth_) {
352 if (tree_mode == kTreeMode_Command) {
353 // if we're at maximum depth in command mode then create a command
354 node_type = kCommand;
355 } else {
356 // in mod mode at maximum depth pick either a constant or parameter
357 if (stdx::Chance(g_probability_mod_node_constant)) {
358 node_type = kConstant;
359 } else {
360 node_type = kParameter;
361 }
362 }
363 } else {
364 if (tree_mode == kTreeMode_Command) {
365 if (stdx::Chance(g_probability_command_node_command)) {
366 node_type = kCommand;
367 } else {
368 node_type = kSpecial;
369 }
370 } else {
371 // mod mode below max depth, any node type is ok
372 node_type = static_cast<NodeType>(node_type_selector());
373 }
374 }
375 if (node_type == kCommand) {
376 return CreateCommand(graph, rate, depth);
377 } else if (node_type == kSpecial) {
378 int command = stdx::Random(Command::kNumberOfSpecialCommands);
379 return SpecialCommand(command, graph, rate, tree_mode, depth + 1);
380 } else if (node_type == kConstant) {
381 return boost::add_vertex(sg::Node(stdx::RandomCoefficient()), graph);
382 } else if (node_type == kParameter) {
383 return boost::add_vertex(sg::Node(RandomParameter(graph), sg::kParameter),
384 graph);
385 }
386 throw std::runtime_error("Grammar::RandomTree - unknown node type");
387 }
388
389 int Grammar::RandomParameter(sg::Graph& graph) const {
390 // as a simple rule, randomly pick an existing parameter, or if chosen
391 // parameter is greater than # of parameters, then add a new one
392 std::vector<double>& parameters = graph[boost::graph_bundle].parameters_;
393 int number_of_parameters = static_cast<int>(parameters.size());
394 // parameter ids start at 1
395 int parameter = stdx::RandomRangeInt(1, number_of_parameters + 1);
396 // bump parameter count if we've got a new parameter
397 if (parameter > number_of_parameters) {
398 // store a random value for the new parameter
399 parameters.push_back(stdx::RandomCoefficient());
400 }
401 return parameter;
402 }
403
404 void Grammar::AddTreeToSpecialCommand(sg::Vertex command_node,
405 sg::Graph& graph,
406 int input_id,
407 TreeMode tree_mode,
408 sg::CommandRate command_rate,
409 bool* constant_added,
410 std::set<int>* parameters,
411 int depth) const {
412 bool success = false;
413 while (!success) {
414 sg::Vertex tree;
415 // first channel or audio command should enforce a command source
416 if (input_id == 0) {
417 tree = RandomTree(graph, command_rate, kTreeMode_Command, depth + 1);
418 } else {
419 tree = RandomTree(graph, command_rate, tree_mode, depth + 1);
420 }
421 sg::NodeType tree_type = graph[tree].type_;
422 if (tree_type == sg::kConstant) {
423 // we only need one constant per mixer
424 if (*constant_added) {
425 RemoveSubTree(tree, graph);
426 continue;
427 }
428 *constant_added = true;
429 } else if (tree_type == sg::kParameter) {
430 // we don't want duplicated parameters for special command inputs
431 if (parameters->find(graph[tree].id_) != parameters->end()) {
432 RemoveSubTree(tree, graph);
433 continue;
434 }
435 // store the parameter id
436 parameters->insert(graph[tree].id_);
437 }
438 // apply weighting
439 double weight = stdx::RandomCoefficient();
440 if (command_rate == sg::kAudioRate) {
441 // scale audio to curve
442 weight *= weight;
443 }
444 // make the connection to the generated tree
445 boost::add_edge(tree, command_node, sg::Connection(weight), graph);
446 success = true;
447 }
448 }
449
450 sg::Vertex Grammar::SpecialCommand(int command_id,
451 sg::Graph& graph,
452 sg::CommandRate command_rate,
453 TreeMode tree_mode,
454 int depth /* = 0 */,
455 int minimum_channels /* = 2 */) const {
456 // create the special command node
457 sg::Node command_info(command_id, sg::kSpecial, command_rate);
458 sg::Vertex mixer = boost::add_vertex(command_info, graph);
459 // pick a random number of channels to create for this command
460 int channels = stdx::RandomRangeInt(minimum_channels,
461 g_maximum_mixer_channels);
462 bool constant_added = false;
463 std::set<int> parameters;
464 // add a tree for each channel
465 for (int i = 0; i < channels; i++) {
466 AddTreeToSpecialCommand(mixer,
467 graph,
468 i,
469 tree_mode,
470 command_rate,
471 &constant_added,
472 &parameters,
473 depth);
474 }
475 return mixer;
476 }
477
478 void Grammar::RandomGraph(sg::Graph& graph) const {
479 graph.clear();
480 // root output node
481 SpecialCommand(sc::Command::kMixer, graph, sg::kAudioRate, kTreeMode_Command);
482 }
483
484 void Grammar::PickCrossoverNodes(const sg::Graph& parent_1,
485 const sg::Graph& parent_2,
486 sg::Vertex* crossover_node_1,
487 sg::Vertex* crossover_node_2) const {
488 sg::Vertex source_node_1;
489 sg::Vertex source_node_2;
490 bool nodes_found = false;
491 do {
492 // pick random edges and their nodes
493 source_node_1 = boost::random_vertex(parent_1, bx::RandomEngine());
494 source_node_2 = boost::random_vertex(parent_2, bx::RandomEngine());
495 // avoid the root output node
496 if ((source_node_1 == 0) || (source_node_2 == 0)) {
497 continue;
498 }
499 // check it's ok to swap these nodes
500 const sg::Node& info_1 = parent_1[source_node_1];
501 const sg::Node& info_2 = parent_2[source_node_2];
502 // command rates need to match
503 if (info_1.rate_ != info_2.rate_) {
504 continue;
505 }
506 // we don't want to swap two constants
507 if (info_1.type_ == sg::kConstant && info_2.type_ == sg::kConstant) {
508 continue;
509 }
510 // we don't want to swap parameter inputs with same parameter id
511 if ((info_1.type_ == sg::kParameter)
512 && (info_2.type_ == sg::kParameter)
513 && (info_1.id_ == info_2.id_)) {
514 continue;
515 }
516 // check that we're not replacing a control modifer's input with non-command
517 sg::Edge connection_1 = *(boost::out_edges(source_node_1, parent_1).first);
518 sg::Edge connection_2 = *(boost::out_edges(source_node_2, parent_2).first);
519 sg::Vertex target_node_1 = boost::target(connection_1, parent_1);
520 sg::Vertex target_node_2 = boost::target(connection_2, parent_2);
521 const sg::Node& info_target_1 = parent_1[target_node_1];
522 const sg::Node& info_target_2 = parent_2[target_node_2];
523 if ((info_target_1.type_ == sg::kCommand)
524 || (info_target_1.type_ == sg::kSpecial)) {
525 const sc::Command& target_command_1 = commands_[info_target_1.id_];
526 const std::vector<Argument>& arguments_1 = target_command_1.Arguments();
527 const sg::Connection& info_connection_1 = parent_1[connection_1];
528 if (arguments_1[info_connection_1.input_].name_ == "in") {
529 // target input is a modifier's 'in' argument, only allow commands to
530 // be swapped in
531 if (!(info_2.type_ == sg::kCommand || info_2.type_ == sg::kSpecial)) {
532 continue;
533 }
534 }
535 }
536 if ((info_target_2.type_ == sg::kCommand)
537 || (info_target_2.type_ == sg::kSpecial)) {
538 const sc::Command& target_command_2 = commands_[info_target_2.id_];
539 const std::vector<Argument>& arguments_2 = target_command_2.Arguments();
540 const sg::Connection& info_connection_2 = parent_1[connection_2];
541 if (arguments_2[info_connection_2.input_].name_ == "in") {
542 // target input is a modifier's 'in' argument, only allow commands to
543 // be swapped in
544 if (!(info_1.type_ == sg::kCommand || info_1.type_ == sg::kSpecial)) {
545 continue;
546 }
547 }
548 }
549 nodes_found = true;
550 } while (!nodes_found);
551 *crossover_node_1 = source_node_1;
552 *crossover_node_2 = source_node_2;
553 }
554
555 bool Grammar::MutationReplaceSubTree(sg::Graph& graph) const {
556 // sanity check
557 if (boost::num_vertices(graph) <= 1) {
558 throw std::runtime_error("sc::Grammar::MutationReplaceSubTree - graph has too few nodes");
559 }
560 // pick node to replace
561 sg::Vertex node_to_replace;
562 do {
563 node_to_replace = boost::random_vertex(graph, bx::RandomEngine());
564 } while (node_to_replace == 0);
565 sg::CommandRate new_tree_rate = graph[node_to_replace].rate_;
566 sg::Edge connection = *(boost::out_edges(node_to_replace, graph).first);
567 sg::Vertex connection_target = boost::target(connection, graph);
568 // store the connection info contained in the connection edge
569 sg::Connection connection_info = graph[connection];
570 const sg::Node& target_info = graph[connection_target];
571 // determine new tree mode
572 TreeMode new_tree_mode = kTreeMode_Mod;
573 if (new_tree_rate == sg::kAudioRate) {
574 // audio rate nodes always require command tree replacements
575 new_tree_mode = kTreeMode_Command;
576 } else if (target_info.type_ == sg::kCommand) {
577 // if the connection target is a modifier
578 // and the connection input is 0 then a command tree is needed
579 if (commands_[target_info.id_].Mode() == Command::kModifier) {
580 if (connection_info.input_ == 0) {
581 new_tree_mode = kTreeMode_Command;
582 }
583 }
584 } else if (target_info.type_ == sg::kSpecial) {
585 // first input to special commands is a command tree
586 if (connection_info.input_ == 0) {
587 new_tree_mode = kTreeMode_Command;
588 }
589 }
590 // remove the sub tree
591 RemoveSubTree(connection, graph);
592 // generate new tree
593 sg::Vertex new_tree = RandomTree(graph, new_tree_rate, new_tree_mode, 0);
594 // connect tree to graph
595 boost::add_edge(new_tree, connection_target, connection_info, graph);
596 return true;
597 }
598
599
600
601 bool Grammar::MutationAddSubTree(sg::Graph& graph) const {
602 // sanity check
603 if (graph[0].type_ != sg::kSpecial) {
604 throw std::runtime_error("sc::Grammar::MutationAddSubTree - missing root mixer node");
605 }
606 bool mutation_occurred = false;
607 while (!mutation_occurred) {
608 // pick a command or special node to add a tree input to
609 sg::Vertex node;
610 sg::NodeType node_type;
611 do {
612 node = boost::random_vertex(graph, bx::RandomEngine());
613 node_type = graph[node].type_;
614 } while (!(node_type == sg::kCommand || node_type == sg::kSpecial));
615 if (node_type == sg::kCommand) {
616 int node_id = graph[node].id_;
617 const std::vector<Argument>& arguments = commands_[node_id].Arguments();
618 std::size_t number_of_arguments = arguments.size();
619 sg::InEdgeIterator edge;
620 sg::InEdgeIterator edge_end;
621 std::vector<bool> used_arguments(arguments.size(), false);
622 for (boost::tie(edge, edge_end) = boost::in_edges(node, graph);
623 edge != edge_end;
624 ++edge) {
625 used_arguments[graph[*edge].input_] = true;
626 }
627 // check that there is an unused argument available for this command
628 bool argument_available = false;
629 for (std::size_t i = 0; i < number_of_arguments; i++) {
630 if (used_arguments[i] == false) {
631 // we don't want to attach a tree to a preset argument
632 if (arguments[i].scaling_mode_ != Argument::kConstant) {
633 argument_available = true;
634 break;
635 }
636 }
637 }
638 if (argument_available) {
639 // find a random argument to insert a tree into
640 std::size_t argument;
641 do {
642 argument = stdx::Random(arguments.size());
643 } while (used_arguments[argument] == true
644 || arguments[argument].scaling_mode_ == Argument::kConstant);
645 // add a tree and connect to the unused argument
646 sg::Vertex tree = RandomTree(graph,
647 sg::kControlRate,
648 kTreeMode_Mod,
649 0);
650 sg::Connection connection(static_cast<int>(argument));
651 GenerateWeightAndOffset(arguments[argument],
652 sg::kControlRate,
653 &connection);
654 boost::add_edge(tree, node, connection, graph);
655 mutation_occurred = true;
656 }
657 } else if (node_type == sg::kSpecial) {
658 int node_inputs = static_cast<int>(boost::in_degree(node, graph));
659 // we need to check the special command's inputs to avoid adding redundant
660 // constants or parameters
661 bool constant_added = false;
662 std::set<int> parameters;
663 sg::InEdgeIterator edge;
664 sg::InEdgeIterator edge_end;
665 for (boost::tie(edge, edge_end) = boost::in_edges(node, graph);
666 edge != edge_end;
667 ++edge) {
668 const sg::Node& source = graph[boost::source(*edge, graph)];
669 if (source.type_ == sg::kConstant) {
670 constant_added = true;
671 }
672 if (source.type_ == sg::kParameter) {
673 parameters.insert(source.id_);
674 }
675 }
676 // select tree mode based on command rate
677 TreeMode tree_mode;
678 if (graph[node].rate_ == sg::kAudioRate) {
679 tree_mode = kTreeMode_Command;
680 } else {
681 tree_mode = kTreeMode_Mod;
682 }
683 // add the tree
684 AddTreeToSpecialCommand(node,
685 graph,
686 node_inputs,
687 tree_mode,
688 graph[node].rate_,
689 &constant_added,
690 &parameters,
691 0);
692 mutation_occurred = true;
693 }
694 }
695 return true;
696 }
697
698 bool Grammar::MutationModifyInputs(sg::Graph& graph) const {
699 // sanity check
700 if (graph[boost::graph_bundle].parameters_.size() == 0) {
701 sg::VertexIterator vertex;
702 sg::VertexIterator vertex_end;
703 bool constant_found = false;
704 for (boost::tie(vertex, vertex_end) = boost::vertices(graph);
705 vertex != vertex_end;
706 ++vertex) {
707 sg::Node& node = graph[*vertex];
708 if (node.type_ == sg::kConstant) {
709 constant_found = true;
710 break;
711 }
712 }
713 if (!constant_found) {
714 // no parameters, no constants, nothing to do..
715 return false;
716 }
717 }
718 // make a random generator with a narrow guassian distribution
719 bx::GaussianGenerator gaussian = bx::MakeGaussianGenerator(0, 0.125);
720 bool mutation_occurred = false;
721 while (!mutation_occurred) {
722 // mutate parameters
723 foreach (double& parameter, graph[boost::graph_bundle].parameters_) {
724 if (stdx::Chance(g_probability_input_mutation)) {
725 MutateValue(&parameter, &mutation_occurred);
726 }
727 }
728 // mutate constants
729 sg::VertexIterator vertex;
730 sg::VertexIterator vertex_end;
731 for (boost::tie(vertex, vertex_end) = boost::vertices(graph);
732 vertex != vertex_end;
733 ++vertex) {
734 sg::Node& node = graph[*vertex];
735 if (node.type_ == sg::kConstant) {
736 // constant value
737 if (stdx::Chance(g_probability_input_mutation)) {
738 MutateValue(&node.constant_value_, &mutation_occurred);
739 }
740 }
741 }
742 }
743 return true;
744 }
745
746 bool Grammar::MutationModifyConnections(sg::Graph& graph) const {
747 // sanity check
748 if (boost::num_edges(graph) < 1) {
749 throw std::runtime_error("sc::Grammar::MutationModifyConnections - graph has no connections");
750 }
751 bx::GaussianGenerator gaussian = bx::MakeGaussianGenerator(0, 0.125);
752 bool mutation_occurred = false;
753 while (!mutation_occurred) {
754 sg::EdgeIterator edge;
755 sg::EdgeIterator edge_end;
756 for (boost::tie(edge, edge_end) = boost::edges(graph);
757 edge != edge_end;
758 ++edge) {
759 if (stdx::Chance(g_probability_connection_mutation)) {
760 sg::Connection& connection = graph[*edge];
761 const sg::Node& target = graph[boost::target(*edge, graph)];
762 const sc::Argument& argument = GetCommandArgument(target.id_,
763 connection.input_);
764 if (!argument.fixed_range_) {
765 // mutate weight
766 MutateValue(&connection.weight_,
767 &mutation_occurred,
768 g_minimum_connection_weight);
769 // special command inputs and modifier command input arguments
770 // have offsets set to zero
771 if ((target.type_ != sg::kSpecial) && (argument.name_ != "in")) {
772 MutateValue(&connection.offset_, &mutation_occurred,
773 0.0, 1.0 - connection.weight_);
774 }
775 }
776 }
777 }
778 }
779 return true;
780 }
781
782 const std::vector<sc::Argument>& Grammar::CommandArguments(int command) const {
783 return commands_[command].Arguments();
784 }
785
786 bool Grammar::MutationReplaceCommand(sg::Graph& graph) const {
787 // sanity check
788 if (boost::num_vertices(graph) <= 1) {
789 throw std::runtime_error("sc::Grammar::MutationReplaceCommand - graph has too few nodes");
790 }
791 sg::Vertex node_to_replace;
792 sg::Node* node_info;
793 // find command to replace
794 do {
795 node_to_replace = boost::random_vertex(graph, bx::RandomEngine());
796 node_info = &graph[node_to_replace];
797 } while (node_info->type_ != sg::kCommand);
798 int old_id = node_info->id_;
799 // generate new command id
800 int new_id;
801 int number_of_commands = static_cast<int>(commands_.size());
802 do {
803 // new command must match old output and mode
804 new_id = stdx::Random(number_of_commands);
805 } while (new_id != old_id
806 && commands_[new_id].Output() != commands_[old_id].Output()
807 && commands_[new_id].Mode() != commands_[old_id].Mode());
808 // new command info
809 const std::vector<sc::Argument>& arguments = CommandArguments(new_id);
810 std::size_t number_of_arguments = commands_[new_id].Arguments().size();
811 // remove invalid parameters
812 // removing an in edge invalidates the iterators, so we need to restart the
813 // search each time an edge is removed
814 sg::InEdgeIterator edge;
815 sg::InEdgeIterator edge_end;
816 bool do_it_again;
817 do {
818 do_it_again = false;
819 for (boost::tie(edge, edge_end) = boost::in_edges(node_to_replace, graph);
820 edge != edge_end;
821 ++edge) {
822 int input = graph[*edge].input_;
823 // remove input tree if it's for an extraneous parameter
824 // or if the argument is a constant
825 if (input >= number_of_arguments
826 || arguments[input].scaling_mode_ == sc::Argument::kConstant) {
827 RemoveSubTree(*edge, graph);
828 do_it_again = true;
829 // break out of for loop
830 break;
831 }
832 }
833 } while (do_it_again);
834 // now invalid parameters have been removed, validate the rest
835 for (boost::tie(edge, edge_end) = boost::in_edges(node_to_replace, graph);
836 edge != edge_end;
837 ++edge) {
838 // if the new argument is fixed range then correct the connection weight
839 int input = graph[*edge].input_;
840 if (arguments[input].fixed_range_) {
841 GenerateWeightAndOffset(arguments[input],
842 node_info->rate_,
843 &graph[*edge]);
844 }
845 }
846 // apply new command id
847 node_info->id_ = new_id;
848 return true;
849 }
850
851 int Grammar::GetCommandInputID(int command) const {
852 const std::vector<sc::Argument>& arguments = commands_[command].Arguments();
853 for (std::size_t i = 0, size = arguments.size(); i < size; i++) {
854 if (arguments[i].name_ == "in") {
855 return static_cast<int>(i);
856 }
857 }
858 return -1;
859 }
860
861 const Argument& Grammar::GetCommandArgument(int command_id,
862 int argument_id) const {
863 return commands_[command_id].Arguments()[argument_id];
864 }
865
866 bool Grammar::MutationInsertCommand(sg::Graph& graph) const {
867 // sanity check
868 if (boost::num_vertices(graph) == 0) {
869 throw std::runtime_error("sc::Grammar::MutationInsertCommand - graph is empty");
870 }
871 // pick a random edge as insertion point
872 sg::Edge edge_to_replace;
873 sg::Vertex source_command;
874 sg::NodeType source_type;
875 do {
876 edge_to_replace = boost::random_edge(graph, bx::RandomEngine());
877 source_command = boost::source(edge_to_replace, graph);
878 source_type = graph[source_command].type_;
879 } while (source_type != sg::kCommand);
880 // store target connection info
881 sg::Vertex target_command = boost::target(edge_to_replace, graph);
882 sg::Connection target_connection = graph[edge_to_replace];
883 // make the new command
884 const sg::Node& source_node = graph[source_command];
885 sg::CommandRate rate = source_node.rate_;
886 sg::Vertex new_command = CreateCommand(graph, rate, 0, true, false);
887 // remove the existing connection
888 boost::remove_edge(source_command, target_command, graph);
889 // connect the new command to the source
890 sg::Connection source_connection(GetCommandInputID(graph[new_command].id_));
891 // generate weighting for new connection
892 double weight = stdx::RandomCoefficient();
893 if (rate == sg::kAudioRate) {
894 weight *= weight;
895 }
896 source_connection.weight_ = weight;
897 boost::add_edge(source_command, new_command, source_connection, graph);
898 // connect new command to the target, use old connection weighting
899 boost::add_edge(new_command, target_command, target_connection, graph);
900 // success!
901 return true;
902 }
903
904 } // sc namespace