← Архив: Common Lisp

указатели, pure lisp

Author: · 28.03.2010 20:32
· original author: allchemist
Как стандартными средствами можно организовать указатель на что-нибудь?
Задача примерно такая: есть некая структура данных (пусть будет список). нужно построить другой список (неравного размера), элементы которого будут равны некоторым элементам, взятым из исходного списка. например, исходный список
(defparameter *list1* '(1.0 2.0 3.0)) ;; исходный список
(defparameter *list2* '(3.0 2.0))       ;; производный список, первый элемент которого является ссылкой на третий элемент первого списка, второй, соответственно, - на второй элемент первого списка.
(setf (elt *list2* 1) 4.0)    ;; *list2* теперь '(3.0 4.0).
необходимо сделать так, чтобы *list1* изменялся синхронно с изменением *list2*, т.е сейчас *list1* был бы '(1.0 4.0 3.0).
наиболее простой способ мне видится через указатели: *list2* - список указателей на элементы *list1*. можно ли для этой цели заюзать weak-pointerы, которые weak настолько, что убиваются после первого прохода мусорщика?
· original author: allchemist
хм, weak-pointerов в CLHS нету, оказывается, хотя в большинстве реализаций они присутствуют (sbcl, cmucl, ecl) и походу даже с одинаковым интерфейсом. значит, в стандарте уазатели никак не предусмотрены?
· original author: mkvasiliev
А чем такой вариант не подходит?
[1]> (defparameter *list1* '(1 2.0 3.0))
*LIST1*
[2]> (defparameter *list2* (cdr *list1*))
*LIST2*
[3]> *list2*
(2.0 3.0)
[4]> (setf (first *list2*) 4.0)
4.0
[5]> *list1*
(1 4.0 3.0)
[6]>
· original author: mkvasiliev
а, понял :) - невнимательно прочитал.
· original author: allchemist
да, я привел очень простой пример. реальные данные разбросаны по нескольким массивам.
· original author: archimag
to allchemist:Если в качестве элементов списков будут какие-нибудь сложные структуры, и изменяться будут они, то твоя цель будет достигнута автоматически. Т.е. просто храни в списках сложные данные и изменяй их с помощью их собственных методов. Если нужны простые данные (типа целых), то сделай над ними обёрку.
· original author: allchemist
да, но тогда отпадает смысл в этих сложных структурах. доступ к самописным структурам, тем более на основе списков, на порядки медленнее, чем доступ к массиву в foreign memory.
короче говоря, никак нельзя сделать сабж в рамках стандарта, получается?
· original author: Ander Skirnir
Да норм вариант archimag предлагает, списочная ячейка хуже обычного указателя только тем, что это два указателя,
а вот пример, в нём элементы второго списка суть списочные ячейки, каждая из которых занимает память, требуемую для двух указателей :
(defparameter *list1* '(1.0 2.0 3.0))
(defparameter *list2* `(,(cddr *list1*) ,(cdr *list1*)))
(rplaca (elt *list2* 0) 7.0)
(rplaca (elt *list2* 1) 8.0)
;; *list2* = ((7.0) (8.0 7.0))
;; *list1* = (1.0 8.0 7.0)
· original author: allchemist
хм, это все хорошо, но у меня матрица, причем не native, да еще и динамически изменяемая. нужно производить нелинейную оптимизацию функции, зависящей от некоторых элементов этой матрицы. каждый раз вычислять положения этих элементов можно, но это убьет весь профит от использования матричного умножения.
к слову - это взвешенная матрица смежности графа сети (размерами порядка 300x300). узлы сети, а также расстояния между ними, меняются динамически в соответствии с двумя последовательными оптимизационными процедурами, которые зависят от текущего состояния сети. погоня за своим хвостом получается.
короче, лучше попробовать другой путь.
· original author: treep
CFFI, наверное, и дока очень вменяемая по нему. Выглядит как стандарт дефакто с бакендами для 10 реализаций, а в SBCL SB-ALIEN, через который работает CFFI, завязан на виртуальной машине используемой компилятором, т.е. все типизации компилятора отражаются на FFI типизации. То бишь CFFI можно использовать как для интерфейса, так и самодостаточно - брать кусок foreign памяти (SAP в SBCL, доступ к памяти без контроля типов), а далее сам себе голова - никаких проверок, приведенией и GC, т.е. всю аллокацию/освобождение, представление рабочих структур в памяти, - всё нужно делать самому.
Как-то я не очень понял что там за матрицы у вас, может одну принять за базу, а другую - что-то вроде сетки (указателей) над первой. Но я всё равно не въезжаю :) Хочу вот что спросить - вы делали для нейронных сетей библиотеку для работы с матрицами? Я пока не видел ни одной нормальной и сегодня подумал что нужно сделать :)) Тогда что туда нужно заложить? Допустим матричную алгебру можно постепенно сделать над готовой структурой даных, т.е. главное это эффективное представление, при этом дёргать си-шные библиотеки не очень удобно, нужно именно pure CL, ну с учётом современных реалий.
· original author: treep
Кстати, array это массив, vector - вектор, matrix - матрица, а многомерный массив ранга > 2 как будет? Не тензор ведь (там дополнительные условия).
· original author: allchemist
> библиотеку для работы с матрицами? Я пока не видел ни одной нормальной и сегодня подумал что нужно сделать :))
вот у этого перца есть кое-что на гитхубе. плюс ко всему, в той же gsll в качестве зависимостей есть gsd (она же grid), в которой помимо тулзов для marrays есть пакет affi для работы с native-массивами. но ничего из вышеперечисленного мне не нравится, поэтому я когда-то накатал простенькую библиотечку нужных мне матричных функций, на которую потом накинул всякий линал (решение систем уравнений, нахождение собственных значений и функций, диагонализация, разложения матриц (QR, LU, холецкого) и даже SVD(!)). потом я попробовал gsll, которая оказалась намного производительнее, чем мой самопальный код, и чем любая из других виденных мной библиотек. сейчас пользуюсь простенькой оберткой вокруг gsll (которая сама себе обетрка). кстати, сейчас решил посмотреть, как будут выглядеть на этих задачах хэш-таблицы.
короче говоря, будет время - накатаю статейку о числодробильне (матричной, в первую очередь) в лиспе.
>  а многомерный массив ранга > 2 как будет? Не тензор ведь (там дополнительные условия).
родные лисповые массивы могут быть любой размерности. 
(type-of (make-array '(3 3 3))) ;=> (simple-array t (3 3 3))  -- тензор 3-го ранга
те, что в gsl, не выше 2-го ранга. но тензоры нужны бывают нечасто.
· original author: allchemist
>  Допустим матричную алгебру можно постепенно сделать над готовой структурой даных
а в лиспе есть что-нибудь сопоставимое с foreign memory по эффективности доступа?
· original author: allchemist
> те, что в gsl, не выше 2-го ранга. но тензоры нужны бывают нечасто.
я немного не точен
(gsl:make-marray 'single-float :dimensions '(3 3 3)) все же прокатит, но тип будет vector-single-float почему-то, и достать элемент этого массива будет нельзя, т.к. maref принимает только два аргумента. короче говоря, тензоры не предусмотрены ни в gsll, ни в самой gsl.
· original author: treep
>> вот у этого перца есть кое-что на гитхубе.
Интересно кстати. array-operations работает на лисп-массивах, а ffa с помощью cffi, только я себе не так это представлял - мне кажется нужно не макросами делать а с помощью define-foreign-type, который создаёт ещё и CLOS класс - если использовать его только для интерфейса и упорядочивания внешних типов/методов, а в остальном всё делать по аккесорам, то это не отразится на производительности. Вот, кстати тест был на предмет работы аккессоров в классах, там всё достаточно быстро (они создаются единожды).

>> а в лиспе есть что-нибудь сопоставимое с foreign memory по эффективности доступа?

Ну вот в этом и весь Гамлет... Нужно поставить тесты array vs. foreigne-array, может fm окажется хуже, кто её знает. Пока я склоняюсь к тому, чтобы по-пробовать сделать нечто под названием cl-raw-data для работы с сырой памятью и потом проверить. FM можно брать из стека или из кучи, она по-идее не должна "ездить" и, наверно, gc её не должен трогать. Соответственно, нужно брать указатель на начало, размер памяти и делать адресную арифметику по оффсетам - самое то для плоских структур; ну а списки, конечно, родные должны быть.
>>  а многомерный массив ранга > 2 как будет? Не тензор ведь (там дополнительные условия).
> родные лисповые массивы могут быть любой размерности.
multidimensional arrays по научному, посмотрел у перца))
· original author: allchemist
> Ну вот в этом и весь Гамлет... Нужно поставить тесты array vs. foreigne-array, может fm окажется хуже, кто её знает. Пока я склоняюсь к тому, чтобы по-пробовать сделать нечто под названием cl-raw-data для работы с сырой памятью и потом проверить. FM можно брать из стека или из кучи, она по-идее не должна "ездить" и, наверно, gc её не должен трогать. Соответственно, нужно брать указатель на начало, размер памяти и делать адресную арифметику по оффсетам - самое то для плоских структур; ну а списки, конечно, родные должны быть.
Ага, но тот же Гамлет в том, что это не получится сделать в рамках стандарта.
· original author: treep
>> Ага, но тот же Гамлет в том, что это не получится сделать в рамках стандарта.
Стандарту уже > 20 лет... Во времена его введения такие вещи казались чем-то несущественным, может потому что предполагалась какая-то иная сфера применения для CL, но современные реализации вышли очень продвинутые по части работы с системой. Я вот на CFFI смотрю как на стандарт.
>> Я: Пока _ склоняюсь к тому, чтобы по-пробовать сделать нечто под названием cl-raw-data для работы с сырой памятью.
Зря склоняюсь :) В CFFI есть многомерные массивы в foreign memory (heap SAP), красиво сделаны, однако. Можно взглянуть на какую-нибудь числодробилку на матрице? Небольшую - чтобы проверить её тут.
· original author: treep
Ещё репа с матричными либами.
· original author: treep
CFFI-arrays vs. Lisp-array
(in-package :cl-user)
(defvar *moo* (make-random-matrix 1000 1000 :element-type 'short))
(export '*moo*)
(in-package :cffi)
(with-foreign-array (fa-pointer cl-user:*moo* '(:array :int 1000 1000))
  (time
    (dotimes (i 1000)
      (dotimes (j 1000)
        (foreign-aref fa-pointer '(:array :int 1000 1000) i j)
)
)
)
)

Evaluation took:
 9.548 seconds of real time
 9.478559 seconds of total run time (9.438565 user, 0.039994 system)
 [ Run times consist of 0.276 seconds GC time, and 9.203 seconds non-GC time. ]
 99.28% CPU
 24,190,658,763 processor cycles
 208,380,528 bytes consed
Без комментариев, как говорится. Там наверное какой-то контекст переключается.
· original author: allchemist
слушай, treep, а может не будем спамить на полфорума матричными вопросами? :)
жаббер не катит?
· original author: treep
А как в нём листинги форматить? И почему бы нет (не спамить форум).
Просто жаббером не пользуюсь - это где одно-строчками пищут :))
· original author: allchemist
ок, продолжаем спамить форум
ты просто упоминал, что хочешь создать yet another матричную библиотеку. Но их и так уже много, как чисто коммонлисповых, так и разных биндингов. Поэтому надо подумать, как избежать велосипедостроения.
приняв, что на основе ansi-стандарта нужной эффективности не получится, есть два пути.
1. cffi-биндинги.
1.1 наиболее достойно выглядят биндинги к gnu scientific library. сама библиотека неплоха и содержит много всего, плюс ко всему, активно развивается. есть, правда, мнение, что в плане производительности ей есть куда стремиться. биндинги к gsl уже сделаны, и проект gsll развивается более-менее активно. но пощупав довольно плотно gsll, я заметил ряд неприятных моментов, которые в приводят к нестыковке с коммон-лисп идеологией. насчет производительности gsll: непосредственно работа с матрицами в gsl менее эффективна, чем с лисп-массивами (выше я приводел некоторые циферки. другие подобные сравнения тоже соответствуют тенденции). однако, более высокоуровневые алгоритмы, даже типа умножения матриц, не говоря уже о каких-нибудь матричных разложений, выполняются в gsl более быстрее за счет оптимизированного кода и алгоритмов.
1.2 из других иноязычных библиотек есть вечные blas и lapack, как fortran, так и c-версии. есть проект cl-blapack, который вроде бы делает биндинги к ним, но его жизнеспособность сомнительна.
2. взять какую-то реализацию (default=sbcl) и точить под нее, как этот перец, например. таких библиотек пока еще не встречал, есть версия, что их просто нету. зависимость от реализации остается, зато нет зависимостей от чего-либо еще, что очень большой плюс.
Последний вариант хорош тем, что можно попытаться сохранить некоторые фишки лисп-массивов, которых нет в иноязычных библиотеках (например, adjust-array может нехило сократить затраты, ясно почему). Но он же (второй вариант) плох тем, что помимо матричных операций было бы неплохо иметь набор базовых процедур линейной алгебры (без этого никуда). писать самому - либо быстро и неэффективно, либо эффективно, но очень долго. Есть варианты f2cl и иже с ним, но тут тоже непросто.
Если совсем кратко, идея такая:
необходим единообразный интерфейс, к матричным операциям, взятым из биндингов gsl, lapack, функций, заточенных под sbcl или функций, реализованные на основе стандарта.
чтобы можно было использовать одну, другую или третью, без необходимости модифицировать код программы, написанный с помощью этой прослойки. Потому что на одной задаче будет удобнее использовать gsll, на другой lapack или вообще pure-lisp реализацию. довольно криво изложил, но иногда такое бывает. подобную идею я уже частично реализовал (переключение между gsll и pure-lisp без модификации вышележащего кода), но текущая реализация достойна лучшего места в биореакторе.
Вот. и еще один момент. везде (в лиспе, gsl и проч) матрица хранится в памяти в виде вектора. что будет, если реализовать представление матрицы в виде хэш-таблицы? она тоже одномерная, но только время доступа не зависит от размера, да и плюшек много. интересно, насколько можно приблизиться по эффективности к нормальным матрицам? если были бы доступны двумерные хэш-таблицы, то это было бы круто, но для cl по ходу нет таких библиотек.
· original author: treep
Ок.
Я писал, что хочу попробывать сделать некоторые линейные типы данных (массивы, матрицы) в foreign memory (raw-data), после чего обнаружил, что в cffi/src/types.lisp есть блок
;;;# Dereferencing Foreign Arrays
В котором уже реализованы массивы (любой размерности) - как отдельные аллокаторы, методы, так и with-* макрос. При этом аллокаторы на SBCL честно возвращают SAP указатели. Суть в том, что всё это не экспортируется из CFFI, но доступно через общий интерфейс with-foreign-object. В примере выше я просто залез в пакет cffi, попробовал и увидел - производительность массивов из CFFI меньше на порядок-два чем производительность родных лисповых массивов. Незнаю, на что можно грешить - на то что в CFFI активно юзаются классы, или сами SAP'ы такие. Либо там косяк с with-* макросом - например, он тягает какой-нибудь метод без дела.
Так что в отношении FFI, нужно брать конкретно SBCL и попробовать непосредственно SAP'ы и уже делать вывод. А то разглядование array.lisp в компиляторе навевает - там наверно не мало разных оптимизаций, т.е. родные массивы могут быть гораздо более лучшим вариантом, и тогда предположение о том, что FFI можно использовать не только как интерфейс к Си никак не оправдается.
Ну если CFFI как биндинг, то да. Но о gsll я слушу от тебя впервые, поэтому не могу ничего сказать. Вобщем, верую в pure-lisp :)
Насчёт хэш-таблиц не понял - доступ к элементу массива тоже не зависит от его размера, на то он и массив :) Многомерные массивы - да, это просто одномерный массив с индексной арифметикой. В чём вобще привлекательность "двумерных хэш-таблиц"? Что-то вроде матрицы указателей? Это в НС такие штуки нужны?
· original author: treep
Блин, чего у перцев нет своего DSL, для написания статически типизированного кода на CL?
· original author: treep
Проверил SAP-ы. Итак, ситуация такова:
1. pure-lisp массивы, которые определены в стандарте, на SBCL работают быстрее всех других вариантов.
(let ((lisp-array (make-array '(500 500) :element-type 'fixnum
                                         :initial-element 0
)
)
)

  (time
    (dotimes (i 500)
      (dotimes (j 500)
        (setf (aref lisp-array i j) (+ i j))
)
)
)

  (let ((summ (the fixnum 0)))
    (time
      (dotimes (i 500)
        (dotimes (k 500)
          (incf summ (aref lisp-array i k))
)
)
)

    summ
)
)

Evaluation took:
 0.001 seconds of real time
 0.001000 seconds of total run time (0.001000 user, 0.000000 system)
 100.00% CPU
 2,784,735 processor cycles
 0 bytes consed
 
Evaluation took:
 0.002 seconds of real time
 0.003000 seconds of total run time (0.002000 user, 0.001000 system)
 150.00% CPU
 6,226,851 processor cycles
 0 bytes consed
2. Foreign Arrays / SBCL. Работают так же быстро как и обычные массивы, с той оговоркой, что при размере > 500*500 они у меня ломают SBCL - Memory fault. С другой стороны тут возможны настоящие указатели, но это только для SBCL (в других реализациях, видимо, будет иначе) и нельзя быть уверенным, что не будет других "сырых" ошибок, подобных Memory fault.
(with-alien ((sap-array (array int 1500 1500)))
  (time
    (dotimes (i 500)
      (dotimes (k 500)
        (setf (deref sap-array i k) (+ i k))
)
)
)

  (let ((summ (the fixnum 0)))
    (time
      (dotimes (i 500)
        (dotimes (k 500)
          (incf summ (deref sap-array i k))
)
)
)

    summ
)
)

Evaluation took:
 0.001 seconds of real time
 0.001000 seconds of total run time (0.001000 user, 0.000000 system)
 100.00% CPU
 2,708,593 processor cycles
 0 bytes consed
 
Evaluation took:
 0.002 seconds of real time
 0.002999 seconds of total run time (0.002999 user, 0.000000 system)
 150.00% CPU
 7,022,561 processor cycles
 0 bytes consed
3. CFFI arrays. Тут ничего не остаётся, как сказать, что они реализованы не лучшим образом - хотя в итоге они дают те же SAP-ы производительность в 100 раз меньше.
(in-package :cl-user)
(defvar *moo* (make-array '(500 500) :element-type 'fixnum :initial-element 0))
(export '*moo*)
(in-package :cffi)
(with-foreign-array (fa-pointer cl-user:*moo* '(:array :int 500 500))
  (time
    (dotimes (i 500)
      (dotimes (j 500)
        (foreign-aref fa-pointer '(:array :int 500 500) i j)
)
)
)
)

Evaluation took:
 1.725 seconds of real time
 1.701742 seconds of total run time (1.692743 user, 0.008999 system)
 [ Run times consist of 0.032 seconds GC time, and 1.670 seconds non-GC time. ]
 98.67% CPU
 4,370,638,866 processor cycles
 52,069,696 bytes consed
(defvar *moo* (foreign-array-alloc cl-user:*moo* '(:array :int 500 500)))
(time
  (dotimes (i 500)
    (dotimes (j 500)
      (foreign-aref *moo* '(:array :int 500 500) i j)
)
)
)

Evaluation took:
 1.737 seconds of real time
 1.714740 seconds of total run time (1.711740 user, 0.003000 system)
 [ Run times consist of 0.022 seconds GC time, and 1.693 seconds non-GC time. ]
 98.73% CPU
 4,402,804,640 processor cycles
 52,063,376 bytes consed
 

4. FFI. Нужно думать - что быстрее: сделать лисп-матрицы и посчитать, например, произведение; либо - сделать лисп-матрицу, преобразовать во внешнюю форму (а пример выше с CFFI заставляет сомневаться в быстроте этой операции), передать внешнему коду через описанный FFI, дождаться возвращения, и перевести обратно в лисп-форму. Наверно имеет смысл для каких-нибудь монстрячных мат. вычислений, для которых уже есть соответствующий внешний код.
· original author: treep
Да, под указателями наверно имеется в виду "связи", что-то вроде многомерных хэш-таблиц? Эт можно сделать так - обычная модель памяти (flat memory) это фактически битовый массив, а указатель - просто целое число, т.е. индекс массива. Соответственно "необычная" память n-ого порядка будет n-мерным массивом, а указатель - вектором целых чисел длинной n.
· original author: allchemist
сейчас провел много разных тестов. выкладывать их сюда ну очень лениво, но суть такая, что родные лисповые массивы в sbcl чуть ли не самые эффективные не только на fixnum, но и на других числовых типах. (до этого я предполагал, что на том же single-float будет наоборот). я попробую переписать свой код на лисп-массивах и посмотрю, что будет. если окажется, что потери производительности не очень страшные, то будет здорово.
вот. а насчет процедур линейной алгебры - их можно будет попытаться прикрутить к лисп-массивам. это уже сделано, например, в maxima, но там некошерная mk-defsystem и вообще довольно мутный код.
· original author: allchemist
выложил на гитхуб некоторую весию матричных процедур, которую накатал сегодня вечером. это не конечная версия, и не факт, что хотя бы одна функция окажется нетронутой через некоторе время. с типами не заморачивался пока.
· original author: Jax
>выложил на гитхуб некоторую весию матричных процедур, которую накатал сегодня вечером.
Можешь еще http://github.com/tpapp/lla посмотреть:LLA stands for Lisp Linear Algebra, and aims to provide a convenient
and fast library for matrix operations in Common Lisp, by binding to
LAPACK and other libraries.
· original author: dmitry_vk
Еще очень занятная библиотечка sb-cga
· original author: ilowry
выложил на гитхуб некоторую весию матричных процедур, [...] с типами не заморачивался пока.Начнешь разбираться с типами, посмотри, плиз, сюда http://www.cliki.net/the (или cl-the.googlecode.com). Может как-то пригодится.  Это минималистическая версия рекурсивного аналога THE:


(THE* FLOAT 1) --> (THE FLOAT 1.0)
(THE* SIGNLE-FLOAT (SIN (/ 1 4))  -->  (THE SINGLE-FLOAT (SIN (THE SINGLE-FLOAT (/ (THE SINGLE-FLOAT 1.0) (THE SINGLE-FLOAT 4.0)))))


Поскольку написал ее просто так, без реальной нужды, а просто в очередной раз увидев где-то кучу вложенных операторов THE, ничего не могу сказать о ее практической пользе. 
Пока разбирает самые простейшие случаи, хотя, честно говоря, я думаю, что большего и не надо. 
· original author: treep
>> Это минималистическая версия рекурсивного аналога THE.
Хорошая кстати штука, у меня тоже нечто подобное есть, думаю как-нибудь допилить.
· original author: allchemist
> Можешь еще http://github.com/tpapp/lla посмотреть
так это ж все одного человека рук дело. это биндинги к blas и lapack, надетые на его же либу xarray. сама либа xarray показала отставание на простеньких задачах по сравнению с моей неоптимизированной pure-lisp реализацией, в среднем в 3-5 раз. Ну это и понятно, там делается новый тип данных, все на clos'е завернуто, это дает о себе знать. Из плюсов - более-менее унифицированный интерфейс к матрицам и векторам. Из минусов - тонны тупых проверок. Вообще, я считаю, что стандартные лисп-средства для работы с массивами очень приличные, а про векторы (для которых применимы все операции с последовательностями) и говорить нечего.
> Еще очень занятная библиотечка sb-cga
уже смотрю, спасибо. код там интересный, местами очень даже брутальный:
(defun matrix-determinant (matrix)
  "Determinant of MATRIX."
  (macrolet ((a (i j)
               `(mref matrix (- ,i 1) (- ,j 1))))
    (- (+ (* (a 1 1) (a 2 2) (a 3 3) (a 4 4))
          (* (a 1 1) (a 2 3) (a 3 4) (a 4 2))
          (* (a 1 1) (a 2 4) (a 3 2) (a 4 3))
 
          (* (a 1 2) (a 2 1) (a 3 4) (a 4 3))
          (* (a 1 2) (a 2 3) (a 3 1) (a 4 4))
          (* (a 1 2) (a 2 4) (a 3 3) (a 4 1))
 
          (* (a 1 3) (a 2 1) (a 3 2) (a 4 4))
          (* (a 1 3) (a 2 2) (a 3 4) (a 4 1))
          (* (a 1 3) (a 2 4) (a 3 1) (a 4 2))
 
          (* (a 1 4) (a 2 1) (a 3 3) (a 4 2))
          (* (a 1 4) (a 2 2) (a 3 1) (a 4 3))
          (* (a 1 4) (a 2 3) (a 3 2) (a 4 1)))
 
       (* (a 1 1) (a 2 2) (a 3 4) (a 4 3))
       (* (a 1 1) (a 2 3) (a 3 2) (a 4 4))
       (* (a 1 1) (a 2 4) (a 3 3) (a 4 2))
 
       (* (a 1 2) (a 2 1) (a 3 3) (a 4 4))
       (* (a 1 2) (a 2 3) (a 3 4) (a 4 1))
       (* (a 1 2) (a 2 4) (a 3 1) (a 4 3))
 
       (* (a 1 3) (a 2 1) (a 3 4) (a 4 2))
       (* (a 1 3) (a 2 2) (a 3 1) (a 4 4))
       (* (a 1 3) (a 2 4) (a 3 2) (a 4 1))
 
       (* (a 1 4) (a 2 1) (a 3 2) (a 4 3))
       (* (a 1 4) (a 2 2) (a 3 3) (a 4 1))
       (* (a 1 4) (a 2 3) (a 3 1) (a 4 2)))))
 
Про LUP-разложение чувэ по ходу не слышал :)
Там вся фишка в SSE2 для sbcl, я так понимаю
> Начнешь разбираться с типами, посмотри, плиз, сюда http://www.cliki.net/the (или
> cl-the.googlecode.com). Может как-то пригодится.  Это минималистическая
> версия рекурсивного аналога THE
Ок, посмотрю, когда доберусь до такой тонкой опримизации

· original author: allchemist
> sb-cga
ну да, там только четырехмерные матрицы, видимо в компографике такие только и используются. он там даже повороты якоби вручную задает, что ваще жесть. видимо, это сколько-то тактов экономит.
· original author: dmitry_vk
cg - это "computer graphics" и есть. В 3d графике всегда используются матрицы 4x4.
В той библиотечке из интересного не сами матрицы, а то, что для SBCL там написаны VOP'ы для компилятора (VOP = virtual operation), которые позволяют эффективно обрабатывать эти массивы. SSE2 для поточных операций дает существенные преимущества.
>это сколько-то тактов
Там речь идет не про такты, а про ускорение операций в несколько раз за счет SSE2, как я понимаю.