Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

return reference to a submatrix from function in numpy

>>> a = np.arange(9).reshape((3, 3))
>>> a
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])
>>> def sub(a):
...  return a[:2, :2]
... 
>>> sub(a)
array([[0, 1],
       [3, 4]])
>>> sub(a) = np.arange(4).reshape((2, 2))
  File "<stdin>", line 1
SyntaxError: cant assign to function call
>>> t = a[:2, :2]
>>> t = np.arange(4).reshape((2, 2))
>>> a
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])
>>> a[:2, :2] = np.arange(4).reshape((2, 2))
>>> a
array([[0, 1, 2],
       [2, 3, 5],
       [6, 7, 8]])

And that's pretty obvious why it happens like this: when I type t = .., I just 'relink' t to other data in memory. But the questions are:

  1. how I can hack that and pass reference to the submatrix out of function? and
  2. still be able to change this submatrix's values?
like image 395
Ben Usman Avatar asked Oct 31 '22 17:10

Ben Usman


2 Answers

  1. how I can ... pass reference to the submatrix out of function?

Simply put, you cannot return an l-value from a function call in Python like you can in C++. In your case, Python assumes that you are assigning a value directly to the sub(a), which is a function call, and not to the object returned by it.

Offcourse, you can use Indexing to get the reference to the original object as described later. That will allow you to change the part of the original matrix.

  1. and still be able to change this submatrixes values?

You can change the values of the submatrix of an array inside the function itself, like so:

def sub(a):
    a[:2, :2] = np.arange(4).reshape((2,2))
    return a[:2, :2]

This will not only return the modified submatrix, but it will also change the array itself.


Objects are Pass-by-Reference but their References are Pass-by-Value:

Just like Java, Python is pass-by-value, so all objects are passed as references into the function, and these references are passed by value.

So when you index this array object and modify its value inside the function, you are modifying the value of the location in memory that this reference points to, but if you change the reference itself then it won't modify the original object because its reference was only passed by value.

Use Indexing to Pass the Reference to an Object by Value:

Following this explanation, you can even go further and return the object's reference from the function by value and modify the matrix using it outside the function:

  1. Call the function, sub(a), which will return the reference by value to the submatrix, which is itself the reference to the original matrix passed by value.
  2. Assign this call to function to another variable, which will pass the reference to the submatrix by value to this new variable, x = sub(a)
  3. Index this submatrix to select all its contents and modify their value: x[:] = np.ones((2,2))
  4. This will also modify the original matrix a because you have modified the value of the location in memory to which x is referring.

    >>> x = sub(a)
    >>> x[:] = np.ones((2,2))
    >>> x
    array([[1, 1],
           [1, 1]])
    >>> a
    array([[1, 1, 2],
           [1, 1, 5],
           [6, 7, 8]])
    

    OR, as a shortcut:

    >>> sub(a)[:] = np.ones((2,2))
    >>> a
    array([[1, 1, 2],
           [1, 1, 5],
           [6, 7, 8]])
    

Changing the Reference doesn't change the Object:

  1. However, now if you set variable x to np.ones((2,2)) then a would not change, because by doing so, you are changing the reference itself which was passed by value.

    >>> x = 2         # this won't change a because x is a reference passed by value
    >>> a
    array([[1, 1, 2],
           [1, 1, 5],
           [6, 7, 8]])
    
like image 140
Pacific Stickler Avatar answered Nov 04 '22 11:11

Pacific Stickler


The correct action in Python is:

In [97]: t=sub(a)
In [98]: t
Out[98]: 
array([[0, 1],
       [3, 4]])
In [100]: t[:]=np.arange(4).reshape(2,2)
In [101]: t
Out[101]: 
array([[0, 1],
       [2, 3]])
In [102]: a
Out[102]: 
array([[0, 1, 2],
       [2, 3, 5],
       [6, 7, 8]])

The function returns a view of the original array, the same as doing t = a[:2, :2] outside the function. t=... reassigns the variable, but t[:]=... modifies the contents (array elements) of the view. And since it is a view, the original array contents also change. This is same as your a[:2, :2] =....

As long as you use the [:], you don't need the intermediate variable assignment:

In [104]: sub(a)[:]=np.zeros((2,2))
In [105]: a
Out[105]: 
array([[0, 0, 2],
       [0, 0, 5],
       [6, 7, 8]])
like image 43
hpaulj Avatar answered Nov 04 '22 10:11

hpaulj