-
Notifications
You must be signed in to change notification settings - Fork 36
ElementManipulation
This section shows how to set individual elementsj, rows or columns in the matrix as well as resizing, reshaping it.
A matrix is a 2 diminesional array of elements. The default property setter/getter is actually a direct Pascal matrix syntax:
property Items[x, y : integer] : double read GetItems write SetItems; default;
An exception is raised in case x or y exceed the selected matrix range. Note that one can select sub matrices in a matrix and that the getter and setter methods take this into account (e.g. shifting index 0 to the selected position).
There is also the possibility to treat a whole matrix as a vector and assigning individual elements by accessing the properties:
property Vec[idx : integer] : double read GetVecItem write SetVecItem; // matrix as vector
property VecLen : integer read GetVecLen; // width*height
The property VecLen is calculated by width x height and elements are set following the row major principle. E.g. if you have a 3x3 matrix x and assign x.Vec[1] := 2 then this maps actually to x.Items[1, 0] := 2 .
Setting diagonal elements:
procedure TestDiag;
var x : IMatrix;
i : integer;
begin
x := TDoubleMatrix.Create(3, 3);
for i := 0 to x.width - 1 do
x[i, i] := 2;
end;
Filling a vector with a sine:
procedure TestVec;
var x : IMatrix;
i : integer;
begin
// create a vector
x := TDoubleMatrix.Create(1, 100);
for i := 0 to x.VecLen - 1 do
x.Vec[i] := sin( 2*pi/50*i );
end;
Setting the inner matrix to ones:
procedure TestSetItem;
var x : IMatrix;
i, j : integer;
begin
// create a larger matrix
x := TDoubleMatrix.Create(50, 50);
// fill matrix x(25:30, 25:30) = 100
// select a 5x5 matrix
x.SetSubMatrix(24, 24, 5, 5);
for i := 0 to x.Width - 1 do
for j := 0 to x.Height - 1 do
x[i, j] := 1;
// undo the selection
x.UseFullMatrix;
// the above is the same as:
for i := 24 to 29 do
for j := 24 to 29 do
x[i, j] := 1;
end;
The library conveniently supports writing full rows/colums, copying matrices to sub elements of the actual matrix. Note that the functions only work on the selected sub matrix on the matrix:
procedure SetRow(row : integer; const Values : Array of Double); overload;
procedure SetRow(row : integer; Values : TDoubleMatrix; ValRow : integer = 0); overload;
procedure SetRow(row : integer; Values : IMatrix; ValRow : integer = 0); overload;
procedure SetColumn(col : integer; const Values : Array of Double); overload;
procedure SetColumn(col : integer; Values : TDoubleMatrix; ValCols : integer = 0); overload;
procedure SetColumn(col : integer; Values : IMatrix; ValCols : integer = 0); overload;
procedure SetValue(const initVal : double); overload;
procedure SetValue(const initVal : double; x, y, aWidth, aHeight : TASMNativeInt); overload;
procedure AssignSubMatrix(Value : TDoubleMatrix; X : integer = 0; Y : integer = 0); overload;
procedure AssignSubMatrix(Value : IMatrix; X : integer = 0; Y : integer = 0); overload;
The SetRow/SetColumn take either an array of doubles (a vector) or a reference matrix. If it's a reference matrix one can select a row/column from this input matrix (the last ValRow, ValCols parameter) which is copied to the current one.
The procedure SetValue initializes the whole matrix with that value.
One can assign a smaller reference matrix to the current one and also use an offset. Note that an exception is raised in case the matrix does not fit.
A very special function to initialize a whole matrix is:
procedure MaskedSetValue(const Mask : Array of boolean; const newVal : double);
The mask has to be the same size as the matrix and is treated as row major. Only elements with true are initialized with the given newVal Value.
Initialize columns/rows
procedure TestRowCol;
var x, y : IMatrix;
begin
x := TDoubleMatrix.Create(3, 3, 2);
y := TDoubleMatrix.Create(3, 3, 1);
// set the first column to 0
x.SetColumn(0, [0, 0, 0]);
// copy the second row from matrix y
x.setRow(1, y, 1);
// copy the second column from y to the 3 column of x
x.SetColumn(2, y, 1);
end;
Initialize matrix with a different value:
procedure TestSetValue;
var x : IMatrix;
begin
x := TDoubleMatrix.Create(3, 3, -1);
// set the matrix elements (which are currently -1) to 1
x.SetValue(1);
end;
The library supports to resize, append and reshape a matrix with the following methods:
procedure Resize(aNewWidth, aNewHeight : Integer);
function Reshape(newWidth, newHeight : integer; RowMajor : boolean = False) : TDoubleMatrix;
procedure ReshapeInPlace(newWidth, newHeight : integer; RowMajor : boolean = False);
procedure RepeatMatrixInPlace(numX, numY : integer);
function RepeatMatrix(numX, numY : integer) : TDoubleMatrix;
procedure Append(Value : IMatrix; appendColumns : boolean); overload;
procedure Append(Value : TDoubleMatrix; appendColumns : boolean); overload;
Resize allows to grow or shrink a matrix and preserves data (like realloc memory). New elements are initalized with 0. If only a subset of the matrix is selected this selection is lost but actually the submatrix is preserved.
_Reshape_s a matrix e.g. a vector to a matrix and vice versa. RowMajor determines how e.g. a vector fills up a new matrix. Note that an exception is raised if the dimesions do not fit.
RepeatMatrix extends the current matrix numX*numY times. The content of the first "field" (the initial matrix) is repeated over the new blocks.
Append allows you to merge two matrices either on the right (apendColumns = True) or on the bottom (appendColumns = False). If the width or height params do not fit then an exception is raised.
Load a matrix from the file and extend the content. This is part of the unit test ;)
procedure TestLoadExtend;
var x : IMatrix;
begin
x := ReadObjFromFile('mahalonobis.dat') as TDoubleMatrix;
x.Resize(x.Width, x.Height + 1);
// add outlier ;)
x.SetRow(x.Height - 1, [ 1000, 1000 ]);
end;
Testcase for append:
procedure TestAppend;
var x : TDoubleMatrix;
i: Integer;
j: Integer;
begin
x := TDoubleMatrix.CreateRand(4, 4);
x.Append(x.Clone as IMatrix, False);
for i := 0 to x.Width - 1 do
for j := 0 to x.Height div 2 - 1 do
Assert( x[i, j] = x[i, j + 4], 'Appending rows failed' );
x.Free;
end;
In lending the idea of the matlab selectors one can now use the following functions to extract selected rows, columns or even items from a matrix:
function SubColMtx( colIdx : TIntegerDynArray ) : TDoubleMatrix; overload;
function SubRowMtx( rowIdx : TIntegerDynArray ) : TDoubleMatrix; overload;
function SubMtx( colIdx : TIntegerDynArray; rowIdx : TIntegerDynArray ) : TDoubleMatrix; overload;
function SubColMtx( fromIdx, toIdx : integer ) : TDoubleMatrix; overload;
function SubRowMtx( fromIdx, toIdx : integer ) : TDoubleMatrix; overload;
function SubMtx( fromColIdx, ToColIdx, fromRowIdx, ToRowIdx : integer ) : TDoubleMatrix; overload;
In effect all functions boil down to SubMtx . One can there use a column and row vector indices array to select certain values in a new resulting matrix.
Invert columns:
procedure Invertcolumn;
var mtx, mtx1 : TDoubleMatrix;
x, y : integer;
rows, cols : TIntegerDynArray;
begin
mtx := TDoubleMatrix.CreateRand(20, 20);
// invert the columns
mtx1 := mtx.SubColMtx(19, 0);
for x := 0 to mtx.Width - 1 do
begin
for y := 0 to mtx.Height - 1 do
Check( mtx[19 - x, y] = mtx1[x, y], 'Bad selection');
end;
mtx1.Free;
mtx.Free;
end;
Select subsections from the matrix
procedure TestSelect;
var mtx, mtx1 : TDoubleMatrix;
x, y : integer;
rows, cols : TIntegerDynArray;
begin
mtx := TDoubleMatrix.CreateRand(20, 20);
// sub selection
rows := Arr([0, 2, 4, 5, 6, 7, 14, 19]);
cols :=Arr([0, 1, 3, 4, 5, 6, 7, 12, 18]);
mtx1 := mtx.SubMtx(cols, rows);
for x := 0 to Length(cols) - 1 do
begin
for y := 0 to Length(rows) - 1 do
Check( mtx1[x, y] = mtx[ cols[x], rows[y] ], 'Bad selection');
end;
mtx1.Free;
mtx.Free;
end;