Які алгоритми можна виразити за допомогою загальної функціональної мови з операторами паралельних даних?


11

Уявіть функціональну мову програмування, єдиним типом даних якої є чисельні скаляри та довільні вставки масивів. У мові відсутні будь-які засоби безмежної ітерації, тому заборонено наступне:

  • явні петлі (не так багато використання без побічних ефектів)
  • рекурсія
  • довільні функції першого класу (без у-комбінатора)

Однак мова має:

  • функції верхнього рівня
  • лексично охоплені нехай палітурки
  • розгалуження контрольного потоку
  • загальні скалярні математичні та логічні функції
  • деякий простий конструктор масиву, як fill (n, x), який створює масив n елементів з однаковими значеннями x
  • найголовніше: обмежений набір операторів вищого порядку, які виконують паралельну структуровану ітерацію (наприклад, карта, зменшення, сканування, всі пари).

Щоб бути більш конкретними щодо операторів паралельних даних:

  • y = карта (f, x) => y [i] = f [i]
  • y = зменшити (f, a, x) => y = f (a, f (y [p [0]], f (y [p [1]], ...))) для деякої перестановки p
  • y = сканування (f, a, x) => y [i] = зменшити (f, a, y [0 ... i-1])
  • y = allpairs (f, x, y) => y [i, j] = f (x [i], y [j])

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

Очевидно, є деякі конструкції, які неможливо виразити цією мовою, такі як:

while f(x) > tol:
    x <- update(x)   

Що ми можемо виразити в цій системі? Чи ми обмежені лише пошуком проблем у ПП? Чи можемо ми захопити всі поліноміальні алгоритми часу? Також є якийсь мінімальний набір операторів для цього класу?

Відповіді:


7

Ви можете поглянути на стару мову програмування NESL, яка сприйняла ці ідеї серйозно. Мова заснована на операціях над колекціями, по суті списками та списками списків тощо, але, я думаю, дерева та графіки також розглядалися через складні кодування. Деякі дослідження, проведені в рамках цього проекту (в середині 1990-х), можуть допомогти відповісти на ваше запитання. Кандидатська дисертація (доступна як книга) Гай Е. Блеллох. Векторні моделі для паралельних обчислень даних. MIT Press, 1990 може дати відповіді. Це було певний час тому, як я на це подивився.

Робота, проведена над формалізмом птахів-міртенів (BMF), також підпадає під цю категорію, як і мова благодійності . На сторінці Вікіпедії Благодійності сказано, що мова не є Тюрінгом повною, але може виражати функцію Акермана, це означає, що це більше, ніж примітивна рекурсивна. І BMF, і благодійність залучають таких операторів, як складки та сканування (катаморфізми, анаморфізми тощо), і вони мають своє коріння в теорії категорій.

Я короткий, неточна відповідь, що ви можете висловити досить багато.


1
NESL - це не загальна мова.
Per Vognsen

Я деякий час невідомо знав NESL, але просто вперше детально прочитав одну з робіт Блеллоха. Дякую за пораду. NESL досить схожий на мову, яку я описав вище, за винятком того, що, як зауважив Пер Вогсен, він дозволяє рекурсію.
Алекс Рубінштейн

Мене також цікавить вибір приміщень операторів Blelloch: карта, dist (я вважаю те саме, що я назвав "заповнити"), довжина, зчитування масиву, запис масиву, сканування, розділ, вирівнювання. Чи є "примітними" примітиви NESL, чи є якась інша операція з паралельною реалізацією даних, яку неможливо ефективно виразити за допомогою них?
Алекс Рубінштейн

2
Видаліть рекурсію, тоді ви володієте досить виразною мовою, особливо якщо врахувати складки тощо. Тоді, дивлячись на BMF та подальші роботи, можливо, це зацікавить більше. Вибачте, але я не в курсі цієї галузі.
Дейв Кларк

7

Інша моя відповідь вказала на недоліки в мові, як вона є. У цій відповіді я детальніше розповім про питання співіснування складок і розгортань загальною мовою. Тоді я запропоную резолюцію і покажу, що всі проблеми в Р (і справді багато інших) можна вирішити цією розширеною мовою.

