Подтвердить что ты не робот

Неожиданный результат с + = на массивах NumPy

Я создаю симметричные матрицы/массивы в Python с NumPy, используя стандартный метод:

x = rand(500,500)
x = (x+x.T)
all(x==x.T)
> True

Теперь позвольте быть умным:

x = rand(500,500)
x += x.T
all(x==x.T)
> False

Подождите, что?

x==x.T
> array([[ True,  True,  True, ..., False, False, False],
       [ True,  True,  True, ..., False, False, False],
       [ True,  True,  True, ..., False, False, False],
       ..., 
       [False, False, False, ...,  True,  True,  True],
       [False, False, False, ...,  True,  True,  True],
       [False, False, False, ...,  True,  True,  True]], dtype=bool)

Верхний левый и нижний правый сегменты симметричны. Что делать, если я выбрал меньший массив?

x = rand(50,50)
x += x.T
all(x==x.T)
> True

OK....

x = rand(90,90)
x += x.T
all(x==x.T)
> True

x = rand(91,91)
x += x.T
all(x==x.T)
> False

И просто чтобы быть уверенным...

x = rand(91,91)
x = (x+x.T)
all(x==x.T)
> True

Является ли это ошибкой, или я собираюсь узнать что-то сумасшедшее о += и массивах NumPy?

4b9b3361

Ответ 1

Операция transpose возвращает представление массива, что означает, что новый массив не выделяется. Это, в свою очередь, означает, что вы одновременно читаете и изменяете массив. Трудно сказать, почему некоторые размеры или некоторые области результата работают, но, скорее всего, это связано с тем, как numpy имеет дело с добавлением массива (возможно, он делает копии подматриц) и/или представлениями массива (возможно, для небольших размеров он создает новый массив).

Операция x = x + x.T работает, потому что вы создаете новый массив, а затем назначаете x, конечно.

Ответ 2

Детали реализации, упомянутые другими, называются буферизацией. Вы можете больше узнать об этом в документах по итерации массива.

Если вы посмотрите на свой неудачный пример чуть подробнее:

>>> a = np.random.rand(91, 91)
>>> a += a.T
>>> a[:5, -1]
array([ 0.83818399,  1.06489316,  1.23675312,  0.00379798,  1.08967428])
>>> a[-1, :5]
array([ 0.83818399,  1.06489316,  1.75091827,  0.00416305,  1.76315071])

Итак, первое неправильное значение - 90*91 + 2 = 8192, что неудивительно, что мы получаем от:

>>> np.getbufsize()
8192

И мы также можем установить его выше, а затем:

>>> np.setbufsize(16384)  # Must be a multiple of 16
8192  # returns the previous buffer size
>>> a = np.random.rand(91, 91)
>>> a += a.T
>>> np.all(a == a.T)
True

Хотя теперь:

>>> a = np.random.rand(129, 129)
>>> a += a.T
>>> np.all(a == a.T)
False

Это, конечно, опасная вещь, чтобы полагаться на правильность, поскольку это действительно деталь реализации, подлежащая изменению.

Ответ 3

Проблема заключается в том, что добавление не выполняется "сразу"; x.T - это вид x, так как вы начинаете добавлять к каждому элементу x, x.T мутируется. Это испортит более поздние дополнения.

Тот факт, что он работает для размеров ниже (91, 91), почти определенно является детальностью реализации. Использование

x = numpy.random.rand(1000, 1000)
x += x.T.copy()
numpy.all(x==x.T)
#>>> True

исправляет это, но тогда у вас действительно нет свободного места.