Using the Eigen C++ Template Library with C++Builder and VCL

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.

Dependency notice about minimal changes made to allow Eigen to work with C++Builder
Eigen Installation Window showing progress of the download, installation and tests

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 VCL Form containing a button and memo
Application Output using addition, subtraction and multiplication

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 Matrix Operations example VCL form
Example Output after subtracting the x and y matrices giving z

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 VCL Form for the UI
The demand matrix of baked goods for each person, the prices for each baked good by shop and the resulting optimal shop spending 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

GetIt Package Manager

C++ Libraries in GetIt

Eigen C++ Template Library information

Eigen C++ Template Library main page

Eigen Library Documentation (version 3.3.7 as of this posting)

Eigen Quick Reference Guide

Eigen latest development version documentation (ver 3.3.90 as of this posting)

Eigen Advanced Initialization

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

Leave a Reply

Your email address will not be published. Required fields are marked *