Matrix Algebra (%*%, solve, t)

Reviewed & published by Brayan K

Matrix algebra in R is the set of tools — matrix multiplication with %*%, transpose with t(), and solving systems with solve() — that let you do real linear algebra on two-dimensional grids of numbers.

Part of the free R course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

By the end of this lesson you'll build matrices, multiply them the right way, transpose and invert them, and solve a system of linear equations in a single call.

What You'll Learn in This Lesson

1️⃣ Building Matrices

Use matrix() with a vector of values plus nrow and/or ncol. By default R fills down the columns; add byrow = TRUE to fill across rows the way you'd read them. The dim(), nrow(), and ncol() helpers report the shape.

# A matrix is a 2-D grid of values of the SAME type.
# Build one with matrix(); R fills DOWN columns by default.

A <- matrix(1:6, nrow = 2, ncol = 3)
A
#      [,1] [,2] [,3]
# [1,]    1    3    5
# [2,]    2    4    6

# Fill across rows instead with byrow = TRUE:
B <- matrix(1:6, nrow = 2, byrow = TRUE)
B
#      [,1] [,2] [,3]
# [1,]    1    2    3
# [2,]    4    5    6

dim(A)        # rows, cols -> [1] 2 3
nrow(A)       # -> [1] 2
ncol(A)       # -> [1] 3

2️⃣ Multiplying and Transposing

The operator %*% performs true matrix multiplication, where each output cell is a row-times-column dot product. A plain * multiplies matching cells instead — useful, but a different operation. The transpose t() flips rows and columns.

# %*% is MATRIX multiplication. A plain * multiplies element by element!
# For A %*% B, the columns of A must equal the rows of B.

A <- matrix(c(1, 2, 3, 4), nrow = 2)   # 2x2, filled by column
B <- matrix(c(5, 6, 7, 8), nrow = 2)   # 2x2

A %*% B        # true matrix product (rows x cols)
#      [,1] [,2]
# [1,]   23   31
# [2,]   34   46

A * B          # element-by-element — NOT the same thing
#      [,1] [,2]
# [1,]    5   21
# [2,]   12   32

# t() TRANSPOSES — rows become columns:
t(A)
#      [,1] [,2]
# [1,]    1    2
# [2,]    3    4

Whenever you see a dimension-mismatch error from %*%, check that the inner dimensions agree: an m×n times an n×p gives an m×p result.

3️⃣ Inverses and Solving Systems

The single function solve() does double duty. Called with one matrix it returns the inverse; called as solve(A, b) it solves the linear system A x = b for the unknown vector x. Use det() to check invertibility and diag() to build an identity matrix.

# solve() with ONE argument returns the matrix INVERSE.
# solve(A, b) solves the linear system A %*% x = b for x.

A <- matrix(c(2, 1, 1, 3), nrow = 2)   # 2x2 coefficient matrix

# Solve  2x + y = 5 ,  x + 3y = 10
b <- c(5, 10)
x <- solve(A, b)
x              # -> [1] 1 3   (so x = 1, y = 3)

det(A)         # determinant; non-zero means invertible -> [1] 5

# diag() makes an identity matrix (or pulls a diagonal):
diag(2)
#      [,1] [,2]
# [1,]    1    0
# [2,]    0    1

Your turn. Fill in the ___ blank, run it, and compare with the expected output.

# 🎯 YOUR TURN — replace each ___ then run it.

# 1) Build a 2x2 matrix M from the numbers 2, 0, 0, 3 (by column)
M <- matrix(c(2, 0, 0, 3), nrow = 2)

# 2) Build a second 2x2 matrix N from 1, 1, 1, 1
N <- ___   # 👉 replace ___ with matrix(c(1, 1, 1, 1), nrow = 2)

# 3) Print the matrix product M %*% N
print(M %*% N)

# 4) Print the transpose of M
print(t(M))

# ✅ Expected output:
#      [,1] [,2]
# [1,]    2    2
# [2,]    3    3
#      [,1] [,2]
# [1,]    2    0
# [2,]    0    3

Mini-Challenge: Crack the Prices

Two equations, two unknowns — exactly what solve(A, b) is built for. Remember to use byrow = TRUE so your coefficient matrix matches the way you wrote the equations.

