Hard Math Question

Hey everyone, this is a difficult math-related algorithm
that I’ve been trying to get to the bottom of for ages.

It comes straight from a critical project, the code here
is slightly modified for testing.

Using a code-based interface for an 8x8 hardware matrix,
I want to first supply the one dimensional array for
the MIDI noteOn/Off messages within the instantiation
of the two-dimensional lookup using Array2D.

Simple. This part is a no-brainer.

The difficult part comes next… I want to be able to translate
the 2-dimensional lookup into it’s exact 3-dimensional counterpart.

That’s where I need someone’s help… all the code and the testing
is directly below.

The main advantage of this is working towards a more universal
design scheme… only one line would need to be changed (inside
Array2D) and everything else would follow suit.

Here’s the code:

(((
    this .y_
    
    (
        (  ) .x_
        
        (
            
            Array2D ( 9 , 9 ) .with
            
            (
                ( 11 , 21 .. 91 )  +.x  ( ..8 )
            )
            
        )
    )
    
    // y.x [ 0 , 0 ] -> lower left corner / origin
    
    .n_
    
    (
        
        fill3D
        
        (
            Array , function:
            
            {
                |x , y , z|
                
                var a = 0 ;
                
                var b = 0 ;
                
                (
                    this.n ??
                    
                    {
                        this.n_ ( MultiLevelIdentityDictionary [] ) .n
                    }
                )
                
                . put
                
                (
                    x , y , z , this.
                    
                    y.x
                    
                    [
                        (a
                            // a ?
                        )
                        
                        ,
                        
                        (b
                            // b ?
                        )
                    ]
                )
            }
            
            , planes: 4 , rows: 4 , cols: 4
            
            // planes represent
            
            // adjacent quadrants
            
            // in the 8x8 matrix
            
            // [ 2 ] [ 3 ]
            
            // [ 0 ] [ 1 ]
        )
    )
    
    ;
    
    ~n = // the current implementation... 100% equivalent
    
    (
        MultiLevelIdentityDictionary [
            
            
        ]
        
        . putTree
        
        (
            0 , [ 0 , [ 0
                , y.x[0, 0] , 1
                , y.x[0, 1] , 2
                , y.x[1, 0] , 3
                , y.x[1, 1] ] ] ,
            0 , [ 1 , [ 0
                , y.x[0, 2] , 1
                , y.x[0, 3] , 2
                , y.x[1, 2] , 3
                , y.x[1, 3] ] ] ,
            0 , [ 2 , [ 0
                , y.x[2, 0] , 1
                , y.x[2, 1] , 2
                , y.x[3, 0] , 3
                , y.x[3, 1] ] ] ,
            0 , [ 3 , [ 0
                , y.x[2, 2] , 1
                , y.x[2, 3] , 2
                , y.x[3, 2] , 3
                , y.x[3, 3] ] ] ,
            1 , [ 0 , [ 0
                , y.x[0, 4] , 1
                , y.x[0, 5] , 2
                , y.x[1, 4] , 3
                , y.x[1, 5] ] ] ,
            1 , [ 1 , [ 0
                , y.x[0, 6] , 1
                , y.x[0, 7] , 2
                , y.x[1, 6] , 3
                , y.x[1, 7] ] ] ,
            1 , [ 2 , [ 0
                , y.x[2, 4] , 1
                , y.x[2, 5] , 2
                , y.x[3, 4] , 3
                , y.x[3, 5] ] ] ,
            1 , [ 3 , [ 0
                , y.x[2, 6] , 1
                , y.x[2, 7] , 2
                , y.x[3, 6] , 3
                , y.x[3, 7] ] ] ,
            2 , [ 0 , [ 0
                , y.x[4, 0] , 1
                , y.x[4, 1] , 2
                , y.x[5, 0] , 3
                , y.x[5, 1] ] ] ,
            2 , [ 1 , [ 0
                , y.x[4, 2] , 1
                , y.x[4, 3] , 2
                , y.x[5, 2] , 3
                , y.x[5, 3] ] ] ,
            2 , [ 2 , [ 0
                , y.x[6, 0] , 1
                , y.x[6, 1] , 2
                , y.x[7, 0] , 3
                , y.x[7, 1] ] ] ,
            2 , [ 3 , [ 0
                , y.x[6, 2] , 1
                , y.x[6, 3] , 2
                , y.x[7, 2] , 3
                , y.x[7, 3] ] ] ,
            3 , [ 0 , [ 0
                , y.x[4, 4] , 1
                , y.x[4, 5] , 2
                , y.x[5, 4] , 3
                , y.x[5, 5] ] ] ,
            3 , [ 1 , [ 0
                , y.x[4, 6] , 1
                , y.x[4, 7] , 2
                , y.x[5, 6] , 3
                , y.x[5, 7] ] ] ,
            3 , [ 2 , [ 0
                , y.x[6, 4] , 1
                , y.x[6, 5] , 2
                , y.x[7, 4] , 3
                , y.x[7, 5] ] ] ,
            3 , [ 3 , [ 0
                , y.x[6, 6] , 1
                , y.x[6, 7] , 2
                , y.x[7, 6] , 3
                , y.x[7, 7] ] ]
        )
    )
    
    ;
    
    Post << (toUpper(cs
        
        (
            Array.fill3D( 4,4,4, 
                
                { |x y z| 
                    
                    ~n[x,y,z] === n[x,y,z] 
                }
            )
            
            .flat  // remove the tilde in line 81 to see desired result
            
            .every { |x|  x.notNil and: x } // 
            
        )
        
    )) << ("!") << $\n // -> TRUE!
    
)))

