2012-06-11 17 views
14

Me gustaría utilizar la biblioteca de matriz Eigen como motor de álgebra lineal en mi programa. Eigen usa plantillas de expresión para implementar evaluación diferida y simplificar bucles y cálculos.¿Cómo integrar una biblioteca que usa plantillas de expresión?

Por ejemplo:

#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; 
} 

Desde Eigen utiliza plantillas de expresión, código como

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

En la muestra se ha mencionado anteriormente se reduce a un solo bucle de longitud 10 (no 40, los flotadores se ponen en register por trozos de 4) sin crear un temporal. ¿Cuan genial es eso?

Pero si integro la biblioteca de la siguiente manera:

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_; 
} 

A continuación, las expresiones como:

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

no puede tomar ventaja de la forma en que se implementa Eigen. Y esto no es lo último. Hay muchos otros ejemplos, donde las plantillas de expresión se usan en Eigen.

La solución fácil sería no definir los operadores por mí mismo, hacer data_ pública y acaba de escribir expresiones como:

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

Esto rompe la encapsulación, pero preserva la eficiencia de Eigen.

Otra forma podría ser crear mis propios operadores, pero dejarles devolver plantillas de expresión. Pero dado que soy un principiante en C++, no sé si este es el camino correcto a seguir.

Lo siento si la pregunta es de naturaleza demasiado general. Soy un principiante y no tengo a nadie que preguntar. Hasta ahora estaba usando std::vector<float> en todas partes, pero ahora también necesito usar matrices. Pasar de std::vector<float> a Eigen en todo mi proyecto es un gran paso y tengo miedo de hacer una llamada incorrecta desde el principio. Cualquier consejo es bienvenido!

+1

Problema interesante. Lo primero, sin embargo, es: ¿por qué quieres encapsular los vectores de bibliotecas Eigen de esta manera? ¿Qué comportamiento agregan tus clases? –

+0

Mis clases no agregan ninguna funcionalidad a las clases de la biblioteca Eigen, pero úselas. Por ejemplo, una de mis clases principales almacena dos vectores. Uno como entrada para un cierto cálculo matemático y el otro como salida. Estos objetos necesitan interoperar de una manera similar a la mencionada anteriormente. Cuando agrega dos objetos de este tipo, las entradas deben agregarse. –

+1

