Matrix Unary Arithmetic and Transpose
Learn to overload the unary arithmetic operators for matrices and learn how to take the transpose of a matrix.
Introduction to the unary operators
In this lesson, we’ll explore unary operators and their usage. Unary operators differ from binary operators, which we previously implemented, as they operate on a single object instead of two objects. Here’s a list of the unary operators we’ll be discussing:
Increment: The
++
operator (both pre- and post-increment++M
orM++
)Decrement: The
--
operator (both pre- and post-decrement--M
orM--
)Additive inverse: The minus
-
operatorTranspose: The logical NOT
!
operatorMultiplicative inverse: The
~
operator
Here’s the playground where we’ll implement the unary operators:
Exercise playground
3 2 2 1 2 4 5 2 2 7 8 2 2 5 3 4 6 5 3 3 4 5 8 9 4 5 3 7 2 3
Let’s move toward the implementation of unary operators.
The increment (++
) operator
The increment (++
) operator is used to increment the value of an object by one.
Pre-increment
We’re already acquainted with increment operators, which are used to increase the value of an object by one. In the given code, the pre-increment operator (++M
) is implemented for a Matrix
object. It allows us to increment each element of the matrix by one, resulting in an updated Matrix
object.
Here’s how we implement it:
// ++Mi: Pre-incrementconst Matrix Matrix::operator++(){Matrix result = (*this) + 1;*this = result;return *this;}
-
Line 4:
Matrix
object calledresult
is created and initialized with the sum of the currentMatrix
object, denoted as(*this) + 1
. -
Line 5: The current
Matrix
object, represented as*this
, is assigned the value of theresult
Matrix
, effectively updating its content. -
Line 6: Finally, the incremented
Matrix
object,*this
, is returned.
Instruction: Write the above code in Exercise playground to test the correct output.
Post-increment
The provided code snippet demonstrates the implementation of the post-increment operator (Mi++
) in the Matrix
class.
// Mi++: Post-incrementconst Matrix Matrix::operator++(int){Matrix result = (*this);*this = *this + 1;return result; // The previous value is returned}
Line 2: The operator is defined as a member function with the signature
Matrix Matrix::operator++(int)
. Theint
parameter is used to distinguish the post-increment operator from the pre-increment operator.Line 4: Inside the function, a copy of the current matrix (
*this
) is created and stored in theresult
variable. This copy represents the matrix before the increment operation.Line 5: Then, the current matrix (
*this
) is incremented by1
using the+
operator. This operation modifies the matrix itself, increasing each element by1
.Line 6: Finally, the function returns the
result
matrix, which represents the matrix before the increment operation. This behavior matches the expected behavior of the post-increment operator, where the value is incremented, but the original value is returned.
Note: It is important to note that the return type of the operator function is constant (
const Matrix
). This is done to prevent chaining multiple post-increment operators (Mi++++
), which would involve incrementing a temporary result. Chaining like this is prohibited to avoid unnecessary and potentially problematic operations on temporary copies generated by the compiler.
By implementing the post-increment operator in the Matrix
class, we can now use the Mi++
syntax to increment a matrix and obtain its previous value for further computations or assignments.
Tip: Write the above code in Exercise playground to test the correct output.
Add the above two operators (
const Matrix operator++(int)
andconst Matrix operator--(int)
) as prototypes in the header file (Matrix.h
).Implement the two operators in the cpp file (
Matrix.cpp
).Uncomment lines 12–13 in your
main.cpp
file to test these two operators.
The decrement (--
) operator
The decrement operator (--
) is used to decrease the value of an object by one. Now, it’s your turn to implement the decrement operator yourself. The requirement is that it should decrease the value of every element of the matrix by one.
Tip: Try your implementation in Exercise playground.
If you have trouble getting to the solution, you can click the “Show solution” button to see how to implement it.
The additive inverse (-
) operator
The unary minus (-
) operator is used to negate or change the sign of an object’s value. In this code snippet, we’ll implement this operator for a Matrix
object by multiplying each element of the matrix by -1
.
const Matrix Matrix::operator-()const{Matrix result(this->rows, this->cols);for (int r = 0; r < this->rows; r++){for (int c = 0; c < this->cols; c++){result.Vs[r][c] = (this->Vs[r][c] *-1);}}return result;}
-
Line 3:
Matrix
object calledresult
is created, having the same dimensions (rows and columns) as the currentMatrix
object. -
Lines 4–10: Inside the nested loop, each element of the current
Matrix
object, denoted asthis->Vs[r][c]
, is multiplied by-1
and assigned to the corresponding element of theresult
Matrix
object, denoted byresult.Vs[r][c]
. -
Line 11: After completing the iteration, the
result
object, with all elements negated, is returned.
Tip: Write the above code in Exercise playground to test the correct output.
Transpose of a matrix
We’ll implement the logical NOT (!
) operator to take the transpose of the given matrix as shown in the illustration below:
In the Matrix
class, the !
operator creates a new matrix that represents the transpose of the original matrix—swapping the rows and columns—and returns it.
Let’s look at the code below:
const Matrix Matrix::operator!()const{Matrix Transpose(this->cols, this->rows);for (int r = 0; r < this->rows; r++){for (int c = 0; c < cols; c++){Transpose.Vs[c][r] = this->Vs[r][c];}}return Transpose;}
-
Line 3: We create a new
Matrix
object calledTranspose
with the dimensions of the original matrix swapped (the number of columns becomes the number of rows, and vice versa). -
Line 4-10: Inside the nested loop, we assign the value of the current element in the original matrix, represented as
this->Vs[r][c]
, to the corresponding element in the transposed matrix, represented asTranspose.Vs[c][r]
. This swapping ofr
andc
ensures that the elements are correctly placed in the transposed matrix. -
Line 11: Once the iteration is complete, we return the transposed matrix, which is stored in the
Transpose
object.
Multiplicative inverse of a matrix
We’ll implement the ~
operator to compute the inverse of a given matrix. The product of the matrix and its inverse will result in a
Let’s say we have the following matrix
Here's how to calculate
We first need to calculate the determinant of
.
Generally, if we have the following matrix:
- The determinant of a matrix involves multiplying the diagonal elements ( and ) and subtracting the product of the off-diagonal elements ( and ).
If
, then the inverse of the matrix is not possible.
Now that we know that the
it’s confirmed that the inverse of matrix exists. Next, we compute the adjoint of a
matrix by swapping the positions of the diagonal elements and changing the signs of the off-diagonal elements.
Here’s the formula for finding the inverse of a matrix.
With
, the inverse of the matrix will be:
Here’s the implementation to find the inverse of a
const Matrix Matrix::operator~()const{if (this->rows != 2 && this->cols != 2)throw("invalid");int determinant = (this->Vs[0][0] * this->Vs[1][1]) - (this->Vs[0][1] * this->Vs[1][0]);if (determinant == 0)throw(determinant);Matrix inverse(this->rows, this->cols);inverse.Vs[0][0] = this->Vs[1][1];inverse.Vs[1][1] = this->Vs[0][0];inverse.Vs[0][1] = this->Vs[0][1]*-1;inverse.Vs[1][0] = this->Vs[1][0] * -1;inverse = inverse / determinant;return inverse;}
Lines 3–4: We check if the input matrix is of size
. Line 5: We calculate the determinant of the input matrix.
Line 6: We check if the determinant is nonzero.
Lines 8–13: We compute the inverse as stated in above formula.
Line 14: We return the computed inverse.
Tip: Write the above code in Exercise playground to test the correct output.