?

I wouldn’t use Array3D, but only array methods.
The two dimensions are, I suppose mapping amplitudes.
What are the three dimensional ones?

Hello, thank you.

I’m searching for the proper maths that would allow one to (elegantly) supply a one-dimensional set of hardware midi/sysex message #'s, and then produce lookup tables for two and three dimensional matrics.

Array 2D

// y.x.array [ 0 ] → origin
// y.x [ 0 , 0 ] → origin

Now, I want to use this.n to as the selector for three-dimensional access syntax, via:

n [ x , y , z ]

It is far from elegant… however, currently, the only to achieve this, is by hard-coding all of the values:

If you take a good look at the code, everything is carefully applied & demonstrated.

this.y.x -> 2D from 1D

this.n -> solve lines // a? & // b?

~n -> hardcode / inelegant brute force

Post << // test script must return TRUE

You can see an example of a positive test result,
by changing the hardcoded global ~n simply to this.n.

It is at once highly desired & extremely critical for a long-standing project… the n definition still currently persists as an outstanding eyesore of approaching 100 lines… when it should need less than 30.

Observing the incremental nuances in the x & y indices, as they progress within the global ~n definiton, there appears to be strong implication that there is a methodical & mathematical (calculated) relationship

The detail requiring extra attention, appears to be the offset values of each matrix origin (perhaps…)

Instead of using Array.fill3D, as I believe was mentioned earlier, it may exercise more control to implement the following using only array methods:

4.do
{
    |x|

    4.do
    {
        |y|

        4.do
        {
            |z|

        }
    }
}

Without knowing what you are trying to achieve, it is a bit tedious to understand your implementation.
So again:

can you list what the two dimensions and the three dimensions are?

That seems pretty evident… it’s just that, with apologies, I’ve looked over this thread and I just can’t make sense of the requirement.

Could you perhaps show a simpler example, with 3 instead of 9 elements? And instead of code, perhaps show the concrete input and concrete desired output?

Also with apologies, I’m struggling with the indentation and bracketing style in the code examples… it’s different enough from the way I do it that I find it difficult to understand… not saying that either way is wrong or right, only that it isn’t communicating clearly to me.

hjh

Ok & really thank you… this is what I want:


// using i & j, make n come true

i = ( 11 , 21 .. 81 )  +.x  ( ..7 ) 

// i [ index ] 

j = ( Array2D ( 8 , 8 ) .with ( i ) ) 

// j [ row , cell ]

((  n [ 0 , 0 , 0 ] === j [ 0 , 0 ]  and: 
    
    n [ 3 , 3 , 3 ] === j [ 7 , 7 ]    ))

/*  the objective is to produce a 

mathematical & automated script 

in order to use this.n as a 

lookup table for individual cells

in the 8x8 or 64 pad matrix, by 

supplying n with three matrix indices:

n [ quadrant, sub-quadrant, cell ]

[ 2 ] [ 3 ]
[ 0 ] [ 1 ]

*/

// using only i & j, how would one

// mathematically produce the desired

// form of access ?

((  
    
    n = Library[]; 
    
    n.put(0, 0, 0, i[0]);
    
    n.put(3, 3, 3, j[7, 7])

))

 // for the entire 64 pad / 8x8 matrix ?

