Разложение QR на графическом процессоре NVIDIA Используя cuSOLVER Библиотеки

Этот пример демонстрирует, как создать автономный исполняемый файл CUDA®, который усиливает библиотеку CUDA Solver (cuSOLVER). Пример использует приложение аппроксимирования кривыми, которое подражает автоматическому отслеживанию маршрута на дороге, чтобы проиллюстрировать несколько тем, включая:

  • Подбор кривой полиному произвольного порядка к зашумленным данным с помощью матричной QR-факторизации.

  • Используя кодер. Класс LAPACKCallback, чтобы предоставить информацию о библиотеке LAPACK для генератора кода при генерации независимых исполняемых файлов.

Предпосылки

  • CUDA включил NVIDIA®, графический процессор с вычисляет возможность 3.2 или выше.

  • NVIDIA инструментарий CUDA.

  • Библиотека LAPACK, которая оптимизирована для вашей среды выполнения. Для получения дополнительной информации см. реализации поставщиков LAPACK.

  • Переменные окружения для компиляторов и библиотек. Для получения информации о поддерживаемых версиях компиляторов и библиотек, смотрите Сторонние продукты. Для подготовки переменных окружения смотрите Переменные окружения.

Создайте папку и скопируйте соответствующие файлы

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

gpucoderdemo_setup('gpucoderdemo_qr_decomposition');

Проверьте среду графического процессора

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

envCfg = coder.gpuEnvConfig('host');
envCfg.BasicCodegen = 1;
envCfg.Quiet = 1;
coder.checkGpuInstall(envCfg);

Решение Линейной системы Используя Матричную Факторизацию

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

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

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

  • y содержит датчик выходные параметры.

  • x содержит полиномиальные коэффициенты, которые мы должны получить.

  • A является постоянной матрицей, связанной с порядком полинома и местоположениями датчиков.

Мы решим уравнение с помощью QR-факторизации можно следующим образом:

и

где pinv () представляет псевдоинверсию. Учитывая матрицу А, следующий код может использоваться, чтобы реализовать решение этого матричного уравнения. Факторинг A позволяет для более легкого решения системы.

[Q, R] = qr(A);
z = Q' * y;
x = R \ z;
yhat = A * x;

Функция linsolveQR используется, чтобы решить уравнение с помощью QR-факторизации A.

type linsolveQR.m
function [yhat,x] = linsolveQR(A,y)
%#codegen

%   Copyright 2019 The MathWorks, Inc.

[Q, R] = qr(A);
z = Q' * y;
x = R \ z;
yhat = A * x;

end

Модель сигнала для дороги

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

