It’s been almost 50 years since I took a Linear Algebra and Matrices course as one of my Computer Science electives at Cal Poly San Luis Obispo, CA. The textbook for the course was “Mathematics Of Matrices: First Book Of Matrix Theory And Linear Algebra” by Philip J Davis. While I have used arrays and vectors in many programming projects, I have to be honest and say that I have not used a lot of matrix math in my code.
Now that the Eigen C++ Template Library is available for download inside the C++Builder IDE using the GetIt Package Manager, I decided to re-introduce myself to matrix math, manipulations and linear algebra (not bad for a 69 year old software engineer). This blog post will show C++ developers how to download, install and use the Eigen C++ library using C++Builder and VCL.
Installing Eigen via the GetIt Package Manager
Inside the C++Builder IDE, use the Tools | GetIt Project Manager menu item to bring up the list of available add-on components, libraries and demos. Type “Eigen” in the search box to see what is available.
Click the Install button to download, install, build and run tests the Eigen C++ library with your release of C++Builder (I used C++Builder version 10.4.1). As part of the Eigen installation you will see two additional windows.
Once the download and installation is completed, you can start using Eigen in your applications. To learn more about the Eigen C++ Template Library I’ve included some reference links at the end of this post. To dust off my matrix and linear algebra cobwebs and learn a little bit about Eigen, I created three sample C++Builder VCL applications using Eigen.
Simple Eigen Test C++ VCL Application
This first example Button onClick event handler displays the Eigen version number, creates a 3×3 matrix, populates it with random floating point numbers (between -1 and 1) and uses the addition, subtraction and multiplication operators.
The Source Code
MainUnit.h
//--------------------------------------------------------------------------- #ifndef MainUnitH #define MainUnitH //--------------------------------------------------------------------------- #include <System.Classes.hpp> #include <Vcl.Controls.hpp> #include <Vcl.StdCtrls.hpp> #include <Vcl.Forms.hpp> //--------------------------------------------------------------------------- class TForm1 : public TForm { __published: // IDE-managed Components TButton *Button1; TMemo *MatrixOutputMemo; TMemo *DemoCodeMemo; TLabel *Label1; TLabel *Label2; TLabel *EigenVersionLabel; void __fastcall Button1Click(TObject *Sender); private: // User declarations public: // User declarations __fastcall TForm1(TComponent* Owner); }; //--------------------------------------------------------------------------- extern PACKAGE TForm1 *Form1; //--------------------------------------------------------------------------- #endif
MainUnit.cpp
//--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include <Eigen/Dense> using Eigen::MatrixXd; using Eigen::IOFormat; #include "MainUnit.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; // create string stream from Eigen Matrix static std::string ConvertToString(const Eigen::MatrixXd& mat){ std::stringstream ss; IOFormat CleanFmt(Eigen::FullPrecision, 0, ", ", "\n", "[", "]"); ss << mat.format(CleanFmt); return ss.str(); } //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { // first sample app adapted from Eigen documentation and put in VCL app // display Eigen version number EigenVersionLabel->Caption = "Eigen version: " + IntToStr(EIGEN_WORLD_VERSION) + "." + IntToStr(EIGEN_MAJOR_VERSION) + "." + IntToStr(EIGEN_MINOR_VERSION); // define a 3x3 matrix of doubles MatrixXd m(3,3); // Fill matrix with random numbers between -1 and +1 m.setRandom(3,3); // Change three matrix items using + - and * m(0,2) = m(0,0) + m(0,1); m(1,2) = m(1,0) - m(1,1); m(2,2) = m(2,0) * m(2,1); // output matrix using strings MatrixOutputMemo->Lines->Clear(); MatrixOutputMemo->Lines->Text = ConvertToString(m).c_str(); } //---------------------------------------------------------------------------
Matrix Operations C++ VCL Application
The second Eigen C++ VCL application exercises several Eigen operations including adding and subtracting two matricies, multiplying and dividing a matrix by a scalar, multiplying two matricies, transposing a matrix, invert a matrix and sum all elements of a matrix. There are many additional features to explore in Eigen for vector and matrix operations.
In the UI for the application you’ll see a RadioGroup for selecting one of the operations, three StringGrids to display matrix contents, an EditBox for inputing a scalar (I used Pi), and a Label for the summing result.
The Source Code
MainUnit.h
//--------------------------------------------------------------------------- #ifndef MainUnitH #define MainUnitH //--------------------------------------------------------------------------- #include <System.Classes.hpp> #include <Vcl.Controls.hpp> #include <Vcl.StdCtrls.hpp> #include <Vcl.Forms.hpp> #include <Vcl.Grids.hpp> #include <Vcl.ExtCtrls.hpp> #include <Vcl.Samples.Spin.hpp> //--------------------------------------------------------------------------- class TForm1 : public TForm { __published: // IDE-managed Components TStringGrid *StringGridX; TStringGrid *StringGridY; TStringGrid *StringGridZ; TLabel *Label1; TLabel *Label2; TLabel *Label3; TRadioGroup *RadioGroup1; TButton *Button1; TLabel *Label4; TEdit *ScalarEdit; TLabel *ResultLabel; void __fastcall FormShow(TObject *Sender); void __fastcall Button1Click(TObject *Sender); private: // User declarations void __fastcall displayMatricies(); public: // User declarations __fastcall TForm1(TComponent* Owner); }; //--------------------------------------------------------------------------- extern PACKAGE TForm1 *Form1; //--------------------------------------------------------------------------- #endif
MainUnit.cpp
//--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include <Eigen/Dense> using Eigen::MatrixXd; #include <System.SysUtils.hpp> #include "MainUnit.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; MatrixXd x(3,3); MatrixXd y(3,3); MatrixXd z(3,3); void TForm1::displayMatricies() { // display contents of x, y and z matricies int numberRows = x.rows(); int numberColumns = y.cols(); for (int r=0 ; r < numberRows ; r++) { for (int c = 0; c < numberColumns; c++) { StringGridX->Cells[r][c] = FloatToStrF(x(r,c),ffFixed,5,3); StringGridY->Cells[r][c] = FloatToStrF(y(r,c),ffFixed,5,3); StringGridZ->Cells[r][c] = FloatToStrF(z(r,c),ffFixed,5,3); } } } //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- void __fastcall TForm1::FormShow(TObject *Sender) { // populate x and y matricies and their string grids with random numbers x.setRandom(5,5); y.setRandom(5,5); z.setZero(5,5); // clear selections in the string grids TGridRect gr; gr.Left = StringGridX->ColCount; gr.Top = StringGridX->RowCount; StringGridX->Canvas->Brush->Color = clWindow; StringGridX->Canvas->FillRect(Rect(StringGridX->Left,StringGridX->Width,StringGridX->Top,StringGridX->Height)); gr.Left = StringGridY->ColCount; gr.Top = StringGridY->RowCount; StringGridY->Canvas->Brush->Color = clWindow; StringGridY->Canvas->FillRect(Rect(StringGridY->Left,StringGridY->Width,StringGridY->Top,StringGridY->Height)); gr.Left = StringGridZ->ColCount; gr.Top = StringGridZ->RowCount; StringGridZ->Canvas->Brush->Color = clWindow; StringGridZ->Canvas->FillRect(Rect(StringGridZ->Left,StringGridZ->Width,StringGridZ->Top,StringGridZ->Height)); // display current values of matricies displayMatricies(); } //--------------------------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { // do the operation that is currently selected in the Radio Button Group // 0 = addition, 1= subtraction, 2 = multiplication, // 3 = division, 4 = dot product switch(RadioGroup1->ItemIndex) { case 0: // addition z = x + y; break; case 1: // subtraction z = x - y; break; case 2: // multiply x by scalar and save result in z z = x * StrToFloat(ScalarEdit->Text); break; case 3: // divide x by scalar and save result in z z = x / StrToFloat(ScalarEdit->Text); break; case 4: // multiply x * y and save result in z z.noalias() = x * y; break; case 5: // transpose x and save result in z z = x.transpose(); break; case 6: // invert x and save result in z z = x.inverse(); break; case 7: // sum all elements of x and save in result ResultLabel->Caption = "Result: "+FloatToStr(x.sum()); break; } // display current values of matricies displayMatricies(); } //---------------------------------------------------------------------------
Shopping Linear Algebra C++ VCL Application
I wanted to use a “real world” linear algebra example to showcase a few additional features of the Eigen C++ template library. There are many matrix and linear algebra courses and examples out on the Internet. After a quick search I decided to use “Real-world Applications of Linear Algebra Tools” by E. Ulrychova, postgradual student at the University of Economics, Department of Mathematics, Prague, Czech Republic.
The scenario has three people who want to each shop for baked goods at only one of two stores. Each person has a quantity of rolls, buns, cakes and breads that they want to purchase. Each store has a list of prices that they charge for each of the baked goods. This linear algebra example calculates a spending analysis for each person at each store to show where each person should shop.
The user interface includes a button to run code that calculates the lowest shopping cost for each person, a StringGrid for the baked goods demand matrix for each person, a StringGrid for the store pricing for each baked good, and a StringGrid for the results of the optimal spending analysis for each person.
The Source Code
ShoppingUnit.h
//--------------------------------------------------------------------------- #ifndef ShoppingUnitH #define ShoppingUnitH //--------------------------------------------------------------------------- #include <System.Classes.hpp> #include <Vcl.Controls.hpp> #include <Vcl.StdCtrls.hpp> #include <Vcl.Forms.hpp> #include <Vcl.Grids.hpp> //--------------------------------------------------------------------------- class TForm1 : public TForm { __published: // IDE-managed Components TButton *Button1; TStringGrid *DemandMatrixStringGrid; TLabel *Label1; TStringGrid *PriceMatrixStringGrid; TLabel *Label2; TStringGrid *ResultStringGrid; TLabel *Label3; void __fastcall Button1Click(TObject *Sender); private: // User declarations public: // User declarations __fastcall TForm1(TComponent* Owner); }; //--------------------------------------------------------------------------- extern PACKAGE TForm1 *Form1; //--------------------------------------------------------------------------- #endif
ShoppingUnit.cpp
//--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include <Eigen/Dense> using Eigen::MatrixXd; using Eigen::VectorXd; using Eigen::IOFormat; #include "ShoppingUnit.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { // Calculate results of spending analysis by person by store // R = PQ // from: // Real-world Applications of Linear Algebra Tools // by E. Ulrychova, postgradual student // University of Economics, Department of Mathematics, Prague, Czech Republic // https://www.mff.cuni.cz/veda/konference/wds/proc/pdf06/WDS06_106_m8_Ulrychova.pdf int r,c; // row and column indexes used throughout this event handler // matricies used for spending analysis MatrixXd DemandMatrix(4,3); // Q MatrixXd PriceMatrix(2,4); // P MatrixXd ResultMatrix(2,3); // R // vectors used for dot product analysis of spending by person by shop VectorXd DemandVector(4); VectorXd PriceVector(4); // Demanded quantity of foodstuffs per person (3 people, 4 foodstuffs) DemandMatrix << // Q 6,3,3, // foodstuff quantities for rolls for each of the 3 people 5,6,4, // foodstuff quantities for buns for each of the 3 people 3,2,3, // foodstuff quantities for cakes for each of the 3 people 1,2,1; // foodstuff quantities for breads for each of the 3 people // Prices for each foodstuff in each shop (4 foodstuffs, 2 shops) PriceMatrix << //P 1.50,2.00,5.00,16.00, // prices for foodstuffs at Shop 1 1.00,2.50,4.50,17.00; // prices for foodstuffs at Shop 2 // Calculate results of spending analysis by person by store // R = PQ int ResultMatrixNumberRows = ResultMatrix.rows(); int ResultMatrixNumberColumns = ResultMatrix.cols(); for (r=0 ; r < ResultMatrixNumberRows ; r++) { for (c = 0; c < ResultMatrixNumberColumns; c++) { // create price and demand vectors DemandVector = DemandMatrix.col(c); PriceVector = PriceMatrix.row(r); // use dot product of demand and price to get a result ResultMatrix(r,c) = DemandVector.dot(PriceVector); } } // set heading and column strings for Demand Matrix rows and columns DemandMatrixStringGrid->Cells[1][0] = "roll"; DemandMatrixStringGrid->Cells[2][0] = "bun"; DemandMatrixStringGrid->Cells[3][0] = "cake"; DemandMatrixStringGrid->Cells[4][0] = "bread"; DemandMatrixStringGrid->Cells[0][1] = "p1"; DemandMatrixStringGrid->Cells[0][2] = "p2"; DemandMatrixStringGrid->Cells[0][3] = "p3"; // set heading and column strings for Price Matrix rows and columns PriceMatrixStringGrid->Cells[1][0] = "s1"; PriceMatrixStringGrid->Cells[2][0] = "s1"; PriceMatrixStringGrid->Cells[0][1] = "roll"; PriceMatrixStringGrid->Cells[0][2] = "bun"; PriceMatrixStringGrid->Cells[0][3] = "cake"; PriceMatrixStringGrid->Cells[0][4] = "bread"; // set heading and column strings for Result Matrix String Grid rows and columns ResultStringGrid->Cells[1][0] = "s1"; ResultStringGrid->Cells[2][0] = "s1"; ResultStringGrid->Cells[0][1] = "p1"; ResultStringGrid->Cells[0][2] = "p2"; ResultStringGrid->Cells[0][3] = "p3"; int DemandMatrixNumberRows = DemandMatrix.rows(); int DemandMatrixNumberColumns = DemandMatrix.cols(); // populate string grid with the Demand Data for (r=1 ; r <= DemandMatrixNumberRows ; r++) { for (c = 1; c <= DemandMatrixNumberColumns; c++) { DemandMatrixStringGrid->Cells[r][c] = FloatToStrF(DemandMatrix(r-1,c-1),ffFixed,5,3); } } int PriceMatrixNumberRows = PriceMatrix.rows(); int PriceMatrixNumberColumns = PriceMatrix.cols(); // populate string grid with the Price Data for (r=1 ; r <= PriceMatrixNumberRows ; r++) { for (c = 1; c <= PriceMatrixNumberColumns; c++) { PriceMatrixStringGrid->Cells[r][c] = FloatToStrF(PriceMatrix(r-1,c-1),ffFixed,5,3); } } // With the supplied data this program shows it is optimal // for person p1 to buy in the shop s2, // for person p2 to buy in shop s1 // and person p3 will pay the same price in shop s1 and s2 int ResultNumberRows = ResultMatrix.rows(); int ResultNumberColumns = ResultMatrix.cols(); // populate string grid with the results for (r=1 ; r <= ResultNumberRows ; r++) { for (c = 1; c <= ResultNumberColumns; c++) { ResultStringGrid->Cells[r][c] = FloatToStrF(ResultMatrix(r-1,c-1),ffFixed,5,3); } } } //---------------------------------------------------------------------------
References
C++Builder DocWiki information
Eigen C++ Template Library information
Eigen C++ Template Library main page
Eigen Library Documentation (version 3.3.7 as of this posting)
Eigen latest development version documentation (ver 3.3.90 as of this posting)
Eigen IOFormat Class Reference
Eigen and Linear Algebra Tutorials and Information
Real-world Applications of Linear Algebra Tools by E. Ulrychova, postgradual student University of Economics, Department of Mathematics, Prague, Czech Republic (PDF)
Eigen Tutorial – CS2240 Interactive Computer Graphics (PDF) by Daniel Ritchie, Assistant Professor of Computer Science at Brown University
Eigen Library for Matrix Algebra in C++ by The Quantcademy
C++Builder Product Information
C++Builder Product Page – Native Apps that Perform. Build Windows C++ Apps 10x Faster with Less Code
C++Builder Product Editions – C++Builder is available in four editions – Professional, Enterprise, Architect and Community (free). C++Builder is also available as part of the RAD Studio development suite.
My C++Builder Eigen VCL Example Applications
Source Code zip file for all 3 examples