This is the current hardcode solution:

 this.n_

        (
            MultiLevelIdentityDictionary

            [

            ]

            . putTree

            (

                0 , [ 0 , [ 0
                    , y.x[0, 0] , 1
                    , y.x[0, 1] , 2
                    , y.x[1, 0] , 3
                    , y.x[1, 1] ] ] ,
                0 , [ 1 , [ 0
                    , y.x[0, 2] , 1
                    , y.x[0, 3] , 2
                    , y.x[1, 2] , 3
                    , y.x[1, 3] ] ] ,
                0 , [ 2 , [ 0
                    , y.x[2, 0] , 1
                    , y.x[2, 1] , 2
                    , y.x[3, 0] , 3
                    , y.x[3, 1] ] ] ,
                0 , [ 3 , [ 0
                    , y.x[2, 2] , 1
                    , y.x[2, 3] , 2
                    , y.x[3, 2] , 3
                    , y.x[3, 3] ] ] ,
                1 , [ 0 , [ 0
                    , y.x[0, 4] , 1
                    , y.x[0, 5] , 2
                    , y.x[1, 4] , 3
                    , y.x[1, 5] ] ] ,
                1 , [ 1 , [ 0
                    , y.x[0, 6] , 1
                    , y.x[0, 7] , 2
                    , y.x[1, 6] , 3
                    , y.x[1, 7] ] ] ,
                1 , [ 2 , [ 0
                    , y.x[2, 4] , 1
                    , y.x[2, 5] , 2
                    , y.x[3, 4] , 3
                    , y.x[3, 5] ] ] ,
                1 , [ 3 , [ 0
                    , y.x[2, 6] , 1
                    , y.x[2, 7] , 2
                    , y.x[3, 6] , 3
                    , y.x[3, 7] ] ] ,
                2 , [ 0 , [ 0
                    , y.x[4, 0] , 1
                    , y.x[4, 1] , 2
                    , y.x[5, 0] , 3
                    , y.x[5, 1] ] ] ,
                2 , [ 1 , [ 0
                    , y.x[4, 2] , 1
                    , y.x[4, 3] , 2
                    , y.x[5, 2] , 3
                    , y.x[5, 3] ] ] ,
                2 , [ 2 , [ 0
                    , y.x[6, 0] , 1
                    , y.x[6, 1] , 2
                    , y.x[7, 0] , 3
                    , y.x[7, 1] ] ] ,
                2 , [ 3 , [ 0
                    , y.x[6, 2] , 1
                    , y.x[6, 3] , 2
                    , y.x[7, 2] , 3
                    , y.x[7, 3] ] ] ,
                3 , [ 0 , [ 0
                    , y.x[4, 4] , 1
                    , y.x[4, 5] , 2
                    , y.x[5, 4] , 3
                    , y.x[5, 5] ] ] ,
                3 , [ 1 , [ 0
                    , y.x[4, 6] , 1
                    , y.x[4, 7] , 2
                    , y.x[5, 6] , 3
                    , y.x[5, 7] ] ] ,
                3 , [ 2 , [ 0
                    , y.x[6, 4] , 1
                    , y.x[6, 5] , 2
                    , y.x[7, 4] , 3
                    , y.x[7, 5] ] ] ,
                3 , [ 3 , [ 0
                    , y.x[6, 6] , 1
                    , y.x[6, 7] , 2
                    , y.x[7, 6] , 3
                    , y.x[7, 7]
                ] ]
            )

This should explain everything more concisely:

…before the code, in regular terms, my goal is to access this form of syntax:

n [ x , y , z] // n [ quadrant, sub-quadrant, cell ]

…using a script that will iterate over all 64 ordered midi message integer numbers for a typical hardware 8x8 pad matrix / midi controller.

((( this .clearAll
    
    .y_
    
    (   (   )
        
        .x_
        
        (
            Array2D (8 , 8) .with ( (11, 21..81) +.x (..7) )
        )
    )
    
    .n_ ( Library [] )
    
    ; 
    
    # i , j = 0 ! 2 
    
    ;
    
    4.do
    {|q| //
        4.do
        {|r| //
            4.do
            {|v| n.put ( q , r , v , y . x [ i , j ] );    
                //
    }}};
)));

// solve the script for n

// with resulting syntax for n:

// n [ quadrant , sub-quadrant, cell ]

/*
    
    // below demonstrates the 
    
    // brute-force equivalent:

((
    
    MultiLevelIdentityDictionary 
    [ 

    ]
    .putTree 
    (
        0 , [ 0 , [ 0 
            , y.x[0, 0] , 1 
            , y.x[0, 1] , 2
            , y.x[1, 0] , 3
            , y.x[1, 1] ] ] ,
        0 , [ 1 , [ 0
            , y.x[0, 2] , 1
            , y.x[0, 3] , 2
            , y.x[1, 2] , 3
            , y.x[1, 3] ] ] ,
        0 , [ 2 , [ 0
            , y.x[2, 0] , 1
            , y.x[2, 1] , 2
            , y.x[3, 0] , 3
            , y.x[3, 1] ] ] ,
        0 , [ 3 , [ 0
            , y.x[2, 2] , 1
            , y.x[2, 3] , 2
            , y.x[3, 2] , 3
            , y.x[3, 3] ] ] ,
        1 , [ 0 , [ 0
            , y.x[0, 4] , 1
            , y.x[0, 5] , 2
            , y.x[1, 4] , 3
            , y.x[1, 5] ] ] ,
        1 , [ 1 , [ 0
            , y.x[0, 6] , 1
            , y.x[0, 7] , 2
            , y.x[1, 6] , 3
            , y.x[1, 7] ] ] ,
        1 , [ 2 , [ 0
            , y.x[2, 4] , 1
            , y.x[2, 5] , 2
            , y.x[3, 4] , 3
            , y.x[3, 5] ] ] ,
        1 , [ 3 , [ 0
            , y.x[2, 6] , 1
            , y.x[2, 7] , 2
            , y.x[3, 6] , 3
            , y.x[3, 7] ] ] ,
        2 , [ 0 , [ 0
            , y.x[4, 0] , 1
            , y.x[4, 1] , 2
            , y.x[5, 0] , 3 
            , y.x[5, 1] ] ] ,
        2 , [ 1 , [ 0
            , y.x[4, 2] , 1
            , y.x[4, 3] , 2
            , y.x[5, 2] , 3
            , y.x[5, 3] ] ] ,
        2 , [ 2 , [ 0
            , y.x[6, 0] , 1 
            , y.x[6, 1] , 2
            , y.x[7, 0] , 3
            , y.x[7, 1] ] ] ,
        2 , [ 3 , [ 0
            , y.x[6, 2] , 1
            , y.x[6, 3] , 2
            , y.x[7, 2] , 3
            , y.x[7, 3] ] ] ,
        3 , [ 0 , [ 0
            , y.x[4, 4] , 1
            , y.x[4, 5] , 2
            , y.x[5, 4] , 3
            , y.x[5, 5] ] ] ,
        3 , [ 1 , [ 0
            , y.x[4, 6] , 1
            , y.x[4, 7] , 2
            , y.x[5, 6] , 3
            , y.x[5, 7] ] ] ,
        3 , [ 2 , [ 0
            , y.x[6, 4] , 1
            , y.x[6, 5] , 2
            , y.x[7, 4] , 3
            , y.x[7, 5] ] ] ,
        3 , [ 3 , [ 0
            , y.x[6, 6] , 1
            , y.x[6, 7] , 2
            , y.x[7, 6] , 3
            , y.x[7, 7] ] ] 
    )
))

At least for the putTree part, I realized that you can use streams to define the sequences.

Taking:

        0 , [ 0 , [ 0 
            , y.x[0, 0] , 1 
            , y.x[0, 1] , 2
            , y.x[1, 0] , 3
            , y.x[1, 1] ] ] ,

Labeling the changing values – based on structure, not on values:

        a , [ b , [ c 
            , y.x[d, e] , c
            , y.x[d, e] , c
            , y.x[d, e] , c
            , y.x[d, e] ] ] ,

Then:

a = Pdup(4, Pseries(0, 1, inf)).asStream;
b = Pn(Pseries(0, 1, 4), inf).asStream;
c = Pn(Pseries(0, 1, 4), inf).asStream;

z = Pn(Pdup(2, Pseq([0, 2, 0, 2, 4, 6, 4, 6], 1)), inf) +.x Pseq([0, 1], 1);
d = Pdup(2, z).asStream;
e = z.asStream;

~bigArray = Array.fill(16, {
	[a.next, [
		b.next,
		Array.fill(4, {
			// note, I'm not going to try to populate a 'y.x' thingy
			// so I'm just generating a placeholder, you can adjust as you like
			[c.next, "y.x[%, %]".format(d.next, e.next)]
		}).flatten(1)
	]]
}).flatten(1);

theThing.putTree(* ~bigArray)

I’ll admit that I haven’t checked the entire ~bigArray very carefully, but at the beginning it seems to follow the pattern. So there might be some mistake… you can tweak the patterns on your own.

hjh

Very innovative… I see your angle.

There are some very interesting methods listed strictly in MultiLevelIdentityDictionary… a lot can be done with them.