Duration = 2;                                                       % Distance that we look ahead
N = 25;                                                             % Total number of sensors providing estimates of road boundary
Ts = Duration / N;                                                  % Sample interval
FracPeriod = 0.5;                                                   % Fraction of period of sinusoid to match
y = sin(2*pi* (0:N-1)' * (FracPeriod/N)) + sqrt(0.3) * randn(N,1);  % y will contain the simulated sensor outputs

Матрица А Вандермонда может быть сформирована с помощью следующего кода:

Npoly = 3;                  % Order of polynomial to use in fitting
v = (0:Ts:((N-1)*Ts))';
A = zeros(length(v), Npoly);
for i = Npoly : -1 : 1
    A(:,i) = v.^i;
end

Матрица А Вандермонда и датчик выходная матрица y передаются как входные параметры linsolveQR функция точки входа. Эти входные параметры записаны в разделенные от запятой файлы и читаются из рукописного основного qrmain.cu.

 writematrix(reshape(A, 1, 75), 'inputA.csv');
 writematrix(reshape(y, 1, 25), 'inputY.csv');

Пользовательский класс коллбэка для автономной генерации кода

qr функция только частично поддерживается в cuSOLVER библиотека. В таких случаях GPU Coder™ дополнительно использует LAPACK библиотека для определенных вызовов функции линейной алгебры. LAPACK внешняя библиотека программного обеспечения в числовой линейной алгебре. Для целей MEX генератор кода использует LAPACK библиотека, которая поставляется с MATLAB®.

Для автономных целей необходимо задать пользовательский кодер. Класс LAPACKCallback, который задает LAPACK и cuSOLVER библиотеки наряду с заголовочными файлами, чтобы использовать для вызовов линейной алгебры в сгенерированном коде. В этом примере lapackCallback класс коллбэка используется, чтобы задать пути к этим библиотекам в updateBuildInfo метод. Необходимо изменить этот файл с именами библиотеки и путями установки LAPACK на компьютере.

type lapackCallback.m

classdef lapackCallback < coder.LAPACKCallback
%

%   Copyright 2019 The MathWorks, Inc.

    methods (Static)
        function hn = getHeaderFilename()
            hn = 'lapacke.h';
        end

        function updateBuildInfo(buildInfo, buildctx)
            [~, libExt] = buildctx.getStdLibInfo();
            
            % Specify path to CUDA libraries
            if ispc
                cudaPath = getenv('CUDA_PATH');
                libPath = 'lib\x64';
                libName = ['cusolver' libExt];
            else
                [~,cudaPath] = system('which nvcc');
                cudaPath = fileparts(cudaPath);
                while (cudaPath(end)=='/')
                    cudaPath = cudaPath(1:end-1);
                end
                lastIdx = find(cudaPath=='/',1,'last');
                cudaPath = cudaPath(1:(lastIdx-1));
                libPath = 'lib64';
                libName = ['libcusolver' libExt];
            end
           
            % Add include path and cusolver library for linking
            buildInfo.addIncludePaths(fullfile(cudaPath,'include'));
            libPath = fullfile(cudaPath, libPath);
            addLinkObjects(buildInfo, libName, libPath, '', true, true);
            
            % Specify path to LAPACK library
            if ispc
                lapackLocation = fullfile('C:','win64','LAPACK');
                libName = ['liblapacke' libExt];
                buildInfo.addIncludePaths(fullfile(lapackLocation,'include'));            
                libPath = fullfile(lapackLocation, 'lib');
            else
                lapackLocation = fullfile(filesep,'usr','lib','lapack');
                libName = ['liblapack' libExt];
                buildInfo.addIncludePaths(fullfile('usr','include'));            
                libPath = fullfile(lapackLocation);
            end
            
            % Add include path and LAPACK library for linking
            addLinkObjects(buildInfo, libName, libPath, 1000, true, true);

            buildInfo.addDefines('HAVE_LAPACK_CONFIG_H');
            buildInfo.addDefines('LAPACK_COMPLEX_STRUCTURE');
        end
    end
end

Автономная генерация кода

Мы генерируем независимый исполняемый файл путем определения CustomLAPACKCallback свойство в объекте настройки кода и использовании рукописного основного qrmain.cu.

cfg = coder.gpuConfig('exe');
cfg.GpuConfig.EnableCUSOLVER = 1;
cfg.CustomLAPACKCallback = 'lapackCallback';
cfg.CustomSource = 'qrmain.cu';
cfg.CustomInclude = '.';
codegen -config cfg -args {A,y} linsolveQR -report
Code generation successful: To view the report, open('codegen/exe/linsolveQR/html/report.mldatx').

Автономное выполнение кода

Когда мы выполняем сгенерированный независимый исполняемый файл, выходные параметры yhat и x вычислены и записаны в разделенные от запятой файлы. Эти выходные параметры читаются назад в MATLAB, и функция построения графика используется, чтобы визуализировать данные о датчике и кривую по экспериментальным точкам.

if ispc
    system('linsolveQR.exe');
else
    system('./linsolveQR');
end
yhat = reshape(readmatrix('outputYhat.csv'), 25, 1);
x = reshape(readmatrix('outputX.csv'), 3, 1);
figure
plot(v, y, 'k.', v, yhat, 'r')
axis([0 N*Ts -3 3]);
grid;
xlabel('Distance ahead of vehicle');
legend('Sensor data','Curve fit');
title('Estimate of lane boundary looking ahead of vehicle');

Команда выполнения: Очистка

Удалите сгенерированные файлы и возвратитесь к исходной папке.

cleanup