No creo que esto sea posible sin la reproducción de una parte sustancial del framework de plantilla de expresión usted mismo. Por ejemplo, '(a + b) * c' será algo así como' ExprCwiseAdd * UsingEigen' (el nombre está compuesto, no lo recuerdo más), y tendrá que haber 'ExprCwiseAdd * UsingEigen' definido en alguna parte , pero también 'ExprCwiseAdd * ExprCWiseAdd' y así sucesivamente. En resumen, la adición no tendrá 'UsingEigen' como tipo de devolución. (Puede echarle un vistazo a [boost :: proto] (http://www.boost.org/doc/libs/1_49_0/doc/html/proto.html) que es un marco para plantillas de expresiones). Buena suerte. – eudoxos

Respuesta

4

¿Por qué exponer data_ romper la encapsulación? La encapsulación significa ocultar los detalles de implementación y solo exponer la interfaz. Si su clase contenedora UsingEigen no agrega ningún comportamiento o estado a la biblioteca nativa Eigen, la interfaz no cambia. En este caso, debe soltar este envoltorio por completo y escribir su programa usando las estructuras de datos Eigen.

La exposición de una matriz o un vector no rompe la encapsulación: solo exponer la implementación de la matriz o el vector haría eso. La biblioteca Eigen expone los operadores aritméticos pero no su implementación.

Con las bibliotecas de plantillas de expresiones, la forma más común para que los usuarios amplíen la funcionalidad de la biblioteca es agregando comportamiento, sin agregar al agregar estado. Y para agregar comportamiento, no necesita escribir clases contenedoras: también puede agregar funciones no miembro implementadas en términos de las funciones de miembro de clase Eigen. Ver this column "Cómo las funciones no miembro mejoran la encapsulación" por Scott Meyers.

En cuanto a su preocupación de que la transformación de su programa actual a una versión que utiliza explícitamente la funcionalidad Eigen: puede realizar el cambio paso a paso, cambiando partes pequeñas de su programa cada vez, asegurándose de que su unidad prueba (tienes pruebas unitarias, ¿no?) No rompas a medida que avanzas.

+2

Exponer * miembros de datos * rompe la encapsulación. Pero agregar las sobrecargas de plantillas de expresiones para las clases Eigen lo rompería de la misma manera. –

+0

@KonradRudolph No rompe la encapsulación si 'UsingEigen' tiene exactamente la misma interfaz y con la misma implementación (es decir, una clase de contenedor literal con reenvío puro y sin registro, etc.). Tendría razón si 'UsingEigen' definiría una capa extra de abstracción, pero no parece ser el caso (¡y no debería llamarse' UsingEigen' en absoluto porque eso expone la implementación!) – TemplateRex

-2

No entiendo toda su pregunta voy a tratar de responder a que la mayoría de ellos. En esta frase:

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

Tienes un operador de sobrecarga (lo siento, no sé si esta es la forma correcta de escribir en Inglés), por esta razón se puede escribir:

a = b + c + d; 

en lugar de:

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

No tendrá ningún problema, el costo de su programa será el mismo. Además, tendrá encapsulación y eficiencia.

Por otro lado, si quiere definir su propio operador, puede hacerlo como lo hace la plantilla. Puede encontrar información en la web a partir de "operador de sobrecarga", pero es similar a esto:

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

En lugar de "+" se puede poner el operador y hacer las operaciones que necesita.

Si quieres crear una matriz, es simple. Solo necesita crear una matriz de matriz o vector de vector.

Creo que es algo como esto:

std::vector<vector<float>> 

No estoy seguro pero es fácil, por otra parte, se puede utilizar una matriz simple de esta manera:

flotador YourMatrix [tamaño] [tamaño];

Espero que pueda ayudarlo. No entiendo toda su pregunta si necesita algo más agrégueme en google + y trataré de ayudarlo.

Lo siento por mi Inglés, espero que pueda entender todo y que le ayuda.

+1

Lamentablemente, usted estan equivocados. Necesita leer lo que son [plantillas de expresión] (http://en.wikipedia.org/wiki/Expression_templates) y cómo influyen en este código. Los operadores sobrecargados no aprovecharán las plantillas de expresión, exactamente como describió el OP. Además, la biblioteca Eigen proporciona mucho más semántica que el enfoque de matriz anidada que has mostrado, que es completamente inadecuado para OP. –

+0

Gracias Manuel, si utilizo su enfoque, el programa compilará y funcionará correctamente. Pero expresiones como a = b + c + d; dará como resultado algo así: primero b + c se computará y almacenará en un bctemp temporal. Entonces bctemp se agregará a dy creará bcdtemp. Por último, el bcdtemp se asignará a a. Eso es 3 bucles y 2 temporales. Mi objetivo es evitar esto. –

+0

@Manuel Si está interesado en lo que quiero decir evitando los bucles temporales y bucles, puede encontrar una mejor explicación aquí http://eigen.tuxfamily.org/dox/TopicInsideEigenExample.html –

2

En mi opinión, esto parece más bien un problema de diseño orientado a objetos en lugar de un problema de uso de la biblioteca. Lo que sea que lea de los libros son las recomendaciones correctas. es decir, no exponga las variables miembro y proteja las capas superiores de los matices del uso de la capa de terceros.

Lo que podría esperar es abstracciones correctas de las funciones matemáticas que se pueden implementar utilizando esta biblioteca internamente. es decir, podría exponer una biblioteca propia con funciones de alto nivel que las operaciones elementales de vectores y matrices. De esta forma, puede utilizar las peculiaridades de las interacciones entre los objetos de la biblioteca y, al mismo tiempo, no tiene que exponer sus variables miembro a las capas superiores.

Por ejemplo, puede abstraer mis API de nivel superior, como calcular la distancia de un punto a un plano, distancia entre dos planos, calcular las nuevas coordenadas de un punto con otro sistema de coordenadas usando las matrices de transformación, etc. métodos internamente puede utilizar los objetos de la biblioteca.Puede restringir para que no se use ninguna de las clases de biblioteca en las firmas de la API para evitar la dependencia de las capas superiores de esta biblioteca.

Las capas superiores de su programa deben ser superiores en el nivel de abstracción y no necesitan preocuparse por los detalles de implementación elemental, como cómo se implementa el cálculo de la distancia desde un punto al plano, etc. Además, ni siquiera necesitan saber si esta capa inferior se implementa utilizando esta biblioteca u otra cosa. Solo usarían las interfaces de tu biblioteca.

Cuestiones relacionadas