Есть ли встроенный Matlab, который вычисляет квадратичную форму (x'*A*x)?

Довольно простой вопрос: учитывая симметричную матрицу A N x N и N-вектор x, есть ли встроенная функция Matlab для вычисления x'*A*x? т. е. вместо y = x'*A*x есть функция quadraticform с.т. y = quadraticform(A, x)?

Очевидно, я могу просто сделать y = x'*A*x, но мне нужна производительность, и кажется, что должен быть способ воспользоваться преимуществами

  1. A симметричен
  2. Левый и правый множители являются одним и тем же вектором

Если нет ни одной встроенной функции, есть ли метод, который быстрее, чем x'*A*x? ИЛИ достаточно ли умен парсер Matlab, чтобы оптимизировать x'*A*x? Если да, можете ли вы указать мне место в документации, подтверждающее этот факт?


person dantswain    schedule 03.12.2011    source источник
comment
stackoverflow .com/questions/2568939/   -  person kol    schedule 03.12.2011
comment
Спасибо. Это это немного быстрее, но я оставлю его открытым, чтобы посмотреть, есть ли какие-либо другие предложения (технически это не тот же вопрос, но он подходит к той же точке). sum(x.*(A*x)) не использует преимущества симметрии или повторения... Это такое вездесущее вычисление, что кажется, что здесь есть встроенный...   -  person dantswain    schedule 03.12.2011


Ответы (2)


Я не смог найти такую ​​встроенную функцию, и я знаю, почему.

y=x'*A*x можно записать как сумму n^2 слагаемых A(i,j)*x(i)*x(j), где i и j идут от 1 до n (где A — матрица nxn). A симметрично: A(i,j) = A(j,i) для всех i и j. Из-за симметрии каждый член появляется в сумме дважды, за исключением тех, где i равно j. Итак, у нас есть n*(n+1)/2 разных терминов. У каждого есть два умножения с плавающей запятой, поэтому наивному методу потребуется всего n*(n+1) умножений. Легко видеть, что наивное вычисление x'*A*x, то есть вычисление z=A*x, а затем y=x'*z, также требует n*(n+1) умножения. Однако есть более быстрый способ суммировать наши n*(n+1)/2 различных термина: для каждого i мы можем вынести x(i), что означает, что достаточно только n*(n-1)/2+3*n умножений. Но это не особо помогает: время работы расчета y=x'*A*x по-прежнему O(n^2).

Итак, я думаю, что вычисление квадратичных форм не может быть выполнено быстрее, чем O(n^2), а так как это может быть достигнуто и по формуле y=x'*A*x, не было бы никакой реальной пользы от специальной функции "квадратичной формы".

=== ОБНОВЛЕНИЕ ===

Я написал функцию «quadraticform» на C как расширение Matlab:

// y = quadraticform(A, x)
#include "mex.h" 

/* Input Arguments */
#define A_in prhs[0]
#define x_in prhs[1]

/* Output Arguments */
#define y_out plhs[0] 

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
  mwSize mA, nA, n, mx, nx;
  double *A, *x;
  double z, y;
  int i, j, k;

  if (nrhs != 2) { 
      mexErrMsgTxt("Two input arguments required."); 
  } else if (nlhs > 1) {
      mexErrMsgTxt("Too many output arguments."); 
  }

  mA = mxGetM(A_in);
  nA = mxGetN(A_in);
  if (mA != nA)
    mexErrMsgTxt("The first input argument must be a quadratic matrix.");
  n = mA;

  mx = mxGetM(x_in);
  nx = mxGetN(x_in);
  if (mx != n || nx != 1)
    mexErrMsgTxt("The second input argument must be a column vector of proper size.");

  A = mxGetPr(A_in);
  x = mxGetPr(x_in);
  y = 0.0;
  k = 0;
  for (i = 0; i < n; ++i)
  {
    z = 0.0;
    for (j = 0; j < i; ++j)
      z += A[k + j] * x[j];
    z *= x[i];
    y += A[k + i] * x[i] * x[i] + z + z;
    k += n;
  }

  y_out = mxCreateDoubleScalar(y);
}