# 🎯 MINI-CHALLENGE: solve a small system of equations.
#
# A farm sells apples (a) and pears (p). Two days of sales:
#   Day 1:  3a + 2p = 13   (3 apples, 2 pears, $13)
#   Day 2:  1a + 4p = 16   (1 apple,  4 pears, $16)
#
# 1. Build the 2x2 coefficient matrix A (by ROW, byrow = TRUE).
# 2. Build the right-hand-side vector b = c(13, 16).
# 3. Use solve(A, b) to find the price of an apple and a pear.
# 4. Confirm by checking A %*% prices equals b.
#
# ✅ Expected: apple = 2, pear = 3.5  ->  [1] 2.0 3.5

# your code here

Common Errors (and the fix)

Pro Tips

📋 Quick Reference — Matrix Algebra

TaskR Syntax
Create matrixmatrix(1:6, nrow = 2)
Matrix productA %*% B
Transposet(A)
Inversesolve(A)
Solve A x = bsolve(A, b)
Determinantdet(A)
Identity matrixdiag(n)

🎉 Lesson Complete!

Practice quiz

What does the %*% operator do?

  • Element-wise multiplication
  • True matrix multiplication
  • Division
  • Transposition

Answer: True matrix multiplication. %*% performs linear-algebra matrix multiplication (row-times-column dot products).

What does a plain * do between two matrices?

  • Matrix product
  • Solves a system
  • Inverts them
  • Element-by-element multiplication

Answer: Element-by-element multiplication. A single * multiplies matching cells (Hadamard), requiring equal shapes.

For A %*% B to work, what must be true?

  • A's columns equal B's rows
  • A and B are identical
  • Both must be square
  • A's rows equal B's rows

Answer: A's columns equal B's rows. The inner dimensions must match: ncol(A) must equal nrow(B).

What does t(A) compute?

  • The total
  • The determinant
  • The transpose
  • The inverse

Answer: The transpose. t() transposes A, turning rows into columns.

What does solve(A) with one argument return?

  • The inverse of A
  • The determinant
  • The transpose
  • The diagonal

Answer: The inverse of A. solve(A) returns the matrix inverse of A.

Which is the preferred way to solve the system A x = b?

  • solve(A) %*% b
  • A %*% b
  • solve(A, b)
  • det(A) * b

Answer: solve(A, b). solve(A, b) is faster and more numerically stable than inverting A first.

What does det(A) tell you?

  • The matrix size
  • Whether A is invertible (non-zero means yes)
  • The number of rows
  • The transpose

Answer: Whether A is invertible (non-zero means yes). A non-zero determinant means the matrix is invertible.

How does R fill matrix(1:6, nrow = 2) by default?

  • Row by row
  • Randomly
  • Diagonally
  • Down the columns

Answer: Down the columns. R is column-major: 1 and 2 fill the first column, then 3 and 4, and so on.

What does diag(2) produce?

  • A 2x2 identity matrix
  • The number 2
  • A diagonal of twos
  • A 2-row vector

Answer: A 2x2 identity matrix. diag(n) builds an n-by-n identity matrix.

What does 'system is computationally singular' mean?

  • The matrix is too large
  • There are too many rows
  • The matrix has no inverse (determinant ~ 0)
  • b is the wrong length

Answer: The matrix has no inverse (determinant ~ 0). It signals a non-invertible matrix, so the system has no unique solution.

Continue this course

Frequently asked questions

What's the difference between * and %*% for matrices?

A single * does element-by-element (Hadamard) multiplication: it lines up the two matrices cell-for-cell and multiplies matching positions, so both must be the same shape. The %*% operator does true linear-algebra matrix multiplication, where each output cell is the dot product of a row from the left matrix and a column from the right. For A %*% B the number of columns in A must equal the number of rows in B. Mixing these up is the single most common matrix bug in R.

Why does my matrix fill column-by-column instead of row-by-row?

R is column-major by default, so matrix(1:6, nrow = 2) puts 1 and 2 in the first column, 3 and 4 in the second, and so on. If you wrote your numbers out row by row in your head, add byrow = TRUE so matrix() fills across rows to match. This matters constantly when you hand-build a coefficient matrix for solve().

When should I use solve(A) versus solve(A, b)?

solve(A) returns the inverse of A, which is occasionally useful but numerically wasteful. To solve a linear system A x = b, prefer solve(A, b) directly — it is faster and more numerically stable than computing solve(A) %*% b. As a rule, if you only need x, never form the inverse explicitly.

What does it mean when solve() errors with 'system is computationally singular'?

It means the matrix has no inverse (or is so close to non-invertible that R refuses). Geometrically the equations are redundant or contradictory — the determinant is zero or near it. Check det(A): if it's zero, your system doesn't have a unique solution. This often happens when two rows are multiples of each other (collinear data).