27

I'm trying to construct the identity matrix in Julia 1.1. After looking at the documentation I found that I could compute a 4x4 Identity matrix as follows:

julia> Id4 =1* Matrix(I, 4, 4)

4×4 Array{Int64,2}:
 1  0  0  0
 0  1  0  0
 0  0  1  0
 0  0  0  1

Is this the most julianic way of coding it or is there a better/shorter way, as it is an often used matrix?

ecjb
  • 5,169
  • 12
  • 43
  • 79

1 Answers1

53

Given using LinearAlgebra, the most julianic way of expressing the identity matrix is:

I

This answer may seem trite, but it is also kind of profound. The whole point of the operator I is that in the vast majority of cases where users want an identity matrix, it is not necessary to actually instantiate that matrix.

Let's say you want a 1000x1000 identity matrix. Why waste time building the entire matrix, when you could just use I, noting that sizeof(I) evaluates to 1 (ie the size of the object is 1 byte). All functions in base Julia (including LinearAlgebra) understand what I is, and can use it appropriately without having to waste time building the actual matrix it represents first.

Now, it may be the case that for some reason you need to specify the type of the elements of your identity matrix. Note:

julia> I
UniformScaling{Bool}
true*I

so in this case, you are using a notional identity matrix with a diagonal of true and off-diagonal of false. This is sufficient in many cases, even if your other matrices are Int or Float64. Internally, Julia will use methods that specialize on the types. However, if you want to specify your identity matrix to contain integers or floats, use:

julia> 1I
UniformScaling{Int64}
1*I

julia> 1.0I
UniformScaling{Float64}
1.0*I

Note that sizeof(1I) evaluates to 8, indicating the notional Int64 types of the members of that matrix.

Also note that you can use e.g. 5I if you want a notional matrix with 5 on the diagonal and 0 elsewhere.

In some cases (and these cases are much rarer than many might think), you may need to actually build the matrix. In this case, you can use e.g.:

Matrix(1I, 3, 3)    # Identity matrix of Int type
Matrix(1.0I, 3, 3)  # Identity matrix of Float64 type
Matrix(I, 3, 3)     # Identity matrix of Bool type

Bogumił has also pointed out in the comments that if you are uncomfortable with implying the type of the output in the first argument of the constructors above, you can also use the (slightly more verbose):

Matrix{Int}(I, 3, 3)      # Identity matrix of Int type
Matrix{Float64}(I, 3, 3)  # Identity matrix of Float64 type
Matrix{Bool}(I, 3, 3)     # Identity matrix of Bool type

and specify the type explicitly.

But really, the only times you would probably need to do this are as follows:

  • When you want to input an identity matrix into a function in a package written in such a way that the input must be a concrete matrix type.
  • When you want to start out with an identity matrix but then mutate it in place into something else via one or several transformations.
STJ
  • 1,478
  • 7
  • 25
Colin T Bowers
  • 18,106
  • 8
  • 61
  • 89
  • 2
    or write `Matrix{Int}(I, 3, 3)` to give an explicit target type specifier. – Bogumił Kamiński Jul 30 '19 at 11:47
  • @BogumiłKamiński Thanks, I've adjusted the answer to include that alternative. – Colin T Bowers Jul 30 '19 at 11:52
  • 3
    Explicitly constructing the matrix might also be useful in cases when you use it as an initialization of something that will later be mutated. Or if, say, you start out with `M = I`, which you then iteratively update like `M *= A`, with some matrix `A`. If `M` is initialized to `I` you would then get a type instability. – DNF Jul 30 '19 at 12:12
  • Thank you all for your insight – ecjb Jul 30 '19 at 19:53
  • 3
    I'm new to Julia. Is `I` a built-in? When I put it in my interpreter I get `ERROR: UndefVarError: I not defined`. – Bill Jun 20 '20 at 22:02
  • 2
    Apparently it was moved to the Linear Algebra package so you need to do `using LinearAlgebra`. – Bill Jun 20 '20 at 22:26
  • 1
    @Bill Thanks I've updated my answer to include this. I have `using LinearAlgebra` in my `startup.jl` so tend to otherwise forget this step :-) – Colin T Bowers Jun 22 '20 at 01:41
  • 1
    Another case in which I think you would need to build the identity matrix properly is when performing the Kronecker product to build an operator that acts on a tensor product space. – diatomicDisaster Apr 01 '21 at 23:30
  • 1
    Excellent answer. If you do not want to import the whole package, you can import `I` with `using LinearAlgebra: I` or (less elegant) replace every instance of `I` with `LinearAlgebra.I`. – PatrickT May 12 '21 at 08:54