Skip to content

Commit 4e1e0ab

Browse files
committed
Merge pull request #5810 from JuliaLang/anj/arraydiv
Restrict +,- and / to algebraic operations (for matrices). Use . versions for elementwise operations. Add UniformScaling matrix.
2 parents b871f72 + e4c79b0 commit 4e1e0ab

20 files changed

+211
-69
lines changed

NEWS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,10 @@ Library improvements
156156
the same length. This generalizes and replaces `normfro` ([#6057]),
157157
and `norm` is now type-stable ([#6056]).
158158

159+
* + and - now only works when the sizes of the arrays are the same, i.e. the
160+
operations no longer do broadcasting. New `UniformScaling` type and identity
161+
`I` constant (#5810).
162+
159163
* Sparse linear algebra
160164

161165
* Faster sparse `kron` ([#4958]).

base/abstractarray.jl

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -326,11 +326,9 @@ imag{T<:Real}(x::AbstractArray{T}) = zero(x)
326326
*(A::Number, B::AbstractArray) = A .* B
327327
*(A::AbstractArray, B::Number) = A .* B
328328

329-
/(A::Number, B::AbstractArray) = A ./ B
330329
/(A::AbstractArray, B::Number) = A ./ B
331330

332331
\(A::Number, B::AbstractArray) = B ./ A
333-
\(A::AbstractArray, B::Number) = B ./ A
334332

335333
./(x::Number,y::AbstractArray ) = throw(MethodError(./, (x,y)))
336334
./(x::AbstractArray, y::Number) = throw(MethodError(./, (x,y)))
@@ -398,7 +396,7 @@ function circshift(a, shiftamts)
398396
for i=1:n
399397
s = size(a,i)
400398
d = i<=length(shiftamts) ? shiftamts[i] : 0
401-
I[i] = d==0 ? (1:s) : mod([-d:s-1-d], s)+1
399+
I[i] = d==0 ? (1:s) : mod([-d:s-1-d], s).+1
402400
end
403401
a[I...]::typeof(a)
404402
end
@@ -1184,10 +1182,6 @@ function mapslices(f::Function, A::AbstractArray, dims::AbstractVector)
11841182
ndimsA = ndims(A)
11851183
alldims = [1:ndimsA]
11861184

1187-
if dims == alldims
1188-
return f(A)
1189-
end
1190-
11911185
otherdims = setdiff(alldims, dims)
11921186

11931187
idx = cell(ndimsA)

base/array.jl

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,7 @@ for f in (:+, :-, :div, :mod, :&, :|, :$)
782782
end
783783
end
784784
end
785-
for f in (:+, :-, :.*, :./, :.%, :div, :mod, :rem, :&, :|, :$)
785+
for f in (:.+, :.-, :.*, :./, :.%, :div, :mod, :rem, :&, :|, :$)
786786
@eval begin
787787
function ($f){T}(A::Number, B::StridedArray{T})
788788
F = similar(B, promote_array_type(typeof(A),T))
@@ -802,7 +802,7 @@ for f in (:+, :-, :.*, :./, :.%, :div, :mod, :rem, :&, :|, :$)
802802
end
803803

804804
# functions that should give an Int result for Bool arrays
805-
for f in (:+, :-)
805+
for f in (:.+, :.-)
806806
@eval begin
807807
function ($f)(A::Bool, B::StridedArray{Bool})
808808
F = Array(Int, size(B))
@@ -818,12 +818,16 @@ for f in (:+, :-)
818818
end
819819
return F
820820
end
821+
end
822+
end
823+
for f in (:+, :-)
824+
@eval begin
821825
function ($f)(A::StridedArray{Bool}, B::StridedArray{Bool})
822826
F = Array(Int, promote_shape(size(A), size(B)))
823827
for i=1:length(A)
824828
@inbounds F[i] = ($f)(A[i], B[i])
825829
end
826-
return F
830+
return F
827831
end
828832
end
829833
end

base/bitarray.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -898,7 +898,7 @@ for f in (:+, :-)
898898
return r
899899
end
900900
end
901-
for f in (:+, :-),
901+
for f in (:.+, :.-),
902902
(arg1, arg2, T, fargs) in ((:(B::BitArray), :(x::Bool) , Int , :(b, x)),
903903
(:(B::BitArray), :(x::Number) , :(promote_array_type(typeof(x), Bool)), :(b, x)),
904904
(:(x::Bool) , :(B::BitArray), Int , :(x, b)),

base/deprecated.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,16 @@ export PipeString
189189
@deprecate svdfact(A,thin) svdfact(A,thin=thin)
190190
@deprecate svdfact!(A,thin) svdfact(A,thin=thin)
191191
@deprecate svd(A,thin) svd(A,thin=thin)
192+
@deprecate (+)(A::Array{Bool},x::Bool) A .+ x
193+
@deprecate (+)(x::Bool,A::Array{Bool}) x .+ A
194+
@deprecate (-)(A::Array{Bool},x::Bool) A .- x
195+
@deprecate (-)(x::Bool,A::Array{Bool}) x .- A
196+
@deprecate (+)(A::Array,x::Number) A .+ x
197+
@deprecate (+)(x::Number,A::Array) x .+ A
198+
@deprecate (-)(A::Array,x::Number) A .- x
199+
@deprecate (-)(x::Number,A::Array) x .- A
200+
@deprecate (/)(x::Number,A::Array) x ./ A
201+
@deprecate (\)(A::Array,x::Number) A .\ x
192202

193203
deprecated_ls() = run(`ls -l`)
194204
deprecated_ls(args::Cmd) = run(`ls -l $args`)

base/exports.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export
5151
GeneralizedSVD,
5252
Hermitian,
5353
Hessenberg,
54+
UniformScaling,
5455
InsertionSort,
5556
IntSet,
5657
IO,
@@ -189,6 +190,7 @@ export
189190
γ, eulergamma,
190191
catalan,
191192
φ, golden,
193+
I,
192194

193195
# Operators
194196
!,

base/linalg.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export
3333
Symmetric,
3434
Triangular,
3535
Diagonal,
36+
UniformScaling,
3637

3738
# Functions
3839
axpy!,
@@ -145,7 +146,10 @@ export
145146
At_mul_Bt,
146147
At_mul_Bt!,
147148
At_rdiv_B,
148-
At_rdiv_Bt
149+
At_rdiv_Bt,
150+
151+
# Constants
152+
I
149153

150154
typealias BlasFloat Union(Float64,Float32,Complex128,Complex64)
151155
typealias BlasReal Union(Float64,Float32)
@@ -201,6 +205,7 @@ include("linalg/woodbury.jl")
201205
include("linalg/tridiag.jl")
202206
include("linalg/diagonal.jl")
203207
include("linalg/bidiag.jl")
208+
include("linalg/uniformscaling.jl")
204209
include("linalg/rectfullpacked.jl")
205210
include("linalg/givens.jl")
206211
include("linalg/special.jl")

base/linalg/factorization.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ Ac_ldiv_Bc{T<:BlasComplex}(A::LU{T}, B::StridedVecOrMat{T}) = @assertnonsingular
248248

249249
/{T}(B::Matrix{T},A::LU{T}) = At_ldiv_Bt(A,B).'
250250

251-
inv{T<:BlasFloat}(A::LU{T})=@assertnonsingular LAPACK.getri!(copy(A.factors), A.ipiv) A.info
251+
inv{T<:BlasFloat}(A::LU{T}) = @assertnonsingular LAPACK.getri!(copy(A.factors), A.ipiv) A.info
252252

253253
cond{T<:BlasFloat}(A::LU{T}, p::Number) = inv(LAPACK.gecon!(p == 1 ? '1' : 'I', A.factors, norm(A[:L][A[:p],:]*A[:U], p)))
254254
cond(A::LU, p::Number) = norm(A[:L]*A[:U],p)*norm(inv(A),p)

base/linalg/generic.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,12 +212,14 @@ trace(x::Number) = x
212212
inv(a::AbstractVector) = error("argument must be a square matrix")
213213
inv{T}(A::AbstractMatrix{T}) = A_ldiv_B!(A,eye(T, chksquare(A)))
214214

215-
function \{TA<:Number,TB<:Number}(A::AbstractMatrix{TA}, B::AbstractVecOrMat{TB})
215+
function \{TA,TB}(A::AbstractMatrix{TA}, B::AbstractVecOrMat{TB})
216216
TC = typeof(one(TA)/one(TB))
217217
A_ldiv_B!(convert(typeof(A).name.primary{TC}, A), TB == TC ? copy(B) : convert(typeof(B).name.primary{TC}, B))
218218
end
219219
\(a::AbstractVector, b::AbstractArray) = reshape(a, length(a), 1) \ b
220220
/(A::AbstractVecOrMat, B::AbstractVecOrMat) = (B' \ A')'
221+
# \(A::StridedMatrix,x::Number) = inv(A)*x Should be added at some point when the old elementwise version has been deprecated long enough
222+
# /(x::Number,A::StridedMatrix) = x*inv(A)
221223

222224
cond(x::Number) = x == 0 ? Inf : 1.0
223225
cond(x::Number, p) = cond(x)

base/linalg/uniformscaling.jl

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import Base: +, -, *, /, copy, ctranspose, getindex, showarray, transpose
2+
import Base.LinAlg: SingularException
3+
immutable UniformScaling{T<:Number} <: AbstractMatrix{T}
4+
λ::T
5+
end
6+
7+
const I = UniformScaling(1)
8+
9+
getindex(J::UniformScaling, i::Integer,j::Integer) = ifelse(i==j,J.λ,zero(J.λ))
10+
11+
showarray(io::IO,J::UniformScaling;kw...) = print(io,"$(typeof(J))\n$(J.λ)*I")
12+
copy(J::UniformScaling) = UniformScaling(J.λ)
13+
14+
transpose(J::UniformScaling) = J
15+
ctranspose(J::UniformScaling) = UniformScaling(conj(J.λ))
16+
17+
+(J1::UniformScaling,J2::UniformScaling) = UniformScaling(J1.λ+J2.λ)
18+
+{T}(B::BitArray{2},J::UniformScaling{T}) = bitunpack(B) + J
19+
+(J::UniformScaling,B::BitArray{2}) = J + bitunpack(B)
20+
function +{TA,TJ}(A::AbstractMatrix{TA},J::UniformScaling{TJ})
21+
n = chksquare(A)
22+
B = similar(A,promote_type(TA,TJ))
23+
copy!(B,A)
24+
@inbounds for i = 1:n
25+
B[i,i] += J.λ
26+
end
27+
B
28+
end
29+
+(J::UniformScaling,A::AbstractMatrix) = A + J
30+
31+
-(J1::UniformScaling,J2::UniformScaling) = UniformScaling(J1.λ-J2.λ)
32+
-(B::BitArray{2},J::UniformScaling) = bitunpack(B) - J
33+
-(J::UniformScaling,B::BitArray{2}) = J - bitunpack(B)
34+
function -{TA,TJ<:Number}(A::AbstractMatrix{TA},J::UniformScaling{TJ})
35+
n = chksquare(A)
36+
B = similar(A,promote_type(TA,TJ))
37+
copy!(B,A)
38+
@inbounds for i = 1:n
39+
B[i,i] -= J.λ
40+
end
41+
B
42+
end
43+
function -{TA,TJ<:Number}(J::UniformScaling{TJ},A::AbstractMatrix{TA})
44+
n = chksquare(A)
45+
B = -A
46+
@inbounds for i = 1:n
47+
B[i,i] += J.λ
48+
end
49+
B
50+
end
51+
52+
*(J1::UniformScaling,J2::UniformScaling) = UniformScaling(J1.λ*J2.λ)
53+
*(B::BitArray{2},J::UniformScaling) = *(bitunpack(B),J::UniformScaling)
54+
*(J::UniformScaling,B::BitArray{2}) = *(J::UniformScaling,bitunpack(B))
55+
*(S::SparseMatrixCSC,J::UniformScaling) = J.λ == 1 ? S : J.λ*S
56+
*{Tv,Ti}(J::UniformScaling,S::SparseMatrixCSC{Tv,Ti}) = J.λ == 1 ? S : S*J.λ
57+
*(A::AbstractMatrix,J::UniformScaling) = J.λ == 1 ? A : J.λ*A
58+
*(J::UniformScaling,A::AbstractVecOrMat) = J.λ == 1 ? A : J.λ*A
59+
60+
*(x::Number,J::UniformScaling) = UniformScaling(x*J.λ)
61+
*(J::UniformScaling,x::Number) = UniformScaling(J.λ*x)
62+
63+
/(J1::UniformScaling,J2::UniformScaling) = J2.λ == 0 ? throw(SingularException(1)) : UniformScaling(J1.λ/J2.λ)
64+
/(J::UniformScaling,A::AbstractMatrix) = J.λ*inv(A)
65+
/(A::AbstractMatrix,J::UniformScaling) = J.λ == 0 ? throw(SingularException(1)) : A/J.λ
66+
67+
/(J::UniformScaling,x::Number) = UniformScaling(J.λ/x)
68+
69+
\(J1::UniformScaling,J2::UniformScaling) = J1.λ == 0 ? throw(SingularException(1)) : UniformScaling(J1.λ\J2.λ)
70+
\{T<:Number}(A::Union(Bidiagonal{T},Triangular{T}),J::UniformScaling) = inv(A)*J.λ
71+
\(J::UniformScaling,A::AbstractVecOrMat) = J.λ == 0 ? throw(SingularException(1)) : J.λ\A
72+
\(A::AbstractMatrix,J::UniformScaling) = inv(A)*J.λ
73+
74+
\(x::Number,J::UniformScaling) = UniformScaling(x\J.λ)

base/sparse/sparsematrix.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1229,7 +1229,7 @@ function vcat(X::SparseMatrixCSC...)
12291229
rX2 = X[i].colptr[c + 1] - 1
12301230
rr2 = rr1 + (rX2 - rX1)
12311231

1232-
rowval[rr1 : rr2] = X[i].rowval[rX1 : rX2] + mX_sofar
1232+
rowval[rr1 : rr2] = X[i].rowval[rX1 : rX2] .+ mX_sofar
12331233
nzval[rr1 : rr2] = X[i].nzval[rX1 : rX2]
12341234
mX_sofar += mX[i]
12351235
rr1 = rr2 + 1
@@ -1261,7 +1261,7 @@ function hcat(X::SparseMatrixCSC...)
12611261
nnz_sofar = 0
12621262
nX_sofar = 0
12631263
for i = 1 : num
1264-
colptr[(1 : nX[i] + 1) + nX_sofar] = X[i].colptr + nnz_sofar
1264+
colptr[(1 : nX[i] + 1) + nX_sofar] = X[i].colptr .+ nnz_sofar
12651265
rowval[(1 : nnzX[i]) + nnz_sofar] = X[i].rowval
12661266
nzval[(1 : nnzX[i]) + nnz_sofar] = X[i].nzval
12671267
nnz_sofar += nnzX[i]
@@ -1303,8 +1303,8 @@ function blkdiag(X::SparseMatrixCSC...)
13031303
nX_sofar = 0
13041304
mX_sofar = 0
13051305
for i = 1 : num
1306-
colptr[(1 : nX[i] + 1) + nX_sofar] = X[i].colptr + nnz_sofar
1307-
rowval[(1 : nnzX[i]) + nnz_sofar] = X[i].rowval + mX_sofar
1306+
colptr[(1 : nX[i] + 1) + nX_sofar] = X[i].colptr .+ nnz_sofar
1307+
rowval[(1 : nnzX[i]) + nnz_sofar] = X[i].rowval .+ mX_sofar
13081308
nzval[(1 : nnzX[i]) + nnz_sofar] = X[i].nzval
13091309
nnz_sofar += nnzX[i]
13101310
nX_sofar += nX[i]

base/statistics.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -274,15 +274,15 @@ function quantile!(v::AbstractVector, q::AbstractVector)
274274
lv = length(v)
275275
lq = length(q)
276276

277-
index = 1 + (lv-1)*q
277+
index = 1 .+ (lv-1)*q
278278
lo = ifloor(index)
279279
hi = iceil(index)
280280
sort!(v)
281281
isnan(v[end]) && error("quantiles are undefined in presence of NaNs")
282282
i = find(index .> lo)
283283
r = float(v[lo])
284-
h = (index-lo)[i]
285-
r[i] = (1-h).*r[i] + h.*v[hi[i]]
284+
h = (index.-lo)[i]
285+
r[i] = (1.-h).*r[i] + h.*v[hi[i]]
286286
return r
287287
end
288288
quantile(v::AbstractVector, q::AbstractVector) = quantile!(copy(v),q)

doc/manual/arrays.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ operator should be used for elementwise operations.
286286
5. Binary Boolean or bitwise — ``&``, ``|``, ``$``
287287

288288
Some operators without dots operate elementwise anyway when one argument is a
289-
scalar. These operators are ``+``, ``-``, ``*``, ``/``, ``\``, and the bitwise
289+
scalar. These operators are ``*``, ``/``, ``\``, and the bitwise
290290
operators.
291291

292292
Note that comparisons such as ``==`` operate on whole arrays, giving a single

doc/manual/linear-algebra.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ for them in LAPACK are available.
5151
+--------------------+-----------------------------------------------------------------------------------+
5252
| ``Diagonal`` | `Diagonal matrix <http://en.wikipedia.org/wiki/Diagonal_matrix>`_ |
5353
+--------------------+-----------------------------------------------------------------------------------+
54-
54+
| ``UniformScaling`` | `Uniform scaling operator <http://en.wikipedia.org/wiki/Uniform_scaling>`_ |
55+
+--------------------+-----------------------------------------------------------------------------------+
5556

5657
Elementary operations
5758
---------------------
@@ -74,6 +75,8 @@ Elementary operations
7475
| ``Diagonal`` | X | X | XY | XY | ``inv``, ``det``, |
7576
| | | | | | ``logdet``, ``/`` |
7677
+--------------------+-------+-------+-------+-------+---------------------+
78+
| ``UniformScaling`` | X | X | XYZ | XYZ | ``/`` |
79+
+--------------------+-------+-------+-------+-------+---------------------+
7780

7881
Legend:
7982

@@ -116,3 +119,7 @@ Legend:
116119
| D | An optimized method to find the characteristic vectors corresponding to the characteristic values ``x=[x1, x2,...]`` is available | ``eigvecs(M, x)`` |
117120
+---+-----------------------------------------------------------------------------------------------------------------------------------+------------------------+
118121

122+
The uniform scaling operator
123+
--------------------------
124+
A ``UniformScaling`` operator represents a scalar times the identity operator, ``λ*I``. The identity operator ``I`` is defined as a constant and is an instance of ``UniformScaling``. The size of these operators are generic and match the other matrix in the binary operations ``+``,``-``,``*`` and ``\``. For ``A+I`` and ``A-I`` this means that ``A`` must be square. Multiplication with the identity operator ``I`` is a noop (except for checking that the scaling factor is one) and therefore almost without overhead.
125+

test/arrayops.jl

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ b = a+a
1212
@test length((1,)) == 1
1313
@test length((1,2)) == 2
1414

15-
@test isequal(1+[1,2,3], [2,3,4])
16-
@test isequal([1,2,3]+1, [2,3,4])
17-
@test isequal(1-[1,2,3], [0,-1,-2])
18-
@test isequal([1,2,3]-1, [0,1,2])
15+
@test isequal(1.+[1,2,3], [2,3,4])
16+
@test isequal([1,2,3].+1, [2,3,4])
17+
@test isequal(1.-[1,2,3], [0,-1,-2])
18+
@test isequal([1,2,3].-1, [0,1,2])
1919

2020
@test isequal(5*[1,2,3], [5,10,15])
2121
@test isequal([1,2,3]*5, [5,10,15])
22-
@test isequal(1/[1,2,5], [1.0,0.5,0.2])
22+
@test isequal(1./[1,2,5], [1.0,0.5,0.2])
2323
@test isequal([1,2,3]/5, [0.2,0.4,0.6])
2424

2525
a = ones(2,2)
@@ -703,10 +703,9 @@ begin
703703
@test all(b.==6)
704704

705705
# issue #5141
706+
## Update Removed the version that removes the dimensions when dims==1:ndims(A)
706707
c1 = mapslices(x-> maximum(-x), a, [])
707708
@test c1 == -a
708-
c2 = mapslices(x-> maximum(-x), a, [1,2])
709-
@test c2 == maximum(-a)
710709

711710
# other types than Number
712711
@test mapslices(prod,["1" "2"; "3" "4"],1) == ["13" "24"]
@@ -859,7 +858,7 @@ end
859858
@test isequal(flipdim([2,3,1], 2), [2,3,1])
860859
@test isequal(flipdim([2 3 1], 1), [2 3 1])
861860
@test isequal(flipdim([2 3 1], 2), [1 3 2])
862-
@test_throws flipdim([2,3,1] -1)
861+
@test_throws flipdim([2,3,1], -1)
863862
@test isequal(flipdim(1:10, 1), 10:-1:1)
864863
@test isequal(flipdim(1:10, 2), 1:10)
865864
@test_throws flipdim(1:10, -1)

0 commit comments

Comments
 (0)