Skip to content

Commit

Permalink
Streaming library compatibility (#79)
Browse files Browse the repository at this point in the history
* Added Printable-interface for test-environment

* Removed stream operators and replaced with Printable-interface

* Adapt prints in examples

---------

Co-authored-by: Nils Mueller <[email protected]>
  • Loading branch information
insalt-glitch and Nils Mueller authored Jun 1, 2024
1 parent a11530c commit 0080e10
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 62 deletions.
25 changes: 24 additions & 1 deletion BasicLinearAlgebra.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@
#include <stdlib.h>
#include <string.h>

#include "Arduino.h"
#include "Printable.h"

#include "ElementStorage.h"

namespace BLA
{

template <typename DerivedType, int rows, int cols, typename d_type>
struct MatrixBase
struct MatrixBase : public Printable
{
public:
constexpr static int Rows = rows;
Expand Down Expand Up @@ -147,6 +150,26 @@ struct MatrixBase

return ret;
}

size_t printTo(Print& p) const final
{
size_t n;
n = p.print('[');

for (int i = 0; i < Rows; i++)
{
n += p.print('[');

for (int j = 0; j < Cols; j++)
{
n += p.print(static_cast<const DerivedType *>(this)->operator()(i, j));
n += p.print((j == Cols - 1) ? ']' : ',');
}

n += p.print((i == Rows - 1) ? ']' : ',');
}
return n;
}
};

template <typename DerivedType>
Expand Down
6 changes: 4 additions & 2 deletions examples/CustomMatrix/CustomMatrix.ino
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ void setup()
diag.diagonal.Fill(1);

// So multiplying it with mat will do nothing:
Serial << "still ones: " << diag * mat << "\n";
Serial.print("still ones: ");
Serial.println(diag * mat);

// Diagonal matrices have the handy property of scaling either the rows (premultiplication) or columns
// (postmultiplication) of a matrix
Expand All @@ -68,7 +69,8 @@ void setup()
for (int i = 0; i < diag.Rows; i++) diag.diagonal(i) = i + 1;

// And multiply again, we'll see that the rows have been scaled
Serial << "scaled rows: " << diag * mat;
Serial.print("scaled rows: ");
Serial.print(diag * mat);

// Point being, if you define a class which serves up something when called upon by the () operator, you can embed
// it in a matrix and define any kind of behaviour you like. Hopefully that'll let this library support lots more
Expand Down
14 changes: 8 additions & 6 deletions examples/HowToUse/HowToUse.ino
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,17 @@ void setup()
// For convenience, there's also Inverse, which will return the inverse, leaving the input matrix unmodified.
BLA::Matrix<3, 3> also_C_inv = Inverse(C);

// If you want to print out the value of any element in the array you can do that like so:
Serial << "v(1): " << v(1) << '\n';
// If you want to print out the value of any element in the array you can do that with the Serial object:
Serial.print("v(1): ");
Serial.println(v(1));

// Alternatively, you can write the whole matrix to Serial using a C++ style inserter, like so:
Serial << "B: " << B << '\n';
// Alternatively, you can write the whole matrix to Serial, which works like so:
Serial.print("B: ");
Serial.println(B);

// You can even write some quite complex compound statements very succinctly. For example:
// Serial << "identity matrix: " << C << C_inv << '\n';
Serial << "identity matrix: " << AleftOfB * AonTopOfB - (A * A + B * B) + C * C_inv;
Serial.print("identity matrix: ");
Serial.print(AleftOfB * AonTopOfB - (A * A + B * B) + C * C_inv);

// Or as a more real life example, here's how you might calculate an updated state estimate for a third order state
// space model:
Expand Down
12 changes: 8 additions & 4 deletions examples/References/References.ino
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ void setup()
bigMatrixRef(0, 0) = 45.67434;

// And we can see that the original matrix has been set accordingly
Serial << "bigMatrix(4,2): " << bigMatrix(4, 2) << "\n";
Serial.print("bigMatrix(4,2): ");
Serial.println(bigMatrix(4, 2));

// The submatrix function actually returns a RefMatrix so if you like you can just use it directly. For example you
// can set a section of bigMatrix using an array like so:
Expand All @@ -64,17 +65,20 @@ void setup()
Invert(bigMatrixRef);

// Print them
Serial << "bigMatrixRef: " << bigMatrixRef << "\n";
Serial.print("bigMatrixRef: ");
Serial.println(bigMatrixRef);

// You can even make a reference to a reference matrix, do arithmetic with that and then print the result
Serial << "result of convoluted operation: " << (anotherRef += bigMatrixRef.Submatrix<2, 4>(0, 0)) << "\n";
Serial.print("result of convoluted operation: ");
Serial.println(anotherRef += bigMatrixRef.Submatrix<2, 4>(0, 0));

// The only thing that you can't (shouldn't) really do is operate on two matrix references whose underlying memory
// overlap, particularly when doing matrix multiplication.

// Lastly, let's look at what became of bigMatrix after all of this, you might be able to make out the values of
// bigMatrixRef and anotherRef in their respective areas of bigMatrix.
Serial << "bigMatrix: " << bigMatrix;
Serial.print("bigMatrix: ");
Serial.print(bigMatrix);
}

