Julia: Arraypocalypse сейчас и тогда

Созданный на 16 сент. 2015  ·  276Комментарии  ·  Источник: JuliaLang/julia

Эта проблема заменяет 0,4 работу по массиву нирваны (# 7941), и отслеживает вопросы , которые мы намерены завершить в течение 0,5 и за ее пределами - в настоящее время обновляется через работу на 0,7. Это общий вопрос, который позволяет отслеживать конкретные задачи в других вопросах. Не стесняйтесь добавлять то, что я пропустил.

Требуемые базовые технологии

  • [x] Проверка и удаление родных границ Джулии (# 7799). Было сделано несколько попыток, но я считаю, что текущий план действий состоит в том, чтобы скрыть блоки кода @inbounds elide внутри макроса @boundscheck , распространяясь только на один уровень встраивания (https: / /github.com/JuliaLang/julia/issues/7799#issuecomment-117362695). Это строгое требование для последующих шагов. (реализовано в # 14474)
  • [x] ReshapedArrays (# 10507). Требуется лучшая производительность: https://groups.google.com/d/msg/julia-dev/7M5qzmXIChM/kOTlGSIvAwAJ

Основные изменения поведения взлома 0.5

  • [x] Параметры перетаскивания, проиндексированные скаляром (https://github.com/JuliaLang/julia/issues/4774#issuecomment-81228816; в более общем плане, срез в стиле APL, где ранг среза является суммой рангов индексы, см. ниже). PR на # 13612.
  • [x] Установите переключатель в положение устаревания конкатенации (# 8599)
  • [x] Убрано бездействие по умолчанию для (c) транспонирования (# 13171).
  • [x] Изменить изменение поведения sub на slice (# 16846)

Основные изменения поведения взлома 0.6

  • [x] Векторное транспонирование возвращает ковектор (https://github.com/JuliaLang/julia/issues/4774#issuecomment-81228816). Реализация в # 19670.
  • [x] Сопряжение векторов возвращает ленивую оболочку (# 20047)

Возможные будущие критические изменения

  • [x] Транспонирование и спряжение матриц возвращают ленивые оболочки (# 25364)
  • [] Возвращать фрагменты как просмотры. По-прежнему неясно, являются ли возможные изменения производительности последовательными и достаточно большими, чтобы оправдать поломку. См. Https://github.com/JuliaLang/julia/issues/3701.
  • [] Должны ли сокращения уменьшать размеры? # 16606

Новый функционал

  • [x] Разрешить выражение переменных определенной длины (# 11242). Это позволяет нам в полной мере использовать # 10525.
  • [x] Откажитесь от специального понижения Ac_mul_Bt, вместо этого используйте диспетчерскую отправку в оболочках с отложенным транспонированием. (# 5332, # 25217)
  • [x] Измерения, проиндексированные многомерными массивами, добавляют измерения (полный APL-стиль: размерность результата - это сумма размерностей индексов). (# 15431)
  • [x] Разрешить любой тип индекса в нескалярной индексации (# 12567). Ужесточить скалярную индексацию до индексов <: Integer и расширить нескалярную индексацию до <: Union{Number, AbstractArray, Colon} (https://github.com/JuliaLang/julia/pull/12567#issuecomment-170982983). Более систематическое преобразование индексов, так что любой тип индекса может быть преобразован в Int или AbstractArray : # 19730
  • [] Более легкое создание неизменяемых массивов с кортежами и # 12113.

Другие спекулятивные возможности

  • [] Изменяемый тип буфера фиксированного размера, который допускает родное для Julia определение Array (# 12447); этот тип также может использоваться для буферов ввода-вывода и хранения строк.
  • [x] База IndexSet IntSet на BitArray или, возможно, на любом AbstractArray{Bool} . (# 20456)
  • [x] Переработайте нескалярную индексацию, чтобы предотвратить вызов find для логических массивов, и просто оберните его вместо IndexSet LogicalIndex ? (# 19730)
  • [] Отрицательное индексирование с дополнением IndexSet (https://github.com/JuliaLang/julia/issues/1032) или специальным типом Not ? (Возможно в пакете: https://github.com/mbauman/InvertedIndices.jl)
  • [x] Устарела линеаризация конечных размеров, когда предоставляется более одного индекса (частичная линейная индексация). (# 20079)
  • [x] Разрешить индексирование только в N-мерные массивы с 1 или N индексами, исключая «частичное» индексирование и конечные одиночные измерения (https://github.com/JuliaLang/julia/issues/5396 и # 14770).
  • [] Найдите альтернативный синтаксис для типизированных массивов - индексация в тип ( T[...] ) - это своего рода плохой каламбур. Этот синтаксис особенно плох, поскольку некоторые варианты анализируются как индексация в тип, а другие анализируются как специальные формы (типизированный hvcat, типизированные понимания)
  • [x] Измените хеширование на кодирование длин серий различий массивов, что позволит целочисленным диапазонам и массивам снова хешировать как равные. (https://github.com/JuliaLang/julia/issues/12226#issuecomment-122952826, # 16401)
  • [x] Переместить разреженные массивы из базы в стандартный пакет. (# 25249)
  • [x] Разрешить нетрадиционные индексы (# 16260)
  • [x] @sub @view макрос (# 16564)
arrays linear algebra

Самый полезный комментарий

Base и ArrayViews могут использовать view . В конце концов, ImageView также использует view :)

Все 276 Комментарий

Похоже, это отличный список, Мэтт, спасибо. Я немного боюсь
последствия, но это будет огромный шаг вперед для языка.

Во вторник, 15 сентября 2015 г., Мэтт Бауман [email protected]
написал:

Эта проблема заменяет работу 0,4 по направлению к массивной нирване (# 7941
https://github.com/JuliaLang/julia/issues/7941) и будет отслеживать
проблемы, которые мы стремимся решить в течение 0.5. Это общий вопрос, и он будет
отслеживать конкретные задачи в других вопросах. Не стесняйтесь добавлять то, что
Я пропустил (по крайней мере, в первой половине 0.5).

_Требуемые базовые технологии_

  • Проверка и удаление родных границ Джулии (# 7799
    https://github.com/JuliaLang/julia/issues/7799). Несколько попыток
    было сделано, но я считаю, что текущий план действий состоит в том, чтобы
    @inbounds удаляет блоки кода, скрытые в макросе @boundscheck ,
    распространение вниз только на один уровень встраивания (# 7799 (комментарий)
    https://github.com/JuliaLang/julia/issues/7799#issuecomment-117362695).
    Это строгое требование для последующих шагов.
  • ReshapedArrays (# 10507
    https://github.com/JuliaLang/julia/pull/10507). Требуется лучше
    спектакль:
    https://groups.google.com/d/msg/julia-dev/7M5qzmXIChM/kOTlGSIvAwAJ

_Решающие изменения поведения_

  • Отбросьте размеры, проиндексированные скаляром (# 4774 (комментарий)
    https://github.com/JuliaLang/julia/issues/4774#issuecomment-81228816)
  • Возвращать фрагменты в виде представлений. Первая попытка была сделана на # 9150.
    https://github.com/JuliaLang/julia/pull/9150. Особое внимание может
    быть необходимо для BitArrays.
  • Установите переключатель в положение устаревания конкатенации (# 8599
    https://github.com/JuliaLang/julia/pull/8599)

_Новая функциональность_

  • Разрешить выражение varargs определенной длины (# 11242
    https://github.com/JuliaLang/julia/pull/11242). Это позволяет нам
    воспользоваться всеми преимуществами # 10525
    https://github.com/JuliaLang/julia/pull/10525.
  • Транспонирование возвращает ковектор или тип транспонирования (# 4774 (комментарий)
    https://github.com/JuliaLang/julia/issues/4774#issuecomment-81228816)
  • Откажитесь от специального понижения Ac_mul_Bt, вместо этого используйте отправку.
  • Измерения, проиндексированные многомерными массивами, добавляют измерения (полная
    APL-стиль: размерность результата - это сумма
    размерности индексов)
  • Разрешить любой тип индекса в нескалярном индексировании (# 12567
    https://github.com/JuliaLang/julia/pull/12567)
  • Более легкое создание неизменяемых массивов с кортежами и # 12113
    https://github.com/JuliaLang/julia/pull/12113.

_Другие спекулятивные возможности_

  • Изменяемый тип буфера фиксированного размера, который позволяет
    Определение массива Julia-native (# 12447
    https://github.com/JuliaLang/julia/issues/12447)
  • Базовый набор индексов на BitArray или, возможно, на любом AbstractArray {Bool}.
  • Переработайте нескалярное индексирование, чтобы предотвратить вызов поиска для логических массивов.
    а вместо этого просто обернуть его IndexSet?
  • Отрицательное индексирование с дополнением IndexSet? (Возможно в упаковке)

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/JuliaLang/julia/issues/13157.

Да, отличный список! Засучим рукава!

Есть ли из этого невостребованные части? Я бы хотел помочь, но я
не хочу никому наступать на пятки.

Во вторник, 15 сентября 2015 г., Джефф Безансон [email protected]
написал:

Да, отличный список! Засучим рукава!

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/JuliaLang/julia/issues/13157#issuecomment -140586293.

Это феноменальный список. На самом деле есть только несколько очень ломких изменений, что приятно, но они достаточно значительны.

Работы определенно более чем достаточно! Я не думаю, что какая-либо из этих задач востребована.

Такие вещи, как удаление скалярных размеров, - это довольно простые изменения, но для поиска и исправления ошибок потребуется много работы ... и над этой частью легко сотрудничать. То же самое касается представлений (если вы игнорируете проблемы производительности с ReshapedArrays и inbounds). Любой желающий может окопаться!

Просмотры - это сложно, вы должны пройти через бутстрап без: hocho: себя в: eyes :.

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

Спасибо за это @mbauman , так много, чтобы переварить

Я добавил в качестве элемента «Удалить поведение по умолчанию для (c) транспонирования». Я ожидаю много жалоб, но, как мы обсуждали ранее, просто неправильно предполагать, что <:Any является скаляром, и логическая ошибка поднимается вверх каждый раз, когда кто-то пытается обернуть и / или реализовать пользовательские типы массивов / матриц. . cc @jakebolewski @andreasnoack

Думаю, нам нужно тщательно продумать варианты этого. Довольно идиоматично написать A' для транспонирования несложной матрицы.

Возможно ли, что оболочка транспонирования (c) решит эту проблему? Придется проделать большую работу по дизайну, но:

transpose(A::AbstractVectorOrMatrix) = TransposeType(A) # Will call `transpose` upon indexing, too
transpose(::AbstractArray) = error("cannot take transpose of 3+ dim array") # yeah, error methods aren't the best…
transpose(x) = x

связанный: я думаю, что некоторые из проблем с типизацией в linalg (и поведением транспонирования) возникают из-за того, что мы представляем заблокированный линейный оператор как массив массивов. Возможно, мы захотим переключиться на новый тип, который знает о различных размерах внутри, для этого, я помню, как обсуждал это с @andreasnoack. 0.5 может быть поводом хотя бы подумать об этом.

Возможно ли, что оболочка транспонирования (c) решит эту проблему?

Может быть; мы должны подумать об этом.

Проблема с блокировкой в ​​прошлый раз заключается в том, что транспонирование должно быть рекурсивным, чтобы правильно обрабатывать такие случаи, как Matrix{Matrix} (Пример: A = [rand(1:5, 2, 2) for i=1:2, j=1:2]; A' ), но люди хотят писать A' на 2D-массивах на не -числовые типы (например, изображения, as Matrix{<:Colorant} ) и ожидать, что транспонирование не будет применяться к скалярным элементам. Для обработки таких случаев существует метод no-op transpose(x::Any) . Однако это определение конфликтует с матричными объектами, которые имеют алгебраическую семантику матриц, но не хранятся внутри в какой-либо форме, подобной массиву, и, следовательно, согласно # 987 не должно быть AbstractArray ( QRCompactWYQ - дочерний элемент-плакат, но у нас много таких примеров). Если вы вводите новый матричный тип, вам нужно явно определить (c)transpose иначе вы получите откат без операции, который является источником многих ошибок.

Чтобы было ясно, поведение, которое мы явно нарушим, - это утверждение (которое вы можете найти в справке для permutedims ), что

Транспонирование эквивалентно permutedims (A, [2,1]).

Эта эквивалентность не имеет смысла для типов, которые не являются AbstractArray s и представляют собой AbstractArray s нескаляров, и у нас действительно есть матричные типы, которым требуется этот более абстрактный смысл транспонирования.

Я думаю, если предположить, что Matrix {Matrix} будет автоматически рекурсивно
переставлять элементы плохо и опасно. Я бы предпочел особый тип
BlockMatrix {Матрица}, которая делает то, что вы ищете.

В среду, 16 сентября 2015 г., в 10:30, Цзяхао Чен [email protected]
написал:

Возможно ли, что оболочка транспонирования (c) решит эту проблему?

Может быть; мы должны подумать об этом.

Проблема с блокировкой в ​​прошлый раз заключается в том, что транспонирование должно быть рекурсивным для
обрабатывать такие случаи, как Matrix {Matrix} (пример: A = [rand (1: 5, 2, 2) для
i = 1: 2, j = 1: 2]; A ') правильно. Однако люди хотят писать A 'на 2D
массивы нечисловых типов (например, изображения в виде Matrix {<: Colorant}) и
ожидайте, что транспонирование не будет применяться к скалярным элементам. Безоперационный
Для обработки таких случаев существует метод transpose (x :: Any). Однако это
определение конфликтует с матричными объектами, которые имеют алгебраический
семантика матриц, но не хранятся внутри в какой-либо форме, подобной массиву,
и, следовательно, # 987 https://github.com/JuliaLang/julia/issues/987 должен
не быть AbstractArray (QRCompactWYQ - дочерний элемент плаката, но у нас есть
много таких примеров). Если вы вводите новый матричный тип, вы должны
явно определить (c) транспонировать, иначе вы получите откат без операции, который
является источником множества ошибок.

Чтобы было ясно, поведение, которое мы бы явно нарушили, заключается в следующем:

Транспонирование эквивалентно permutedims (A, [2,1]).

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

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/JuliaLang/julia/issues/13157#issuecomment -140759002.

@tbreloff , это именно моя точка зрения. Большинство людей считают странным, что транспонирование должно быть рекурсивным, но оно _ должно_ быть математически правильным транспонированием, и это беспокойство выявляет угловые случаи, когда транспонирование - это не просто permutedims(A, [2,1]) . (Хотя верно, что Matrix{Matrix}} самом деле не является типом заблокированной матрицы, потому что нет абсолютно никаких гарантий, что внутренние матрицы имеют размеры, согласующиеся с любым разбиением более крупной матрицы.)

Ах да, я забыл обо всех тензорных объектах, которые не являются AbstractArrays. Независимо от того, что происходит, либо авторам тензороподобных объектов нужно будет каким-то образом сообщить Джулии, что они не скаляры (иногда это работает AbstractArray, но не всегда), либо авторам скалярных объектов нужно будет сделать обратное (иногда число работает, но не всегда) или и то, и другое. Тот же самый вопрос «скаляр или нет» возникает повсюду… например, индексирование: https://github.com/JuliaLang/julia/pull/12567.

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

Поскольку мы не можем передать это через супертипы (https://github.com/JuliaLang/julia/issues/987#issuecomment-31531602), я думаю, что это должно быть либо за счет лучшей документации требуемых методов, либо за счет явного кодирования этих методов в систему, основанную на признаках. И если мы удалим все запасные варианты (что гарантирует правильное поведение за счет отсутствия методов для всех), я думаю, нам нужно сделать это как можно проще с помощью трейтов.

+1

Я действительно думаю, что нам следует начать перечисление признаков для AbstractArray. Линейное индексирование, кажется, было удивительным примером силы нескольких тщательно продуманных проектных решений, связанных с особенностями.

Одно из возможных решений - оставить AbstractArray <: Any и ввести рядом с ним AbstractNonArrayTensor <: Any . Все остальное будет считаться "скалярным" в том, что касается индексации и линейной алгебры.

Обратите внимание, что это отличается и гораздо более четко определено, чем различие Atom vs. Collection (https://github.com/JuliaLang/julia/pull/7244#issuecomment-46333261) ; A[:] = (1,2,3) и A[:] = "123" ведут себя совершенно иначе, чем A[:] = 1:3 для A = Array(Any, 3) , как и должно быть.

Я действительно думаю, что нам следует начать перечисление признаков для AbstractArray.

@johnmyleswhite Вы случайно не имели в виду, что язык поддерживает "черты характера"? Это то, что я действительно хотел видеть в языке после JuliaCon.

Да, я имел в виду черты, поддерживаемые языком.

Есть ли у вас какие-либо идеи / предложения о том, как синтаксически добавить черты к Джулии? Они были бы очень полезны для массивов, строк, кодировок, по крайней мере.

Я предпочитаю оставить эти решения Джеффу, а не строить предположения.

Учитывая, что это общий вопрос, было бы неплохо обсудить конкретные вопросы в их собственных вопросах.

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

Пожалуйста, переместите обсуждение характеристик в # 5, а вопрос (c) перенесите в # 13171.

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

Хорошо, хорошо, если черты характера рассматриваются как часть дизайна.

Верхний и нижний для треугольных матриц? Мне они кажутся хорошими кандидатами на роль черт характера.

Добавлен спекулятивный элемент для замены индексации на тип в качестве синтаксиса для типизированных массивов.

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

По мере того, как мы перемещаем некоторые другие функции из базы, мы можем сначала убрать различные разреженные решатели. Если в этот момент в JuliaSparse хорошо формируются альтернативные разреженные структуры данных, мы можем в конечном итоге переместить туда и SparseMatrixCSC.

Как насчет небольшой задачи по уборке дома - переместить весь код массива в его собственный подкаталог и модуль в Base?

Есть множество вещей, отмеченных как веха 0.5. Я хотел бы предложить, чтобы упомянутые здесь проблемы были помечены как 0.5 blocker , а остальные 0,5, по сути, были 0.5 nice to have . Тогда у нас может быть период времени 1-2 недели для накопления полного списка проблем 0.5 blocker и выпуска 0.5, как только все блокировщики будут готовы. Любые приятные проблемы, которые будут исправлены в те же временные рамки, превратятся в 0.5, но выпуск не будет блокировать какие-либо приятные функции, которые не готовы.

Хочу добавить несколько дополнительных возможностей. Я очень взволнован Arraypocolypse!

Во-первых, я думаю, что имеет смысл иметь операторы для тензорной суммы и тензорного произведения. Это могут быть ++ и ** или символы юникода \otimes и \oplus . Тензорное произведение может заменить kron() . Тензорная сумма дает конкатенацию векторов и, кроме того, это отличное описание конкатенации строк (об этом было много, много разговоров, и основные разработчики Julia, похоже, обеспокоены тем, что + соответствует неправильному моноиду здесь - и поэтому перейдите к * потому что он, возможно, кажется менее коммутативным - хотя я бы сильно утверждал, что правильная операция - это тензорная сумма \oplus которая также не коммутативна). Их также можно использовать для массивов / тензоров более высокого порядка (см. Ниже) и, возможно (я предполагаю здесь) операций реляционной алгебры на DataFrames ?.

Во-вторых, мне пришло в голову, что Джулия может быть действительно хороша в изучении многолинейной алгебры с очень красивым синтаксисом. Я играл с пакетом для присвоения имен каждому индексу в виде массива как части типа с использованием типа Tuple{...} . Массив превращается в тензор, индексируя его как A[Tuple{:index1,:index2}] и если Julia 0.5 использует {} для Tuple{} тогда он значительно улучшается до A[{:index1,:index2}] и мы получаем следующая удивительность:

  1. Перестановка: A[{1,2,3}] = B[{3,2,1}]
  2. Новый способ выполнения матричного и / или векторного умножения, A[{:a}] = B[{:b,:a}] * C[{:b}] (сжимается по индексу :b , поэтому A = B' * C ).
  3. Аналогично, произвольное тензорное сжатие высшего порядка (также известное как einsum): например, A[{:a,:b}] = B[{:a,:c,:d}] * C[{:c,:e}] * D[{:d,:f}] * E[{:e,:f:,:b}] . Скобки можно использовать для выбора самого быстрого порядка усадки.
  4. Тензорная сумма и тензорное произведение здесь также имеют смысл - так, например, люди могут брать внешние произведения массивов более высокого порядка и легко размещать индексы в желаемом порядке.

Синтаксис перестановки может решить еще одну проблему - transpose - это элемент линейной алгебры, но поскольку набрать ' он используется вместо permutedims , например, для отражения изображение (2D-массив значений пикселей). Эти различные варианты использования, по-видимому, вызывают проблему относительно того, вызывается ли transpose рекурсивно, но я бы сказал, что этот «тензорный» синтаксис для permutedims достаточно ясен для использования в общем программировании и данных / манипулирование массивами.

Я не уверен, понравится ли людям такой подход к полилинейной алгебре (он довольно сильно зависит от сгенерированных функций), но я был бы рад обратной связи!

Не могли бы вы подробнее рассказать о тензорной сумме и конкатенации векторов (и строк)? Я считаю, что было бы здорово иметь «общий» оператор конкатенации, который также нравится математикам.

Тензорная сумма (или прямая сумма v = [v_1, v_2, v_3] и w = [w_1, w_2, w_3] записывается как v \ oplus w = [v_1, v_2, v_3, w_1, w_2, w_3]. (Представьте себе Я использую LaTeX - \ oplus и \ otimes уже работает в Julia REPL и выглядит как + (или раз), нарисованный кружком вокруг них).

Тензор / прямая сумма - это действительно математический способ объединения векторов (например, vcat ), и я бы также предложил строки. Для матриц A \ oplus B - это большая блочно-диагональная матрица [A 0; 0 B], (и является хорошим кандидатом для обозначения BlockedArray s, упомянутого выше). Для транспонированных (строковых) векторов тензорная сумма снова представляет собой просто конкатенацию, заданную hcat , поэтому к этому придется относиться по-разному в зависимости от того, был ли у вас вектор-строка или матрица 1 на n (что, надеюсь должно быть ОК в Юлии 0.5).

Итак, если мы думаем о строках как о векторах символов, тогда прямые / тензорные суммы - это путь, если основные разработчики собираются использовать такие слова, как «моноид», в качестве руководящего принципа синтаксиса. Можно ли рассматривать манипуляции со строками как моноидальную категорию, в которой параллельные стрелки являются конкатенациями ?? Конечно, полилинейная алгебра над действительными (или комплексными) числами задается (кинжалом) симметричными бимоноидальными категориями, где тензор / прямая сумма (\ oplus) и тензор / внешнее / прямое произведение (\ otimes) являются двумя «моноидами».

@andyferris, пожалуйста, откройте новый выпуск или PR для тензорной конкатенации. Этот выпуск достаточно длинный и сложный, но без дополнительных функций.

Взаимосвязь между многомерными массивами и полилинейной алгеброй обсуждалась в литературе по APL 1970-х годов, особенно Тренчардом Мором и другими, работавшими над APL2.

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

( @andyferris , см. также https://github.com/Jutho/TensorOperations.jl, https://github.com/mbauman/AxisArrays.jl, https://github.com/timholy/AxisAlgorithms.jl.)

Я хотел бы поделиться с вами некоторым кодом, который я сделал некоторое время назад и который, как мне кажется, обрабатывает, достигает одной точки в пространстве дизайна для операций с массивами, которые отражают локальность и поддерживают разреженность как первый класс. Я подозреваю, что для его переноса потребуются черты характера, но вы можете найти некоторые из идей полезными https://github.com/wellposed/numerical/tree/master/src/Numerical/Array/ Разместите модули макета специально

Спасибо @timholy за эти ссылки; Я не знал двух последних.

Как насчет небольшой задачи по уборке дома - переместить весь код массива в его собственный подкаталог и модуль в Base?

@ViralBShah Звучит как очень хорошая идея - также переместите модульные тесты массива, чтобы между реализацией и тестовыми файлами было хорошее соответствие 1-1.

Должны ли мы сдвинуть дело с мертвой точки, щелкнув переключатель устаревания конкатенации (# 8599)?

+1

У @mbauman есть ветка, в которой реализовано большинство транспонированных изменений. @jiahao меня поправят , если я ошибаюсь здесь, но новое поведение tranpose могут быть разделены на транспонированной матрицы и вектора транспонирования , где бывший меньше controversal так , чтобы добиться определенного прогресса здесь и начать процесс настройки кода, я Предлагаю начать только с типа транспонирования матрицы и немного отложить перенос вектора. @mbauman Как вы думаете, это возможно, и есть ли у вас время, чтобы сделать это разделение, если мы решим, что это полезный способ продолжить?

Сами типы транспонирования довольно просты - всего несколько десятков LOC. И типы транспонирования матрицы и вектора сами по себе не связаны между собой (за исключением того, что создание матрицы транспонирования представлений без изменения векторов было бы даже более странным, чем статус-кво).

Самая сложная часть направления, в котором я двигался в mb / transpose, - это то, что я _также_ пытался удалить специальное понижение до Ax_mul_Bx. Эта часть представляет собой тонну работы, в которой каждый оператор содержит сотни перестановок типов транспонирования, специальных типов матриц и типов элементов, совместимых с BLAS. Я не совсем добрался до конца туннеля за mul , и я даже не начал заниматься другими операторами в этой ветке. Удаление специального понижения также является той точкой, где нам нужно принять решение о транспонировании вектора.

Тем не менее, нет необходимости связывать эти два изменения друг с другом. Тип транспонирования _ позволяет_ удалить специальные шаги понижения Ax_op_Bx и даже упрощает часть этого беспорядка, но удаление Ax_op_Bx не обязательно для введения транспонированных представлений. Я думаю, что здесь нужно делать более мелкие шаги. Хотя было очень информативно попытаться решить обе проблемы вместе (это работает! И может упростить жизнь!), Это слишком сложно, чтобы откусить сразу.

Я думаю, что должна быть возможность добавить простые резервные варианты, например, *(A::Transpose, B::AbstractArray) = At_mul_B(untranspose(A),B) , чтобы транспонировать представления, работающие с текущей системой, не затрагивая сотни методов. Сейчас мое время очень ограничено, поэтому я не могу обещать, что найду время, чтобы заняться этим сам. Но я думаю, что это можно сделать за относительно небольшой PR. Здесь может поднять факел любой желающий.

Не могли бы мы начать с определения методов Ax_mul_Bx в терминах
транспонировать типы?

В четверг, 5 ноября 2015 г., Мэтт Бауман [email protected] написал:

Реализация самих типов транспонирования
https://github.com/JuliaLang/julia/blob/335200c142e368cad9aba885df5539d2755195ad/base/transpose.jl
все довольно просто - всего несколько десятков LOC.

Самая сложная часть направления, в котором я двигался в mb / transpose, - это
что я _также_ пытался убрать специальное понижение до Ax_mul_Bx. Тот
часть - это тонна работы, где каждый оператор содержит сотни
перестановки типов транспонирования, специальных типов матриц и BLAS-совместимых
типы элементов. Я не добрался до конца туннеля для мула,
и я даже не стал заниматься другими операторами в этой ветке.
Удаление специального опускания также является той точкой, где нам нужно сделать
решение о переносе вектора.

При этом нет необходимости связывать эти два изменения с каждым
разное. Тип транспонирования _ позволяет_ удалить Ax_op_Bx
специальные шаги опускания, и даже упрощает часть этого беспорядка, но удаление
Ax_op_Bx не требуется для введения транспонированных видов. я думаю
что путь вперед - делать меньшие шаги. Хотя было очень
информативно, чтобы попытаться решить обе проблемы вместе (это работает! и это может сделать вещи
симпер!), это слишком, чтобы откусить за один раз.

Я думаю, что должна быть возможность добавить простые резервные варианты, например, * (A :: Transpose,
B :: AbstractArray) = At_mul_B (непереносить (A), B). Мое время очень ограничено
в наши дни, поэтому я не могу обещать, что найду время, чтобы сделать это сам. Но я
думаю, это можно было бы сделать при относительно небольшом пиаре. Добро пожаловать в
возьми факел здесь.

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/JuliaLang/julia/issues/13157#issuecomment -154101995.

Как дела с Arraypocalypse? Планируется ли к концу января выпустить релиз 0.5 со всеми перечисленными выше вещами?

В 0.5 происходит много других вещей, но AFAICT мало что происходит с массивами этого конкретного списка. Некоторые из нас, которые в прошлом много делали для массивов Джулии, заняты (отчасти пожинают плоды нашей предыдущей работы: wink :). Чтобы это произошло, возможно, другим придется подойти к тарелке (подсказка). Но только что опубликованный номер 14474, возможно, является самым захватывающим событием на этом фронте за долгое время - во многих смыслах это САМОЕ критическое узкое место.

Если другие вещи будут готовы к работе, мы можем отложить массивы до более позднего выпуска.

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

Вот один голос за задержку 0,5 до тех пор, пока не будут реализованы все критические изменения в массивах. Насколько я понимаю, нарезка в стиле APL (которая нарушает обратную совместимость) уже включена в master, поэтому авторам пакетов придется обновлять большой объем кода независимо от того, что для 0.5. Если другое серьезное изменение (возврат просмотров вместо копий из [] ) произойдет только в каком-то выпуске julia после 0.5, всем придется снова просматривать / обновлять все те же строки кода, что действительно было бы МНОЖЕСТВО дополнительной работы, которой можно было бы полностью избежать, если бы оба этих критических изменения были внесены в один выпуск.

Кроме того, с точки зрения пользователей, было бы здорово, если бы серьезные критические изменения в языке произошли раньше, чем позже ... С моей точки зрения, было бы гораздо предпочтительнее иметь такие вещи, как отладчик, совместимость с ARM, более быстрое закрытие. , потоки и т. д. все появляются после того, как критические изменения языка сделаны. Весь код, который я пишу в наши дни, необходимо будет обновить, как только эти критические изменения попадут в мастер, тогда как все другие упомянутые хорошие вещи (скорее всего) не повлияют ни на один из кодов, которые люди пишут прямо сейчас ... такие вещи, как Arraypocalypse, - основная причина, по которой многие люди, которых я знаю, пока держатся подальше от Джулии ...

Конечно, я не понимаю, осталось ли еще кто-нибудь, у кого есть возможность поработать над этим, кажется, что некоторые из людей, которые руководили этим ранее, ушли дальше ... Но, может быть, Julia Computing сможет выделить некоторые ресурсы для завершения этой работы?

В любом случае, может ли кто-нибудь обновить срок выполнения вехи 0,5? По крайней мере, если моя интерпретация комментария @ViralBShah выше верна, 31 января не будет выпуска julia 0.5, а это текущая информация, связанная с этапом 0.5, верно?

На самом деле это не проблема выделения ресурсов. Есть много открытых вопросов, и многие люди все еще опасаются представления массивов. Придется сделать это, попробовать и посмотреть, каково это. Как упоминалось выше, PR @blakejohnson - это первый шаг, и давайте объединим его. Конечно, нет планов по выпуску 0.5 31 января. Я удаляю эту дату.

@davidanthoff : Неужели возвращение просмотров вместо копий действительно sub чтобы получить представление в массиве.

Хотя я согласен с тем, что всегда лучше вносить серьезные изменения на раннем этапе, есть просто вещи, на которые нужно время, и, с моей точки зрения, команда разработчиков Julia проделала довольно удивительную работу, решив, какая функция готова для включения в выпуск, а какая нет. На мой взгляд, 0,3 и 0,4 довольно стабильны.

У меня создалось впечатление, что идея была быстрой версией 0.5 _ точно_, потому что скоро должны появиться критические изменения массива (они должны быть в одном выпуске). Если это произойдет в ближайшие несколько месяцев, следует отодвинуть 0,5?

@ViralBShah Веха для этой проблемы по-прежнему 0,5, не так ли? Не может быть 0.4.x?

Некоторое время назад я добавил этот тест, чтобы отслеживать, насколько производительность нашего массива была относительно Numpy (что быстрее, чем мы на этом тесте). Тест представляет собой факторизацию LU с полным поворотом, и он намеренно выделяет множество временных массивов, работая с векторами, а не перебирая элементы матрицы. Версия Julia имеет версии теста _copy_ и _view_, так что это также дает представление о том, сколько ускорения мы получим от изменения _views по умолчанию_. На моей машине результаты

➜  arrayalloc git:(master) ✗ make
python lucompletepiv.py
size  100 matrix factorized in  0.010 seconds
size  250 matrix factorized in  0.047 seconds
size  500 matrix factorized in  0.246 seconds
size 1000 matrix factorized in  2.330 seconds

Julia version with copy slices
size  100 matrix factorized in  0.016 seconds
size  250 matrix factorized in  0.093 seconds
size  500 matrix factorized in  0.517 seconds
size 1000 matrix factorized in  4.126 seconds

Julia version with view slices
size  100 matrix factorized in  0.004 seconds
size  250 matrix factorized in  0.078 seconds
size  500 matrix factorized in  0.453 seconds
size 1000 matrix factorized in  3.555 seconds

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

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

Это отличная отправная точка. Следующим очевидным шагом кажется выяснение того, что нам нужно сделать, чтобы сделать подмассивы быстрее. Возможно, @timholy или @mbauman уже знают.

Нам нужно, чтобы компилятор полностью исключил конструкцию подмассивов. Я думаю, что код подмассива уже хорошо подходит для этого, так что это, вероятно, «просто» тяжелая работа над генерацией кода, которая необходима.

@andreasnoack, разница с маленькими матрицами делает это полностью оправданным для моих рабочих нагрузок.

Согласны, и обратите внимание, что мы быстрее, чем python для небольших матриц.

Хм, интересно, происходит ли что-то еще - возможно, это вообще не эталон для подмассивов. Что произойдет, если вы замените https://github.com/JuliaLang/julia/blob/6d7a50b880fe2189b1efa34eb47d4dfeb181b674/test/perf/arrayalloc/lucompletepiv.jl#L39 вызовом BLAS.syrk2! ?

@blakejohnson Я

@timholy Это было бы быстрее, но суть теста в том, что он выделяет временные syrk2 .

@ViralBShah Я интерпретировал комментарий @timholy как призыв к дополнительным ресурсам (т.е. рабочей силе) для решения этой проблемы, потому что разработчики, которые намеревались выполнить эту работу,

@tknopp Не отсутствие просмотров из [] удерживает людей от использования julia, а ожидание серьезных критических изменений, которые появятся в следующих 1-2 выпусках. По крайней мере, меня окружают люди, для которых julia была бы идеальной, но которые просто не желают использовать язык, на котором им, возможно, придется обновить свой код, чтобы он по-прежнему работал правильно в следующем выпуске julia.

Я не говорю, что любое дизайнерское решение нужно торопить из-за этой группы, я бы предпочел, чтобы все уладилось навсегда и заняло немного больше времени. В основном я просто хотел запустить эту ветку и посмотреть, работает ли кто-нибудь еще над чем-то здесь :)

@andreasnoack Кроме того, в вашей версии просмотра нет, например, работы @blakejohnson по удалению границ, верно? Я полагал, что одним из основных моментов общей проблемы здесь было то, что существуют известные проблемы с производительностью, которые необходимо решить, чтобы ускорить просмотр, но это еще не сделано. Если что-то по-прежнему не будет быстрее после того, как они будут выполнены, я бы понял, что этот аргумент просто придерживается копий по умолчанию. Но до того, как будет сделан этот вызов, необходимо завершить работу по производительности, обсуждаемую в начале этого вопроса, верно?

просто не хотят использовать язык, на котором им, возможно, придется обновить свой код, чтобы он по-прежнему работал правильно в следующем выпуске julia.

Это потому, что они не будут использовать какой-либо язык до 1.0, или они специально слышали, что скоро будут нарушаться изменения массива (после чего они будут использовать julia)?

@hayd Я не уверен на 100%, но я думаю, что массивы особенно пугают людей, потому что они влияют на так много кода (хотя, я полагаю, индексация стиля APL, которая уже используется в мастере, даже больше, чем просмотр / копирование) ).

NumPy и Julia используют здесь один и тот же BLAS? Я могу получить существенное (> 1/3) ускорение в случае матрицы размера 1000, вызвав Base.disable_threaded_libs() .

@davidanthoff :
Если это беспокоит, то Юля, вероятно, слишком молода для этих людей. На мой взгляд, количество изменений основного языка за последние 3 года было не таким значительным.
Конечно, есть довольно стабильные языки, но если я думаю о C ++ 11 или Python3, то в зрелых языках есть даже большие изменения.

@davidanthoff , да, это призыв к большему количеству людей внести свой вклад в это усилие - предыдущие участники, вероятно, еще не «закончили» (я нет), но список длинный, и у людей есть несколько приоритетов.

Немногое о массивах на самом деле не так уж сложно, потому что это сфокусированная часть julia. Или, может быть, я просто предубежден из-за того, что работал над ним с кучей супер-умных людей: smile :. Но я думаю, что это поддается вкладу со стороны более крупного сообщества, и ожидание, пока какие-то таинственные «они» сделают всю работу, может быть не лучшей стратегией, если у «них» нет времени.

В частности, что касается удаления проверки границ: в настоящее время представления довольно быстрые, но они небезопасны, потому что нет проверки границ. Чтобы сделать их общедоступными, нам нужно _добавить_ проверку границ, но до тех пор, пока работа

@andreasnoack , не могли бы вы открыть отдельный выпуск о вашем бенчмарке lu? Есть еще что обсудить, но я думаю, что это отвлекает от более серьезных проблем.

Как насчет того, чтобы разрешить полям в абстрактных типах (https://github.com/JuliaLang/julia/issues/4935) что-нибудь новое в этом направлении?

Как насчет того, чтобы украсть синтаксис {} из запланированных типов кортежей и заставить A{:, 1} создать представление, а A[:, 1] прежнему создает копию?

@JeffBezanson предложил A@[:,1] для просмотров и A[:,1] для копий.

Наивный вопрос, почему A [:, 1] не является представлением по умолчанию, и если вам нужна копия, вы называете «копией» представления?

Чтобы сделать это кратким в связанном использовании, мы перегружали '*', например, * (A [:, 1]) - если унарный * был в синтаксическом анализаторе, тогда это становится кратким * A [:, 1]. Я не сторонник использования *, но, на мой взгляд, он соответствует использованию C.

Я все еще за. @JeffBezanson имел некоторые сомнения по этому поводу - я позволю ему объяснить.

Есть две основные проблемы с представлениями по умолчанию IMO

1) это критическое изменение, одно из наихудших, поскольку оно вводит ошибки псевдонима (в основном, если не считать устаревшего индексирования диапазона для полной версии, вам придется проверять каждый бит кода, который его использует)

2) неизменяемые объекты, содержащие ссылки на julia, пока довольно неэффективны, поэтому, если люди начнут использовать представления для удобства (то есть, чтобы избежать ручного вычисления индексов), они, вероятно, увидят снижение производительности.

Я признаю, что (2) - это то, над чем мы все равно должны поработать, но это может быть важно сделать до широкого использования представления. Я не понимаю, как можно решить (1) без отдельного синтаксиса.

edit: думая об этом, менее сложным переходом для (1) может быть возврат представлений только для чтения для выпуска, избегая некоторых ошибок псевдонима (но не тех, где вы пишете в исходный массив), и разрешить запись через представление позже

Хотя я ценю усилия, направленные на минимизацию критических изменений и / или обеспечение путей отказа от поддержки ... до версии 1.0 не должны ли мы быть готовы нарушить некоторые вещи?

Мне нравится идея иметь новый синтаксис для представлений массива. Текущее поведение индексации (которое генерирует множество временных файлов) должно стать быстрее, поскольку мы сможем лучше анализировать избегание и повторное использование памяти. Я лично не знаю, нравятся ли мне представления массивов по умолчанию, и мне нравится идея хотя бы изучить потенциально другой синтаксис представлений.

В общем, я сторонник того, чтобы что-то ломать до версии 1.0, но это не тот случай, когда ясно, что новое поведение - верная победа.

Если сделать еще пару шагов вперед, каковы потенциальные кандидаты на использование синтаксиса для представлений массива?

В синтаксисе A@[:,1] есть кое-что интересное, так как он читается как A at the location of the following indices .

В синтаксисе A @ [:, 1] есть кое-что интересное, поскольку он читает A в месте расположения следующих индексов.

Согласен, кажется на удивление интуитивно понятным.

Кроме того, он продолжает расти во мне, потому что очень легко экспериментировать с представлениями в коде с таким синтаксисом.

Мое мнение (ba-ding!) По поводу A@[:,1] состоит в том, что вводить макрос-символ @ в совершенно другой контекст сбивает с толку.

Это была моя первая реакция, но я преодолел ее по причинам, о которых писал выше.

Но аналогично, {} вводит нечто вроде типа в совершенно другой контекст.

view (A,:, 1)?

@kkmann : представлениям нужен специальный синтаксис, чтобы могли работать такие функции, как end .

A@[:,1] имеет потенциал. +1 за тестирование.

Я бы сказал, что в большинстве случаев вам нужны просмотры, и лишь изредка вам нужны копии. Будет множество потоков, посвященных медленному выполнению кода, ответом на которые будет «засорять код массивом @ [...]». Разве представления массивов не могут быть неизменными по умолчанию? Тогда вам придется явно запросить изменяемое представление.

Неизменяемые представления по умолчанию - интересная идея. Синтаксис A@[...] может быть для получения изменяемого представления, а copy(A[...]) - для получения фрагмента без просмотра.

если не считать устаревшей индексации диапазона для полной версии, вам придется проверять каждый бит кода, который ее использует

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

Но неизменные (только для чтения) представления превратят любую потенциальную ошибку в ошибку, верно? Потому что чтение из представлений безопасно.

@ mauro3 , не все возможные ошибки.

из @carnaval :

edit: думая об этом, менее сложным переходом для (1) может быть возврат представлений только для чтения для выпуска, избегая некоторых ошибок псевдонима ( но не тех, где вы пишете в исходный массив ), и разрешить запись через представление позже

Предполагаемое значение кода меняется с версией julia, и люди надеются, что к подобному обновлению относятся более осторожно, чем к Pkg.update.

По умолчанию просмотры по-прежнему кажутся мне правильным долгосрочным выбором. Наименее рискованным способом попасть туда может быть переход через представления только для чтения по умолчанию.

+1 к неизменному переходному периоду просмотра, за которым следует изменяемый просмотр

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

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

Я голосую за просмотр по умолчанию. На мой взгляд, это абсолютно соответствует текущему поведению setindex!

julia> a= zeros(3,3)
3x3 Array{Float64,2}:
 0.0  0.0  0.0
 0.0  0.0  0.0
 0.0  0.0  0.0

julia> a[:,1] = 1
1

julia> println(a)
[1.0 0.0 0.0
 1.0 0.0 0.0
 1.0 0.0 0.0]

Такое поведение несовместимо со случаем, когда a[:,1] является R-значением и копируется без уведомления. Поэтому я также против того, чтобы представления были неизменяемыми (что, кстати, не было бы таким же, как неизменяемый тип).

Конечно, все это дизайнерское решение, и есть свои плюсы и минусы. Но давайте решим это пораньше и не откладываем на следующий выпуск.

@johnmyleswhite , я думаю, что A@[...] ) не будет предательством доверия. Могут быть веские причины не делать индексирование изменяемым по умолчанию.

Например, когда мы движемся к многопоточности, представления по умолчанию будут означать, что мне внезапно придется беспокоиться о том, что данные, с которыми я работаю, могут быть изменены другим потоком (даже если мой взгляд на них неизменен). Поэтому мне может потребоваться добавить семафоры или явно скопировать данные, и моя программа и мои рассуждения об этом внезапно станут более сложными.

По умолчанию не воспринимайте мои комментарии как противоположные точки зрения, моя основная мысль заключается в том, что мы должны знать, чего следует ожидать от перехода.

Также было бы интересно, сколько кода это действительно ломается. У нас были большие дискуссии о том, что Int8+Int8 -> Int8 нарушит работу большого количества кода, но в конце концов изменения вошли, и серьезных поломок не было.

@kmsquire : На мой взгляд, многопоточность полностью ортогональна этой функции. Да, есть сценарии, в которых может иметь смысл копировать данные в многопоточном коде, чтобы каждый поток имел свою собственную копию, но обычно это делается путем разбиения массива и работы с частями этого массива. И именно для этого потребуются просмотры. Никто не хочет запускать кучу потоков, чтобы копировать код (который в настоящее время выполняет [:,:]) в «горячем» цикле.

Было бы хорошо иметь синтаксис, который всегда дает вам представление, и который работает одинаково как в 0.4, так и в 0.5. То же самое для синтаксиса, который делает копию (без двойного копирования). Если вас беспокоит обратная совместимость, вы можете использовать этот синтаксис и избегать синтаксиса, который меняет значение.

Разве это не всегда происходит при внесении критических изменений? Обсуждение здесь объединяет два вопроса: каким должно быть долгосрочное будущее состояние и как осуществить это изменение.

У меня нет четкого представления о том, каким должно быть окончательное решение, я мог бы легко согласиться с любым из предложений, сделанных здесь.

Единственное, что меня действительно волнует, это то, что было бы здорово иметь все эти критические изменения массива в одном выпуске, а именно 0.5, а не растягивать их на несколько выпусков. Мне придется просмотреть весь свой код массива, чтобы приспособить индексацию стиля APL для 0,5, и было бы большим подспорьем, если бы какие-либо критические изменения с точки зрения представлений произошли в том же выпуске, так что мне нужно только изменить свой код массива один раз. Я предполагаю, что изрядное количество разработчиков / пользователей пакетов будут чувствовать то же самое.

Я думаю, вы могли бы найти некоторые ошибки, когда представление изменяется, потому что вы записали в исходный массив, заставив индексирование возвращать объект, который обертывает как копию, так и представление, и проверяет, соответствует ли копия представлению на getindex (и не имеет setindex! ). Но это было бы неразумным вариантом по умолчанию, поскольку он медленнее, чем копии или просмотры.

Но в дополнение к пунктам @carnaval выше, третий Vector{Int} , если массив проходит достаточно много раз, вероятно, будет быстрее создать копию. Представление в разреженной матрице с вектором целых чисел в виде строк кажется трудным сделать хоть сколько-нибудь быстро, как копию для большинства операций. Учитывая, что предпочтительность представления перед копией зависит от того, что индексируется и что с ним делается, я думаю, что должны быть простые способы сделать то же самое.

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

Этот!! Просмотры не следует рассматривать как однозначный выигрыш в производительности. В частности, изменяемые представления - очень мощная и продуманная функция. Я думаю, слишком сложно обещать, что индексация всегда будет возвращать ссылку на базовые данные, а не копирование. Мы не можем сказать всем, кто когда-либо определял getindex что они должны теперь организовать возврат представления.

Бьюсь об заклад, никто не хочет, чтобы скалярная индексация возвращала представление, да и не похоже, чтобы это было эффективно реализовано. Однако с отдельным синтаксисом мы могли бы иметь даже это, т.е. A@[1,1] дает представление 0-d, в которое можно записать с помощью v[] = x .

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

Предлагаемый синтаксис для представлений массива позволяет мне использовать представления, когда они мне нужны, и сохранять существующий код таким, какой он есть, без поломок - все с добавлением / удалением одного символа.

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

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

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

Извините, что звоню поздно.

@eschnett ,

Было бы хорошо иметь синтаксис, который всегда дает вам представление, и который работает одинаково как в 0.4, так и в 0.5.

У нас уже есть это: две функции slice и sub .

@johnmyleswhite ,

существенное предательство доверия

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

Кроме того, некоторые из нас периодически появлялись в списке рассылки, чтобы сказать: «Не нужно ждать Arraypocalypse, теперь у нас есть эффективные представления. В любом случае, что за синтаксис?» Так что, по крайней мере, эта точка зрения, неоднократно высказываемая за последний год, относительно соответствует направлению, в котором движется этот разговор.

@JeffBezanson ,

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

Потратив немало времени на размышления о том, как составить два разных типа представления, slice и reshape , я не мог с этим согласиться. Не то чтобы я думаю, что мы не можем этого сделать, но далеко не очевидно, что мы достигнем производительности, такой же хорошей, как если бы вы сделали копию. (Нам все еще нужно реализовать его, чтобы это было возможно, но не очевидно, что мы хотим сделать его по умолчанию.)

Я смутно припоминаю, как предлагалось добавить в базу какие-то скобки из двух символов (нашел - # 8892). Такие вещи, как [| ... |] , {| ... |} и т. Д. Не могли бы мы использовать одно из них для построения представления? Я думаю, что первое могло бы работать хорошо, не вводя использование @ которое не означает макрос.

Мое предложение:

  • @ зарезервировано для макросов
  • { } зарезервировано для типов кортежей
  • x[1,:] означает индексирование и создание копии
  • x[|1,:|] означает индексирование и создание представления

Насколько я могу судить, этот синтаксис бесплатный ( | не унарный оператор) и вряд ли удивит людей. Обратной стороной является то, что набрать [| и |] немного больше, чем [ и ] .

A@[1,:] кажется более интуитивным для представления, чем x[|1,:|] . Лично я не чувствую путаницы с синтаксисом макросов.

Является ли различение изменчивых и неизменных взглядов выгодным не только в ближайшем будущем, но и на неопределенный срок?

+1 за A@[1,:]

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

Есть предположения, какой выгоды можно было бы достичь с помощью такого рода вещей?

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

В комментарии о том, что @ в настоящее время зарезервировано для макросов, я бы также отметил, что у Джулии _already_ есть два символа, которые определены (или идиоматически используются) для изменения поведения операторов и функций (и я все еще говорю о [] как оператор, _a la_ C ++). Это символы . и ! , и я беспокоюсь, что добавление третьего символа @ к смеси добавляет (возможно, ненужную) путаницу.

Рассматривали ли люди A.[1,:] или даже A![1,:] или A[1,:]! как возможный синтаксис? То есть измените оператор [] на .[] или []! ? Интуитивно первое может быть интерпретировано как поэлементное индексирование (которое, возможно, может быть интерпретировано как принудительное копирование ??), а второе - вид изменяющейся индексации (которая кажется похожей на представления, где изменение результирующего представления приводит к мутациям. к исходной матрице).

A![1,:] уже является допустимым синтаксисом для индексации в массив с именем A! . Доступны другие синтаксисы.

Кроме того, @[] все еще можно сделать доступным для определения для выполнения макроподобной магии с индексами (я думаю о чем-то вроде @jutho 's TensorOperations.jl , здесь, возможно, даже Devectorize .. . просто догадываюсь здесь)

@StefanKarpinski Да, я подумал об этом после того, как []! имеет для меня наибольший смысл.

См. Https://github.com/JuliaLang/julia/issues/7721#issuecomment -170942938, где синтаксически похожее предложение, о котором следует помнить: f(x)! для вызова f(x) и автофинализации результата на область выхода.

Мне очень не нравится использовать ! для чего-либо, кроме операций изменения. Например, reshape! было предложено для изменения формы совместного использования данных. Я думаю, что это создает большую путаницу в том, что означает ! . Взгляды не видоизменяются; на самом деле это особенно хорошо работает в программах, которые вообще не используют мутацию!

( @JeffBezanson : включает ли это противодействие синтаксису f(x)! предложенному в # 7721?)

@JeffBezanson Это справедливый вопрос ... Иногда меня уже путает ! . Представление допускает своего рода возможную отсроченную мутацию, но не изменяет массив сразу, поэтому я допускаю, что это немного растянутая интерпретация ! .

f(x)! из # 7721 меня не так сильно беспокоит, возможно, потому, что он также включает в себя какие-то побочные эффекты, тогда как возвращение представления массива - это просто чистая функция.

Хорошо, а как насчет другого оператора, .[] ? Я вижу две альтернативы:

В мире, где [] возвращает представление (где определено, e, g. Array , или же копию, где это имеет смысл), .[] может быть удобным для _force_ копия. Конечно, как уже говорилось, трудно предсказать, что вы получите от [] .

Или, возможно, .[] можно рассматривать как альтернативу @[] для просмотров, если это кому-то кажется интуитивно понятным? Как люди интерпретируют «поэлементное индексирование»?

Как люди интерпретируют «поэлементное индексирование»?

Точечная индексация - одна из возможностей. A.[1:3,1:3] вернет первые три элемента по диагонали. # 2591

Я действительно хочу, чтобы здесь был четкий правильный ответ ... но ни семантика, ни производительность представлений не делают это решение очевидным. Возможно, поддержка двух операторов - это правильное решение.

Хорошо, я согласен, # 2591 имеет гораздо больше смысла для .[]

+1 за A@[1,:]

Я также думаю, что возможность сделать массив временно неизменяемым было бы очень полезно для отслеживания ошибок, когда массив изменяется, и вы не можете понять, почему (или просто чтобы проверить, не изменяется ли он внутри функции). Это не обязательно должно быть частью slice но может быть. Есть ли способ сделать это сейчас?

Я действительно хотел бы, чтобы у нас было больше данных о влиянии на производительность здесь. У меня просто нет времени преследовать все изменения, которые необходимы для того, чтобы эта работа работала, но вот едва ли функциональный старт, обходящий проблему начальной загрузки с представлениями по умолчанию:

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

@andyferris Разве имена макросов не всегда должны быть идентификаторами (по крайней мере, сейчас)? Я думаю, что потребуется проделать специальную работу, чтобы позволить использовать такие символы, как [ , ( или { в качестве имен макросов, и чтобы соответствующий закрывающий символ обрабатывался правильно .
Я думаю, что синтаксис A[ind]! может быть неоднозначным. Например, что означает A[ind]!=5 ?
Я думаю, что предложенный синтаксис f(x)! также может столкнуться с проблемами двусмысленности. (да, вам может потребоваться пробел между ! и = , но это может сбивать с толку)

А как насчет простого <strong i="5">@view</strong> A[1,:] ? (см. https://github.com/JuliaLang/ArrayViews.jl/pull/14)

Я думаю, что это слишком многословно, если, например, вы хотите использовать все представления в алгоритме.

нельзя ли применить макрос @view для замены getindex во всем блоке begin ... end ?

я не понимаю, почему нет

Еще одна вещь, которую следует учитывать, если мы вводим новый синтаксис для представлений (например, @[...] ): какой должен быть соответствующий вариант setindex!

A@[1,:] = x

делать? Поскольку это кажется неясным, лучше всего запретить его, но тогда вам придется не забыть исключить @ при выполнении присваивания.

Классная идея @inbounds .

не совсем так: @inbounds немного волшебнее (см. https://github.com/JuliaLang/julia/blob/9bfd27bd380124174a5f37c342e5c048874d71a4/test/boundscheck.jl). Это было бы простое преобразование синтаксиса.

То же самое в использовании, а не в реализации. Во всяком случае, мне это нравится: P

Но проблематично для этого варианта использования: A - разреженная матрица (так что, допустим, копировать быстрее, чем строить представление), B плотная и имеет быстрые представления. Что делать <strong i="7">@view</strong> c += A[1:10,2:15]*B[:,j] ?

Разве нельзя было написать что-то вроде c += A[1:10,2:15]*(<strong i="5">@view</strong> B[:,j]) где @view работает только с тем, что находится внутри скобок. Не знаю достаточно о макросах, чтобы знать, возможно ли это.

Но, возможно, теперь он проигрывает по многословию ..

Я думаю, что лучше всего иметь оба синтаксиса. Затем используйте A2@[] если требуется детальный контроль. (редактировать: добавлено @ )

@KristofferC , да, это работает, но "многословная" критика применима, если вам регулярно приходится выбирать.

Тем не менее, в целом мне это нравится. Обратите внимание, что реализация довольно проста: она сводится к замене Expr(:ref, :A, args...) на Expr(:call, :slice, :A, args...) (хотя вам также нужно просканировать end и заменить его вручную).

Я не против, по крайней мере, попробовать @view на мастере в течение некоторого времени и посмотреть, каково это, прежде чем мы сделаем A@[] .

На случай, если кто-то захочет попробовать это, вот текущая логика замены end .

A@[] движется в направлении сигил Perl, поскольку его трудно читать и понимать для новых пользователей. По крайней мере, макрос или функция объясняют, что они делают ...

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

Предложение двух синтаксисов имеет преимущества, но не решает всех вариантов использования. Я действительно вижу троих из них:
1) Вам нужна копия, которую можно изменить, не затрагивая исходный массив -> [] .
2) Вы хотите, чтобы представление изменяло исходный массив -> @[]
3) Вам нужен самый быстрый / дешевый способ извлечь подмножество массива, но не нужно его изменять. Вы бы предпочли (возможно, неизменное) представление для комбинаций типов индекса / массива, где это быстрее, и копию в другом месте. Это похоже на использование eachindex(a) вместо 1:length(a) при итерации по массиву. -> нет предлагаемого синтаксиса

Предлагать три синтаксиса - это многовато ... Но мне кажется, что большая часть ожидаемого выигрыша (ed) от использования представлений по умолчанию соответствует случаю 3.

Итак, я согласен с @tkelman в том, что вопрос о том, можно ли сделать представления эффективными для всех базовых типов, заслуживает исследования: это позволит эффективно объединить случаи 2 и 3 и, возможно, украсть синтаксис варианта 1 (т.е. представления по умолчанию). С обобщенным синтаксисом генератора и быстрыми анонимными функциями представления кажутся отличной функцией, позволяющей избежать выделения памяти и обеспечить высокую производительность.

Одна проблема с использованием @view для обертывания выражений / блоков: что, если у меня есть блок, обернутый в @view и мне нужно добавить внутри него индексирование копирования? Тогда мне пришлось бы преломлять весь блок. Что делать, если я не заметил? Что, если я вставлю код в этот блок? Я бы чувствовал себя намного лучше, если бы просмотр или нет, зависел бы только от операции, а не от контекста окружающего кода.

Для чего это стоит:

Я много занимаюсь представлениями в машинном обучении. Многие алгоритмы перебирают наблюдения за некоторым набором данных, который сам по себе чаще всего представлен в виде матрицы. В таких случаях, как SVM, это нельзя сформулировать в терминах умножения матриц, а вместо этого нужно притворяться, что каждое наблюдение является вектором. В этих случаях будет оценен простой читаемый синтаксис, поскольку я стремлюсь к реализации, напоминающей псевдокод. Я не думаю, что A@[1,:] вообще сбивает с толку; он даже читается как «A at [1 ,:]».

Я согласен с @toivoh, что использование @view похоже, заставляет человека выбирать между использованием только представлений или более подробным обманом. @inbounds чувствует себя гораздо более четким в этом отношении

Сработают ли view(arr, dims) и view!(arr, dims) , чтобы вернуть неизменяемое и изменяемое представление соответственно?
Это позволило бы параметризовать его в зависимости от того, является ли arr плотным, разреженным или чем-то еще, верно?

@tkelman В A@[x,y] , почему @ - это сигил? Я читаю @[] как оператор, а не @ как сигилу, прикрепленную к A .

Это забавно выглядящий оператор, не имеющий большого прецедента. Необходимость отслеживать синтаксис для различных видов индексации не кажется идеальной. Это немного похоже на то, как у ржавчины есть разные типы указателей (а раньше их было больше), это не интуитивно понятно, и вам нужно много отслеживать при первой попытке чтения кода. Я, наверное, мог бы жить с A@[] но мне это не нравится.

edit: если бы представления были по умолчанию и строились лениво, то можно было бы надеяться, что copy(A[1,:]) будет точно эквивалентно сегодняшнему копированию A[1,:] но я не уверен, насколько близко мы были бы на практике, используя сегодняшний компилятор.

edit2: Я думаю, что @[] так же или лучше, чем любое из других предложений «двух видов скобок». Альтернативой, с моей точки зрения, является заставить [] всегда возвращать представление (только для чтения для перехода к выпуску) и вызывать copy в выражении индексации, чтобы получить поведение, эквивалентное сегодняшнему. Покопаюсь в

Если мы посмотрим на прецедент, A(1,:) может быть всегда копией, так как это то, что есть в Matlab AFAIK, а A[1,:] всегда просматривается, как это почти так в NumPy. Я думаю, что () - плохая идея, просто бросить ее в мозговой штурм. Еще мне не нравится A@[1,:] .

Если основное возражение против @view заключается в том, как обрабатывать исключительные случаи (например, копия внутри блока @view или представление в обычном режиме), могут ли люди просто использовать существующий sub / slice во втором случае, и, может быть, мы добавим методы к функции copy которые обрабатывают первое? В настоящее время не похоже, что copy имеет какие-либо методы с несколькими аргументами, так что это может быть более короткое имя для того, что сейчас делает getindex .

поэтому copy(A, idx...) всегда будет давать копию, sub(A, idx...) всегда будет предоставлять представление, а A[idx...] будет копировать по умолчанию, но может быть заключен в @view для изменения поведение (или просмотр по умолчанию с помощью макроса @copy для изменения поведения, IDK, что лучше)

Я не большой поклонник тонких синтаксических подсказок, изменяющих тонкое поведение (например, типы скобок, определяющие поведение копирования / просмотра), потому что кажется, что слишком легко сделать ошибку, которая, кажется, работает, пока это не произойдет. Похоже, что это обычная ловушка для новых пользователей, а также при рефакторинге кода или просто просмотре кода, чтобы увидеть, что он делает.

@timholy Реализация не так проста, потому что если вы просто замените :ref Exprs, тогда <strong i="7">@view</strong> X[:, 1] += 2 станет slice(X, :, 1) += 2 . Я думаю, вы могли бы использовать настройку парсера custom_ref из # 8227, чтобы получить желаемое поведение, включая end .

@ssfrr Я думаю, что при рефакторинге кода @view может быть не менее непонятным, чем @[] . Разные люди, несомненно, будут размещать @view вокруг блоков разного размера, и, возможно, некоторые люди даже разместят его вокруг целых файлов.

В качестве отступления: мы с @tkelman прошлой весной провели немного времени.
болтаем о том, какие методы можно использовать для создания разреженной матрицы
манипуляции и нарезка довольно производительны. Обозначение среза обычно
шансы с общей нотацией среза, но редкое дружественное мнение / не
копирование ограниченных версий слайса очень легко определить для
практически любой стандартный плотный макет.

Не уверены, есть ли интерес?

В пятницу, 29 января 2016 г., Саймон Корнблит [email protected]
написал:

@timholy https://github.com/timholy Реализация не совсем
так просто, потому что если вы просто замените: ref Exprs, то @view X [:, 1]
+ = 2 превратится в slice (X,:, 1) + = 2. Я думаю, вы могли бы использовать custom_ref
твик парсера от # 8227 https://github.com/JuliaLang/julia/pull/8227 до
получить желаемое поведение, включая конец.

@ssfrr https://github.com/ssfrr Я думаю, что @view может быть как минимум
неясно как @ [] при рефакторинге кода. Несомненно, разные люди будут
разместите @view вокруг блоков разного размера, и, возможно, некоторые люди
даже поместил бы его вокруг целых файлов.

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/JuliaLang/julia/issues/13157#issuecomment -176787527.

@cartazio Мне интересно

Если вы рассматриваете A[1,:] как сокращение для построения «копирующего типа среза», который применяется к методу вызова - A( copy_slice(1, :) )

Было бы неразумно иметь изменяемое представление A( view_slice( 1, : ) ) ?

Добавление синтаксиса для прямого создания copy_slice и view_slice упрощает использование в строке. Это очень похоже на существующие конструкторы диапазонов

1:3 -> UnitRange{Int64}

Это в основном работает сегодня, если мы утверждаем, что `view_slice является псевдонимом для кортежа (к сожалению, это противоположность NumPy / matlab), но у вас есть выбор, чтобы быть ясным и явным

1,:,1:2 -> (1,Colon(),1:2)

Так

A( 1,: ) -> A( view_slice( 1,: ))

Это так многословно писать

A( view( 1, : ) )

Это также позволяет расширять неизменяемые представления

A( immutable_view( 1,: ) )

Вы можете реализовать это сегодня без изменений парсера (и, вероятно, обратно совместимым способом, отправив getindex и др.), За исключением поддержки end .

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

1:end -> 1 : :end -> colon( ::Int64, ::Symbol )

[изменить: хотя я бы предпочел Type {End}]

Прежде чем беспокоиться о синтаксисе, мне бы очень хотелось увидеть больше инженерных усилий, вложенных в представления, и более пристально взглянуть на компромиссы производительности. Одна вещь, которая была бы большим подспорьем и довольно механическая, - это обновление методов индексирования SubArray для использования новой причудливой идиомы <strong i="5">@boundscheck</strong> checkbounds(A, I...) . (В общем, все случаи использования unsafe_getindex можно заменить на <strong i="8">@inbounds</strong> A[...] , а все определения его методов удалить.)

@nalimilan написал:

3) Вам нужен самый быстрый / дешевый способ извлечь подмножество массива, но не нужно его изменять. Вы бы предпочли (возможно, неизменное) представление для комбинаций типов индекса / массива, где это быстрее, и копию в другом месте. Это похоже на использование eachindex (a) вместо 1: length (a) при итерации по массиву. -> нет предлагаемого синтаксиса

Я бы хотел, чтобы все было так просто. Даже не беспокоясь о изменчивости, проблема в том, что самый быстрый вариант зависит не только от структуры, в которую вы индексируете, но и от того, сколько данных задействовано и как вы в конечном итоге используете возвращаемый объект. Еще одна трудность заключается в том, что специализированные массивы внезапно становятся неспециализированными. В настоящее время мы работаем над этим для BLAS-совместимых плотных массивов и представлений с типами StridedArray , но потребуется очень много времени, чтобы найти все отсутствующие методы и несоответствия типов, которые в целом будут встречаться с подмассивами. Подумайте о недостающих методах LinSpace … но гораздо более распространенных.

И еще есть проблема класса сложности для специализированных массивов. Например, создание разреженных представлений не является дорогостоящим, но проблема с ними в том, что возвращенные SubArrays больше не попадают в специализированные разреженные структуры. Итак, sum(slice(S, :, 1) попадает в абстрактный резерв и больше не O (nnz).

@mdcfrancis - одно из самых эффективных применений ключевого слова end - его использование в арифметике, например, A[(end+1) ÷ 2] или A[mod1(x, end)] .

-1 для A @ []

Это действительно похоже на введение контекстов Perl, а символ @ предполагает, что здесь задействованы макросы. Мне нравится идея A [| x, y |] или какой-то другой новый вид скобок.

Я думаю, что люди ищут не «просмотры по умолчанию», а сужение разницы в производительности между тестами, представленными на https://github.com/JuliaLang/julia/issues/13157#issuecomment -168378436. Было бы неплохо, если бы это было по умолчанию, но простая настройка, которую можно найти в разделе Советы по производительности, почти так же хороша.

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

END + 1 -> EndExpr( length(A))

@ mauro3 поработал над этим в пакете с рваным массивом . Это не легко.

-1 для A@[] . Доступен ли синтаксис A[[index]] ? Это вызовет что-то вроде getindexview(A, index) ? Я считаю, что это означает, что мы получаем уровень косвенности, соответствующий точке зрения.

@GlenHertz Это уже кое-что значит:

julia> a = ["pablo","martín","garcía"] ; a[[1,2]]
2-element Array{UTF8String,1}:
 "pablo" 
 "martín"

@ lucasb-eyer написал:

Если мы посмотрим на прецедент, A (1, :) может быть всегда копией, так как это то, что он есть в Matlab AFAIK, а A [1 ,:] всегда отображается, как это почти так в NumPy. Я думаю, что () - плохая идея, просто бросьте ее в мозговой штурм. Еще мне не нравится A @ [1 ,:].

Это предложение относительно () для копий и [] для представлений кажется мне наиболее кратким решением и может быть наиболее интуитивно понятным для новых пользователей из MATLAB и Python (это увеличило бы количество запускаемых Код MATLAB). Кажется "правильным", что C-подобный [] делает что-то указательное, а () выполняет функцию для возврата нового объекта (и, естественно, это не имеет смысла в левой части. -side ... но нам нужно было бы добавить предупреждение, чтобы избежать логических ошибок). В целом мне это нравится.

К сожалению, мы теряем возможность использовать call для объектов, подобных массиву ...

Использование () для индексации устраняет главное преимущество наличия синтаксиса для индексации, а именно позволяет работать таким вещам, как end .

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

По-настоящему C-подобным было бы использование &A[x,y] для представлений, что в настоящее время является синтаксической ошибкой и поэтому действительно может быть возможным.

@JeffBezanson Разве это не было бы двусмысленным, с оператором & ? Наличие синтаксиса, в котором значение изменяется с пробелами или без них, затрудняет понимание. Разве Джулия тоже не должна быть лучше Си?

Хороший язык не обязательно означает, что вам нужно менять написание каждого оператора.

Использование () для индексирования устраняет главное преимущество наличия синтаксиса для индексирования, а именно позволяет работать таким вещам, как конец.

Из любопытства, нельзя ли реализовать end как : / Colon и позволить компилятору заниматься этим, а не синтаксическому анализатору? То есть сделать его полностью типизированной и перегружаемой функцией, а не функцией синтаксического сахара? Я понимаю, что он должен поддерживать кучу арифметических операторов ( end - 1 и т. Д.), Но он должен быть в области возможного, не так ли?

РЕДАКТИРОВАТЬ: Извините, теперь я вижу обсуждение этого выше, например Ragged.jl.

По-настоящему C-подобным было бы использование & A [x, y] для представлений, что в настоящее время является синтаксической ошибкой и поэтому действительно возможно.

Можем ли мы представить себе использование & для внесения изменений в кортежи, NTuple s, неизменяемые массивы и т. Д.? (т.е. полезно для массивов в стеке, например # 11902)

&A[x,y] тоже неплохо выглядит! Это кажется интуитивно понятным, и я бы предпочел его ()

Наличие синтаксиса, в котором значение изменяется с пробелами или без них, затрудняет понимание.

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

Это работает на версии 0.4:

A = rand(Int, 10,10)
3&A[1:2,3:5]
2x3 Array{Int64,2}:
 1  0  3
 3  0  2

@andyferris : чтобы заставить end работать в полном объеме с этим подходом, требуется отложенная оценка; без него вы можете в лучшем случае реализовать полужирную версию с типом, который кэширует все операции с ней.

Вот случайная мысль, которую я еще не продумал. Я просто подношу это сюда для мозгового штурма.

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

Я хотел бы думать о массиве таким образом: как абстрактные объекты, массив ничем не отличается от функции. Оба они принимают некоторый ввод (индекс для массива) и выдают результат (значение). Массив v = A (i) - функция, входной параметр - index. Функция y = f (x) представляет собой бесконечномерный массив с индексом x. Это верно и математически: векторы в евклидовом пространстве не сильно отличаются от функций в гильбертовом пространстве.

Я хотел бы думать, что семантическое значение A [1] состоит в том, что A - это некоторое хранилище, мы используем [] для доступа к конкретному значению хранилища. Это семантика низкого уровня.

Однако мы могли бы использовать () для моделирования абстракции высокого уровня. A (1) - это функция, вы вводите индекс 1, чтобы получить что-то, затем мы можем использовать карту и другие функции высокого уровня для управления A. Для оценки функции по диапазону значений (или контейнеру) мы могли бы определить диапазон версия f, которая принимает диапазон в качестве параметра, или мы могли бы отобразить f по диапазону. То же самое можно сделать и с массивом.

Для меня представление - это ленивый механизм оценки массива, как продолжение функции. Поэтому мы могли бы построить несколько высокоуровневых функций для массива для создания представления. Так же, как получить монаду продолжения для функции f. Однако я не имею четкого представления, как это сделать.

Может быть, сейчас самое время унифицировать поведение функции и массива. Не знаю, возможно ли это в теории (я не специалист по языкам). Но будет так круто, если это можно будет сделать в Юлии. Опять же, я знаю, что прошу кое-что слишком серьезное для нынешней Джулии. Если это не имеет значения, просто проигнорируйте меня.

+1 за [] просмотров
+/- 1 для () копий
-1 за &A[x,y] просмотров
-1 за A@[x,y] просмотров

Рассуждение:
Существует распространенная идиома, согласно которой общий случай должен быть быстрым и простым (в данном случае кодировать), в то время как необычные случаи должны быть возможны. В настоящее время копии рассматриваются как общий случай с помощью синтаксиса [] (который по сути является сахаром для getindex / setindex! ); просмотры обрабатываются как необычный случай через sub / slice и без сахара. Я думаю, что тот факт, что так много людей сообщили о лучшем подходе к представлениям, указывает на то, что представления следует рассматривать как общий случай с оптимизированным синтаксисом fast / easy.

Вот мой ответ на некоторые нерешенные вопросы, поднятые @JeffBezanson :

Меня действительно беспокоит неправильный способ сказать всем сначала переключить всю свою индексацию на use (), а затем в будущем использовать [] для представлений, когда они вам нужны.

Но действительно ли необходимо заставить всех переключаться? У меня сложилось впечатление, что большая часть кода, вероятно, не полагается на какое-либо автоматическое копирование, которое в настоящее время выполняется [] , и, вероятно, автоматически выиграет с точки зрения производительности от использования представлений. Поскольку в массивах v0.5 уже есть серьезные критические изменения, я не думаю, что этот переключатель слишком важен.

По-настоящему C-подобным было бы использование & A [x, y] для представлений, что в настоящее время является синтаксической ошибкой и поэтому действительно возможно.

Я считаю это довольно странным комментарием. В C на самом деле нет понятия подмассива в общих N измерениях, и либо конечные указатели, либо счетчики элементов (или контрольные точки!) Используются специально для одномерных (под) массивов, и ни один из них на самом деле не использует & . Несмотря на это - как я уже упоминал выше - я думаю, что большая часть кода массива Julia, вероятно, требует просмотров по соображениям производительности, что приведет к неприглядному потоку амперсандов с синтаксисом &A[x,y] .

Наконец, действительно ли нам нужен специальный синтаксис для копий подмассивов? У нас уже есть deepcopy и convert . Если копии действительно важны, я, конечно, не против использования специального синтаксиса, такого как A(x,y) , или какой-либо другой небольшой настройки A[x,y] например, с вашим любимым символом. Я просто не думаю, что копии так же важны, как просмотры

Если у кого-то есть доказательства того, что копии должны быть наиболее распространенным / простым в использовании случаем, например, большой пакет, который будет трудно изменить, сообщите мне / нам.

Но действительно ли необходимо заставить всех переключаться? У меня сложилось впечатление, что большая часть кода, вероятно, не полагается на какое-либо автоматическое копирование, которое в настоящее время выполняет [], и, вероятно, автоматически выиграет от использования представлений с точки зрения производительности. Поскольку в массивах v0.5 уже есть серьезные критические изменения, я не думаю, что этот переключатель слишком важен.

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

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

Я думаю, что большей части кода массива Julia, вероятно, нужны просмотры по соображениям производительности

Ага! Это абсолютно важное различие: вы просто хотите, чтобы программы работали быстрее, или вы хотите, чтобы объект A был таким, что изменение A изменяет другой массив B?

Если вас не волнует _semantics_ представлений, то лучший подход - вернуть объект представления, копируемый при записи, и библиотека может решить, на основе любых критериев, которые она хочет, возвращать представление. Он может возвращать просмотры только для определенных смежных регионов, которые, по его мнению, будут эффективными. Вы получаете ускорение без необходимости думать о том, является ли что-то видом или нет.

Напротив, с семантикой представления мы бы _обещали_, что эти две функции эквивалентны:

function f1(A)
    A[1,:] = 2
end

function f2(A)
    row = A[1,:]
    row[:] = 2
end

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

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

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

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

Что касается очень хорошего замечания Оскара, да, в некоторых
чехлы точно. Но большинству разработчиков пакетов следует хорошо знать
критические изменения появятся в 0.5, поэтому он должен быть относительно простым
исправления ошибок я думаю. Опять же, я открыт для убеждения, если у кого-то есть конкретный
пример базы кода, которую сложно переключить на просмотр семантики.
5 февраля 2016 г. в 14:16 «Джефф Безансон» [email protected] написал:

Я думаю, что большей части кода массива Julia, вероятно, нужны просмотры по соображениям производительности

Ага! Это абсолютно важное различие: вам просто нужны программы?
чтобы работать быстрее, или вы хотите, чтобы объект A был таким, что изменение A изменяет
другой массив B?

Если вас не волнует семантика просмотров, тогда лучше
подход состоит в том, чтобы вернуть объект представления копирования при записи, и библиотека может
решить, на основе каких критериев он хочет, возвращать ли представление. Это
может возвращать просмотры только для определенных прилегающих регионов, которые, по его мнению, будут
быть эффективным. Вы получаете ускорение, не думая о том, когда
что-то есть мнение или нет.

Напротив, с семантикой представления мы бы обещали, что эти два
функции эквивалентны:

функция f1 (A)
A [1 ,:] = 2
конец

функция f2 (A)
row = A [1 ,:]
строка [:] = 2
конец

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/JuliaLang/julia/issues/13157#issuecomment -180506892.

Я думаю, что наиболее консервативный (и, вероятно, правильный) подход - сохранить текущую функциональность копирования в квадратных скобках. Это безопасно и вряд ли разрушит жизнь людей (не стоит недооценивать скрытые ошибки, которые могут возникнуть). Проблема в том, что я лично хочу часто использовать представления, поэтому у них должен быть короткий выделенный синтаксис.

Опять же, я открыт для убеждения, если у кого-то есть конкретный
пример базы кода, которую сложно переключить на просмотр семантики.

Я думаю, лучше спросить , если кто - то есть пример бетона базы кода , который не трудно переключаться. Проблема в том, что сопровождающий пакета может даже не знать о проблеме, даже при хорошем тестовом покрытии. Такова природа тонких, трудно обнаруживаемых ошибок.

Можно ли использовать скобки для представлений?

julia> x = rand(1,5)
1x5 Array{Float64,2}:
 0.877481  0.18628  0.739978  0.306893  0.037569

julia> x{:,2:3}
ERROR: TypeError: Type{...} expression: expected Type{T}, got Array{Float64,2}

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

В свете того факта, что мы не можем использовать A [[2,21]]. А что насчет чего-то несимметричного: A [[2,21]? Все предложения с символами & и @ заставили бы код выглядеть действительно чужеродным. Я знаю, что предложение не симметрично, но его легко набрать, и он выглядит более знакомым.

@nstiurca Да, семантика представления очень полезна, но для меня этого все еще недостаточно, чтобы разрешить этот случай. A[1,1] - просмотр? Если нет, то (1) квадратные скобки - странная конструкция, которая иногда возвращает ссылку, а иногда - нет; (2) если вам нужен указатель на один элемент, вам нужно переключиться на другой синтаксис. Это усугубляется тем фактом, что некоторые типы, например разреженные матрицы, либо не реализуют представления, либо имеют очень медленные представления. Похоже, что упрощение получения представлений _1 символ_ не стоит добавлять всю эту путаницу к скромному оператору квадратной скобки. Нам понадобится какой-то другой оператор, который будет оператором «разумного индексирования», для которого некоторые предлагают A( ) но этого _не произойдет_. Новая, более сложная операция получит новый синтаксис.

Что насчет чего-то несимметричного

Потому что это свело бы с ума многих программистов: https://xkcd.com/859/

@JeffBezanson A[1,1] - это представление, если оно находится слева от = , и его копия в настоящее время ... Для меня это на самом деле _сложнее, чем "всегда представление" для одного синтаксиса и " всегда копия »для другого синтаксиса. Но вы уже так решили.

@Evizero @musmo
oh-god-why

В A[1,1] = z , возможно, более правильно рассматривать [1,1] как модификатор для = , а не A . Например (и для вставки скобок там, где они на самом деле не разрешены), это A ([1,1]=) z (фактическое представление, однако, setindex!(A, z, [1, 1] ), аналогично тому, как A (+=) z анализирует до A = A + z вместо (несуществующей конструкции) (A+) = z .

Результат &x[3] уже можно ввести: Ref(x, 3)

Правильно, не имеет смысла говорить, что A[1,1] слева от = - это представление --- что это будет означать, если вместо этого будет копия?

Я полностью согласен с тем, что правильно иметь один синтаксис, означающий «всегда представление», а другой - «всегда копию». Единственное разногласие заключается в том, каким должен быть синтаксис для каждого. Я считаю, что изменение A[ ] слишком радикально. А как насчет, скажем, поиска по словарю? Будет ли это поддерживать и взгляды?

На данный момент я думаю, что A[...] всегда означает копирование, в то время как A@[...] всегда означает, что представление стоит попробовать, чтобы увидеть, как оно выглядит. Точный синтаксис на самом деле не так важен, как попробовать. Как бы то ни было, я не совсем убежден в том, что я собираюсь назвать «позицией Джеффа», по нескольким причинам:

  1. Мне не так сложно создать общий ленивый тип представления, который может быть типом результата для нарезки общих контейнеров. Тот факт, что плотные массивы имеют более эффективную реализацию представления, является хорошей оптимизацией, но всего лишь оптимизацией - вы можете просто вернуть ленивое представление, и поведение будет таким же. Создание ленивого представления было бы дешевым, и если copy представления свернет ленивое представление, чтобы оно больше не было представлением, тогда у вас есть последовательный способ выражения как представлений, так и копий: A[...] - это представление а copy(A[...]) - это сегмент без просмотра. Не нужно вводить лишний синтаксис. Это также имеет синтаксическое преимущество, заключающееся в том, что самая короткая, наиболее очевидная вещь - дешевая (и часто быстрая в использовании), а дорогая вещь - то есть создание копии - очень явная; вы точно знаете, что делается копия.
  2. Я не верю аргументу скалярной индексации. Это потому, что самое большое различие остается в любом случае: нескалярные срезы - например, A[1,:] и v[1:5] - возвращают контейнеры; скалярные срезы - например, A[1,2] и v[1] - возвращают одиночные значения. По сравнению с этой ОГРОМНОЙ семантической разницей разница между нескалярным срезом, являющимся представлением, и скалярным срезом, который не является представлением, кажется тривиальной. Поскольку мы уже должны понимать эту разницу, я не понимаю, насколько этот аргумент убедителен.

Я знаю, что в # 8892 мы обсуждали угловые скобки на основе юникода, но как насчет угловых скобок ASCII ( A<:,3> )? Я понимаю, что они наименее читабельны из всех вариантов скобок, но они действительно выглядят как «v» вместо «view»: smile :.

Очевидная проблема: в настоящее время

julia> 2<3>1
true

julia> 1<2>3
false

Я думаю, что лично я был бы готов перейти на 1 < 3 && 3 > 2 в обмен на получение еще одной односимвольной скобки. В некотором отличие от невозможности выдачи предупреждения для семантики копирования-> просмотра, мне интересно, можно ли выдать информативную ошибку парсера для этого изменения синтаксиса.

Или мы могли бы полностью перейти на C ++ и использовать A<<:,3>> . Меня уже трепещут мысли об этом великолепии!

Для копирования при записи: 'COW' (U + 1F404)
Для ломтиков: «КУСОЧКА ПИЦЦЫ» (U + 1F355)

Хорошо, я согласен, что аргумент скалярной индексации не является сильным. Более важным аспектом является то, как это влияет на разработчиков getindex (которых много) в целом. Является ли указание, что вы не должны реализовывать getindex , вместо этого позволяя ему вернуться к значению по умолчанию, которое создает представление, и вместо этого реализовать copy(::View{MyType}) ?

Мне не так сложно создать общий ленивый тип представления, который может быть типом результата для нарезки общих контейнеров.

Мы сделали это, и это было тяжело. @timholy Я помню, что самой большой проблемой была линейная индексация; это правильно? Хотя это правда, что частные случаи для плотных массивов - это «просто оптимизация», мы уже знаем, что не существует эффективной реализации, например, для разреженных массивов на горизонте. А что насчет чего-то вроде DataFrames?

Я бы сказал, что DataFrames трудно использовать в качестве ясного аргумента для обеих сторон: индексирование в pandas DataFrames обычно создает представление; индексация в R data.frame всегда создает копию «копирование при записи».

Реализовать линейную индексацию несложно - реализовать ее _эффективно_ сложно. Но линейная индексация в любом случае эффективна только для нескольких структур данных (массивов, которые хранятся непрерывно) - и мы хотели бы отойти от нее в целом. Если вы реализуете скалярную индексацию, то реализация представления V = A[I,J] - это просто вопрос A , I и J и определение V[i,j] = A[I[i],J[j]] . Если есть более эффективный способ сделать это, то вы можете перегрузить нескалярное индексирование, но в целом я думаю, что это действительно должно быть _less_ работой для большинства разработчиков контейнеров. Обратите внимание, что композиция представлений может быть в целом эффективной: V[I,J][K,L] = V[I[K],J[L]] . Поскольку нарезка диапазона с помощью диапазона дает диапазон, это работает довольно хорошо.

Дополнение к моему последнему комментарию: хотя случай скалярной индексации на самом деле не работает как аргумент «семантической согласованности», он лучше работает как аргумент «будущей общности». Хотя у нас этого нет или мы не хотим этого сейчас, если мы когда-либо хотели иметь возможность возвращать указатель на один элемент массива, тогда специальный синтаксис представления уже дает вам это.

@johnmyleswhite Могу я получить более категоричный ответ --- что бы вы предпочли? :)

Отлично сыграно, @JeffBezanson. :)

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

Как я уже говорил, что действительно беспокоит меня в этой теме, так это отмена давнего плана по переключению на представления в качестве семантики по умолчанию для нарезки, который сильно понравился некоторым людям (например, некоторым разработчикам Torch). Этот поворот кажется мне особенно странным, учитывая сложность темы и отсутствие четкого правильного ответа: никто не доказал, что копии были правильным решением с самого начала, просто стало ясно, что ни копии, ни взгляды не доминируют друг над другом.

Что касается DataFrames, я пришел к выводу, что предоставление любой формы индексации вообще является ошибкой: вам действительно нужен более высокий уровень абстракции, подобный тому, который предоставляется R dplyr.

Я не думаю, что произошло обратное; Я вполне могу потерять это. Просто проблема не обсуждалась полностью - при повторном чтении # 3701 обсуждение в основном касалось производительности, и прошло слишком много времени, прежде чем семантическая проблема и проблема производительности были четко разделены. Я думаю, что нам сейчас больше всего нужно закончить # 9150, и если эти дебаты зайдут в тупик, просто следуйте первоначальному плану.

@johnmyleswhite Я согласен с тем, что просмотры действительно понравятся разработчикам Torch и другим, кто предпочитает просмотры, но какой вред от использования A@[] для достижения именно этого? Если им не нравится делать копии, они могут просто избегать синтаксиса A[] .

Я лично предпочел бы, чтобы A[] имела семантику копирования, при которой реализация может выбрать использование копирования при записи в некоторых случаях по соображениям производительности. Тогда семантика представления гарантируется синтаксисом A@[] .

@timholy , я согласен, было бы хорошо иметь возможность использовать <> но у него есть несколько проблем, например, с <> логическая индексация тоже будет сложной:

A[x.<1]
A<x.<1>

Вероятно, слишком много угловых случаев / дополнительных правил, чтобы <> работало.

A[...] - это просмотр, а copy(A[...]) - сегмент без просмотра

Мне это нравится, потому что всякий раз, когда кто-то заботится о получении представления или копии по семантическим причинам (в отличие от производительности), он обычно не находится в середине выражения, поэтому дополнительная многословность copy() кажется слишком большой. для меня разумнее, чем дополнительная магия @[] .

@ lucasb-eyer Я думаю, проблема в том, что концепция представления не всегда имеет смысл. Итак, A[...] будет иметь разное концептуальное значение для разных типов A . Кто-нибудь поправит меня, если я это неправильно понял.

Кроме того, есть опасение, что переход на представления по умолчанию привнесет множество тонких ошибок в существующую экосистему.

Кстати, я все еще за A@[...] .

Мы сделали это, и это было тяжело. @timholy Я помню, что самой большой проблемой была линейная индексация; это правильно?

Линейное индексирование составило 2 из 3 основных проблем, но также справедливо сказать, что sub vs slice составляют 2 из 3 проблем:

  • _inferring_, имеет ли SubArray эффективную линейную индексацию, является самой сложной проблемой. Это наиболее сложно в случае, когда вы создаете представление представления, потому что с помощью sub мы конвертируем целочисленные индексы в диапазоны. Это актуально, потому что можно сделать вывод, что sub(A, 3, :) имеет эффективную линейную индексацию, но sub(A, 3:3, :) не может (измените это на sub(A, 3:4, :) и вы нарушите линейное индексирование), но внутренне во время хранения мы эффективно конвертируем 3-> 3: 3. Несмотря на это преобразование, нам удалось это осуществить:
julia> A = rand(5,5);

julia> B = sub(A, 3, :);

julia> Base.linearindexing(B)
Base.LinearFast()

julia> C = sub(B, :, 2:4);

julia> Base.linearindexing(C)
Base.LinearFast()

а также это:

julia> A = rand(5,5,5);

julia> B = sub(A, :, 2:3, 2:3);

julia> Base.linearindexing(B)
Base.LinearSlow()

julia> C = sub(B, :, :, 1);

julia> Base.linearindexing(C)
Base.LinearFast()

потому что мы такие классные. (Уловка сводится к последнему параметру LD и рассуждениям о том, как вы, должно быть, получили то, что было для родительского SubArray.) Если бы я сделал это снова, я бы не пытался быть крутым, и сохранить «неразрезанные» измерения с помощью нового типа индекса, NoSlice(3) , который упрощает вывод.

  • построение или индексация с меньшей размерностью:
A = rand(5,5,5)
B = sub(A, :, 3:17)  # constructs a 2d view from a 3d array

или же

A = rand(5,5,5)
B = sub(A, 2:4, 2:4, 2:4)
B[1, 7]

Если мы избавимся от последнего и заменим первое композицией из двух типов представлений, SubArray{ReshapedArray{Array}} , все станет чище (но, возможно, с меньшей производительностью).

  • Пока мы не сгенерировали функции, я не видел способа решения этой проблемы с нулевыми накладными расходами:
S1 = slice(A, :, 5, 2:6)
S2 = slice(A, 5, :, 2:6)
S1[i,j] -> A[i,5,(2:6)[j]]
S2[i,j] -> A[5,i,(2:6)[j]]

Теперь, когда я более привык к стилям кодирования "lisp-y", я подозреваю, что мы сможем обойтись без сгенерированных функций, если удастся решить различные проблемы с производительностью от сплаттинга (# 13359).

@ mauro3 , хорошее замечание.

Думаю, проблема в том, что концепция представления не всегда имеет смысл. Итак, A [...] будет иметь разное концептуальное значение для разных типов A. Кто-нибудь поправит меня, если я это неправильно понял.

@Evizero, не могли бы вы объяснить, что вы имели в виду? А [...] уже имеет разные концептуальные значения для разных типов А.

  1. Я не верю аргументу скалярной индексации. Это потому, что самое большое различие остается в любом случае: нескалярные срезы - например, A [1 ,:] и v [1: 5] - возвращают контейнеры; скалярные срезы - например, A [1,2] и v [1] - возвращают одиночные значения. По сравнению с этой ОГРОМНОЙ семантической разницей разница между нескалярным срезом, являющимся представлением, и скалярным срезом, который не является представлением, кажется тривиальной. Поскольку мы уже должны понимать эту разницу, я не понимаю, насколько этот аргумент убедителен.

Я согласен с @StefanKarpinski и @ lucasb-eyer здесь. Да, copy(A[...]) более подробный, но более читаемый, интуитивно понятный и явный. A@[...] , вероятно, недоступен для людей, приходящих к Julia из Python, Matlab, C и т. Д.

Более важным аспектом является то, как это влияет на разработчиков getindex в целом.

Что, если мы просто изменим A[…] на slice вместо getindex и сохраним семантику getindex , в каком она есть сейчас? Тогда copy(::SubArray) можно записать так:

copy(S::SubArray) = getindex(S.parent, S.indexes...)

Разработчикам getindex прежнему нужно обрабатывать только скалярный случай. Базовая библиотека по-прежнему предоставляет (копирует) нескалярные резервные варианты для getindex. Если им доступна оптимизация, они могут специализироваться на getindex (как они это делают сейчас). Или, если они могут реализовать более эффективный тип представления, они могут специализироваться на slice . Это также означает, что copy(A[…]) фактически то же самое, что и старый добрый getindex(A, …) . Это просто переворачивает привилегию синтаксиса на представления без потери функциональности.

(Это не совсем работает как есть, поскольку сейчас slice(A, 1, 1) является 0-мерным представлением, но может измениться либо это, либо имя выделяющего getindex.)

не могли бы вы объяснить, что вы имели в виду? A [...] уже имеет разные концептуальные значения для разных типов A

Я полагаю, я хотел сказать, что было бы странно, если бы A [1 ,:] возвращал представление для плотных массивов и копию для разреженных массивов. Другими словами, для некоторых типов манипулирование возвращаемым значением фактически изменит исходный объект, а для других типов - нет.

Но почему A[1,:] вообще определено для разреженных массивов? Мне кажется, что это определено только для выполнения полного интерфейса AbstractArray (что бы это ни было). Понятно, что типы, у которых нет данных для непосредственно доступного среза, должны вычислять его на лету и, следовательно, естественно создавать копию.

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

getindex(v::View, i) = v.a[v.idx[i]]

и поэтому работает для всего, что имеет скалярную индексацию. Иногда это может быть медленным, но это лучше, чем иметь индексирующую копию (и, следовательно, быть медленной) в общем случае, и лучше, чем вводить странный синтаксис (пытаясь обосновать здесь Стефана :)).

Поскольку мы меняем роли здесь, я должен указать, что написание копий-срезов как copy(A[...]) имеет недостаток, который, по крайней мере семантически и при отсутствии умных оптимизаций, означает, что вам придется платить за создание представления. объект _и_ затем копирует его. Эту дополнительную стоимость можно было бы оптимизировать (особенно правдоподобно, если нам удастся получить выделение стека для объектов, содержащих ссылки на другие объекты), но без работы по оптимизации это было бы медленнее, чем наши текущие срезы копирования.

Я должен отметить, что написание копий-срезов как copy (A [...]) имеет недостаток, который, по крайней мере, семантически и при отсутствии умных оптимизаций, означает, что вы должны платить за создание объекта представления, а затем его копирование .

Может быть, это можно было бы решить аналогично тому, как у нас сейчас есть sub с copy(A, ...) ?

Может быть, это можно решить аналогично тому, как у нас сейчас есть подпрограмма с копией (A, ...)?
: +1:

Может быть, это можно было бы решить аналогично тому, как у нас сейчас есть sub с copy(A, ...) ?

Это будет иметь ту же проблему, что и sub , а именно, что вы не можете использовать end в качестве выражения индексации.

copy(A[...]) имеет большую привлекательность для версии для копирования: он полностью читается, не скрывает две операции внутри одной и выглядит так же, как copy(foo(A)) для любой другой операции foo() .

А теперь несколько сумасшедшее предложение в качестве временной меры при отсутствии достаточно умных оптимизаций: что, если copy(A[...]) был просто синтаксическим сахаром для комбинированной копии и getindex? Я знаю, что люди будут счастливы избавиться от таких вещей, как Ac_mul_B , но они также служили полезным временным промежутком, пока не появилось что-то лучшее.

действительно ли copy(A[...]) проблема? Если строительство подмассивов дешево, комбинированная операция будет иметь лишь небольшие накладные расходы.

@ c42f Я думаю, что для синтаксического сахара должно быть возможно реализовать макрос @copy который это делает. Создание синтаксического сахара copy(A[...]) потребует изменений в синтаксическом анализаторе.
@tknopp Мне тоже это интересно. Стоимость копирования должна свести к минимуму затраты на создание подмассива.

Что, если A относится к типу, который не поддается просмотру (т.е. A[...] в любом случае должен быть скопирован для A ). Выполняет ли copy(A[...]) тогда две копии? Похоже на тонкий источник проблем с производительностью.

Можем ли мы получить вариант использования, в котором желательно копирование? С моей точки зрения, мы должны сосредоточиться на большинстве вариантов использования, а не рассматривать угловые варианты, которые могут сделать [...] более универсальным. Я действительно не покупаю аргумент SparseMatrix поскольку [...] действительно не должен быть предоставлен для этого типа.

Что касается аргумента о скрытой ошибке: есть люди, которые считают следующее удивительным:

julia> a = [1,2,3];
julia> b = a;
julia> b[1] = 0;
julia> a
3-element Array{Int64,1}:
 0
 2
 3

Кажется, это очень похоже на массив, возвращающий мне представление.

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

@tknopp Вы предлагаете, чтобы A[:, 1:2] просто не работало для SparseMatrices? Это кажется довольно странным.

Так ты думаешь, что scipy довольно странный?

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

Для scipy это зависит от формата вашей матрицы. Для CSC вы можете выполнять нарезку, но только с размером шага 1.

Так ты думаешь, что scipy довольно странный?

IMHO python - не лучший образец для подражания, когда дело доходит до синтаксиса линейной алгебры

Но использование этих методов в качестве аргумента в пользу отказа от просмотра массивов для меня не так убедительно.

Я не думаю, что кто-нибудь будет против просмотра массивов. Вопрос в том, как они должны выглядеть.

Я хотел сказать, что A[:, 1:2] - это удобный метод для разреженных матриц. И я хотел знать, считают ли люди, что это очень важный вариант использования, который должен стимулировать обсуждение синтаксиса, или нет. Мы узнали из этого потока, что мы можем быть более универсальными с копированием [...] но одного этого ИМХО недостаточно, чтобы гарантировать особый синтаксис. Также важно, чтобы синтаксис был тем, что люди действительно хотят в большинстве ситуаций. Я думаю, это мнение, но это мое личное предпочтение, и поэтому я думаю, что это следует обсудить здесь.

Я подозреваю, что SciPy работает именно так, потому что индексирование возвращает просмотры. Вы бы никогда не захотели использовать неэффективное представление с разреженной матрицей, поэтому у вас его не может быть. Но мне все равно кажется странным, если вы можете сделать A[1, 2] но не A[:, 1:2] . Вам все равно понадобится способ выразить последнюю операцию, и это не может быть copy(...) если вы не можете построить представление. Это также кажется полной катастрофой, если вы хотите написать код, который может работать как с плотными, так и с разреженными матрицами.

Одна из тем этой беседы состоит в том, что есть два компонента того, «что люди на самом деле хотят»: семантика, которую они хотят, и то, что сейчас быстро. Я предпочитаю семантику копирования, и когда я использую sub это обычно связано с производительностью, а не потому, что мне действительно нужна семантика представления. Я думаю, что идеальным подходом могут быть просмотры с копированием при записи, как @JeffBezanson, предложенный в https://github.com/JuliaLang/julia/issues/13157#issuecomment -180506892.

Я подозреваю, что SciPy работает именно так, потому что индексирование возвращает просмотры. Вы бы никогда не захотели использовать неэффективное представление с разреженной матрицей, поэтому у вас его не может быть. Но мне все равно кажется очень странным, если вы можете выполнить A [1, 2], но не A [:, 1: 2]. Вам все равно понадобится какой-то способ выразить последнюю операцию, и это не может быть copy (...), если вы не можете создать представление. Это также кажется полной катастрофой, если вы хотите написать код, который может работать как с плотными, так и с разреженными матрицами.

  • да, скалярная индексация не имеет особого смысла для разреженных массивов. Так что к этому нужно относиться аналогично
  • Если вы хотите написать общий код, который работает с разреженными и плотными матрицами, вы бы использовали индексирование диапазона? Конечно, я понимаю ваш аргумент, но было бы интересно, если это то, что люди используют в такой форме, или это гипотетический вариант использования.

Одна из тем этой беседы состоит в том, что есть два компонента того, «что люди на самом деле хотят»: семантика, которую они хотят, и то, что сейчас быстро. Я предпочитаю семантику копирования, и когда я использую sub, это обычно для производительности, а не потому, что мне действительно нужна семантика представления. Я думаю, что идеальным подходом могут быть просмотры с копированием при записи, как @JeffBezanson, предложенный в # 13157 (комментарий).

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

Что касается копирования при записи: я был действительно удивлен, увидев это предложение @JeffBezanson, так как оно усложнило бы всю систему массивов. С развитием многопоточности для меня не очевидно, как это можно реализовать эффективно.

Копирование WRT при записи, вы можете заставить ядро ​​делать это, но только в OS X AFAICT, и это, вероятно, по-прежнему будет иметь многие затраты, связанные с распределением кучи. Если мы не будем использовать ядро, вам, возможно, придется сделать некоторые причудливые вещи, чтобы сделать копирование при записи эффективным для циклов, которые читают и затем записывают в массив, но если проще сделать компилятор умнее, чем сделать пользователей умнее , возможно, оно того стоит.

Распределение потоков уже является сложным с семантикой массива, которая у нас есть сейчас, потому что можно изменить указатель массива, используя push! и т. Д., И тогда некоторые потоки могут обращаться к неправильному буферу. Я не уверен, решается ли / как это в настоящее время, но похоже на аналогичную проблему.

Если копирование при записи отсутствует в таблице, то, по сути, вопрос заключается в том, должны ли мы строить семантику нашего языка на основе того, что дает наилучшую производительность для наиболее распространенного случая (плотные массивы), или мы примем снижение производительности для неоптимизированного кода в интерес общности. Главное предостережение в отношении выбора «лучшей производительности» заключается в том, что на самом деле он не очень помогает. Создание представлений [...] return не избавит вас от размышлений о том, как вы распределяете память, если вы не используете только скалярные операции, поскольку сложение, умножение и т. Д. Массивов по-прежнему будет выделять вывод. Таким образом, вам все равно придется вручную оптимизировать выделение памяти, когда вы заботитесь о производительности, а повторное использование памяти во всех других местах, которые вы можете выделить, часто намного более болезненно, чем переключение индексации [...] на sub . Учитывая этот факт, а также тот факт, что изменение [...] для возврата представлений нарушит очень большую часть существующего кода Julia, я не думаю, что это хорошая идея.

Имейте в виду, что мы можем делать просмотры разреженных массивов с относительно небольшими затратами, просто индексируя через объекты срезов: то есть для индексации в S[1, 3:25] просто требуется одно дополнительное целочисленное добавление для каждого доступа к элементу. Если срез является вектором, то нам нужно зацепиться за этот вектор, но я не вижу в этом препятствия для демонстрации - он может быть построен за постоянное время, и работать с ним немного медленно.

Верно, что если вы выполняете скалярное индексирование в представлении разреженной матрицы, накладные расходы будут минимальными по сравнению со стоимостью скалярного индексирования для начала, потому что скалярное индексирование разреженных матриц не очень эффективно. Но в отсутствие специализированных методов / алгоритмов многие вещи, которые масштабируются только с помощью nnz для SparseMatrix, будут масштабироваться с общим количеством элементов (нулей и ненулевых). Учитывая, что разреженные матрицы разрежены, это кажется довольно плохим.

SparseMatrices - наихудший случай, но для практически любого подтипа AbstractMatrix, который не является StridedMatrix, а также StridedMatrices с неединичным шагом между соседними элементами, вероятно, будет быстрее выделить новую матрицу, прежде чем пытаться выполнить умножение матриц. Представления действительно могут использоваться напрямую только для типов, для которых нет более эффективного способа сделать что-либо, кроме выполнения скалярной индексации, или для подмножества случаев, когда можно написать эффективные алгоритмы для работы с представлениями (потенциально только для определенных типов) и кто-то реализовал эти алгоритмы.

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

Вот почему я не уверен, стоит ли слишком волноваться из-за проблем производительности с разреженными представлениями массивов; да, наивные алгоритмы наказываются видом, но исправить их довольно просто. Предположительно довольно много алгоритмов можно было бы просто переписать, используя

for M in (SparseMatrixCSC, SubArray{,...,SparseMatrixCSC, ...})
    <strong i="6">@eval</strong> begin
        ....
    end
end

С другой стороны, мысль об оптимизации просмотров BitArrays меня просто пугает.

Извините, что вмешиваюсь как чистый пользователь, с тревогой наблюдающий за этими дебатами:

Предложение сделать срезы _copy on write_ - лучшая новость, которую я слышал о Джулии за очень долгое время. Я сам и практически все мои соавторы по нашим кодам Джулии будут в восторге, если стандартная нотация срезов A[...] будет вести себя семантически как копия.

PS: Мне также нравится идея использования простых обозначений, таких как A@[...] для представлений.

@cortner : Не могли бы вы

@tknopp : извините, если я не A[2:5, 6:12] копию или абстрактный массив «копирование при записи», если он ведет себя как копия. В какой-то степени это может быть просто личными предпочтениями, но, в конце концов, разве эта дискуссия не идет по существу о лучшем варианте для наибольшего числа пользователей, то есть личных предпочтениях? Лично для меня подход представления, который использует Python, раздражает меня каждый раз, когда я пишу код Python, поэтому я боялся того дня, когда Джулия переключилась на это поведение в версии 0.5.

Я твердо верю, что «поведение по умолчанию» должно быть максимально простым и безопасным. В этом конкретном вопросе я просто считаю, что это поведение в стиле копирования (будь то копирование или копирование при записи) достигает этого.

И чтобы быть уверенным: меня было бы безмерно раздражать, если бы мне пришлось писать copy(A[2:5, 6:12]) же как меня раздражает необходимость писать slice(A, 2:5, 6:12) a в тот момент, когда я хочу отказаться от одноэлементных измерений. (Это просто, чтобы доказать, что я не просто евангелист Matlab.)

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

Кто-нибудь знает о рецензии или обсуждении разработчиков Matlab или NumPy об их решении копировать по умолчанию и просматривать по умолчанию? Я не верю, что это обсуждается впервые в истории языков программирования, и те, кто прошел через это, могут поднять соответствующие вопросы.

Представления в NumPy восходят к Numeric 1. Вы можете увидеть некоторую дискуссию вокруг отозванного PEP209 от 2001 года для предлагаемого редизайна, в котором упоминается производительность. В списке рассылки они обсуждают возможность компоновки и синтаксис :

Да, производительность была главной причиной. Но есть еще один: если
нарезка возвращает представление, вы можете сделать копию на его основе, но если
нарезка возвращает копию, нет возможности создать представление. Итак, если вы
измените это, вы должны предоставить другой способ создания представления, и
пожалуйста, держите синтаксис простым (есть много практических случаев, когда
просмотр обязателен).

Звучит знакомо. На их выбор могла также повлиять поддержка абстракции списков списков для многомерных массивов; A[0] - это ссылка на первую строку матрицы.

Что интересно, Matlab, похоже, использует совместное копирование при записи только для синтаксиса A(:) и изменений. Все остальные синтаксисы индексации являются непосредственными копиями (проверено в 2014a). Я был бы очень удивлен, если бы по этому поводу возникло какое-либо публичное обсуждение… но представления были бы огромным отклонением от семантики копирования при записи для привязок.

Как пользователь Julia, вот некоторые из моих опасений по поводу использования представлений массива по умолчанию:

  • Это изменение может незаметно нарушить работу существующих пакетов и пользовательского кода, без периода устаревания или автоматического предупреждения пользователей (представления только для чтения смягчают это в некоторой степени, но не полностью). Это может иметь катастрофические последствия для ничего не подозревающих программистов Julia или обычных людей, на жизнь которых влияет программное обеспечение, написанное на Julia.
  • Поддержка этого изменения в основном связана с производительностью, но аналогичное улучшение производительности может быть достигнуто без изменения семантики [] (посредством улучшений компилятора).
  • Если представления были сделаны по умолчанию для индексации стиля A [:, 1], должны ли они также использоваться по умолчанию для индексации стиля A [bool_vec, 2]? В NumPy индексирование с помощью логического вектора или вектора индекса дает копию (предположительно из соображений производительности), а индексирование с диапазоном дает представление. Это сбивает пользователей с толку, усложняет семантику [] и приводит к возникновению тонких ошибок.
  • Если бы представления были выбраны по умолчанию для индексации логических векторов и векторных индексов, какое влияние это оказало бы на производительность существующего (и будущего) кода, использующего эти типы индексации? Помимо необходимости хранить индексный вектор в течение всего времени существования представления, для таких представлений, вероятно, потребуются две проверки границ для каждого доступа (одна для вектора индекса, затем одна для базового массива).
  • Что произойдет с представлением, если форма / размер базового массива изменится? Должно ли представление быть немедленно признано недействительным, и при последующих доступах будет возникать ошибка? Или доступ к представлению должен вызывать ошибку, только если переведенный индекс находится за пределами базового массива? Оба подхода, вероятно, повлияют на производительность и сложность, что также повлияет на 99% случаев, когда базовый массив никогда не изменяется в размере.
  • Для массивов, содержащих неизменяемые объекты (наиболее распространенный случай), A [1, 1] возвращает копию скаляра. Мне показалось бы непоследовательным, если бы визуально похожая конструкция A [1, 1: 2] вместо этого возвращала представление.

@annalam : спасибо за отличный список.

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

Не могли бы вы указать, на чем вы основываете свое мнение, что это изменение незаметно нарушает значительный объем существующих пакетов? Это честная просьба. Пожалуйста, дайте нам ссылки на важные пакеты Julia, которые ломаются и где они ломаются.

Что произойдет с представлением, если форма / размер базового массива изменится

Размер массивов с более чем одним измерением изменить нельзя. Vector может быть изменен, поэтому некоторые ограничения проверки неэффективности могут закрасться.

@tknopp : Вы абсолютно правы. Я не должен был писать «значительные суммы», это было необоснованно. Я отредактировал предложение, чтобы сказать «может сломать существующие пакеты и код пользователя», и добавил предложение, указывающее, что период просмотра только для чтения может несколько смягчить эту проблему (хотя и не полностью).

Моя скромная просьба ко всем присутствующим - не вдаваться в дальнейшие доводы за и против. Я думаю, что здесь описано почти все, и что нам действительно нужно, так это набор пользовательских кодов, которые служат ориентирами, чтобы помочь сделать это более осознанное решение.

Я счастлив создать репо и управлять им, если каждый, кто вмешался здесь, готов предоставить несколько автономных кодов и тестовых примеров, которые мы можем протестировать.

Я счастлив создать репо и управлять им

Пожалуйста, сделайте @ViralBShah. Я думаю, что некоторый реальный код здорово поможет обсуждению. Что бы я хотел увидеть:

  • Как часто массивы читаются или записываются?
  • Как часто требуются копии?
  • Насколько легко обнаружить ошибки, когда данные незаметно перезаписываются из-за забытой копии?
  • Каковы настоящие различия в скорости между просмотром / копированием / копированием при записи?

Я думаю, что на нескольких «реальных» примерах некоторые из них могут стать более очевидными.

Это отличное предложение Viral. Я могу предложить пример использования, в котором поведение копирования вводит в заблуждение. Мы должны суммировать все subtle bugs там.

Что касается COW, есть ли реализация, которая _не_ замедлит доступ? Я думаю об этом:

type COWArray{T,N,A<:AbstractArray} <: AbstractArray
    iscopy::Bool
    parent::A
end

function setindex!(A::COWArray, val, indexes...)
    if !A.iscopy
        A.parent = copy(A.parent)
        A.iscopy = true
    end
    A.parent[indexes...] = val
end

Теперь прогнозирование _maybe_ ветвления будет означать, что проверка не замедляет доступ без SIMD, но тестирование этого будет первым приоритетом.

COW может быть не таким плохим в Matlab, потому что язык изначально не был разработан для шаблонов поэлементного доступа - если единственный разумный способ использования массивов - это векторизованный синтаксис, вы эффективно поднимаете эту проверку COW.

@timholy Если мы можем оговорить, что мы заботимся только о накладных

Я думаю, что это все еще не имеет той же семантики, что и копия, поскольку вы увидите мутации исходного массива

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

@carnaval : В правильной реализации COW «материнскому» массиву потребуется элементы и инициировать копию, если она изменится. IMHO слишком сложный и даже более сложный (-> медленный) при рассмотрении многопоточности.

Я хочу привести конкретный пример для рассмотрения. Предположим, я хочу реализовать полностью измененный LU, алгоритм, который вы увидите в учебниках, описанных так (Голуб и Ван Лоан 4 / e, стр. 132, алгоритм 3.4.3):

screenshot 2016-02-23 11 17 40

Этот конкретный алгоритм довольно богат поведением индексирования и срезов.

Наивный пользователь мог бы написать для этого алгоритма что-то вроде

function lucompletepiv!(A)
  n=size(A, 1)
  rowpiv=zeros(Int, n-1)
  colpiv=zeros(Int, n-1)
  for k=1:n-1
    As = abs(A[k:n, k:n])
    μ, λ = ind2sub(size(As), indmax(As))
    μ += k-1; λ += k-1
    rowpiv[k] = μ
    A[[k,μ], 1:n] = A[[μ,k], 1:n]
    colpiv[k] = λ
    A[1:n, [k,λ]] = A[1:n, [λ,k]]
    if A[k,k] ≠ 0
      ρ = k+1:n
      A[ρ, k] = A[ρ, k]/A[k, k]
      A[ρ, ρ] = A[ρ, ρ] - A[ρ, k] * A[k, ρ]
    end
  end
  return (A, rowpiv, colpiv)
end

но из-за всех копий, которые делаются с помощью выражений индексации, этот код Julia в версии 0.4 примерно в два раза медленнее, чем эквивалентный код Python

import numpy as np

def lucompletepiv(A):
    assert np.size(A, 0) == np.size(A, 1)
    n = np.size(A, 1)
    rowpiv = np.zeros(n-1, dtype=int)
    colpiv = np.zeros(n-1, dtype=int)
    for k in range(n-1):
        Asub = abs(A[k:n, k:n])
        mu, lam = np.unravel_index(np.argmax(Asub), np.shape(Asub))
        mu, lam = mu + k, lam + k
        rowpiv[k] = mu
        A[[k, mu], :n] = A[[mu, k], :n]
        colpiv[k] = lam
        A[:n, [k, lam]] = A[:n, [lam, k]]
        if A[k, k] != 0:
            rho = slice(k+1, n)
            A[rho, k] /= A[k, k]
            A[rho, rho] -= np.dot(np.reshape(A[rho, k], (n - (k + 1), 1)),
                                  np.reshape(A[k, rho], (1, n - (k + 1))))
    return (A, rowpiv, colpiv)

Мне было бы любопытно посмотреть, насколько быстрее люди могут сделать этот код Julia, изменив только операции индексации.

: +1:

Для сравнения, вот что нам нужно было бы написать в версии 0.4 для лучшей производительности:

function lucompletepiv3!(A)
    n = size(A, 1)
    lda = stride(A, 2)
    rowpiv = zeros(Int, n - 1)
    colpiv = zeros(Int, n - 1)
    <strong i="7">@inbounds</strong> begin
        for k = 1:n - 1
            offsetk = (k - 1)*lda
            μ, λ = idxmaxabs2(A, k:n, k:n)
            rowpiv[k] = μ
            swaprows!(A, k, μ)
            colpiv[k] = λ
            swapcols!(A, k, λ)
            if A[k,k] ≠ 0
                ρ = k + 1:n
                scale!(1/A[k + offsetk], sub(A, ρ, k))
                for j in ρ
                    offsetj = (j - 1)*lda
                    Akj = A[k + offsetj]
                    <strong i="8">@simd</strong> for i in ρ
                        A[i + offsetj] -= A[i + offsetk] * Akj
                    end
                end
            end
        end
    end
    return (A, rowpiv, colpiv)
end

У меня есть некоторый код здесь , что я был бы рад внести свой вклад в тестирование репо. Код представляет собой упрощенную модель одной из частей решателя CFD, над которой я работаю (вычисление потока Эйлера для части сетки). Это тестирование работы с большим количеством небольших массивов.

Вау - примеры пришли быстро. Я думаю, нам нужно гораздо больше кодов, которые ближе к рабочим нагрузкам или реальным приложениям. Возможно, пакеты станут хорошим источником.

Я только что создал это репо: https://github.com/JuliaLang/IndexingBenchmarks

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

Проблема с разрешениями в репо:

remote: Permission to JuliaLang/IndexingBenchmarks.git denied to JaredCrean2.
fatal: unable to access 'https://github.com/JuliaLang/IndexingBenchmarks.git/': The requested URL returned error: 403

@ JaredCrean2 Теперь вы можете писать в это репо. В общем, для людей, у которых нет доступа к фиксации, они должны будут отправить PR, но будут рады добавить всех, кто вносит свой вклад / помогает в этом.

Вместо этого я отправил PR (я не видел вашего комментария до сих пор).

Какой минимальный набор тестов будет полезным? Версия julia (0.4) алгоритма с использованием представлений и одна с использованием копий?

Два флажка за два дня. С такими темпами мы закончим в кратчайшие сроки: smile :.

В этом выпуске было опубликовано несколько примеров, демонстрирующих, что простого возврата представлений по умолчанию недостаточно для решения многих проблем с производительностью - многие из них лучше решать с помощью «автоматической девекторизации». Это вводит нас в сферу достаточно умного компилятора. Однако в https://github.com/JuliaLang/julia/pull/6837#issuecomment -213617933 меня поразило, что после слияния jb/functions у нас теперь есть механизм, по крайней мере, для представления этих операций в эффективный способ.

Конечно, еще предстоит проделать огромную работу над компилятором.

Я бы добавил, что мы также не знаем, насколько быстро может быть реализован подход «представления по умолчанию» после того, как объекты представления могут быть размещены в стеке. Поскольку возможность сделать это появится до версии 1.0, это решение может быть любым - отсюда и решение не принимать решения.

Какие элементы здесь еще доступны для версии 0.5?

Я думаю, что нашел способ избежать большого замедления времени тестирования при введении типа Transpose так что «Отказ от специального понижения Ac_mul_Bt» все еще должно быть в рамках. Работаю над этим.

Что касается «основных» элементов, у меня сложилось впечатление, что мы решили не возвращать фрагменты как просмотры. То, над чем работает

Надеюсь на # 16260, но никаких обещаний. В моем локальном филиале (который намного опережает то, что я нажимал) у меня странная ошибка логического вывода, которую я не могу отследить.

Может ли кто-нибудь указать на обсуждение «вернуть срезы как просмотры»? Я знаю, что это, вероятно, хешировалось снова и снова, но есть ли в Base хотя бы способ сделать что-то вроде view(A, i:j) (например, предоставленное пакетом ArrayViews)?

Я думаю, что это было в основном здесь, но сейчас github рушится по-настоящему долгих дискуссий. В середине показа скрытых комментариев есть кнопка.

Я бы охарактеризовал решение «срезы как просмотры» как «еще нет, возможно, никогда». В текущем состоянии нашей технологии компиляторов возвращение представлений, а не копий, снижает производительность: иногда лучше, а иногда нет, а иногда хуже. В будущем, когда мы сможем выделять объекты в стеке, которые ссылаются на кучу, это может измениться, и в этот момент мы сможем переоценить компромисс производительности. Возможно, что с такой возможностью распределения представлений в стеке возвращение представлений будет таким выигрышем в производительности, что мы решим это сделать, но также возможно, что это не так, и мы решаем против. Учитывая неопределенность и чрезвычайно (слегка) разрушительный характер изменения, казалось, что лучше пока воздержаться.

Спасибо за резюме @StefanKarpinski; Но есть ли у нас хотя бы возможность вернуть взгляд? Я думаю, что это минимальная функция, так что в моем собственном коде я знаю, что могу сделать view(A, i:j) когда я знаю, что это все, что мне нужно / нужно для производительности.

У нас это было целую вечность ; см. sub и slice , которые ускорились для julia-0.4. Теперь у нас также есть несколько дополнительных типов представления (как минимум ReshapedArray и неэкспортированный PermutedDimsArray ).

Спасибо @timholy , извините за мое незнание здесь. :)

«Любой тип индекса», вероятно, можно было бы быстро завершить.

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

  • Резервные варианты скалярного индексирования необходимо сократить до Integer . Это приведет к ошибке A[1.0] с сообщением о том, что индексирование с помощью Float64 не поддерживается. И тогда можно удалить устаревание в to_index .
  • Некалярная индексация и методы SubArray могут быть расширены для поддержки всех Number s. Им нужно только знать, что эти индексы являются скалярными, а затем передавать их скалярным методам индексации. Если в настраиваемый массив добавлена ​​поддержка индексов с плавающей запятой или других более сложных индексов, он будет работать * ⁱˢʰ. В противном случае будет ошибка, как указано выше.

Здесь есть две хитрые части:

  • Что делать с to_index ? Теперь он действительно делает только две вещи: вызывает find для логических массивов и преобразует целые числа в Int . Было бы странно, если бы SubArrays и нескалярные методы индексирования пропускали все скаляры через нетронутые, кроме целых чисел - они сначала преобразуются в Int помощью to_index . Вероятно, им вообще не стоит возиться со скалярами. Мы могли бы разделить to_index на две части: to_nonscalar_index (вызывает find и позволяет перехватить, например, NullableArrays чтобы оптимизировать их использование в качестве индексов) и to_scalar_index (просто конвертируется в Int )? Пока мы проводим здесь рефакторинг, хотим ли мы также, чтобы индексируемый массив имел возможность высказаться по поводу своих индексов, например https://github.com/JuliaLang/julia/pull/15750/files#diff -a21a7fc275830bf9efb5b7c17c4fb98eR439 ?
  • Мечта здесь - заставить это работать для причудливых настраиваемых массивов, таких как Interpolations. Это дает нам 99% пути, но -ish часть заключается в том, что у него будут проблемы с выполнением нескалярной индексации с типами, которые заставляют их возвращать не eltype (например, интерполяция с двойными числами). Они по-прежнему будут выделять массив из eltype s для вывода, но при попытке присвоения ему выдаст ошибку. На данный момент это нормально, а в будущем мотивированный человек, возможно, сможет реализовать специализацию promote_eltype_op за getindex .

Резервные варианты скалярного индексирования необходимо преобразовать в Integer. Это приведет к ошибке A[1.0] с сообщением о том, что индексирование с помощью Float64 не поддерживается.

Есть ли проблема с разрешением использовать числа с плавающей запятой для индексации: техническая или философская?

У нас это было целую вечность; см. подпункт и фрагмент, которые ускорились ко времени выхода julia-0.4. Теперь у нас также есть несколько дополнительных типов представлений (по крайней мере, ReshapedArray и неэкспортированный PermutedDimsArray).

Рассматривалось ли переименование sub в view ? sub действительно не очень хорошо указывает, что он вернет представление ...

view используется пакетом ArrayViews , который в настоящее время имеет значительные преимущества в производительности в некоторых случаях (небольшие непрерывные представления).

Вслед за @davidanthoff , я думаю, нам следует отказаться от sub или slice и, вероятно, это должно быть sub теперь, когда поведение getindex соответствует slice .

Есть ли проблема с разрешением использовать числа с плавающей запятой для индексации: техническая или философская?

Это сочетание того и другого, но если мы распутываем скалярную / нескалярную to_index как я предлагаю выше, это устраняет (последнюю из?) Технические причины. Линейное индексирование требует вычисления индексов, для чего требуются целые числа. С тех пор, как мы объединили эту устаревшую версию, многое изменилось (https://github.com/JuliaLang/julia/pull/10458).

Ах, я думал, что базовые возможности теперь являются полным набором вещей ArrayViews и всегда предпочтительнее ... Немного жаль, что в пакете используется более интуитивное имя, а в базе остается что-то менее ясное ...

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

Я предполагаю / надеюсь, что разрыв в производительности исчезнет, ​​когда мы сможем складывать в кучу контейнеры с распределением стека со ссылками ... и я скептически отношусь к тому, что мы можем что-то с этим поделать без этого. Обертка ArrayView просто меньше, поэтому возможность встроить и исключить создание оболочки должно помочь.

Вот почему разница проявляется только при создании небольших массивов - в большинстве других тестов SubArray равно или превосходит ArrayViews .

О, и я согласен отказаться от поддержки sub .

Base и ArrayViews могут использовать view . В конце концов, ImageView также использует view :)

Я полагаю, что это может сработать, потому что ArrayViews определяет методы только для Array а Base определяет методы для AbstractArray (я так думаю?). Как это будет работать, когда существуют разные области:

module A
  function myfunc(a::AbstractMatrix)
     av = view(a, :, 1)
     # do something with av
   end
end

module B 
  using ArrayViews
end

Меняется ли typeof(av) зависимости от того, был ли загружен модуль B (при условии, что a - это Array )?

ArrayViews должен прекратить экспорт view , и каждый раз, когда вы захотите использовать версию ArrayViews' вы должны будете сказать ArrayViews.view(A, :, 1) .

ImageView также пришлось бы прекратить экспорт view , но меня устраивает эта идея.

Из оставшихся проблем остаются только # 5332 и # 16846 для 0.5; перенос этого вопроса на 0.6.

Я также планирую сделать https://github.com/JuliaLang/julia/pull/16260#discussion_r67140179 :

  • переходно (только julia-0.5) заставить size и length выдать ошибку для массивов с нестандартной индексацией
  • введите @arraysafe чтобы переписать вызовы size и length во что-то, что не вызывает ошибки
  • объединить allocate_for в similar

К сожалению, вряд ли это будет сделано до завершения JuliaCon. В том же обсуждении @eschnett привел linearindices - подходящий момент для этого; Я не возражаю, но я не думаю, что у меня будет время заняться этим сам, так что этот вопрос заслуживает внимания.

Пока вы на нем, @timholy - нужно закончить к следующей неделе, чтобы мы могли пометить RC. Если вы хотите открыть проблему и поместить ее в этап 0.5.0, чтобы мы могли ее отслеживать, вы можете.

Не следует ли адаптировать название, если веха перемещена?

Я считаю, что 0,6 части этого отражены в более конкретных вопросах и PR; переход на 1.0.

См. Также # 20164, чтобы упростить подписку на просмотр большого блока кода.

Все в этой проблеме либо уже сделано, либо имеет свою проблему, либо не произойдет (я проверял).

Была ли эта страница полезной?
0 / 5 - 0 рейтинги