# Parsing array into interpretable string for genetic algorithm

I was wondering whether a solution exists to parse an array of strings and numbers into a string that can be executed by the interpreter. A very simple example like:

``````[2, 0.1, 440, "SinOscAr", "*", "!"]
``````

would be parsed into:

``````"(SinOsc.ar(440)*0.1)!2"
``````

The underlying idea is to create a genetic algorithm approach to sound creation, each array being an individual. And if I could avoid having to write a parser for the project, that would save me a lot of time

Thanks!

1 Like

Are you receiving the input, in your example `[2, 0.1, 440, "SinOscAr", "*", "!"]`, as a String or a structured collection (for example an Array)? And is there a reason your output must be a string rather than the actual array of SinOsc’s?

Two thoughts on this:

.) Following Brian’s question: if you want to have different data in an Array and then produce a SynthDef therewith, you might want to wrap UGens in Functions, then you can take them over. See these discussions: {}.play on a UGen stored in variable?
https://www.listarc.bham.ac.uk/lists/sc-users-2012/msg22935.html

.) You could also use the preProcessor method for Strings (more hassle)
https://www.listarc.bham.ac.uk/lists/sc-users-2016/msg52531.html

.) You could also poke around with the ‘interpret’ method, but this is also often a complication.

I suppose the first approach could fit your demands. Here an example how to integrate operators like ‘+’ and ‘*’:

``````// in your application x,y could be items of an array

x = { |a, b| BinaryOpUGen('+', a, b) };

y = { |a, b| BinaryOpUGen('*', a, b) };

u = { x.(LFTri.ar(200), LFTri.ar(163)) * 0.1 }.play

u.release

v = { y.(LFTri.ar(200), LFTri.ar(163)) * 0.1 }.play

v.release``````
1 Like

One of the things that I like to say about programming is that all programming reduces to three questions:

1. What information do I have? (Arrays that have been manipulated by a genetic algorithm.)
2. What information do I want? (A SynthDef.)
3. What operations will transform #1 into #2?

Particularly #3 is not a trivial question (and this is usually an iterative process) – but it all comes down to this.

One catch in this specific case is: If there should be a deterministic relationship between 1 and 2, then 1 needs to be unambiguous.

• OK: Input A --> Output Z, and Input B --> Output Z. (In SC, `rrand(1, 5)` and `1 rrand: 5` are different inputs but compile to the same result.)
• Not OK: Input A --> Output Y and Input A --> Output Z.

You might run into problems with the input design that you posted.

It looks like a kind of FORTH style reverse Polish notation – and if they’re already separated into items like this, then the parsing is finished. (Parsing = converting a flat code string into a data structure of tokens – but you already have the tokens.) It would be fairly easy to create a stack and then let the opcodes manipulate the stack:

• Push 2
• Push 0.1
• Push 440
• SinOsc pulls 440 and pushes the UGen instance.
• `*` pulls the SinOsc instance and 0.1 and pushes the `*` result.
• etc.

That wouldn’t necessarily give you a code string, but it would give you the UGen graph as a resulting data structure. (Or perhaps the "push"es are all strings.)

BUT: To be unambiguous, you can’t skip arguments.

SinOsc takes two arguments: frequency and phase. In that data structure, how do you know that 0.1 is not the SinOsc phase? Currently, there’s no information to disambiguate. That is: Input A `[0.1, 440, "SinOscAr"]` may correspond to Output Y `SinOsc.ar(440)` with a residual of 0.1 to be used in a later operation, or to Output Z `SinOsc.ar(440, 0.1)`. That’s exactly a situation that’s disallowed in compiler design.

So the data structure may need some revision.

hjh

1 Like

Maybe an alternative approach: if you unparse your DNA into valid supercollider syntax, you could write the unparsed code in a file, then load the file with .loadRelative or using the Require quark (https://github.com/scztt/Require.quark).

``````(
~conv = {
arg bits=[];
var str;
str=format("{(%(%)%%)%%}",bits[3],bits[2],bits[4],bits[1],bits[5],bits[0]);
str.compile;
};
)

f=~conv.value([2, 0.1, 440, "SinOsc.ar", "*", "!"]);
f.play;
``````

Not super-generic but if your array is well structured, it might work.

A more general solution might follow these lines. It’s extensible by adding to ~opcodes.

``````(
~stack = (
stack: Array.new,
pushItem: { |self, item|
self
},
popItem: { |self, item|
var return = self[\stack].last;
self[\stack] = self[\stack].drop(-1);
return
},
topItem: { |self| self[\stack].last },
resetToEmpty: { |self|
self[\stack] = Array.new;
self
}
);

~opcodes = Dictionary[
// entries are "regular expression string" -> { |stack, item| ... }
// the function should push its result back onto the stack
// assuming only freq as an argument
"SinOscAr" -> { |stack, item|
var freq = stack.popItem;
stack.pushItem("SinOsc.ar(%)".format(freq));
},
// regexp requires + at the end of the []... well, OK
"^[-*/%&|!<=>?+]+\$" -> { |stack, item|
var a = stack.popItem;
var b = stack.popItem;
stack.pushItem("(% % %)".format(a, item, b))
},
\default -> { |stack, item|
stack.pushItem(item)
}
// more opcodes as needed
];

~findOpcode = { |item|
var match = block { |break|
~opcodes.keysDo { |k, v|
if(k.respondsTo(\matchRegexp) and: { k.matchRegexp(item) }) {
break.(k);
};
};
\default
};
~opcodes[match]
};

~compile = { |array|
var stack = ~stack.copy.resetToEmpty;
array.do { |item|
item = item.asString;
~findOpcode.(item).value(stack, item);
};
stack.topItem
};
)

~compile.([2, 0.1, 440, "SinOscAr", "*", "!"]);
-> ((SinOsc.ar(440) * 0.1) ! 2)
``````

Note though that `[1, 4, "-"]` would be 4-1 rather than 1-4 – perhaps surprising to humans but legit if you’re generating the arrays algorithmically.

hjh

2 Likes

First of all, thanks to everyone for their answers, I really appreciate!

The only important part is that I start with a collection of tokens (operators, UGens, numbers), and with it I can create a synth that produces a sound (hopefully since the original arrays will be generated randomly).

You’re totally right. I’ll have a fixed number of arguments for each token (with a default value in case the stack is empty).

Yep! I was inspired by the HP 48 calculator that I used when I was at uni. (https://en.wikipedia.org/wiki/HP_48_series#Programming)

This is an excellent starting point, thanks for taking the time!

Together with @jamshark70’s stack, I think we’ve got a good starting point.

I’ll keep you posted as the experiment continues!

1 Like