Складання загальної мови споживає списки:

fold :: (a -> b -> b) -> b -> List a -> b

Розгортання загальною мовою генерує потоки, які потенційно не пов'язані:

unfold :: (b -> Maybe (a, b)) -> b -> Stream a

На жаль, списки та потоки живуть в різних світах, тому ці оператори не можуть бути складені. Нам потрібна часткова переписка:

stream :: List a -> Stream a
list :: Int -> Stream a -> List a

Оператор потоку вбудовує список у обмежений потік. Функція списку витягує перші п елементів (або менше, якщо потік закінчується раніше) до списку. Таким чином, у нас є таке рівняння:

for all xs :: List a, xs == list (length xs) (stream xs)

Для оптимізації ефективності ми можемо повністю вирізати потоки як проміжну структуру даних:

unfoldList :: Int -> (b -> Maybe (a, b)) -> b -> List a

Зараз я буду наводити доказ того, що це (з іншими операторами, що вже мається на увазі в оригінальному запитанні) дозволяє нам моделювати будь-який алгоритм багаточленного часу.

За визначенням, мова L знаходиться в P, коли є машина Тьюрінга M і поліном p таким, що про членство x в L можна визначити, виконавши M не більше, ніж p (n) ітерацій, де n = | x |. За допомогою стандартного аргументу стан стрічки машини в ітерації k може бути закодований зі списком довжини не більше 2k + 1, навіть якщо стрічка машини нескінченна.

Зараз ідея полягає в тому, щоб представити перехід M як функцію зі списків до списків. Виконання машини буде здійснюватися шляхом розгортання початкового стану з функцією переходу. Це створює потік списків. Припущення, що L знаходиться в P, означає, що нам потрібно шукати не більше, ніж p (n) елементів у потік. Таким чином, ми можемо скласти розгортання, list p(n)щоб отримати кінцевий список. Нарешті, ми складемо його, щоб перевірити, чи відповідь на проблему рішення було так чи ні.

Загалом це свідчить про те, що коли ми маємо алгоритм, час закінчення якого може бути обмежений функцією, обчислюваною мовою, ми можемо імітувати його. Це також говорить про те, чому щось на кшталт функції Акермана не може бути симульовано: це власне обмеження, тому існує проблема з куркою та яйцями.


4

Це дуже хитра мова. Спробуйте запрограмувати функціональну функцію:

fact 0 = 1
fact n = n * fact (n-1)

Проблема полягає в тому, що у вашій мові є лише складки, але не розгортання. Природним способом вираження факторіального є складання розгортання n у списку [1, 2, ..., n] зі складанням, яке розриває його під час множення.

Вас справді цікавить ця конкретна мова чи загальне функціональне програмування взагалі? Очевидно, що ваша мова може висловити щонайбільше алгоритми поліноміального часу. Система F (поліморфне обчислення лямбда) може легко виражати монстрів, як функція Акермана. Навіть якщо ваш інтерес викликає алгоритми багаточленного часу, вам часто потрібна кімната суперполінома, щоб висловити їх природним чином.

Редагувати: Як зазначає Дейв, NESL є однією з функціональних мов програмування паралельних даних, але це дуже далеко від загального (навіть не намагається). Сім'я APL мала паралельний слід еволюції і має загальну алгебраїчну підмножину (уникайте операторів з фіксованою точкою). Якщо у вашому питанні є цілісність, Девід Тернер написав кілька хороших робіт у цій галузі, хоча не конкретно щодо програмування паралельних даних.


Вибачте, відсутність операторів розгортання - це нагляд з мого боку ... Я мав намір додати його, але забув поставити його на посаду. Мене не цікавить ця конкретна мова обов'язково, а швидше виразність та межі паралельних обчислень даних.
Алекс Рубінштейн

Розгортання є природним потоком, а не значенням масиву, що є основною проблемою з основним курсом у загальних строгих мовах.
Per Vognsen
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.