Я сохранил этот код как «quadraticform.c» и скомпилировал его с помощью Matlab:

mex -O quadraticform.c

Я написал простой тест производительности, чтобы сравнить эту функцию с x'Ax:

clear all; close all; clc;

sizes = int32(logspace(2, 3, 25));
nsizes = length(sizes);
etimes = zeros(nsizes, 2); % Matlab vs. C
nrepeats = 100;
h = waitbar(0, 'Please wait...');
for i = 1 : nrepeats
  for j = 1 : nsizes
    n = sizes(j);
    A = randn(n); 
    A = (A + A') / 2;
    x = randn(n, 1);
    if randn > 0
      start = tic;
      y1 = x' * A * x;
      etimes(j, 1) = etimes(j, 1) + toc(start);
      start = tic;
      y2 = quadraticform(A, x);
      etimes(j, 2) = etimes(j, 2) + toc(start);      
    else
      start = tic;
      y2 = quadraticform(A, x);
      etimes(j, 2) = etimes(j, 2) + toc(start);      
      start = tic;
      y1 = x' * A * x;
      etimes(j, 1) = etimes(j, 1) + toc(start);
    end;
    if abs((y1 - y2) / y2) > 1e-10
      error('"x'' * A * x" is not equal to "quadraticform(A, x)"');
    end;
    waitbar(((i - 1) * nsizes + j) / (nrepeats * nsizes), h);
  end;
end;
close(h);
clear A x y;
etimes = etimes / nrepeats;

n = double(sizes);
n2 = n .^ 2.0;
i = nsizes - 2 : nsizes;
n2_1 = mean(etimes(i, 1)) * n2 / mean(n2(i));
n2_2 = mean(etimes(i, 2)) * n2 / mean(n2(i));

figure;
loglog(n, etimes(:, 1), 'r.-', 'LineSmoothing', 'on');
hold on;
loglog(n, etimes(:, 2), 'g.-', 'LineSmoothing', 'on');
loglog(n, n2_1, 'k-', 'LineSmoothing', 'on');
loglog(n, n2_2, 'k-', 'LineSmoothing', 'on');
axis([n(1) n(end) 1e-4 1e-2]);
xlabel('Matrix size, n');
ylabel('Running time (a.u.)');
legend('x'' * A * x', 'quadraticform(A, x)', 'O(n^2)', 'Location', 'NorthWest');

W = 16 / 2.54; H = 12 / 2.54; dpi = 100;
set(gcf, 'PaperPosition', [0, 0, W, H]);
set(gcf, 'PaperSize', [W, H]);
print(gcf, sprintf('-r%d',dpi), '-dpng', 'quadraticformtest.png');

Результат очень интересный. Время работы как x'*A*x, так и quadraticform(A,x) сходится к O(n^2), но у первого коэффициент меньше:

quadraticformtest.png

person kol    schedule 03.12.2011
comment
Я согласен, что нижняя граница - это n * (n + 1)/2 термина (каждый из них представляет собой умножение 3 чисел с плавающей запятой, а затем суммирует их все). Если Matlab делает x'Ax как z = A*x, тогда y = x'*z, то есть N^2 + N терминов, и, вероятно, добавляет N для выделения/освобождения z. Я нахожусь в режиме, когда N обычно фиксировано, и разница между N^2 + 2*N и N^2/2 + N/2 важна. - person dantswain; 04.12.2011
comment
Если у вас есть компилятор Matlab, вы можете написать свою квадратичную функцию на C и вызвать ее из Matlab. (Компилятор Matlab компилирует файл MEX, который по сути является DLL в Windows.) Вы можете использовать метод, который я предложил в ответе, его довольно просто реализовать, и он масштабируется как n*(n-1)/2+3*n. - person kol; 04.12.2011
comment
Вы можете сделать это без компилятора MATLAB. - person Sam Roberts; 04.12.2011
comment
@SamRoberts Да, вы можете скомпилировать библиотеки C DLL без Matlab, но я бы не хотел бороться с внутренним представлением матриц Matlab. - person kol; 04.12.2011
comment
@kol Я имею в виду, что вы можете создавать MEX-функции только с MATLAB, вам не нужен компилятор MATLAB. - person Sam Roberts; 04.12.2011
comment
@SamRoberts Ах, да. Ты прав. Это было очень давно... :) - person kol; 04.12.2011
comment
Интересно, что я написал mex-файл, и на самом деле это немного медленнее, чем просто выполнение x'Ax, что по-прежнему остается самым быстрым способом, который я нашел. Код c (без всех дополнительных проверок) находится здесь: gist.github.com/1428255 I можно предположить, что потенциальный прирост производительности компенсируется накладными расходами при вызове файла MEX? - person dantswain; 04.12.2011
comment
@dantswain При вызове функции возникают некоторые накладные расходы, но если это всего лишь один вызов, это, вероятно, не будет иметь большого значения. Я предполагаю, что MATLAB выполняет некоторые оптимизации на уровне BLAS, которые компилятор C не использует. - person Sam Roberts; 04.12.2011
comment
@SamRoberts Я написал квадратичную функцию на C и скомпилировал ее с помощью Matlab. Тест Matlab в настоящее время выполняется, пожалуйста, наберитесь терпения :) - person kol; 04.12.2011
comment
@kol Это отличная работа, но для правильного тестирования вещей в MATLAB вам действительно нужно измерять время в функции, а не в сценарии, иначе MATLAB не сделает большую часть оптимизации, на которую он способен. Попробуйте использовать утилиту timeit Стива Эддинса из MATLAB Central File Exchange, которая позаботится о многих тонких проблемах, связанных с синхронизацией, таких как прогрев функции для вас. - person Sam Roberts; 04.12.2011
comment
@SamRoberts Спасибо за совет. В качестве быстрого теста я изменил тестовый код на функцию, поместил x'Ax в функцию, добавил повторы прогрева и вычислил медиану вместо среднего. Ни один из них существенно не изменил результат. - person kol; 04.12.2011
comment
Спасибо, парни. Я думаю, что суть заключалась в том, что x'*A*x был самым быстрым, поэтому я выбрал его. Я бы разделил ответ на вопрос, если бы мог :-/ - person dantswain; 04.12.2011
comment
В MATLAB x' * A * x выполняется с использованием библиотеки BLAS, которая векторизована и использует многопоточность. Если вы добавите это в свой код C, я думаю, вы сможете превзойти MATLAB. - person Royi; 18.07.2019

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

Однако это не то, что MathWorks склонен документировать, потому что а) это обычно оптимизируется только внутри функций, а не в сценариях, в командной строке или при отладке б) это может работать только в некоторых обстоятельствах, например, для реального неразреженный A c) он может меняться от релиза к релизу, поэтому они не хотят, чтобы вы на него полагались d) это одна из проприетарных вещей, которые делают MATLAB таким хорошим.

Чтобы подтвердить, вы можете попробовать сравнить тайминги для y=x'*A*x и B=A*x; y=x'*B. Вы также можете попробовать feature('accel','off'), который отключит большинство подобных оптимизаций.

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

person Sam Roberts    schedule 03.12.2011
comment
MATLAB достаточно умен, чтобы распознавать и оптимизировать такие выражения. Можете ли вы как-то доказать это утверждение? Никто из нас не знает, что происходит в фоновом режиме, когда Matlab компилирует и запускает такое выражение. - person kol; 04.12.2011
comment
Также было бы интересно посмотреть, что с этим делает Octave, в смысле, есть ли какая-то оптимизация. Octave имеет открытый исходный код, поэтому, если у кого-то есть время (много времени...), это не невозможно понять. - person kol; 04.12.2011