Как я могу определить, является ли матрица «положительно определенной» с помощью SQL? - PullRequest
3 голосов
/ 18 ноября 2010

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

A C D G H I
A 1.00 0.68 0.24 0.62 0.90 0.00
C 0.68 1.00 0.25 0.46 0.61 0.00
D 0.24 0.25 1.00 0.60 0.08 0.00
G 0.62 0.46 0.60 1.00 0.46 0.00
H 0.90 0.61 0.08 0.46 1.00 0.00
I 0.00 0.00 0.00 0.00 0.00 1.00

Сейчас мы используем стороннее приложение ExtremeNumerics для обработкиопределение довольно черным способом.Если бы у меня была таблица SQL, в которую я мог бы ввести активы, коррелированный актив и значение, был бы способ сделать математику?

Я кое-что осмотрел и ничего не видел в MSSQLэто обрабатывает математику матрицы.

спасибо.

редактировать: Microsoft SQL 2008

1 Ответ

6 голосов
/ 30 ноября 2010

Хорошо, вот и мы. Это работает, но у нас возникает ощущение, что нам нужно заставить SQL Server делать то, чего он на самом деле не хочет. Я бы не стал рекомендовать делать это в реальной жизни - я уверен, что он будет масштабироваться как O(n^3) в размере матрицы. Возможно, есть лучший способ сделать разложение Холецкого , а не таким образом - я мог бы рассмотреть это позже. С оговорками, скажем, давайте продолжим:

Для этого требуется SQL Server 2008, для его table тип данных

(и даже это не настолько полезно, как могло бы быть, как мы увидим ...)

Во-первых, подход. Мы собираемся использовать критерий Сильвестра , поскольку его проще всего понять: реальная симметричная матрица - это PD, если детерминанты всех главных миноров положительны. Так что нам нужен способ расчета определителей. Опять же, мы собираемся использовать простой подход ( расширение Лапласа ), а не что-либо, предназначенное для вычислительной эффективности.

Groundwork

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

create type Matrix
as table ( Row int, Col int, Val float )
go

Расчет определителей

Для этого мы определим две взаимно-рекурсивные функции, потому что это был единственный способ заставить его работать, учитывая ограниченные возможности данных типа table в SQL Server 2008.

Во-первых, точка входа (которая также обрабатывает базовый вариант):

create function Determinant ( @matrix Matrix readonly )
returns float
as
begin
    -- Base case of recursion
    if ((select count(*) from @matrix) = 1) 
        return (select Val from @matrix)

Установив, что мы не находимся в базовом случае (матрица 1x1), теперь у нас есть работа. Первым делом нужно «канонизировать» номера строк и столбцов в нашем вводе от того, чем они сейчас являются, до 1..n

    -- canonicalize row and col numbers (doesn't affect answer)
    declare @rowMap table ( fr_row int, to_row int )
    declare @colMap table ( fr_col int, to_col int )

    insert @rowMap
    select row, row_number() over(order by row) from @matrix
    group by row

    insert @colMap
    select col, row_number() over(order by col) from @matrix
    group by col

    declare @canonicalMatrix Matrix
    insert @canonicalMatrix 
    select 
        to_row, to_col, Val
    from @matrix m
    inner join @rowMap rm on m.row = rm.fr_row
    inner join @colMap cm on m.col = cm.fr_col

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

    -- apply laplace expansion on first row
    return
    (
        select sum(
            (case col % 2 
            when 1 then 1   -- odd col
            when 0 then -1  -- even col
            end
            )
                    * Val 
                    * dbo.DeterminantOfMinor ( @canonicalMatrix, 1, col ) 
            ) from @canonicalMatrix where row = 1
    )
end
go

Как оказалось, DeterminantOfMinor очень просто, и не было бы необходимости, если бы table значения были более первоклассными в SQL Server:

create function dbo.DeterminantOfMinor ( 
    @matrix Matrix readonly
    , @drop_row int
    , @drop_col int 
)
returns float
as
begin

    declare @minor Matrix
    insert @minor select * from @matrix 
        where row <> @drop_row and col <> @drop_col
    return
        dbo.Determinant( @minor )

end
go

Имеется калькулятор определителей, и мы почти у цели.

Тестирование на положительную определенность

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

create function dbo.is_positive_definite ( @matrix Matrix readonly )
returns bit
as
begin
    -- base case of recursion
    -- a 1x1 matrix is PD iff the single value is positive
    if ((select count(*) from @matrix) = 1) 
        return (select case when Val > 0 then 1 else 0 end from @matrix)

Мы строим матрицу, которая является нашим входом без последней строки и столбца:

    declare @smallerMat Matrix
    insert @smallerMat
    select row, col, Val from @matrix
    where row < (select max(row) from @matrix)
    and col < (select max(col) from @matrix)

и вернем вниз, только , вычисляя детерминант нашего ввода, если все наши основные несовершеннолетние подтверждены как PD:

    -- for our input to be PD, its smaller version must be PD:
    return
        ( select case dbo.is_positive_definite( @smallerMat )
        when 1 then 
                (select case 
                     when dbo.Determinant ( @matrix ) > 0 
                     then 1 
                     else 0 
                     end)
        else 0
        end
        )

end
go

И это все!

Тестирование

Я использовал ваш образец:

declare @test Matrix

insert @test values ( 1, 1, 1.00 )
insert @test values ( 1, 2, 0.68 )
insert @test values ( 1, 3, 0.24 )
/* snip */
insert @test values ( 6, 4, 0.00 )
insert @test values ( 6, 5, 0.00 )
insert @test values ( 6, 6, 1.00 )

select dbo.Determinant ( @test )
select dbo.is_positive_definite ( @test )


----------------------
0.0333962320000001

(1 row(s) affected)


-----
1

(1 row(s) affected)

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

Задержка

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

n   Time (s)
1   < 1
2   < 1
3   < 1
4   < 1
5   1
6   17

Тревожный тренд, я уверен, вы согласитесь. Отсюда мое:

Предостережения

Я бы посчитал этот код не более чем доказательством концепции:

  • Время выполнения для вычисления определителей таким наивным способом увеличивается как O(n^3) в размере матрицы
  • Этот код повторно вычисляет одни и те же значения, не запоминая
  • Нет абсолютно никакой проверки работоспособности или ошибок - например, матрица, которая не является квадратной, или значения во входном значении Matrix, которые не имеют смысла, приведут к падению всего в кучу
  • Я не учел числовую стабильность, которая необходима для численных расчетов в реальном мире

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

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

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