Linear Algebra - Unit vectors e_i

Hi,

Is there an easy way to add a standard unit vector (e_i, which is vector of length ‘l’ with all zeros except for ith position?
Example: Let x be a vector of length 5. I want to add e_3 (= [ 0 0 1 0 0]) to it.
Right now I’m doing it as

y = x + eye(5)[:,3]

Is there any nice way to do it like x + e[3] kind of syntax?

Regards,
vish

x[3] += 1?

2 Likes

My actual use case is a little more complicated: I need to do

y = (1-c) * x + c * e_3
y = (1-c) * x
y[3] += c

maybe

Yeah… that’s one way. But it’s still two lines :slightly_frowning_face:
But is there any way to do this in one line?
Or May be I should settle with this.

y = (1-c) * x; y[3] += c

:troll:

11 Likes

Possibly yes. There is a trade-off between brevity and transparency; introducing special syntax for all of these things in Base is probably not worth it. If, however, you do this a lot in a package, you could write a tiny function for it. Especially if you want to make it generic so that it can handle StaticVector and other special types.

It could maybe be useful to have a lazy array, like I (UniformScaling), defined by e(i)[j] = i == j ? 1 : 0. Then one could define I[:,i] to be equal to that, for instance. That could be a neat little package.

1 Like

y = (1 - c) .* x .+ c .* (1:5 .== 3)

6 Likes

I’ve often wanted this myself. Julia prides itself in being readable and close to Mathematical notation so I think this was a perfectly fair question.

1 Like

Yes, I think this would be an excellent addition to FillArrays, along with the sort of stuff I mentioned in Possibility of a generically sized vector of 1s and 0s similar to the UniformScalingOperator in Juila v0.7

A bold e (as an optional function name in those cases) is one possible syntactic sugar

1 Like

I think for the moment @yuyichao answer is the closest possible solution for this.
And @antoine-levitt also has some interesting stuff!

Another possible use case for a similar lazy array: the totally antisymmetric tensor (Levi-Civita symbol), with bonus points for implementing fast contractions with it.

1 Like
struct lazy_e{T} <: AbstractVector{T}
    i::Int
    n::Int
end
lazy_e(i::Int, n::Int, ::Type{T} = Int) where T = lazy_e{T}(i, n)
function Base.getindex(e::lazy_e{T}, i) where T
    @boundscheck @assert i <= e.n
    ifelse(i == e.i, one(T), zero(T))
end
Base.size(e::lazy_e) = (e.n,)

Then on Julia 0.7:

julia> fe(x) = (@inbounds d = x' * lazy_e(3,4); d)
fe (generic function with 1 method)

julia> using StaticArrays, BenchmarkTools

julia> x = @SVector randn(4)
4-element SArray{Tuple{4},Float64,1,4}:
 -0.02571662495110571 
 -0.2620502046882458  
 -0.8456889511406026  
  0.017055806672449558

julia> y = randn(4)
4-element Array{Float64,1}:
 -1.858893446204762 
  1.883085443252701 
 -0.7957485906904918
 -0.5190800039346426

julia> @btime fe($x)
  1.520 ns (0 allocations: 0 bytes)
-0.8456889511406026

julia> @btime fe($y)
  8.301 ns (0 allocations: 0 bytes)
-0.7957485906904918

julia> lazy_e(2,5)
5-element lazy_e{Int64}:
 0
 1
 0
 0
 0

julia> lazy_e(2,5)'
1×5 LinearAlgebra.Adjoint{Int64,lazy_e{Int64}}:
 0  1  0  0  0

EDIT:
Ideally, you’d do this with a for loop and @eval, and perhaps some other helper macros, but:

Base.:*(A::Matrix, e::lazy_e) = A[:, e.i]

Base.:*(A::Adjoint{T,Matrix{T}}, e::lazy_e) where T = A[:, e.i]
Base.:*(A::Transpose{T,Matrix{T}}, e::lazy_e) where T = A[:, e.i]
Base.:*(A::Adjoint{T,Vector{T}}, e::lazy_e) where T = A[e.i]
Base.:*(A::Transpose{T,Vector{T}}, e::lazy_e) where T = A[e.i]


Base.:*(A::Adjoint{T,lazy_e}, e::Matrix{T}) where T = A[e.i, :]
Base.:*(A::Transpose{T,lazy_e}, e::Matrix{T}) where T = A[e.i, :]
Base.:*(A::Adjoint{T,lazy_e}, e::Vector{T}) where T = A[e.i]
Base.:*(A::Transpose{T,lazy_e}, e::Vector{T}) where T = A[e.i]

I had to get really specific for dispatch to actually favor the newly defined multiplication methods, and I’m not sure why. This yielded:

julia> z = randn(3,4)
3×4 Array{Float64,2}:
  0.478915  -0.542889  0.142261  -0.594415
  0.509348   0.63491   1.77353   -0.545189
 -0.659932   0.995295  1.706     -0.291947

julia> @btime $z * lazy_e(2,4)
  26.288 ns (1 allocation: 112 bytes)
3-element Array{Float64,1}:
 -0.5428894820340465
  0.6349099814045577
  0.9952951252882498

julia> @btime $z[:,2]
  26.266 ns (1 allocation: 112 bytes)
3-element Array{Float64,1}:
 -0.5428894820340465
  0.6349099814045577
  0.9952951252882498

The matrix multiplication functions could be edited to return views if you.

3 Likes

@Elrod
I guess you rather want

struct lazy_e{T} <: AbstractVector{T}
    i::Int
    n::Int
    val::T 
end

at least for isbits(T), in order to quickly (without looping, independent of n) scale a unit vector and in-place add it to some other vector (specialize broadcast!).

1 Like

unit(n)=eachcol(I(n))

unit(n,i)= unit(n)[i]
1 Like