cps-16-mic

git clone https://git.igankevich.com/cps-16-mic.git
Log | Files | Refs

commit e325fb344cc50d3e306db7c596a9cbe6d40d91cc
parent 5814b7cc96900f4de7b4dea18fddb827d46a75bf
Author: Ivan Gankevich <igankevich@ya.ru>
Date:   Wed, 10 Aug 2016 23:09:09 +0300

Generate makefile.

Diffstat:
cps-16-mic.tex | 128+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
main.tex | 128-------------------------------------------------------------------------------
makefile | 16++++++++++++++++
3 files changed, 144 insertions(+), 128 deletions(-)

diff --git a/cps-16-mic.tex b/cps-16-mic.tex @@ -0,0 +1,128 @@ +\documentclass[a4paper]{article} +\usepackage{pmstyle} +%\usepackage{showframe} + +\begin{document} +%\hyphenation{СПбГУ} + +% УДК для статьи берется со страницы http://udc.biblio.uspu.ru/ +\udk{УДК 004.051} + +\author{Милова~Е.\:А., Свешникова~С.\:Ю., Ганкевич~И.\:Г.} +%, Дегтярев~А.\:Б. +%%%%%%%%% Название статьи +\title{Ускорение обучения глубокой нейронной сети \\ путем оптимизации алгоритма \\ для запуска на MIC архитектуре} + +%подпись внизу страницы +\renewcommand{\thefootnote}{ } +{\footnotetext{{\it Милова Евгения Андреевна} -- студент, Санкт-Петербургский государственный университет; e-mail: milova.evg@gmail.com, тел.: +7(911)038-38-35}} +{\footnotetext{{\it Свешникова Светлана Юрьевна} -- студент, Санкт-Петербургский государственный университет; e-mail: svetasvesh@yandex.ru, тел.: +7(812)428-47-83}} +{\footnotetext{{\it Ганкевич Иван Геннадьевич} -- аспирант, Санкт-Петербургский государственный университет; e-mail: i.gankevich@spbu.ru, тел.: +7(812)428-47-83}} +%{\footnotetext{{\it Дегтярев Александр Борисович} -- профессор, Санкт-Петербургский государственный университет; e-mail: deg@csa.ru, тел.: +7(812)428-47-83}} + +%сборка заголовка +\maketitle + +%рекомендация научрука +\recprof{Дегтяревым~А.\:Б.} + +\razdel{Введение} +Глубокой нейронной сетью называют перцептрон с более, чем одним скрытым (обучающим) слоем. Для обучения такой сети обычно применяется метод обратного распространения ошибки~\cite{learning-deep-architectures}. Метод обратного распространения ошибки --- итеративный градиентный алгоритм, целью которого является минимизация ошибки при обучении нейронной сети. Итерация алгоритма состоит из трех основных шагов-функций: dnnForward прогоняет через сеть обучающую выборку, получая на выходе некоторый результат; dnnBackward вычисляет ошибку, +%(разницу между результатом выполнения обучающей выборки и правильным ответом) +затем в каждом слое сети, начиная с предпоследнего, для каждого узла вычисляет коррекцию весовых коэффициентов; dnnUpdate обновляет веса нейронов в соответствии с вычисленной ранее поправкой. Обучение сети заканчивается, когда ошибка достигает заданного минимально допустимого значения. +Такая сеть показывает прекрасные результаты во многих областях, в том числе таких, как распознавание изображений и голоса. Однако ее недостатком является очень длительный процесс обучения. В связи с этим, решено исследовать вопрос об эффективности работы такого вида сетей на параллельных вычислительных архитектурах. +Для тестирования взята нейронная сеть из 8 слоев (1 входящий, 6 скрытых, 1 выходящий). Для анализа результатов выбраны такие параметры, как скорость обучения нейронной сети и точность распознавания объектов. %Сеть считается правильно обученной, если точность $\geq19$. + +Запуск задачи производился на процессоре Intel Xeon (спецификация указана в таблице~1). Сначала был произведен запуск задачи с использованием только одного ядра. Затем произведена оптимизация кода для запуска на параллельной архитектуре. Также принято решение протестировать эффективность MIC-архитектуры для решения данной задачи. MIC (Many Integrated Core) --- архитектура, основой которой является использование большого количества вычислительных ядер архитектуры x86 в одном сопроцессоре, подключаемом к основному процессору. Характеристики используемого сопроцессора Intel Xeon Phi также указаны в таблице~1. + +\Table{Характеристики вычислителей, используемых для запуска задачи}{l p{0.7\textwidth}}{ +Процессор & 2$\times$Intel Xeon CPU E5-2695 v2 (12 ядер, 2 потока на ядро, 2,40ГГц ) \\ +Сопроцессор & Intel Xeon Phi-5110P (60 ядер, 4 потока на ядро, 1,053ГГц) \\ +\hline +} + +%\podrazdel{Векторизация внутренних функций} +\razdel{Оптимизация для запуска на параллельных архитектурах} +Каждое ядро процессора Intel Xeon и сопроцессора Intel Xeon Phi содержит блок векторных вычислений. За один такт процессора возможна обработка 16-ти 32-битных чисел или 8-ми 64-битных чисел. Векторизация кода при обработке массивов дает большой потенциал для ускорения работы программы при запуске на параллельных архитектурах. +Для векторизации использовалась технология Array Notation расширения Intel Cilk Plus. Intel Cilk Plus --- расширение языков С и С++ для поддержки параллелизма, реализованное в компиляторе Intel. + +Для работы с массивом вместо цикла for в Array Notation используется конструкция +\verb=array[start_index : length].= +Например, следующий код к каждому $i$-му элементу массива W прибавляет $i$-й элемент массива Wdelta +\begin{verbatim}W[0:count] += Wdelta[0:count]; \end{verbatim} +С помощью Array Notation можно векторизовать выполнение и более сложных операций. +Поиск максимального элемента в массиве выполняется при помощи выражения \verb=__sec_reduce_max= +\begin{verbatim}const float max = __sec_reduce_max(in_vec[base:ncols]); \end{verbatim} +Суммирование элементов массива --- при помощи \verb=__sec_reduce_add= +\begin{verbatim}const float sumexp = __sec_reduce_add(in_vec[base:ncols]); \end{verbatim} + +После проведенной векторизации код был запущен на процессоре Intel Xeon на 12 ядрах (24 потока). Программа ускорилась в 14,5 раз, по сравнению с запуском невекторизованного кода на 1 ядре. + +\razdel{Портирование кода на архитектуру MIC} +Для работы с Intel Xeon Phi была использована offload-модель передачи данных. В режиме offload блок кода, выделенный директивой \verb=#pragma offload= \verb=target (mic)=, выполняется на сопроцессоре, остальной код выполняется на основном процессоре. Также для каждой переменной необходимо указать сопроцессору размер выделяемой под нее памяти. Режим offload поддерживает две модели передачи данных: явную и неявную. + +\podrazdel{Явная модель передачи данных} +При использовании явной модели программист указывает, какие именно переменные должны быть скопированы на сопроцессор. Также указывается направление копирования. Достоинством данной модели является возможность успешной компиляции кода любым компилятором, а не только Intel Compiler. Неизвестные директивы будут просто проигнорированы, без генерации ошибок, код будет скомпилирован для работы только на центральном процессоре. + +\Table{Сравнение времени работы и точности обучения}{l l p{1.2cm} p{1.3cm}l l}{ +Арх. & Версия программы & Кол-во потоков & Время, сек. & Ускорение & Точность \\ +\hline +x86 & Исходная & \noindent\hphantom{00}1 & 7952 & $\times$\noindent\hphantom{0}1 & 19,19 \\ +x86 & Параллельная & \noindent\hphantom{0}48 & \noindent\hphantom{0}542 & $\times$14,7 & 18,99 \\ +MIC & Offload & 240 & 6889 & $\times$\noindent\hphantom{0}1,2 & 20,05 \\ +MIC & Cilk & 240 & \noindent\hphantom{0}589 & $\times$13,5 & 20,05 \\ +\hline +} + +Функции обучения нейронной сети вызываются внутри двух вложенных циклов. Внутренний цикл был помечен для выполнения на сопроцессоре. +К сожалению, у явной модели работы с памятью есть существенный недостаток. Она поддерживает лишь побитовое копирование данных. +%Структура, содержащая поля-указатели, не может быть скопирована таким образом. +В данной программе все характеристики нейронной сети содержатся в структуре nodeArg, содержащей поля-указатели. Она указывается в качестве аргумента для отправляемых на исполнение сопроцессору функций. +Для корректного копирования структуры nodeArg на сопроцессор необходимо скопировать по отдельности каждое ее поле, и уже на сопроцессоре собрать структуру заново. + +Запуск на кластере показал, что такая модель передачи данных не подходит для данной задачи. Программа выполняется лишь не-\linebreak\newpage\noindentмного быстрее, чем на одном ядре процессора и в 12 раз медленнее, чем на всех ядрах (таблица~2). В связи с этим, было принято решение использовать неявную модель передачи данных на сопроцессор. + +\podrazdel{Неявная модель передачи данных} +Основным принципом работы неявной модели является использование разделяемой между CPU и MIC памяти в рамках единого виртуального адресного пространства. Данный подход позволяет работать со сложными типами данных. Таким образом отпадает ограничение, связанное с побитовым копированием, возникающее при использование явной модели. Преобразование программы осуществлялось следующим образом. +\begin{enumerate} +\item Используемые данные отмечены ключевым словом\linebreak \verb=_Cilk_shared=, которое позволяет размещать их в разделяемой памяти. +\begin{verbatim} +nodeArg.d_B[i-1]=(_Cilk_shared +float*)_Offload_shared_malloc(N*sizeof(float)); +\end{verbatim} +\item Используемые внутри цикла обучения функции также отмечены как разделяемые: +\begin{verbatim} +#pragma offload_attribute (push, _Cilk_shared) +... +#pragma offload_attribute (pop) +\end{verbatim} +\item Создана отдельная функция для цикла обучения нейронной сети для ее использования в разделяемой памяти: +\begin{verbatim} +_Cilk_shared void dnn(NodeArg&, ChunkContainer&) {...} +\end{verbatim} +\item Функция, отправляемая для выполнения на сопроцессор помечена командой \verb=_Cilk_offload:= +\begin{verbatim} +_Cilk_offload dnn(nodeArg, oneChunk); +\end{verbatim} +\end{enumerate} + +Стоит отметить, что неявная схема работы оказалась более проста в использовании, по сравнению с явной схемой, и позволила достичь приемлемого времени обучения. Получено ускорение времени работы программы в 13,5 раз по сравнению с последовательной версией. + +%\vskip -1cm + +\razdel{Выводы} +Проведено тестирование задачи обучения глубоких нейронных сетей на различных вычислительных архитектурах. Результаты представлены в таблице~2. Версия для MIC не дает прироста производительности по сравнению с параллельной версией для процессора. На это влияет много факторов, которые связаны как с особенностями алгоритма, так и с теми ограничениями, которые были поставлены при решении данной задачи. Итеративность выполнения алгоритма не дает большого потенциала для распараллеливания. Оптимизации поддается только каждый его шаг, связанный с вычислениями на матрице. +Полученное ускорение в 13,5 раз при запуске на MIC архитектуре по сравнению с последовательной версией в целом кореллирует с результатами, полученными в других исследованиях~\cite{dnn-xeon-phi-thesis, dnn-xeon-phi-finance}. Кроме того, не был рассмотрен native-режим работы с сопроцессором, при котором весь код запускается на сопроцессоре без использования основного процессора. Возможно, это позволит добиться большего ускорения, но данный вопрос оставлен для последующих исследований. + +\razdel{Заключение} +Исследован вопрос о возможности ускорения работы нейронных сетей с последовательным алгоритмом обучения, произведена оптимизация алгоритма для параллельных архитектур, указаны причины, влияющие на эффективность распараллеливания данной задачи. Также рассмотрен вопрос эффективности MIC-архитектуры для решения данной задачи. + +%список литературы +\begin{thebibliography}{3} +\bibitem{learning-deep-architectures} Bengio~Y. Learning deep architectures for AI // Foundations and trends in Machine Learning. 2009. Vol. 2, No~1. P. 1--127. +\bibitem{dnn-xeon-phi-thesis} Viebke~A. Accelerated Deep Learning using Intel Xeon Phi: Ph.D. dissertation, Linnaeus University, 2015. +\bibitem{dnn-xeon-phi-finance} Dixon~M., Klabjan~D., Bang~J.\:H. Implementing deep neural networks for financial market prediction on the Intel Xeon Phi // Proceedings of the 8\textsuperscript{th} Workshop on High Performance Computational Finance. 2015. Article No~6. +\end{thebibliography} + +\end{document} +% that's all folks diff --git a/main.tex b/main.tex @@ -1,128 +0,0 @@ -\documentclass[a4paper]{article} -\usepackage{pmstyle} -%\usepackage{showframe} - -\begin{document} -%\hyphenation{СПбГУ} - -% УДК для статьи берется со страницы http://udc.biblio.uspu.ru/ -\udk{УДК 004.051} - -\author{Милова~Е.\:А., Свешникова~С.\:Ю., Ганкевич~И.\:Г.} -%, Дегтярев~А.\:Б. -%%%%%%%%% Название статьи -\title{Ускорение обучения глубокой нейронной сети \\ путем оптимизации алгоритма \\ для запуска на MIC архитектуре} - -%подпись внизу страницы -\renewcommand{\thefootnote}{ } -{\footnotetext{{\it Милова Евгения Андреевна} -- студент, Санкт-Петербургский государственный университет; e-mail: milova.evg@gmail.com, тел.: +7(911)038-38-35}} -{\footnotetext{{\it Свешникова Светлана Юрьевна} -- студент, Санкт-Петербургский государственный университет; e-mail: svetasvesh@yandex.ru, тел.: +7(812)428-47-83}} -{\footnotetext{{\it Ганкевич Иван Геннадьевич} -- аспирант, Санкт-Петербургский государственный университет; e-mail: i.gankevich@spbu.ru, тел.: +7(812)428-47-83}} -%{\footnotetext{{\it Дегтярев Александр Борисович} -- профессор, Санкт-Петербургский государственный университет; e-mail: deg@csa.ru, тел.: +7(812)428-47-83}} - -%сборка заголовка -\maketitle - -%рекомендация научрука -\recprof{Дегтяревым~А.\:Б.} - -\razdel{Введение} -Глубокой нейронной сетью называют перцептрон с более, чем одним скрытым (обучающим) слоем. Для обучения такой сети обычно применяется метод обратного распространения ошибки~\cite{learning-deep-architectures}. Метод обратного распространения ошибки --- итеративный градиентный алгоритм, целью которого является минимизация ошибки при обучении нейронной сети. Итерация алгоритма состоит из трех основных шагов-функций: dnnForward прогоняет через сеть обучающую выборку, получая на выходе некоторый результат; dnnBackward вычисляет ошибку, -%(разницу между результатом выполнения обучающей выборки и правильным ответом) -затем в каждом слое сети, начиная с предпоследнего, для каждого узла вычисляет коррекцию весовых коэффициентов; dnnUpdate обновляет веса нейронов в соответствии с вычисленной ранее поправкой. Обучение сети заканчивается, когда ошибка достигает заданного минимально допустимого значения. -Такая сеть показывает прекрасные результаты во многих областях, в том числе таких, как распознавание изображений и голоса. Однако ее недостатком является очень длительный процесс обучения. В связи с этим, решено исследовать вопрос об эффективности работы такого вида сетей на параллельных вычислительных архитектурах. -Для тестирования взята нейронная сеть из 8 слоев (1 входящий, 6 скрытых, 1 выходящий). Для анализа результатов выбраны такие параметры, как скорость обучения нейронной сети и точность распознавания объектов. %Сеть считается правильно обученной, если точность $\geq19$. - -Запуск задачи производился на процессоре Intel Xeon (спецификация указана в таблице~1). Сначала был произведен запуск задачи с использованием только одного ядра. Затем произведена оптимизация кода для запуска на параллельной архитектуре. Также принято решение протестировать эффективность MIC-архитектуры для решения данной задачи. MIC (Many Integrated Core) --- архитектура, основой которой является использование большого количества вычислительных ядер архитектуры x86 в одном сопроцессоре, подключаемом к основному процессору. Характеристики используемого сопроцессора Intel Xeon Phi также указаны в таблице~1. - -\Table{Характеристики вычислителей, используемых для запуска задачи}{l p{0.7\textwidth}}{ -Процессор & 2$\times$Intel Xeon CPU E5-2695 v2 (12 ядер, 2 потока на ядро, 2,40ГГц ) \\ -Сопроцессор & Intel Xeon Phi-5110P (60 ядер, 4 потока на ядро, 1,053ГГц) \\ -\hline -} - -%\podrazdel{Векторизация внутренних функций} -\razdel{Оптимизация для запуска на параллельных архитектурах} -Каждое ядро процессора Intel Xeon и сопроцессора Intel Xeon Phi содержит блок векторных вычислений. За один такт процессора возможна обработка 16-ти 32-битных чисел или 8-ми 64-битных чисел. Векторизация кода при обработке массивов дает большой потенциал для ускорения работы программы при запуске на параллельных архитектурах. -Для векторизации использовалась технология Array Notation расширения Intel Cilk Plus. Intel Cilk Plus --- расширение языков С и С++ для поддержки параллелизма, реализованное в компиляторе Intel. - -Для работы с массивом вместо цикла for в Array Notation используется конструкция -\verb=array[start_index : length].= -Например, следующий код к каждому $i$-му элементу массива W прибавляет $i$-й элемент массива Wdelta -\begin{verbatim}W[0:count] += Wdelta[0:count]; \end{verbatim} -С помощью Array Notation можно векторизовать выполнение и более сложных операций. -Поиск максимального элемента в массиве выполняется при помощи выражения \verb=__sec_reduce_max= -\begin{verbatim}const float max = __sec_reduce_max(in_vec[base:ncols]); \end{verbatim} -Суммирование элементов массива --- при помощи \verb=__sec_reduce_add= -\begin{verbatim}const float sumexp = __sec_reduce_add(in_vec[base:ncols]); \end{verbatim} - -После проведенной векторизации код был запущен на процессоре Intel Xeon на 12 ядрах (24 потока). Программа ускорилась в 14,5 раз, по сравнению с запуском невекторизованного кода на 1 ядре. - -\razdel{Портирование кода на архитектуру MIC} -Для работы с Intel Xeon Phi была использована offload-модель передачи данных. В режиме offload блок кода, выделенный директивой \verb=#pragma offload= \verb=target (mic)=, выполняется на сопроцессоре, остальной код выполняется на основном процессоре. Также для каждой переменной необходимо указать сопроцессору размер выделяемой под нее памяти. Режим offload поддерживает две модели передачи данных: явную и неявную. - -\podrazdel{Явная модель передачи данных} -При использовании явной модели программист указывает, какие именно переменные должны быть скопированы на сопроцессор. Также указывается направление копирования. Достоинством данной модели является возможность успешной компиляции кода любым компилятором, а не только Intel Compiler. Неизвестные директивы будут просто проигнорированы, без генерации ошибок, код будет скомпилирован для работы только на центральном процессоре. - -\Table{Сравнение времени работы и точности обучения}{l l p{1.2cm} p{1.3cm}l l}{ -Арх. & Версия программы & Кол-во потоков & Время, сек. & Ускорение & Точность \\ -\hline -x86 & Исходная & \noindent\hphantom{00}1 & 7952 & $\times$\noindent\hphantom{0}1 & 19,19 \\ -x86 & Параллельная & \noindent\hphantom{0}48 & \noindent\hphantom{0}542 & $\times$14,7 & 18,99 \\ -MIC & Offload & 240 & 6889 & $\times$\noindent\hphantom{0}1,2 & 20,05 \\ -MIC & Cilk & 240 & \noindent\hphantom{0}589 & $\times$13,5 & 20,05 \\ -\hline -} - -Функции обучения нейронной сети вызываются внутри двух вложенных циклов. Внутренний цикл был помечен для выполнения на сопроцессоре. -К сожалению, у явной модели работы с памятью есть существенный недостаток. Она поддерживает лишь побитовое копирование данных. -%Структура, содержащая поля-указатели, не может быть скопирована таким образом. -В данной программе все характеристики нейронной сети содержатся в структуре nodeArg, содержащей поля-указатели. Она указывается в качестве аргумента для отправляемых на исполнение сопроцессору функций. -Для корректного копирования структуры nodeArg на сопроцессор необходимо скопировать по отдельности каждое ее поле, и уже на сопроцессоре собрать структуру заново. - -Запуск на кластере показал, что такая модель передачи данных не подходит для данной задачи. Программа выполняется лишь не-\linebreak\newpage\noindentмного быстрее, чем на одном ядре процессора и в 12 раз медленнее, чем на всех ядрах (таблица~2). В связи с этим, было принято решение использовать неявную модель передачи данных на сопроцессор. - -\podrazdel{Неявная модель передачи данных} -Основным принципом работы неявной модели является использование разделяемой между CPU и MIC памяти в рамках единого виртуального адресного пространства. Данный подход позволяет работать со сложными типами данных. Таким образом отпадает ограничение, связанное с побитовым копированием, возникающее при использование явной модели. Преобразование программы осуществлялось следующим образом. -\begin{enumerate} -\item Используемые данные отмечены ключевым словом\linebreak \verb=_Cilk_shared=, которое позволяет размещать их в разделяемой памяти. -\begin{verbatim} -nodeArg.d_B[i-1]=(_Cilk_shared -float*)_Offload_shared_malloc(N*sizeof(float)); -\end{verbatim} -\item Используемые внутри цикла обучения функции также отмечены как разделяемые: -\begin{verbatim} -#pragma offload_attribute (push, _Cilk_shared) -... -#pragma offload_attribute (pop) -\end{verbatim} -\item Создана отдельная функция для цикла обучения нейронной сети для ее использования в разделяемой памяти: -\begin{verbatim} -_Cilk_shared void dnn(NodeArg&, ChunkContainer&) {...} -\end{verbatim} -\item Функция, отправляемая для выполнения на сопроцессор помечена командой \verb=_Cilk_offload:= -\begin{verbatim} -_Cilk_offload dnn(nodeArg, oneChunk); -\end{verbatim} -\end{enumerate} - -Стоит отметить, что неявная схема работы оказалась более проста в использовании, по сравнению с явной схемой, и позволила достичь приемлемого времени обучения. Получено ускорение времени работы программы в 13,5 раз по сравнению с последовательной версией. - -%\vskip -1cm - -\razdel{Выводы} -Проведено тестирование задачи обучения глубоких нейронных сетей на различных вычислительных архитектурах. Результаты представлены в таблице~2. Версия для MIC не дает прироста производительности по сравнению с параллельной версией для процессора. На это влияет много факторов, которые связаны как с особенностями алгоритма, так и с теми ограничениями, которые были поставлены при решении данной задачи. Итеративность выполнения алгоритма не дает большого потенциала для распараллеливания. Оптимизации поддается только каждый его шаг, связанный с вычислениями на матрице. -Полученное ускорение в 13,5 раз при запуске на MIC архитектуре по сравнению с последовательной версией в целом кореллирует с результатами, полученными в других исследованиях~\cite{dnn-xeon-phi-thesis, dnn-xeon-phi-finance}. Кроме того, не был рассмотрен native-режим работы с сопроцессором, при котором весь код запускается на сопроцессоре без использования основного процессора. Возможно, это позволит добиться большего ускорения, но данный вопрос оставлен для последующих исследований. - -\razdel{Заключение} -Исследован вопрос о возможности ускорения работы нейронных сетей с последовательным алгоритмом обучения, произведена оптимизация алгоритма для параллельных архитектур, указаны причины, влияющие на эффективность распараллеливания данной задачи. Также рассмотрен вопрос эффективности MIC-архитектуры для решения данной задачи. - -%список литературы -\begin{thebibliography}{3} -\bibitem{learning-deep-architectures} Bengio~Y. Learning deep architectures for AI // Foundations and trends in Machine Learning. 2009. Vol. 2, No~1. P. 1--127. -\bibitem{dnn-xeon-phi-thesis} Viebke~A. Accelerated Deep Learning using Intel Xeon Phi: Ph.D. dissertation, Linnaeus University, 2015. -\bibitem{dnn-xeon-phi-finance} Dixon~M., Klabjan~D., Bang~J.\:H. Implementing deep neural networks for financial market prediction on the Intel Xeon Phi // Proceedings of the 8\textsuperscript{th} Workshop on High Performance Computational Finance. 2015. Article No~6. -\end{thebibliography} - -\end{document} -% that's all folks diff --git a/makefile b/makefile @@ -0,0 +1,16 @@ +NAME = cps-16-mic + +$(NAME).pdf: $(NAME).tex makefile + pdflatex $(NAME) + pdflatex $(NAME) + ls *.bib 2>/dev/null && bibtex $(NAME) || true + pdflatex $(NAME) + +%.eps: %.svg + inkscape --without-gui --export-eps=$@ $< + +clean: + rm -f $(NAME).log $(NAME).aux $(NAME).pdf *-converted-to.pdf + rm -f $(NAME).nav $(NAME).snm $(NAME).toc $(NAME).out + rm -f $(NAME).bbl $(NAME).blg $(NAME).vrb +