Gyrus - item assignment

Hi,
My name is Yuval and I just started to work with gyrus! :slight_smile:
I’m having some difficulties and I was hoping to get some help.
I’m attaching the next simple code, which is trying to calculate some value that depends on gyrus.stimuli (let’s call the stimuli q_hat)

    q = np.array([-0.69066467, -0.20034368,  0.28437363,  0.00342465,  0.10304996], dtype='float')

    def calc(q_hat):
        J = np.zeros((1))
        #J = gyrus.stimuli(np.zeros((1)))
        **J[0] = np.sin(q_hat)**
        return J[0]

    def gyrus_calc(q_hat, dt, synapse=None):
        return q_hat.integrate_fold(
            integrand=lambda q_hat: dt * calc(q_hat) / 1e-3,
            synapse=synapse,
        )

    op = gyrus_calc(
        q_hat=gyrus.stimuli(np.zeros_like(q)),
        dt=0.05,
    ).filter(0.2)  

But when trying to assign np.sin(q_hat) to np.array I get the error:

----> 6   J[0] = np.sin(q_hat)
ValueError: setting an array element with a sequence.

I assumed that’s because np.sin(q_hat)'s type is <class 'gyrus.base.Fold'>
So I’ve tried to make J the same type (by losing the comment in the code)
but then I got the following error:
TypeError: 'Fold' object does not support item assignment

So my question is, how do I assign a value like np.sin(q_hat) to something like J[0] ?

Thanks and very nice tool!
Yuval

Hi Yuval! Thanks for trying out Gyrus and letting us know what the difficulty was.

The first thing that I’d point out is that if you try to do the same thing just in NumPy alone then you also get the exact same error:

q = np.array([-0.69066467, -0.20034368,  0.28437363,  0.00342465,  0.10304996], dtype='float')
J = np.zeros((1))
J[0] = np.sin(q)

=>

----> 3 J[0] = np.sin(q)

ValueError: setting an array element with a sequence.

This is because q is 5-dimensional, and so is np.sin(q), but J[0] is a scalar. NumPy doesn’t like it when you try to use an array to assign to a scalar element.

The same applies to the Gyrus Fold inside of calc. If you add print(q_hat.shape) inside of there you will see it print (5,) which means it is 5-dimensional, just like the NumPy array, q.

Would something like:

def calc(q_hat):
    J = np.sin(q_hat)
    return J

do what you need? This would apply the sin function to each of the 5 elements (using 5 populations of spiking neurons by default).

This also works and is closer to your original code:

def calc(q_hat):
    J = np.zeros(1, dtype=object)
    J[0] = np.sin(q_hat)
    return J[0]

because J is now an array of arbirary objects, and NumPy is okay in this case with assigning an array to an arbitrary object. Here, again J[0] is the result of applying np.sin to every element of q_hat. And so this is just a more complicated way of doing the exact same thing as in the previous function. Thus the previous function would be preferred to this one.

In general it might help to try calling your calc function with a NumPy array (with the same shape as q_hat, which is (5,) in this example), and see if it does what you need purely in NumPy before calling it within Gyrus.

Let me know if you run into anything else here or have more questions. This is a very new piece of software that’s still “pre-alpha” and so there will be some rough edges when it comes to how helpful the error messages are, but I’m happy to help out and the feedback is helpful in making things easier!

Hi,
Thank you so much for the several solutions, my program is a little more complicated than that, so when trying to make a simpler version of it I ran into this silly mistake…

Here is a piece of code that maybe closer to my real program:

q = np.array([-0.69066467, -0.20034368,  0.28437363,  0.00342465,  0.10304996], dtype='float')
A = np.array([0.47833606, 0.46394772, 0.39643308], dtype='float')

def calc(q_hat):
    J = np.zeros((3,5), dtype=object)
    a = np.sin(q_hat)
    b = np.cos(q_hat)
    a0 = a[0]
    b0 = b[0]
    J[0,0] = 0.208*a0*b0
    return J

def gyrus_calc(A, q_hat, dt, synapse=None):
    return q_hat.integrate_fold(
        integrand=lambda q_hat: dt * np.dot(np.linalg.pinv(calc(q_hat)), A) / 1e-3,
        synapse=synapse,
    )

op = gyrus_calc(
        A=gyrus.stimuli(A).configure(
        n_neurons=1000,
        input_magnitude=2,
        seed=0,
    ),
    q_hat=gyrus.stimuli(np.zeros_like(q)),
    dt=0.05,
).filter(0.2)

And I’m getting the following errors:

AttributeError: 'Multiply' object has no attribute 'conjugate'

TypeError: loop of ufunc does not support argument 0 of type Multiply which has no callable conjugate method

Thank you very much for the help, and sorry for the silly questions,
Yuval

No worries – questions aren’t silly at all.

The reason we’re getting a weird error message now basically has to do with the mixed types in J. When calc returns J it contains both a Gyrus operator at index [0, 0] as well as an integer (0) at every other index. So then when NumPy tries to compute np.linalg.pinv it ends up trying to mix these two types together in a way that is not currently supported.

I am guessing that in your full program the array J won’t be mixed-type. In this case it should work after casting J to a Gyrus operator with gyrus.asoperator(J), for example:

def calc(q_hat):
    J = np.zeros((3,5), dtype=object)
    a = np.sin(q_hat)
    b = np.cos(q_hat)
    a0 = a[0]
    b0 = b[0]
    ab = 0.208*a0*b0
    J[:, :] = ab
    return gyrus.asoperator(J)

def gyrus_calc(A, q_hat, dt, synapse=None):
    return q_hat.integrate_fold(
        integrand=lambda q_hat: dt *  calc(q_hat).T.dot(A) / 1e-3,
        synapse=synapse,
    )

However note that I had to get rid of the np.linalg.pinv. If you try to keep that in Gyrus will complain that it does not know how to implement this. I think you’ve seen this matrix inversion example already but this shows how you can implement matrix inversion in Gyrus. If you need the pseudoinverse in particular then you might need to do something similar to this example – I will think about this some more when I have a chance and I hear back. :slight_smile:

Thanks! yes, I am familiar with this example, it’s a really helpful one.
I will try to implement pseudoinverse somehow, and if you have an idea out of the blue, it will be great :slight_smile:
thanks again,
Yuval