API Manual
Compiling Tools (Reexported from NiLangCore)
NiLangCore.@assign
— Macro@assign a b [invcheck]
Perform the assign a = b
in a reversible program. Turn off invertibility check if the invcheck
is false.
NiLangCore.@assignback
— Macro@assignback f(args...) [invcheck]
Assign input variables with output values: args... = f(args...)
, turn off invertibility error check if the second argument is false.
NiLangCore.@code_julia
— Macro@code_julia ex
Get the interpreted expression of ex
.
julia> using MacroTools
julia> prettify(@code_julia x += exp(3.0))
:(@assignback (PlusEq(exp))(x, 3.0))
NiLangCore.@code_preprocess
— Macro@code_preprocess ex
Preprocess ex
and return the symmetric reversible IR.
julia> using MacroTools
julia> prettify(@code_preprocess if (x < 3, ~) x += exp(3.0) end)
:(if (x < 3, x < 3)
x += exp(3.0)
else
end)
NiLangCore.@code_reverse
— Macro@code_reverse ex
Get the reversed expression of ex
.
julia> @code_reverse x += exp(3.0)
:(x -= exp(3.0))
NiLangCore.@dual
— Macro@dual f invf
Define f
and invf
as a pair of dual instructions, i.e. reverse to each other.
NiLangCore.@fieldview
— Macro@fieldview fname(x::TYPE) = x.fieldname
Create a function fieldview that can be accessed by a reversible program
julia> struct GVar{T, GT}
x::T
g::GT
end
julia> @fieldview xx(x::GVar) = x.x
julia> chfield(GVar(1.0, 0.0), xx, 2.0)
GVar{Float64,Float64}(2.0, 0.0)
NiLangCore.@i
— Macro@i function fname(args..., kwargs...) ... end
@i struct sname ... end
Define a reversible function/type.
julia> @i function test(out!, x)
out! += identity(x)
end
julia> test(0.2, 0.8)
(1.0, 0.8)
julia> @i struct CVar{T}
g::T
x::T
function CVar{T}(x::T, g::T) where T
new{T}(x, g)
end
function CVar(x::T, g::T) where T
new{T}(x, g)
end
# warning: infered type `T` should be used in `← new` statement only.
@i function CVar(xx::T) where T
gg ← zero(xx)
gg += identity(1)
xx ← new{T}(gg, xx)
end
end
julia> CVar(0.2)
CVar{Float64}(1.0, 0.2)
julia> (~CVar)(CVar(0.2))
0.2
See test/compiler.jl
and test/invtype.jl
for more examples.
NiLangCore.@icast
— Macro@icast TDEF1 => TDEF2 begin ... end
Define type cast between TDEF1
and TDEF2
. Type TDEF1
or TDEF2
can be T(args...)
or x::T
like.
julia> struct PVar{T}
g::T
x::T
end
julia> struct SVar{T}
x::T
g::T
end
julia> @icast PVar(g, x) => SVar(x, k) begin
g → zero(x)
k ← zero(x)
k += identity(x)
end
julia> x = PVar(0.0, 0.5)
PVar{Float64}(0.0, 0.5)
julia> @instr (PVar=>SVar)(x)
SVar{Float64}(0.5, 0.5)
julia> @instr (SVar=>PVar)(x)
PVar{Float64}(0.0, 0.5)
julia> @icast x::Base.Float64 => SVar(x, gg) begin
gg ← zero(x)
gg += identity(x)
end
julia> x = 0.5
0.5
julia> @instr (Float64=>SVar)(x)
SVar{Float64}(0.5, 0.5)
julia> @instr (SVar=>Float64)(x)
0.5
NiLangCore.@instr
— Macro@instr ex
Execute a reversible instruction.
NiLangCore.@invcheck
— Macro@invcheck ex
@invcheck x val
Pass the check it if ex
is true or x ≈ val
.
NiLangCore.@pure_wrapper
— Macro@pure_wrapper TYPE
Create a reversible wrapper type TYPE{T} <: IWrapper{T}
that plays a role of simple wrapper.
julia> @pure_wrapper A
julia> A(0.5)
A(0.5)
julia> (~A)(A(0.5))
0.5
julia> -A(0.5)
A(-0.5)
julia> A(0.5) < A(0.6)
true
NiLangCore.@selfdual
— Macro@selfdual f
Define f
as a self-dual instructions.
NiLangCore.almost_same
— Methodalmost_same(a, b; atol=GLOBAL_ATOL[], kwargs...) -> Bool
Return true if a
and b
are almost same w.r.t. atol
.
NiLangCore.check_inv
— Methodcheck_inv(f, args; atol::Real=1e-8, verbose::Bool=false, kwargs...)
Return true if f(args..., kwargs...)
is reversible.
NiLangCore.chfield
— Functionchfield(x, field, val)
Change a field
of an object x
.
The field
can be a Val
type
julia> chfield(1+2im, Val(:im), 5)
1 + 5im
or a function
julia> using NiLangCore
julia> struct GVar{T, GT}
x::T
g::GT
end
julia> @fieldview xx(x::GVar) = x.x
julia> chfield(GVar(1.0, 0.0), xx, 2.0)
GVar{Float64,Float64}(2.0, 0.0)
NiLangCore.isprimitive
— Methodisprimitive(f)
Return true
if f
is an instruction
that can not be decomposed anymore.
NiLangCore.isreflexive
— Methodisreflexive(f)
Return true
if a function is self-inverse.
NiLangCore.isreversible
— Methodisreversible(f, ARGT)
Return true
if a function is reversible.
NiLangCore.nargs
— Functionnargs(instr)
Number of arguments as the input of instr
.
NiLangCore.nilang_ir
— Methodnilang_ir(ex; reversed::Bool=false)
Get the NiLang reversible IR from the function expression ex
, return the reversed function if reversed
is true
.
This IR is not directly executable on Julia, please use macroexpand(Main, :(@i function .... end))
to get the julia expression of a reversible function.
julia> ex = :(@inline function f(x!::T, y) where T
anc ← zero(T)
@routine anc += identity(x!)
x! += y * anc
~@routine
end);
julia> nilang_ir(ex) |> MacroTools.prettify
:(@inline function f(x!::T, y) where T
anc ← zero(T)
anc += identity(x!)
x! += y * anc
anc -= identity(x!)
anc → zero(T)
end)
julia> nilang_ir(ex; reversed=true) |> MacroTools.prettify
:(@inline function (~f)(x!::T, y) where T
anc ← zero(T)
anc += identity(x!)
x! -= y * anc
anc -= identity(x!)
anc → zero(T)
end)
NiLangCore.nouts
— Functionnouts(instr)
Number of output arguments in the input list of instr
.
NiLangCore.tget
— Methodtget(i::Int)
Get the i-th entry of a tuple.
NiLangCore.unwrap
— Methodunwrap(x)
Unwrap a wrapper instance (recursively) to get the original value.
NiLangCore.value
— Methodvalue(x)
Get the value
from a wrapper instance.
NiLangCore.DivEq
— TypeDivEq{FT} <: Function
DivEq(f)
Called when executing out /= f(args...)
instruction. See PlusEq
for detail.
NiLangCore.IWrapper
— TypeIWrapper{T} <: Real
IWrapper{T} is a wrapper of for data of type T. It will forward >, <, >=, <=, ≈
operations.
NiLangCore.Inv
— TypeInv{FT} <: Function
Inv(f)
The inverse of a function.
NiLangCore.InvertibilityError
— TypeInvertibilityError <: Exception
InvertibilityError(ex)
The error thrown when a irreversible statement appears in a reversible context.
NiLangCore.MinusEq
— TypeMinusEq{FT} <: Function
MinusEq(f)
⊖(f)
Called when executing out -= f(args...)
instruction. See PlusEq
for detail.
NiLangCore.MulEq
— TypeMulEq{FT} <: Function
MulEq(f)
Called when executing out *= f(args...)
instruction. See PlusEq
for detail.
NiLangCore.Partial
— TypePartial{FIELD, T, T2} <: IWrapper{T2}
Take a field FIELD
without dropping information. This operation can be undone by calling ~Partial{FIELD}
.
NiLangCore.PlusEq
— TypePlusEq{FT} <: Function
PlusEq(f)
⊕(f)
Called when executing out += f(args...)
instruction. The following two statements are same
julia> x, y, z = 0.0, 2.0, 3.0
(0.0, 2.0, 3.0)
julia> x, y, z = PlusEq(*)(x, y, z)
(6.0, 2.0, 3.0)
julia> x, y, z = 0.0, 2.0, 3.0
(0.0, 2.0, 3.0)
julia> @instr x += y*z
3.0
julia> x, y, z
(6.0, 2.0, 3.0)
NiLangCore.XorEq
— TypeXorEq{FT} <: Function
XorEq(f)
⊙(f)
Called when executing out ⊻= f(args...)
instruction. See PlusEq
for detail.
NiLangCore.assign_vars
— Methodassign_vars(args, symres; invcheck)
Get the expression of assigning symres
to args
.
NiLangCore.bcast_assign_vars
— MethodThe broadcast version of assign_vars
NiLangCore.compile_ex
— Methodtranslate to normal julia code.
NiLangCore.dual_ex
— Methoddual_ex(m::Module, ex)
Get the dual expression of ex
.
NiLangCore.struct2namedtuple
— Methodconvert an object to a named tuple.
Instructions
NiLang.@zeros
— MacroCreate zeros of specific type.
julia> @i function f(x)
@zeros Float64 a b c
# do something
end
NiLang.DEC
— MethodDEC(a!) -> a! - 1
NiLang.INC
— MethodINC(a!) -> a! + 1
NiLang.IROT
— MethodIROT(a!, b!, θ) -> ROT(a!, b!, -θ)
NiLang.ROT
— MethodROT(a!, b!, θ) -> a!', b!', θ
NiLang.SWAP
— MethodSWAP(a!, b!) -> b!, a!
NiLang.arshift
— Methodarshift(x, n)
right shift, sign extending.
NiLang.mulint
— Methodmulint(a!, b::Integer) -> a!*b, b
NiLang.plshift
— Methodplshift(x, n)
periodic left shift.
NiLang.prshift
— Methodplshift(x, n)
periodic right shift.
NiLang.rot
— Methodrot(a, b, θ)
rotate variables a
and b
by an angle θ
NiLang.AutoBcast
— TypeAutoBcast{T,N} <: IWrapper{T}
A vectorized variable.
NiLang.NoGrad
— TypeNoGrad{T} <: IWrapper{T}
NoGrad(x)
A NoGrad(x)
is equivalent to GVar^{-1}(x)
, which cancels the GVar
wrapper.
Automatic Differentiation
NiLang.AD.@nograd
— Macro@nograd f(args...)
Mark f(args...)
as having no gradients.
NiLang.AD.check_grad
— Methodcheck_grad(f, args; atol::Real=1e-8, verbose::Bool=false, iloss::Int, kwargs...)
Return true if the gradient of f(args..., kwargs...)
is reversible.
NiLang.AD.grad
— Methodgrad(var)
Get the gradient field of var
.
NiLang.AD.gradient_numeric
— Methodgradient_numeric(f, args...; iloss, kwargs...)
Numeric differentiating f(args..., kwargs...).
NiLang.AD.hessian_backback
— Methodhessian_backback(f, args; iloss::Int, kwargs...)
Obtain the Hessian matrix of f(args..., kwargs...)
by back propagating adjoint program.
NiLang.AD.jacobian
— Methodjacobian(f, args...; iin::Int, iout::Int=iin, kwargs...)
Get the Jacobian matrix for function f(args..., kwargs...)
using vectorized variables in the gradient field. One can use key word arguments iin
and iout
to specify the input and output tensor.
NiLang.AD.jacobian_repeat
— Methodjacobian_repeat(f, args...; iin::Int, iout::Int=iin, kwargs...)
Get the Jacobian matrix for function f(args..., kwargs...)
using repeated computing gradients for each output. One can use key word arguments iin
and iout
to specify the input and output tensor.
NiLang.AD.GVar
— TypeGVar{T,GT} <: IWrapper{T}
GVar(x)
Attach a gradient field to x
.
NiLang.AD.NGrad
— TypeNGrad{N,FT} <: Function
Obtain gradients Grad(f)(Val(i), args..., kwargs...)
, where i
is the index of loss in args
. Grad
object calls forward first, and then backward.
Val(1)
is specially optimized, so putting the loss as the first parameter can avoid potential overhead.
```