Array transitions

hey, i have an array filled with several sub arrays. like this:


[ [ 8 ], [ 3, 5 ], [ 3, 3, 2 ], [ 3, 1, 2, 2 ], [ 2, 1, 1, 2, 2 ], [ 1, 1, 1, 1, 2, 2 ], [ 1, 1, 1, 1, 1, 1, 2 ], [ 1, 1, 1, 1, 1, 1, 1, 1 ] ]

and I am sequencing the indices of the array to acces the different sub arrays.
my idea would be to use this concept:


to gradualy move from index 0 [8] to the last index in the array [ 1, 1, 1, 1, 1, 1, 1, 1 ].
does somebody has an idea how i could approach that?
The result of the function should be an array, which then could be used inside a Pseq.

Ive already found the Array.fib method.

thanks a lot :slight_smile:

Ive made a first attempt to write a function for the first use case with just one value gradually moving towards a second value by the fibonacci series.

I think the function should take 3 arguments. The size of the resulting array, a start value and an end value.
Beside some obvious coding problems. Im having trouble bringing together the size and the fibonacci numbers.

The fibonnaci series should somehow be calculated in relation to the specific array size and should be mirrored when reaching its end value with 1s instead of 0s i guess. any help is very much appreciated :slight_smile:

(
~fibTrans = { |size, start=0, end=1|

	var result;

	result = size.collect{ |item|

		var fib = [8, 5, 3, 2, 1, 1];
		var index = 0;

		if (item < ( fib[index] - 1), { start }, { end } );

	};

	result;
};

~fibTrans.(31, 0, 1);
)

The desired result should look like this:

[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1];

Maybe these functions will help get you started…

(
~fib_mirror_summing_to_at_least = {
    // returns a mirrored fibonacci series (e.g. [5, 3, 2, 1, 2, 3, 5])
    // having sum >= threshold
    |threshold = 30, a = 1, b = 1|
    var result = [];
    var total = 0;
    var temp;
    threshold = (threshold + b) / 2; //account for mirroring
    while {total < threshold}
    {
        result = result.add(b);
        total = total + b;
        temp = b;
        b = a + b;
        a = temp;
    };
    result.reverse.mirror;
};

~fib_transition = {
    |min_length = 30, word = #[0, 1], a = 1, b = 1|
    var num_pairs, fib_threshold, fib, result;
    num_pairs = word.size - 1;
    fib_threshold = min_length / num_pairs;
    fib = ~fib_mirror_summing_to_at_least.(fib_threshold, a, b);
    result = [];
    word.doAdjacentPairs {|a, b|
        var pair = [a, b];
        fib.do {|f, index|
            if(index == fib.size.div(2)) // reverse pair at the halfway point...
            {pair = pair.reverse};
            result = result.addAll(pair[0] ! (f - 1) ++ pair[1])
        };
    };
    result;
};
)

// test:
~fib_transition.(31);

// try with a string:
~fib_transition.(82, "seqh");

// try different fibonacci series:
~fib_transition.(21, "se", a: 2, b: 1);

// sound example:
(
~seq = ~fib_transition.(100, (0..7).scramble);
Pbind(\degree, Pseq(~seq).trace, \dur, 0.1).play;
)
2 Likes

thank you very much I would not have been able to figure that out. im still digesting the word.doAdjacentPairs part and was comparing the result for:

~fib_transition.(31);
-> [ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0 ]

with the example for (fibonacci-transition 31)
-> [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1];

a.)
one thing ive noticed is that the final array size of the example equals the min_length argument.
with ~fib_transition.(31); you get an array size of 37 and not 31. i think it would be favourable if the final array size would be equal to min_length.

b.) and additionally have a special case for the last item of the mirrored fibonacci series. i think the last value of the fibonacci series should only give the new value and not the old any more. in the example for ~fib_transitions.(31) you get 0 as the last item in the array. i think it would be more musically if it would be 1.

do you maybe have an idea how these things could be adjusted?
thanks so much :slight_smile:

a) It’s not possible to get a mirrored Fibonacci series with a sum equal to 31, at least not with the series that start at 1 with an initial step size of 0 or 1:

[5,3,2,1,2,3,5].sum; // 21
[5,3,2,1,1,1,2,3,5].sum; // 23
[8,5,3,2,1,2,3,5,8].sum; // 37
[8,5,3,2,1,1,1,2,3,5,8].sum; // 39

