2012-06-11 3 views
14

Я хотел бы использовать библиотеку Eigen-матрицы в качестве механизма линейной алгебры в своей программе. Eigen использует шаблоны выражений для реализации ленивой оценки и упрощения циклов и вычислений.Как интегрировать библиотеку, которая использует шаблоны выражений?

Например:

#include<Eigen/Core> 

int main() 
{ 
    int size = 40; 
    // VectorXf is a vector of floats, with dynamic size. 
    Eigen::VectorXf u(size), v(size), w(size), z(size); 
    u = 2*v + w + 0.2*z; 
} 

Поскольку Эйген использует шаблоны экспрессии, подобный код

u = 2*v + w + 0.2*z; 

В вышеупомянутом образце сводится к одной петле длины 10 (не 40, поплавки ставятся в реестре кусками 4) без создания временного. Как это круто?

Но если я интегрировать библиотеку так:

class UsingEigen 
{ 
    public: 
     UsingEigen(const Eigen::VectorXf& data): 
      data_(data) 
     {} 

     UsingEigen operator + (const UsingEigen& adee)const 
     { 
      return UsingEigen(data_ + adee.data_); 
     } 

     ... 
    private: 
     Eigen::VectorXf data_; 
} 

Тогда выражения, как:

UsingEigen a, b, c, d; 
a = b + c + d; 

не может воспользоваться способом Эйген реализуется. И это не последнее. Существует много других примеров, в которых шаблоны экспрессии используются в Eigen.

Простое решение было бы не определять операторы самостоятельно, сделать data_ общественности и просто писать такие выражения, как:

UsingEigen a, b, c, d; 
a.data_ = b.data_ + c.data_ + d.data_; 

Это нарушает инкапсуляцию, но сохраняет эффективность Эйгена.

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

Прошу прощения, если вопрос носит слишком общий характер. Я новичок, и мне не о чем спросить. До сих пор я использовал std::vector<float> везде, но теперь мне также нужно использовать матрицы. Переключение с std::vector<float> на Eigen во всем моем проекте - большой шаг, и я боюсь ошибиться в самом начале игры. Любые советы приветствуются!

+1

Интересная проблема. Однако первое: почему вы хотите инкапсулировать векторы Eigen библиотеки таким образом? Какое поведение добавляются вашими классами? –

+0

Мои классы не добавляют функциональности самим классам библиотеки Eigen, но используют их. Например, один из моих основных классов хранит два вектора. Один - как вход для определенного математического вычисления, а другой как вывод. Эти объекты должны взаимодействовать так же, как я упоминал выше. Когда вы добавляете два таких объекта, входы должны быть добавлены. –

+1

