Trying to understand Prewrite

Hello,

I am trying to wrap my head around the Prewrite class.

My understanding is that the three arguments it takes are:

  1. first key of the dictionary to start with
  2. IdentityDictionary which defines the rules for rewriting (though Event is used in the
    help file examples)
  3. how many iterations of transformations/rewrites to complete

Here is a slightly modified bit of code from the help file I was hoping to use to understand Prewrite’s behavior:

(
a = Prewrite(0, // start with 0
    (
        0: #[2, 1],
        1: #[1, 0],
        2: #[0, 2]
), 3);
x = a.asStream;
)
x.next.postln

My expectation is that the dictionary in the second argument uses the keys to specify the rules for transformation, so in this case 0 transforms into the sequence 2, 1. 1 transforms into 1, 0; 2 transforms into 0, 2.

I also expect that the third argument sets how many “complete levels” of transforms to complete before either ending the stream, or looping. And also that the first argument provides the first key to transform/start with.

So in this case here’s what I’d expect to see, hopefully my grouping makes things concise, not distracting or confusing:

// iteration one:
0 // first key, provided as the first argument becomes:
-> [ 2, 1 ] // which then gets fed back in as the first arguments:

// iteration two:
[ 2, 1 ] // results of first iteration used as input
-> [ 0, 2 ] [ 1, 0 ] // which then get fed back in:

// iteration three:
[ 0, 2, 1, 0] // results of second iteration used as input
-> [ 2, 1] [ 0, 2 ], [ 1, 0 ] [ 2, 1 ]

// Prewrite seems to loop, so loop back around and start over

And I almost get that, except when I run the code up top, things seem to wrap back to the start prematurely.

As I evaluate x.next.postln the results I get is the following chain:

2, 1, 0, 2, 1, 0, 2, 1, and I’d expect things to keep going and provide 0, 2, 1, 0, 2, 1 next, but
that’s not what happens, at this point the sequence seems to loop back around, providing the next set of numbers: 2, 1, 0, 2, 1, 0, 2, 1 etc… It will keep looping those.

Written another way, again hopefully for clarity, here’s what I get:

// iteration one:
0 // first key becomes:
-> [ 2, 1 ] 

// iteration two:
[ 2, 1 ] // results of first iteration used as input
-> [ 0, 2 ] [ 1, 0 ]

// iteration three, here's where the sequence doesn't seem to complete:
[ 0, 2, 1, 0 ] // results of second iteration used as input
-> [ 2, 1 ] // and instead of the expected rest of the sequence, these are the numbers to come out

-> [ 2, 1 ] [ 0, 2 ] [ 1, 0 ] // and repeats and repeats

So ok, perhaps then I’m misunderstanding the iteration levels, instead of three complete input / output cycles, iterations are how many steps from the start position it takes, in this case 3, which would mean it only considers as the inputs (after starting with/translating from the initial key of 0):

2, 1,  0,

Which in this case, it is then understandable the results I get:

2, 1,  0, 2,  1, 0 

Each input key returns 2 output numbers, 3 inputs gives me a 6 number long chain. So then I sort of expected a 14 value long chain, so if I set the third argument to 14…

And at this point things get more confusing for me- depending on whether the third argument is an even or odd number, it changes the first returned value, thus the entire sequence differs and all of my assumptions break down.

I’ve looked at the source code and that hasn’t helped me get a better understanding of this behavior.

Can someone please provide some clarity as to how this class actually works and why?

Thanks!
Boris

Hey Boris!

Interesting topic - I stumbled upon Prewrite through this article: https://theseanco.github.io/howto_co34pt_liveCode/3-6-L-Systems-For-Rhythm/ - Really inspiring read and the Renick Bell track mentioned there is really captivating.

I also had some trouble wrapping my head around how Prewrite works. I’d think of it as an recursive loop where the number of recursions is determined by the third argument. Prewrite will always feed the last array back into itself and replace all values as specified in the IdentityDictionary. Since in your dictionary 0, 1 and 2 are all replaced by two other values, your array is always gonna double in length with each iteration. As you made explicit, after 3 iterations you’ll have an array with the length 8: 2, 1, 0, 2, 1, 0,2,1
At a 4th iteration it’s gonna turn out a array of sixteen. Since 2 gets replaced by 0, 2, that array is gonna start with 0: 0,2,1,0,2,1,0,2,1,0,2,1,0,2,1,0
Now, if you do a fifth operation that first 0 is gonna be replaced by 2,1 making that new array start with a 2 (and be 32in length).
And so on and so on. That’s why for even numbers your resulting array is always gonna start at 2 and for odd ones with 0.
I hope that explanation is gonna help you. (And I hope I remembered correctly how Prewrite works as I can’t test it right now.)

1 Like

Oh! Thank you @staxl! I think I’m just about getting it. Prewrite doesn’t pass through each level in the values it returns… I’m not sure exactly the best way to phrase it succinctly, but it erm “invisibly” passes through those levels to find the starting point. The returned values are only of the level that’s provided as an argument.

I was thinking of levels as steps, or something, to reach the third one you must first go through the first and second, and it does that, but does not return that aspect.

And thanks for that link! Seems like the Renick Bell track is no longer online, but that is a great resource!

You’re welcome!
Exactly as you said

The returned values are only of the level that’s provided as an argument.

That sums it up pretty well.
Oh, too bad that Renick Bell track’s gone from Soundcloud. But here’s another one in a similar vain: https://iclc2017.bandcamp.com/track/fractal-beats-160316a-edit