Session 4#
Agenda#
C++ Classes
C++ Eigen
C++ Armadillo
C++ Classes#
Simple Vector Class#
1/*
2 Simple demonstration of class to implement mathematical vectors
3
4 Useful References:
5 * Discovering Modern C++ by Peter Gottschling (amongst many other C++ books)
6 * https://en.cppreference.com/
7*/
8
9// This is a header file, so we want to enure if only gets
10// included once in the code so we don't end up with multiple
11// redefinitions of the same functions in a project.
12// On way to do this is to define a uniquely named variable
13// using the prepocessor. Here, we name a variable "_SIMPLE_VECTOR_H_"
14// If that variable is already defined, then nothing happens.
15// Otherwise, we define it and the code defining the class is
16// used.
17#ifndef _SIMPLE_VECTOR_H_
18#define _SIMPLE_VECTOR_H_
19
20// for C++ vectors ... these are arrays not math vectors
21#include <vector>
22
23// Next we'll define a namespace. We don't technically have to
24// do this, but we might want to use other libraries that have
25// the same function or object names, so this will let us
26// use both
27namespace smu_demo_namespace
28{
29
30 // We'll make a class named Vector
31 class Vector
32 {
33
34 // I like to define all of my member variables
35 // at the top of a class. This is a style
36 // preference
37 //
38 // in a class functions, variables, and objects
39 // can be private, public, or protected
40 //
41 // private means only other members of this class
42 // can modify or call
43 //
44 // protected means that other classes that this is
45 // a sub class (also commonly called child) can modify
46 // or call
47 //
48 // public means anything can modify or call.
49 //
50 // Generally speaking, all variables should either
51 // be private or protected. The idea is that if you
52 // need to access data in this class it should be
53 // through a function and not directly.
54
55 private:
56 // an array to contain the elements of our vector
57 // its under the private heading because nothing outside
58 // our class should be able to access it directly
59 std::vector<double> m_data;
60
61 // Next we can declare the functions. For this, almost
62 // everything will be public because we want to be
63 // able to call them, but it is common to have private
64 // or protected helper functions that you do not want
65 // to be calld directly
66
67 // We'll have a private function to check sizes
68
69 public:
70 // Constructors sets default arguments. The compiler automatically
71 // makes one that doesn't do anything, but we'll want to initialize
72 // our data with a constructor. We'll default to setting the vector
73 // to 0 with the specified number of elements.
74 // The name of the constructor always matches the name of the class
75 Vector(const std::size_t &num_elements = 3);
76
77 // There is always a destructor as well to free memory. We don't need
78 // it here, but it's always good to define a virtual destructor
79 // in case someone uses this class for something else
80 // virtual means it can be redfined.
81 // The name is always ~ClassName
82 virtual ~Vector(){};
83
84 // now we can define some vector functions, we might want to
85 //
86 // 1. add 2 vectors
87 // 2. compute a dot product
88 // 3. compute the norm
89 // 4. access elements so we can modify values
90 // 6. reset the vector a single value (e.g 0 or 1)
91 // 7. normalize the vector
92 // etc.
93 // We won't implement every possible thing we might want,
94 // but just a few to demonstrate the idea
95
96 // First lets define some functions to add vectors
97 void add(const Vector &b);
98 void operator+(const Vector &b);
99
100 // for case 2 to work, we also need to define
101 // an = operator
102 void operator=(const Vector &b);
103
104 // compute a dot product
105 // we shouldn't modify the data so we add const
106 double dot(const Vector &b) const;
107
108 // compute the norm
109 double norm() const;
110
111 // functions to access elements / change values
112 // the first will let us change the value, the
113 // second will not
114 // The next 2 are the same ... but using the []
115 // operator
116 double &get(const std::size_t &index);
117 double get(const std::size_t &index) const;
118 double &operator[](const std::size_t &index);
119 double operator[](const std::size_t &index) const;
120
121 // function to set all the values of the vector to
122 // something
123 void setAllValues(const double &val);
124
125 // function to normalize the vector
126 void normalize();
127
128 // function to print the vector
129 void print() const;
130
131 // function to get the number of elements
132 std::size_t numElements() const;
133
134 private:
135 // function to check sizes
136 void check_sizes(const Vector &b) const;
137
138 }; // notice this ends with ;
139
140} // smu_demo_namespace
141
142// End the preprocessor header guard if / else
143// I like to add a comment after it refering to the variable it set
144#endif //_SIMPLE_VECTOR_H_
1/*
2 Simple demonstration of class to implement mathematical vectors
3
4 This file defines (implements) the classes declared in vector_class.hpp
5
6
7 Useful References:
8 * Discovering Modern C++ by Peter Gottschling (amongst many other C++ books)
9 * https://en.cppreference.com/
10*/
11
12#include <cmath> // for std::sqrt
13#include <iostream> // for IO
14#include <exception> // for throwing exceptions
15#include "vector_class.hpp"
16
17// enter our namespace
18namespace smu_demo_namespace
19{
20
21 // Start by defining our constructor
22 // Since all these functions are inside the class
23 // we need to address them with ClassName::foo
24 // Notice we set a default value in the declaration in the
25 // header and omit it here
26 Vector::Vector(const std::size_t &num_elements)
27 {
28 // initialize our array to be all zeros
29 m_data.assign(num_elements, 0.0);
30 }
31
32 // define our adding operators
33 void Vector::add(const Vector &b)
34 {
35
36 // use our private helper function to
37 // make sure the sizes match
38 check_sizes(b);
39
40 for (std::size_t i = 0; i < numElements(); ++i)
41 {
42 // the .at() checks bounds unless you turn of
43 // debug flags
44 m_data.at(i) += b[i];
45 }
46 }
47
48 void Vector::operator+(const Vector &b)
49 {
50 add(b);
51 }
52
53 void Vector::operator=(const Vector &b)
54 {
55 check_sizes(b);
56 for (std::size_t i = 0; i < numElements(); ++i)
57 {
58 m_data.at(i) = b[i];
59 }
60 }
61
62 double Vector::dot(const Vector &b) const
63 {
64 check_sizes(b);
65 double dot_prod = 0;
66 for (std::size_t i = 0; i < numElements(); ++i)
67 {
68 // the .at() checks bounds unless you turn of
69 // debug flags
70 dot_prod += m_data.at(i) * b[i];
71 }
72
73 return dot_prod;
74 }
75
76 double Vector::norm() const
77 {
78 double norm = 0;
79 for (std::size_t i = 0; i < numElements(); ++i)
80 {
81 // the .at() checks bounds unless you turn of
82 // debug flags
83 norm += m_data.at(i) * m_data.at(i);
84 }
85
86 return std::sqrt(norm);
87 }
88
89 double &Vector::get(const std::size_t &index)
90 {
91 // the .at will check bounds to we don't need
92 // to do our own check
93 return m_data.at(index);
94 }
95
96 double Vector::get(const std::size_t &index) const
97 {
98 // the .at will check bounds to we don't need
99 // to do our own check
100 return m_data.at(index);
101 }
102
103 double &Vector::operator[](const std::size_t &index)
104 {
105 return m_data.at(index);
106 }
107 double Vector::operator[](const std::size_t &index) const
108 {
109 return m_data.at(index);
110 }
111
112 void Vector::setAllValues(const double &val)
113 {
114 m_data.assign(numElements(), val);
115 }
116
117 void Vector::normalize()
118 {
119 double norm = this->norm();
120 if (norm > 0)
121 {
122 for (std::size_t i = 0; i < numElements(); ++i)
123 {
124 // the .at() checks bounds unless you turn of
125 // debug flags
126 m_data.at(i) /= norm;
127 }
128 }
129 else
130 {
131 // TODO: maybe this should throw an error
132 throw std::logic_error("Norm is not positive");
133 }
134 }
135
136 void Vector::print() const
137 {
138 for (std::size_t i = 0; i < numElements() - 1; ++i)
139 {
140 std::cout << m_data.at(i) << ", ";
141 }
142 std::cout << m_data.back() << std::endl;
143 }
144
145 std::size_t Vector::numElements() const
146 {
147 return m_data.size();
148 }
149
150 void Vector::check_sizes(const Vector &b) const
151 {
152 if (numElements() != b.numElements())
153 {
154 throw std::range_error("The vectors are not the same length!");
155 }
156 }
157
158} // smu_demo_namespace
1/*
2 Simple demonstration of our vector class
3
4 This file runs the classes declared in
5 vector_class.hpp and implemented in vector_class.cpp
6
7 Useful References:
8 * Discovering Modern C++ by Peter Gottschling (amongst many other C++ books)
9 * https://en.cppreference.com/
10*/
11
12#include <iostream> // for IO
13#include <exception> // for throwing exceptions
14#include "vector_class.hpp"
15
16int main()
17{
18
19 // use our namespace
20 using namespace smu_demo_namespace;
21
22 // create 3 vectors
23 Vector a, b, c;
24
25 // set a to (1, 2, 3)
26 a[0] = 1;
27 a.get(1) = 2;
28 a[2] = 3;
29
30 std::cout << "a: " << std::endl;
31 a.print();
32
33 // set b to (4, 5, 6)
34 b.get(0) = 4;
35 b[1] = 5;
36 b.get(2) = 6;
37 std::cout << "b: " << std::endl;
38 b.print();
39
40 // print c
41 std::cout << "c: " << std::endl;
42 c.print();
43
44 // add b to a
45 a + b;
46 std::cout << "a: " << std::endl;
47 a.print();
48
49 c = a;
50 std::cout << "c: " << std::endl;
51 c.print();
52
53 for (int i = 0; i < c.numElements(); ++i)
54 {
55 c[i] = 1;
56 }
57 std::cout << "c: " << std::endl;
58 c.print();
59
60 std::cout << "c norm: " << c.norm() << std::endl;
61 std::cout << "normalized c:" << std::endl;
62 c.normalize();
63 c.print();
64
65 std::cout << "c dot c: " << c.dot(c) << std::endl;
66
67 Vector d(4);
68
69 // this should cause an error, but we can catch it
70 // by wrapping it in a try / catch block
71 try
72 {
73 c.dot(d);
74 }
75 catch (const std::exception &e)
76 {
77 // print out the error
78 // usually you stop the program after an error, but we'll keep going
79 std::cerr << e.what() << std::endl;
80 }
81
82 return 0;
83}
Class Inheritance#
1/*
2 Simple demonstration of classes and inheritance
3
4 Useful References:
5 * Discovering Modern C++ by Peter Gottschling (amongst many other C++ books)
6 * https://en.cppreference.com/
7*/
8
9// This is a header file, so we want to enure if only gets
10// included once in the code so we don't end up with multiple
11// redefinitions of the same functions in a project.
12// On way to do this is to define a uniquely named variable
13// using the prepocessor. Here, we name a variable "_SIMPLE_INHERITANCE_H_"
14// If that variable is already defined, then nothing happens.
15// Otherwise, we define it and the code defining the class is
16// used.
17#ifndef _SIMPLE_INHERITANCE_H_
18#define _SIMPLE_INHERITANCE_H_
19
20// Next we'll define a namespace. We don't technically have to
21// do this, but we might want to use other libraries that have
22// the same function or object names, so this will let us
23// use both
24namespace smu_demo_namespace
25{
26
27 // Now we'll declare our first class in our smu_demo_namespace
28 // A class is an object that can container variables, objects,
29 // and functions.
30 //
31 // This is a simple class to define some properties of 2D shapes
32 class ShapeProperties
33 {
34 // in a class functions, variables, and objects
35 // can be private, public, or protected
36 //
37 // private means only other members of this class
38 // can modify or call
39 //
40 // protected means that other classes that this is
41 // a sub class (also commonly called child) can modify
42 // or call
43 //
44 // public means anything can modify or call.
45
46 // we'll say that out shape has a few properties,
47 // namely perimeter and area. These will be protected
48 // because we're going to want anohter class to be
49 // able to change them.
50 protected:
51 // everything below this is protected until we get
52 // to another designation
53 double m_area;
54 double m_perimeter;
55
56 // we might want to be able to print this values out,
57 // so we'll make some functions. In order to call these
58 // functions outside of the class, we need to make them
59 // public
60 public:
61 // the const after the function tells the compiler
62 // that these functions will not modify any of the
63 // variables or objects in this class. They are
64 // only returning a copy of the values.
65 double getArea() const;
66 double getPerimeter() const;
67
68 }; // notice the the class ends with ;
69
70 // Now we have a shape class, but it's not really usable.
71 // What shape is it? A triangle, square, circle ...
72 // We can use the shape class above as a base to define
73 // specific shapes
74 class Triangle : public ShapeProperties
75 {
76 // a triangle has 3 sides so we'll want
77 // variables to hold those lengths
78 private:
79 double m_side1;
80 double m_side2;
81 double m_side3;
82
83 public:
84 // classes create objects using constructors
85 // The compiler will make a default constructor with no
86 // arguments (that doesn't set anything). So we'll make
87 // our own.
88 // First a default constructor with no arguments ... when
89 // we define this, we'll set all the sides to 1 by default
90 // Second, we'll make constructor that takes 3 sides lenghts
91 //
92 // The constructor is always named the same as the class
93 Triangle();
94 Triangle(const double &s1, const double &s2, const double &s3);
95
96 // we might want some helper functions to compute area and
97 // perimeter. These only need to be called by other members
98 // of the class, so we'll make them private
99 private:
100 void computeArea();
101 void computePerimeter();
102 };
103
104 class Circle : public ShapeProperties
105 {
106 // we just need a radius
107 // we'll make this const so it can't be changed
108 private:
109 const double m_radius;
110
111 public:
112 // classes create objects using constructors
113 // The compiler will make a default constructor with no
114 // arguments (that doesn't set anything). So we'll make
115 // our own.
116 // First a default constructor with no arguments ... when
117 // we define this, we'll set the radius to 1 by default
118 // Second, we'll make constructor that takes 1 radius arg
119 //
120 // The constructor is always named the same as the class
121 Circle();
122 Circle(const double &r);
123
124 // we might want some helper functions to compute area and
125 // perimeter. These only need to be called by other members
126 // of the class, so we'll make them private
127 private:
128 void computeArea();
129 void computePerimeter();
130 };
131
132 class Rectangle : public ShapeProperties
133 {
134 // We need the 2 side lengths
135 private:
136 double m_side1;
137 double m_side2;
138
139 public:
140 // classes create objects using constructors
141 // The compiler will make a default constructor with no
142 // arguments (that doesn't set anything). So we'll make
143 // our own.
144 // First a default constructor with no arguments ... when
145 // we define this, we'll set both sides to 1 by default
146 // Second, we'll make constructor that takes the 2 side
147 // lengths as arguments
148
149 // The constructor is always named the same as the class
150 Rectangle();
151 Rectangle(const double &s1, const double &s2);
152
153 // we might want some helper functions to compute area and
154 // perimeter. These only need to be called by other members
155 // of the class, so we'll make them private
156 private:
157 void computeArea();
158 void computePerimeter();
159 };
160
161 // A square is a special case of a rectangle
162 class Square : public Rectangle
163 {
164
165 public:
166 // classes create objects using constructors
167 // The compiler will make a default constructor with no
168 // arguments (that doesn't set anything). So we'll make
169 // our own.
170 // First a default constructor with no arguments ... when
171 // we define this, we'll set the sides to 1 by default
172 // Second, we'll make constructor that takes the a side
173 // length as arguments
174
175 // The constructor is always named the same as the class
176 Square();
177 Square(const double &s);
178 };
179
180} // namespace smu_demo_namespace
181
182// End the preprocessor header guard if / else
183// I like to add a comment after it refering to the variable it set
184#endif //_SIMPLE_INHERITANCE_H_
1/*
2 Simple demonstration of classes and inheritance
3
4 Useful References:
5 * Discovering Modern C++ by Peter Gottschling (amongst many other C++ books)
6 * https://en.cppreference.com/
7*/
8
9// This is a header file, so we want to enure if only gets
10// included once in the code so we don't end up with multiple
11// redefinitions of the same functions in a project.
12// On way to do this is to define a uniquely named variable
13// using the prepocessor. Here, we name a variable "_SIMPLE_INHERITANCE_H_"
14// If that variable is already defined, then nothing happens.
15// Otherwise, we define it and the code defining the class is
16// used.
17#ifndef _SIMPLE_INHERITANCE_H_
18#define _SIMPLE_INHERITANCE_H_
19
20// Next we'll define a namespace. We don't technically have to
21// do this, but we might want to use other libraries that have
22// the same function or object names, so this will let us
23// use both
24namespace smu_demo_namespace
25{
26
27 // Now we'll declare our first class in our smu_demo_namespace
28 // A class is an object that can container variables, objects,
29 // and functions.
30 //
31 // This is a simple class to define some properties of 2D shapes
32 class ShapeProperties
33 {
34 // in a class functions, variables, and objects
35 // can be private, public, or protected
36 //
37 // private means only other members of this class
38 // can modify or call
39 //
40 // protected means that other classes that this is
41 // a sub class (also commonly called child) can modify
42 // or call
43 //
44 // public means anything can modify or call.
45
46 // we'll say that out shape has a few properties,
47 // namely perimeter and area. These will be protected
48 // because we're going to want anohter class to be
49 // able to change them.
50 protected:
51 // everything below this is protected until we get
52 // to another designation
53 double m_area;
54 double m_perimeter;
55
56 // we might want to be able to print this values out,
57 // so we'll make some functions. In order to call these
58 // functions outside of the class, we need to make them
59 // public
60 public:
61 // the const after the function tells the compiler
62 // that these functions will not modify any of the
63 // variables or objects in this class. They are
64 // only returning a copy of the values.
65 double getArea() const;
66 double getPerimeter() const;
67
68 }; // notice the the class ends with ;
69
70 // Now we have a shape class, but it's not really usable.
71 // What shape is it? A triangle, square, circle ...
72 // We can use the shape class above as a base to define
73 // specific shapes
74 class Triangle : public ShapeProperties
75 {
76 // a triangle has 3 sides so we'll want
77 // variables to hold those lengths
78 private:
79 double m_side1;
80 double m_side2;
81 double m_side3;
82
83 public:
84 // classes create objects using constructors
85 // The compiler will make a default constructor with no
86 // arguments (that doesn't set anything). So we'll make
87 // our own.
88 // First a default constructor with no arguments ... when
89 // we define this, we'll set all the sides to 1 by default
90 // Second, we'll make constructor that takes 3 sides lenghts
91 //
92 // The constructor is always named the same as the class
93 Triangle();
94 Triangle(const double &s1, const double &s2, const double &s3);
95
96 // we might want some helper functions to compute area and
97 // perimeter. These only need to be called by other members
98 // of the class, so we'll make them private
99 private:
100 void computeArea();
101 void computePerimeter();
102 };
103
104 class Circle : public ShapeProperties
105 {
106 // we just need a radius
107 // we'll make this const so it can't be changed
108 private:
109 const double m_radius;
110
111 public:
112 // classes create objects using constructors
113 // The compiler will make a default constructor with no
114 // arguments (that doesn't set anything). So we'll make
115 // our own.
116 // First a default constructor with no arguments ... when
117 // we define this, we'll set the radius to 1 by default
118 // Second, we'll make constructor that takes 1 radius arg
119 //
120 // The constructor is always named the same as the class
121 Circle();
122 Circle(const double &r);
123
124 // we might want some helper functions to compute area and
125 // perimeter. These only need to be called by other members
126 // of the class, so we'll make them private
127 private:
128 void computeArea();
129 void computePerimeter();
130 };
131
132 class Rectangle : public ShapeProperties
133 {
134 // We need the 2 side lengths
135 private:
136 double m_side1;
137 double m_side2;
138
139 public:
140 // classes create objects using constructors
141 // The compiler will make a default constructor with no
142 // arguments (that doesn't set anything). So we'll make
143 // our own.
144 // First a default constructor with no arguments ... when
145 // we define this, we'll set both sides to 1 by default
146 // Second, we'll make constructor that takes the 2 side
147 // lengths as arguments
148
149 // The constructor is always named the same as the class
150 Rectangle();
151 Rectangle(const double &s1, const double &s2);
152
153 // we might want some helper functions to compute area and
154 // perimeter. These only need to be called by other members
155 // of the class, so we'll make them private
156 private:
157 void computeArea();
158 void computePerimeter();
159 };
160
161 // A square is a special case of a rectangle
162 class Square : public Rectangle
163 {
164
165 public:
166 // classes create objects using constructors
167 // The compiler will make a default constructor with no
168 // arguments (that doesn't set anything). So we'll make
169 // our own.
170 // First a default constructor with no arguments ... when
171 // we define this, we'll set the sides to 1 by default
172 // Second, we'll make constructor that takes the a side
173 // length as arguments
174
175 // The constructor is always named the same as the class
176 Square();
177 Square(const double &s);
178 };
179
180} // namespace smu_demo_namespace
181
182// End the preprocessor header guard if / else
183// I like to add a comment after it refering to the variable it set
184#endif //_SIMPLE_INHERITANCE_H_
1/*
2 Simple demonstration of classes and inheritance
3
4 This file runs the classes declared in
5 simple_inheritance.hpp and implemented in simple_inheritance.cpp
6
7 Useful References:
8 * Discovering Modern C++ by Peter Gottschling (amongst many other C++ books)
9 * https://en.cppreference.com/
10*/
11
12#include <iostream> // for IO (we could use the fmt library)
13
14// include our class declarations
15#include "simple_inheritance.hpp"
16
17int main()
18{
19
20 // use our namespace so we don't need to append
21 // everything with smu_demo_namespace::
22 using namespace smu_demo_namespace;
23
24 // Create a triangle:
25 Triangle default_triagle;
26
27 // print out area and perimenter
28 std::cout << "The default triangle has area: "
29 << default_triagle.getArea()
30 << " and perimeter: "
31 << default_triagle.getPerimeter()
32 << std::endl;
33
34 // Create an invalid triangle.
35 try
36 {
37 Triangle bad_triangle(-2, 1, 3);
38 std::cout << "The bad triangle has area: "
39 << default_triagle.getArea()
40 << " and perimeter: "
41 << default_triagle.getPerimeter()
42 << std::endl;
43 }
44 // const std::exception &e covers all types of polymorphic exceptions,
45 // but if we wanted to handle different types we could
46 // be more precise (here we know it is std::invalid_argument)
47 // we can also use (...) as a catchall for any thrown value
48 catch (const std::exception &e)
49 {
50 // print out the error
51 // usually you stop the program after an error, but we'll keep going
52 std::cerr << "bad triangle: " << e.what() << std::endl;
53 }
54
55 try
56 {
57 Triangle bad_triangle2(2, 1, 5);
58 std::cout << "The second bad triangle has area: "
59 << default_triagle.getArea()
60 << " and perimeter: "
61 << default_triagle.getPerimeter()
62 << std::endl;
63 }
64 // const std::exception &e covers all types of polymorphic exceptions,
65 // but if we wanted to handle different types we could
66 // be more precise (here we know it is std::invalid_argument)
67 // we can also use (...) as a catchall for any thrown value
68 catch (const std::exception &e)
69 {
70 // print out the error
71 // usually you stop the program after an error, but we'll keep going
72 std::cerr << "bad triangle 2: " << e.what() << std::endl;
73 }
74
75 // Create a circle:
76 Circle default_circle;
77
78 // print out area and perimenter
79 std::cout << "The default circle has area: "
80 << default_circle.getArea()
81 << " and perimeter: "
82 << default_circle.getPerimeter()
83 << std::endl;
84
85 Circle test_circle(2);
86
87 // print out area and perimenter
88 std::cout << "The test circle has area: "
89 << test_circle.getArea()
90 << " and perimeter: "
91 << test_circle.getPerimeter()
92 << std::endl;
93
94 // Create a rectangle:
95 Rectangle default_rec;
96
97 // print out area and perimenter
98 std::cout << "The default rectangle has area: "
99 << default_rec.getArea()
100 << " and perimeter: "
101 << default_rec.getPerimeter()
102 << std::endl;
103
104 Rectangle rect(2, 3);
105
106 // print out area and perimenter
107 std::cout << "The rectangle has area: "
108 << rect.getArea()
109 << " and perimeter: "
110 << rect.getPerimeter()
111 << std::endl;
112
113 Square default_square;
114
115 // print out area and perimenter
116 std::cout << "The default square has area: "
117 << default_square.getArea()
118 << " and perimeter: "
119 << default_square.getPerimeter()
120 << std::endl;
121
122 Square square(3);
123 std::cout << "The square has area: "
124 << square.getArea()
125 << " and perimeter: "
126 << square.getPerimeter()
127 << std::endl;
128
129 // You can use the base class as a datetype for
130 // one of its children
131 ShapeProperties shape;
132 shape = Square(7);
133 std::cout << "The shape has area: "
134 << shape.getArea()
135 << " and perimeter: "
136 << shape.getPerimeter()
137 << std::endl;
138
139 return 0;
140}
Eigen Demo#
1/*
2 Simple demonstration of the Eigen Library
3
4 Useful References:
5 * https://eigen.tuxfamily.org/dox/
6 * Discovering Modern C++ by Peter Gottschling (amongst many other C++ books)
7 * https://en.cppreference.com/
8*/
9
10#include <iostream> // IO
11#include <Eigen/Dense> // The Eigen dense linear algebra library
12#include <Eigen/Geometry> // for rotation matrices
13#include <cmath> // for trig functions
14
15int main()
16{
17
18 // Eigen is a templated linear algebra library. Templating allows
19 // the library to change the size and type of various objects at
20 // compile time and it also allows the compiler to do some clever
21 // optimizations. For instance, it can automatically expand
22 // expressiosn like
23 //
24 // d = a + b + c
25 //
26 // where the variables are all vectors (or matrices) to
27 //
28 // for (int i=0; i<n; ++i) {
29 // d[i] = a[i] + b[i] + c[i]
30 // }
31 //
32 // This makes many linear algebra operations much more efficient than
33 // traditional blas / lapack routines that may require multiple
34 // evaluations
35
36 // For linear algebra, Eigen is built around a Matrix class
37 // see https://eigen.tuxfamily.org/dox/classEigen_1_1Matrix.html
38 // This class has the form
39 //
40 // template<typename Scalar_, int Rows_, int Cols_, int Options_, int MaxRows_, int MaxCols_>
41 // class Eigen::Matrix<Scalar_, Rows_, Cols_, Options_, MaxRows_, MaxCols_>
42 //
43 // Scalar is the datatype, e.g. a doulbe
44 // Rows and Cols are the number of rows and cols, respectively
45 // options lets you set some things like row/col major, alignment, etc.
46 //
47 // Eigen provides some convenient type defs so you don't need to remember all that, e.g.
48 // Matrix2d is a 2x2 square matrix of doubles (Matrix<double, 2, 2>)
49 // Vector4f is a vector of 4 floats(Matrix<float, 4, 1>)
50 // RowVector3i is a row vector of 3 ints(Matrix<int, 1, 3>)
51 // MatrixXf is a dynamic size matrix of floats(Matrix<float, Dynamic, Dynamic>)
52 // VectorXf is a dynamic size vector of floats(Matrix<float, Dynamic, 1>)
53 // Matrix2Xf is a partially fixed size(dynamic - size) matrix of floats(Matrix<float, 2, Dynamic>)
54 // MatrixX3d is a partially dynamic size(fixed - size) matrix of double(Matrix<double, Dynamic, 3>)
55 // you can also make your own typedefs, e.g
56 // this is the same as Vector3d
57 // typedef Eigen::Matrix<double, 3, 1> Vector;
58
59 // Define Vectors a,b,c of length 3
60 Eigen::Vector3d a, b, c;
61
62 // set a to (1,2,3)
63 a << 1, 2, 3;
64
65 // set b to (4, 5, 6)
66 b(0) = 4;
67 b(1) = 5;
68 b(2) = 6;
69
70 // set c = a + b
71 c = a + b;
72
73 // print out a (it's a column vector, so transpose it so it's on 1 line)
74 std::cout << "a: " << a.transpose() << std::endl;
75 std::cout << "b: " << b.transpose() << std::endl;
76 std::cout << "c: " << c.transpose() << std::endl;
77
78 // set c to ones
79 c.setOnes();
80 std::cout << "c: " << c.transpose() << std::endl;
81 c.normalize();
82 std::cout << "c: " << c.transpose() << std::endl;
83 c = Eigen::Vector3d::Random(3);
84 std::cout << "c: " << c.transpose() << std::endl;
85 c.normalize();
86 std::cout << "c: " << c.transpose() << std::endl;
87
88 // create a 3x3 matrix
89 Eigen::Matrix3d A, B, C;
90 double theta = 1.23423; // random angle in radians
91 double omega = 3.854;
92 double gamma = -2.863237;
93
94 // make A & B be a rotation matrix
95 // rotates in xy-plane
96 A << std::cos(theta), -std::sin(theta), 0,
97 std::sin(theta), std::cos(theta), 0,
98 0, 0, 1;
99
100 // rotates in xy-plane
101 B.setIdentity();
102 B(0, 0) = std::cos(omega);
103 B(0, 2) = -std::sin(omega);
104 B(2, 0) = std::sin(omega);
105 B(2, 2) = std::cos(omega);
106
107 // rotates around the vector c
108 C = Eigen::AngleAxisd(gamma, c);
109
110 // create a new rotation matrix that is the combination
111 // of A, B, and C
112 auto R = A * B * C;
113
114 std::cout << "R:" << std::endl
115 << R << std::endl;
116
117 // rotate some vectors
118 std::cout << "a: " << a.transpose() << " (norm = " << a.norm() << ")" << std::endl;
119 std::cout << "b: " << b.transpose() << " (norm = " << b.norm() << ")" << std::endl;
120 std::cout << "c: " << c.transpose() << " (norm = " << c.norm() << ")" << std::endl;
121
122 a = R * a;
123 b = R * b;
124 c = R * c;
125
126 std::cout << "after rotating:" << std::endl;
127 std::cout << "\ta: " << a.transpose() << " (norm = " << a.norm() << ")" << std::endl;
128 std::cout << "\tb: " << b.transpose() << " (norm = " << b.norm() << ")" << std::endl;
129 std::cout << "\tc: " << c.transpose() << " (norm = " << c.norm() << ")" << std::endl;
130
131 // the inverse of rotation matrix is it's transpose ...
132 a = R.transpose() * a;
133 b = R.transpose() * b;
134 c = R.transpose() * c;
135
136 std::cout << "after rotating back:" << std::endl;
137 std::cout << "\ta: " << a.transpose() << " (norm = " << a.norm() << ")" << std::endl;
138 std::cout << "\tb: " << b.transpose() << " (norm = " << b.norm() << ")" << std::endl;
139 std::cout << "\tc: " << c.transpose() << " (norm = " << c.norm() << ")" << std::endl;
140
141 // We might want dynamic sizes, so we can do (X is dynamic, where we had 3 above)
142 const int n = 1987;
143 Eigen::MatrixXd M = Eigen::MatrixXd::Random(n, n);
144 Eigen::VectorXd x = Eigen::VectorXd::Random(n);
145 Eigen::VectorXd f(n);
146
147 // set f = Mx
148 f = M * x;
149
150 // Eigen has solvers built in
151 // see https://eigen.tuxfamily.org/dox/group__TutorialLinearAlgebra.html
152 // We'll use the CompleteOrthogonalDecomposition method here. It's
153 // very accurate and reasonably fast for small to medium matrices
154 Eigen::VectorXd y = M.completeOrthogonalDecomposition().solve(f);
155
156 // if everything worked, we expect y = x (up to floating point rounding
157 // errors anyway). So lets check that:
158 double abs_error = (y - x).norm();
159 double rel_error = abs_error / x.norm();
160
161 std::cout << "Solve gave absolute error: " << abs_error
162 << " and relative error: " << rel_error << std::endl;
163
164 return 0;
165}
Armadillo Demo#
1#include <armadillo>
2#include <fmt/core.h>
3#include <iostream>
4
5int main(int argc, char** argv) {
6 std::cout << "Armadillo version: " << arma::arma_version::as_string() << std::endl;
7
8 // construct a matrix according to given size and form of element initialisation
9 arma::mat A(2,3, arma::fill::zeros);
10
11 // .n_rows and .n_cols are read only
12 fmt::print("A.n_rows: {}\nA.n_cols: {}\n", A.n_rows, A.n_cols);
13
14 A(1,2) = 456.0; // access an element (indexing starts at 0)
15 A.print("A:");
16
17 A = 5.0; // scalars are treated as a 1x1 matrix
18 A.print("A:");
19
20 A.set_size(4,5); // change the size (data is not preserved)
21
22 A.fill(5.0); // set all elements to a specific value
23 A.print("A:");
24
25 A = { { 0.165300, 0.454037, 0.995795, 0.124098, 0.047084 },
26 { 0.688782, 0.036549, 0.552848, 0.937664, 0.866401 },
27 { 0.348740, 0.479388, 0.506228, 0.145673, 0.491547 },
28 { 0.148678, 0.682258, 0.571154, 0.874724, 0.444632 },
29 { 0.245726, 0.595218, 0.409327, 0.367827, 0.385736 } };
30
31 A.print("A:");
32
33 // determinant
34 fmt::print("det(A): {}\n", det(A));
35
36 // inverse
37 inv(A).print("inv(A):");
38
39 // save matrix as a text file
40 A.save("A.txt", arma::raw_ascii);
41
42 // load from file
43 arma::mat B;
44 B.load("A.txt");
45
46 // submatrices
47 B(arma::span(0,2), arma::span(3,4)).print("B( span(0,2), span(3,4) ):");
48 B(0, 3, arma::size(3,2)).print("B( 0,3, size(3,2) ):");
49
50 B.row(0).print("B.row(0):");
51 B.col(1).print("B.col(1):");
52
53 // transpose
54 std::cout << "B.t(): " << std::endl << B.t() << std::endl;
55
56 // maximum from each column (traverse along rows)
57 std::cout << "max(B): " << std::endl << max(B) << std::endl;
58
59 // maximum from each row (traverse along columns)
60 std::cout << "max(B,1): " << std::endl << max(B,1) << std::endl;
61
62 // maximum value in B
63 std::cout << "max(max(B)) = " << max(max(B)) << std::endl;
64
65 // sum of each column (traverse along rows)
66 std::cout << "sum(B): " << std::endl << sum(B) << std::endl;
67
68 // sum of each row (traverse along columns)
69 std::cout << "sum(B,1) =" << std::endl << sum(B,1) << std::endl;
70
71 // sum of all elements
72 std::cout << "accu(B): " << arma::accu(B) << std::endl;
73
74 // trace = sum along diagonal
75 std::cout << "trace(B): " << arma::trace(B) << std::endl;
76
77 // generate the identity matrix
78 arma::mat C = arma::eye<arma::mat>(4,4);
79
80 // random matrix with values uniformly distributed in the [0,1] interval
81 arma::mat D = arma::randu<arma::mat>(4,4);
82 D.print("D:");
83
84 // row vectors are treated like a matrix with one row
85 arma::rowvec r = { 0.59119, 0.77321, 0.60275, 0.35887, 0.51683 };
86 r.print("r:");
87
88 // column vectors are treated like a matrix with one column
89 arma::vec q = { 0.14333, 0.59478, 0.14481, 0.58558, 0.60809 };
90 q.print("q:");
91
92 // convert matrix to vector; data in matrices is stored column-by-column
93 arma::vec v = arma::vectorise(A);
94 v.print("v:");
95
96 // dot or inner product
97 std::cout << "as_scalar(r*q): " << arma::as_scalar(r*q) << std::endl;
98
99 // outer product
100 std::cout << "q*r: " << std::endl << q*r << std::endl;
101
102 // multiply-and-accumulate operation (no temporary matrices are created)
103 std::cout << "accu(A % B) = " << arma::accu(A % B) << std::endl;
104
105 // example of a compound operation
106 B += 2.0 * A.t();
107 B.print("B:");
108
109 // imat specifies an integer matrix
110 arma::imat AA = { { 1, 2, 3 },
111 { 4, 5, 6 },
112 { 7, 8, 9 } };
113
114 arma::imat BB = { { 3, 2, 1 },
115 { 6, 5, 4 },
116 { 9, 8, 7 } };
117
118 // comparison of matrices (element-wise); output of a relational operator is a umat
119 arma::umat ZZ = (AA >= BB);
120 ZZ.print("ZZ:");
121
122 // cubes ("3D matrices")
123 arma::cube Q( B.n_rows, B.n_cols, 2 );
124
125 Q.slice(0) = B;
126 Q.slice(1) = 2.0 * B;
127
128 Q.print("Q:");
129
130 // 2D field of matrices; 3D fields are also supported
131 arma::field<arma::mat> F(4,3);
132
133 for(arma::uword col=0; col < F.n_cols; ++col) {
134 for(arma::uword row=0; row < F.n_rows; ++row) {
135 F(row,col) = arma::randu<arma::mat>(2,3); // each element in field<mat> is a matrix
136 }
137 }
138
139 F.print("F:");
140
141 return 0;
142}