So how do you get 31? It looks like your example is using the array [8,5,3,2,1,1,1,2,3,5,8] and keeping the first 31 items. You can get close with this:

~fib_transition.(31, [0, 1], 0, 1).keep(31);

But this will not perfectly generate the array you are looking for because your array is not a perfectly mirrored fibonacci series. In fact, all 3 examples you gave use different versions of the fibonacci series or some other series, and they are not all symmetrical, so it is hard to tell exactly what algorithm is being used to generate them. It might help if you can describe in more concrete terms what you want the algorithm to do instead of just providing a few examples of input and output. The examples are helpful, but I’m not able to discern the algorithm from them.

1 Like

yes, thats true :slight_smile:

yes, i think so too. i think the example does something like that:

//example: fibonacci-transitions (31)

[
	0, 0, 0, 0, 0, 0, 0, 1, // 8
	0, 0, 0, 0, 1, // 5
	0, 0, 1, // 3
	0, 1, // 2
	1, // 1
	1, // 1
	0, 1, // 2
	0, 1, 1, // 3
	0, 1, 1, 1, 1, 1 // 6 (fill up the remaining steps until min_length with the end value, when no full cycle is possible?)

];

and the other one something like this (which makes no sense to me, could probably conceptualised differently):

//example: fibonacci-transitions (100, seqh)

[
	s, s, s, s, s, s, s, s, s, e // 10
	s, s, s, s, e // 5
	s, s, e, // 3
	s, s, e, // 3
	s, e, // 2
	s, e, e // 3
	s, e, e // 3
	s, e, e, e, e, e, e, e, e, e // 10
	
	q, e, e // 3
	q, e, e // 3
	q, e // 2
	q, e // 2
	q, q, e // 3
	q, q, e // 3
	
	q, q, q, q, q, q, q, q, q, h // 10
	q, q, h // 3
	q, h, // 2
	q, h // 2
	q, h, h // 3
	q, h, h, h, h, h // 6
	q, h, h, h, h, h, h, h, h, h, h, h, h, h, h //15

];

But putting the examples aside:

I think its great to know the final amount of steps it takes for the transition to happen and the easiest way to achieve that, is to have the final array size always equal to min_length
the ametricality isnt too bad i think.
so i have added result.keep(min_length); in the end of ~fib_transition thanks alot.

When i think about a transition i would like to have the end value gradually taking over the start value and in the end you would just have the end value and not the start value anymore.

but i think it would not work for every case to just drop the last item and insert a new value after having kept only min_length (this was my first idea) to get rid of the last 0 in the array.

in the end i would like to use an array with a bigger size instead of #[0, 1] where the first value of the array is the start value and the last value is the end value and all the other values are passed by on the way from start to end.

when i use ~fib_transition.(100, "seqh"); it doesnt look like h is dominating in the end like s was in the beginning.

i have to think more about that.
thanks so much for your help and patience.

okay, ive tried to come up with a solution for modifying the function.
It should fill up the remaining steps until min_length is reached only with the last item in the word array when no “full cycle” of the current item of the fibonnacci series is possible (if this makes any sense). To make sure you get a bunch of end values as the last items in the array.

EDIT: i think i made a mistake. right now its not just only for the last “cycle” in the array, its for the last “cycle” of the adjacent pairs or? so confusing…

(
~fib_mirror = { |threshold = 30, a = 1, b = 1|
	var result = [];
	var total = 0;
	var temp;
	threshold = (threshold + b) / 2;
	while {total < threshold}
	{
		result = result.add(b);
        total = total + b;
        temp = b;
        b = a + b;
        a = temp;
    };
    result.reverse.mirror;
};

~fib_transition = { |min_length = 30, word = #[0, 1], a = 1, b = 1|
    var num_pairs, fib_threshold, fib, result;

    num_pairs = word.size - 1;
    fib_threshold = min_length / num_pairs;
    fib = ~fib_mirror.(fib_threshold, a, b);
    result = [];

    word.doAdjacentPairs {|a, b|
        var pair = [a, b];
        fib.do { |item, index|

			if( index == (fib.size.div(2)),

				{ pair = pair.reverse }
			);
			
			result = result.keep(min_length);

			if ( item > (min_length - result.size - 1),

				{ result = result.addAll(pair[0] ! item ) },

				{ result = result.addAll(pair[0] ! (item - 1) ++ pair[1]) }
			);

			result;
		};

	};

	result;
};
)