void loop() {}
9 changes: 6 additions & 3 deletions examples/SolveLinearEquations/SolveLinearEquations.ino
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@ void setup()
decomp.U; // Will have zeros below its diagonal

// And if we multiply them all together we'll recover the original A matrix:
Serial << "reconstructed A: " << decomp.P * decomp.L * decomp.U << "\n";
Serial.print("reconstructed A: ");
Serial.println(decomp.P * decomp.L * decomp.U);

// Once we've done the decomposition we can solve for x very efficiently:
Matrix<6> x_lusolve = LUSolve(decomp, b);

Serial << "x (via LU decomposition): " << x_lusolve << "\n";
Serial.print("x (via LU decomposition): ");
Serial.println(x_lusolve);

// We can also recompute x for a new b vector without having to repeat the decomposition:
Matrix<6> another_b = {23, 19, 86, 3, 23, 90};
Expand All @@ -49,7 +51,8 @@ void setup()
Invert(A_inv);
Matrix<6> x_Ainvb = A_inv * b;

Serial << "x (via inverse A): " << x_Ainvb;
Serial.print("x (via inverse A): ");
Serial.print(x_Ainvb);

// Fun fact though, we actually calculate A_inv by running LUDecompose then calling LUSolve for each column in A.
// This is actually no less efficient than other methods for calculating the inverse.
Expand Down
3 changes: 2 additions & 1 deletion examples/Tensor/Tensor.ino
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ void setup()
BLA::Matrix<2, 2, BLA::Matrix<2, 2>> hyperB = (hyperA * hyperA);

// Everything can be printed too
Serial << "Hyper B: " << hyperB;
Serial.print("Hyper B: ");
Serial.print(hyperB);

// Inversion doesn't work. If it did, it'd probably take quite a while for arduino to calculate anyway so maybe it's
// for the best
Expand Down
43 changes: 0 additions & 43 deletions impl/BasicLinearAlgebra.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
#include <stdlib.h>
#include <string.h>

#include "Arduino.h"

namespace BLA
{
template <typename MatAType, typename MatBType, int MatARows, int MatACols, int MatBCols, typename DType>
Expand Down Expand Up @@ -447,45 +445,4 @@ bool All(const MatrixBase<DerivedType, DerivedType::Rows, DerivedType::Cols, boo
return true;
}

inline Print &operator<<(Print &strm, const int obj)
{
strm.print(obj);
return strm;
}

inline Print &operator<<(Print &strm, const float obj)
{
strm.print(obj);
return strm;
}

inline Print &operator<<(Print &strm, const char *obj)
{
strm.print(obj);
return strm;
}

inline Print &operator<<(Print &strm, const char obj)
{
strm.print(obj);
return strm;
}

// Stream inserter operator for printing to strings or the serial port
template <typename DerivedType, int Rows, int Cols, typename DType>
Print &operator<<(Print &strm, const MatrixBase<DerivedType, Rows, Cols, DType> &mat)
{
strm << '[';

for (int i = 0; i < Rows; i++)
{
strm << '[';

for (int j = 0; j < Cols; j++) strm << mat(i, j) << ((j == Cols - 1) ? ']' : ',');

strm << (i == Rows - 1 ? ']' : ',');
}
return strm;
}

} // namespace BLA
24 changes: 22 additions & 2 deletions test/Arduino.h
Original file line number Diff line number Diff line change
@@ -1,23 +1,43 @@
#pragma once

#include <algorithm>
#include <cstddef>
#include <iomanip>
#include <sstream>

#include "Printable.h"

struct Print
{
std::stringstream buf;

template <typename T>
void print(const T& obj)
typename std::enable_if<!std::is_base_of<Printable, T>::value, size_t>::type
print(const T& obj)
{
buf << obj;
return 0;
}

template <typename T>
void println(const T& obj)
typename std::enable_if<!std::is_base_of<Printable, T>::value, size_t>::type
println(const T& obj)
{
buf << obj << std::endl;
return 0;
}

size_t print(const Printable& x)
{
x.printTo(*this);
return 0;
}

size_t println(const Printable& x)
{
x.printTo(*this);
println("");
return 0;
}

void begin(int)
Expand Down
11 changes: 11 additions & 0 deletions test/Printable.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#pragma once

#include <cstddef>

class Print;

class Printable
{
public:
virtual size_t printTo(Print& p) const = 0;
};

0 comments on commit 0080e10

Please sign in to comment.