Я не думаю, что это возможно без ретрансляции значительной части рамки шаблона выражения. Например, '(a + b) * c' будет чем-то вроде' ExprCwiseAdd * UsingEigen' (имя составлено, не напомните об этом больше), и там должно быть 'ExprCwiseAdd * UsingEigen', определенном где-то , но также «ExprCwiseAdd * ExprCWiseAdd» и т. д. Короче говоря, добавление не будет иметь 'UseEigen' в качестве возвращаемого типа. (Вы можете взглянуть на [boost :: proto] (http://www.boost.org/doc/libs/1_49_0/doc/html/proto.html), который является основой для шаблонов выражений). Удачи. – eudoxos

ответ

4

Почему бы не разоблачить data_ разрыв капсулы? Инкапсуляция означает скрытие деталей реализации и только отображение интерфейса. Если ваш класс-оболочка UsingEigen не добавляет никакого поведения или состояния в собственную библиотеку Eigen, интерфейс не изменяется. В этом случае вы должны полностью удалить эту оболочку и написать свою программу, используя структуры данных Eigen.

Публикация матрицы или вектора не нарушает инкапсуляцию: только просмотр реализации матрицы или вектора мог бы сделать это. Библиотека Eigen предоставляет арифметические операторы, но не их реализацию.

В библиотеках шаблонов выражений наиболее распространенным способом расширения возможностей библиотеки является добавление поведения, а не добавление путем добавления состояния. А для добавления поведения вам не нужно писать классы-оболочки: вы также можете добавлять функции, не являющиеся членами, которые реализованы в терминах функций класса . См. this column «Как не членские функции улучшают инкапсуляцию» Скотта Мейерса.

Что касается вашей обеспокоенности тем, что преобразование вашей текущей программы в версию, которая явно использует функциональность Eigen, вы можете выполнять пошаговое изменение, изменяя небольшие части вашей программы каждый раз, следя за тем, чтобы ваши юнит-тесты (у вас есть модульные тесты, не так ли?) не ломаются, когда вы идете вперед.

+2

Выявление * членов данных * прерывает инкапсуляцию. Но добавление перегрузок шаблонов выражений для классов Eigen могло бы разбить его таким же образом. –

+0

@ KonradRudolph. Он не разрушает инкапсуляцию, если 'UsingEigen' имеет тот же самый интерфейс и с той же самой реализацией (т. Е. Добавлен литеральный класс-оболочка с чистой пересылкой и без регистрации/проверки и т. Д.). Вы были бы правы, если бы 'UsingEigen' определял бы дополнительный слой абстракции, но, похоже, это не так (и его не следует называть' UsingEigen', потому что это раскрывает реализацию!) – TemplateRex

-2

Я не понимаю всех ваших вопросов. Я постараюсь ответить вам на большинство из них. В этом предложении:

UsingEigen operator + (const UsingEigen& adee)const 
    { 
     return UsingEigen(data_ + adee.data_); 
    } 

У вас есть оператор перегрузки (жаль, что я не знаю, если это правильный способ, чтобы написать на английском языке), по этой причине вы можете написать:

a = b + c + d; 

а не:

a.data_ = b.data_ + c.data_ + d.data_; 

У вас не будет проблем, стоимость вашей программы будет одинаковой. Кроме того, у вас будет инкапсуляция и эффективность.

С другой стороны, если вы хотите определить своего оператора, вы можете сделать это, как это делает шаблон. Вы можете найти информацию на веб-поиска «оператор перегрузки», но похоже на это:

UsingEigen operator + (const UsingEigen& adee)const 
    { 
     return UsingEigen(data_ + adee.data_); 
    } 

Вместо «+» вы можете поставить оператора и делать операции вам нужно.

Если вы хотите создать матрицу, это просто. Вам нужно только создать массив массива или вектор вектора.

Я думаю, что это что-то вроде этого:

std::vector<vector<float>> 

Я не уверен, но это легко, с другой стороны, вы можете использовать простую матрицу на этом пути:

поплавок YourMatrix [размер] [размер];

Надеюсь, это может вам помочь. Я не понимаю весь ваш вопрос, если вам нужно что-то еще добавить меня в Google +, и я постараюсь вам помочь.

Извините за мой английский, я надеюсь, вы все поймете, и это поможет вам.

+1

К сожалению, вы не правы. Вам нужно прочитать, что [шаблоны выражения] (http://en.wikipedia.org/wiki/Expression_templates) и как они влияют на этот код. Перегруженные операторы не будут использовать шаблоны выражений, как описано в ОП. Кроме того, библиотека Eigen предоставляет путь большей семантике, чем наивный подход вложенных массивов, который вы показали, что совершенно не подходит для OP. –

+0

Спасибо Мануэль, если я использую ваш подход, программа будет компилироваться и работать правильно. Но выражения типа a = b + c + d; приведет к чему-то вроде этого: сначала b + c будет вычислен и сохранен во временном bctemp. Затем bctemp будет добавлен в d и создадим bcdtemp. Наконец, bcdtemp будет назначен a. Это 3 цикла и 2 временных. Моя цель - избежать этого. –

+0

@Manuel Если вас интересует то, что я имею в виду, избегая временных и петель, вы можете найти лучшее объяснение здесь. Http://eigen.tuxfamily.org/dox/TopicInsideEigenExample.html –

2

На мой взгляд, это скорее проблема объектно-ориентированного проектирования, а не проблема использования библиотеки. Все, что вы читаете из книг, - это правильные рекомендации. то есть не выставлять переменные-члены и защищать верхние уровни от нюансов использования стороннего уровня.

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

Например, вы можете абстрагироваться от моих API более высокого уровня, таких как вычисление расстояния от точки до плоскости, расстояние между двумя плоскостями, вычисление новых координат точки по другой системе координат с использованием матриц преобразования и т. Д. Чтобы реализовать эти методы внутри вы можете использовать объекты библиотеки.Вы можете ограничить не иметь ни одного из классов библиотек, используемых в сигнатурах API, чтобы избежать зависимости для верхних слоев в этой библиотеке.

Верхние уровни вашей программы должны быть выше уровня абстракции и не нужно беспокоиться об элементарных деталях реализации, например, как осуществляется расчет расстояния от точки до плоскости и т. Д. Кроме того, им даже не нужно знаете, реализован ли этот нижний уровень с помощью этой библиотеки или что-то еще. Они просто будут использовать интерфейсы вашей библиотеки.