i think the results have now a nice “cadence” in the end filled with the end value:

~fib_transition.(31, [0, 1], 0, 1);
~fib_transition.(100, "seqh");

Any ideas on my adjustments?

thanks :slight_smile:

hey, ive been trying to work on a sieve transition implementation.

when you use the Sieve class you can build mirrored sets of data (also usable for “non-retrogradable” rhythms)

(
~sieveMirror = { |modulus, residue|
	var sieve, lowestCommonMultiple;
	lowestCommonMultiple = (modulus * residue).debug(\LCM);
	sieve = Sieve.union(modulus, residue, Ref(lowestCommonMultiple));
	sieve = sieve.toIntervals.list.asArray.debug(\Sieve);
};

~sieveMirror.(5, 6);
)

LCM: 30
Sieve: 5, 1, 4, 2, 3, 3, 2, 4, 1, 5
-> [ 5, 1, 4, 2, 3, 3, 2, 4, 1, 5 ]

which would give you more flexibilty when using the transition function instead of the fibonacci series.

However im not sure how to implement its functionality for multiple pairs of data.
When i use the variable sieve_threshold like with the fibonacci transition the sets are used nicely one after the other, but i dont get the sieve length of 30. when I use lowestCommonMultiple as the Ref in sieve the data sets are not correctly used.

Does someone has an idea how to handle the threshold?

(
~sieveMirror = { |modulus, residue, lowestCommonMultiple|
	var sieve;
	sieve = Sieve.union(modulus, residue, Ref(lowestCommonMultiple));
	sieve = sieve.toIntervals.list.asArray.debug(\Sieve);
};

~sieveTrans = { |modulus, residue, word = #[0, 1], a = 0, b = 1|
    var sieve, result, lowestCommonMultiple, num_pairs, sieve_threshold;

	lowestCommonMultiple = (modulus * residue).debug(\LCM);
	
	num_pairs = word.size - 1;
	sieve_threshold = (lowestCommonMultiple / num_pairs).asInteger;

	sieve = ~sieveMirror.(modulus, residue, lowestCommonMultiple);
	//sieve = ~sieveMirror.(modulus, residue, sieve_threshold);
    
	result = [];

    word.doAdjacentPairs {|a, b|
        var pair = [a, b];
        sieve.do { |item, index|

			if( index == (sieve.size.div(2)),

				{ pair = pair.reverse }
			);

			result = result.keep(lowestCommonMultiple);

			if ( item > (lowestCommonMultiple - result.size - 1),

				{ result = result.addAll(pair[0] ! item ) },

				{ result = result.addAll(pair[0] ! (item - 1) ++ pair[1]) }
			);

			result;
		};

	};

	result;
};

~sieveTrans.(5, 6, #[0, 1, 2, 3], 0, 1);
)

i think i found a solution:
The advantage vs. the fibonacci approach is, that you know that the resulting array size is always LCM times num_pairs. what do you think?

(
// sieve transitions
~sieveMirror = { |modulus, residue, lowestCommonMultiple|
	var sieve;
	sieve = Sieve.union(modulus, residue, Ref(lowestCommonMultiple));
	sieve = sieve.toIntervals.list.asArray.debug(\Sieve);
};

~sieveTrans = { |modulus, residue, word = #[0, 1]|
	var sieve, result, lowestCommonMultiple, sieveTransSize, num_pairs;

	lowestCommonMultiple = (modulus * residue).debug(\LCM);
	sieve = ~sieveMirror.(modulus, residue, lowestCommonMultiple);

	num_pairs = word.size - 1;
	sieveTransSize = (lowestCommonMultiple * num_pairs).debug(\size);

	result = [];

	word.doAdjacentPairs {|a, b|
		var pair = [a, b];

		sieve.do { |item, index|

			if( index == (sieve.size.div(2)),

				{ pair = pair.reverse }
			);

			result = result.keep(sieveTransSize);

			if ( item > (sieveTransSize - result.size - 1),

				{ result = result.addAll(pair[0] ! item ) },

				{ result = result.addAll(pair[0] ! (item - 1) ++ pair[1]) }
			);

			result;
		};

	};

	result;
};

~sieveTrans.(3, 5, #[0, 1, 2, 3]);
)