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