API Manual

Compiling Tools (Reexported from NiLangCore)

NiLangCore.@assignMacro
@assign a b [invcheck]

Perform the assign a = b in a reversible program. Turn off invertibility check if the invcheck is false.

NiLangCore.@assignbackMacro
@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_juliaMacro
@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_preprocessMacro
@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_reverseMacro
@code_reverse ex

Get the reversed expression of ex.

julia> @code_reverse x += exp(3.0)
:(x -= exp(3.0))
NiLangCore.@dualMacro
@dual f invf

Define f and invf as a pair of dual instructions, i.e. reverse to each other.

NiLangCore.@fieldviewMacro
@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.@iMacro
@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.@icastMacro
@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.@invcheckMacro
@invcheck ex
@invcheck x val

Pass the check it if ex is true or x ≈ val.

NiLangCore.@pure_wrapperMacro
@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.almost_sameMethod
almost_same(a, b; atol=GLOBAL_ATOL[], kwargs...) -> Bool

Return true if a and b are almost same w.r.t. atol.

NiLangCore.check_invMethod
check_inv(f, args; atol::Real=1e-8, verbose::Bool=false, kwargs...)

Return true if f(args..., kwargs...) is reversible.

NiLangCore.chfieldFunction
chfield(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.isprimitiveMethod
isprimitive(f)

Return true if f is an instruction that can not be decomposed anymore.

NiLangCore.nargsFunction
nargs(instr)

Number of arguments as the input of instr.

NiLangCore.nilang_irMethod
nilang_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.noutsFunction
nouts(instr)

Number of output arguments in the input list of instr.

NiLangCore.unwrapMethod
unwrap(x)

Unwrap a wrapper instance (recursively) to get the original value.

NiLangCore.DivEqType
DivEq{FT} <: Function
DivEq(f)

Called when executing out /= f(args...) instruction. See PlusEq for detail.

NiLangCore.IWrapperType
IWrapper{T} <: Real

IWrapper{T} is a wrapper of for data of type T. It will forward >, <, >=, <=, ≈ operations.

NiLangCore.InvType
Inv{FT} <: Function
Inv(f)

The inverse of a function.

NiLangCore.InvertibilityErrorType
InvertibilityError <: Exception
InvertibilityError(ex)

The error thrown when a irreversible statement appears in a reversible context.

NiLangCore.MinusEqType
MinusEq{FT} <: Function
MinusEq(f)
⊖(f)

Called when executing out -= f(args...) instruction. See PlusEq for detail.

NiLangCore.MulEqType
MulEq{FT} <: Function
MulEq(f)

Called when executing out *= f(args...) instruction. See PlusEq for detail.

NiLangCore.PartialType

Partial{FIELD, T, T2} <: IWrapper{T2}

Take a field FIELD without dropping information. This operation can be undone by calling ~Partial{FIELD}.

NiLangCore.PlusEqType
PlusEq{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.XorEqType
XorEq{FT} <: Function
XorEq(f)
⊙(f)

Called when executing out ⊻= f(args...) instruction. See PlusEq for detail.

NiLangCore.assign_varsMethod
assign_vars(args, symres; invcheck)

Get the expression of assigning symres to args.

Instructions

NiLang.@zerosMacro

Create zeros of specific type.

julia> @i function f(x)
           @zeros Float64 a b c
           # do something
       end
source
NiLang.ROTMethod
ROT(a!, b!, θ) -> a!', b!', θ
\[\begin{align} {\rm ROT}(a!, b!, \theta) = \begin{bmatrix} \cos(\theta) & - \sin(\theta)\\ \sin(\theta) & \cos(\theta) \end{bmatrix} \begin{bmatrix} a!\\ b! \end{bmatrix}, \end{align}\]
source
NiLang.NoGradType
NoGrad{T} <: IWrapper{T}
NoGrad(x)

A NoGrad(x) is equivalent to GVar^{-1}(x), which cancels the GVar wrapper.

source

Automatic Differentiation

NiLang.AD.check_gradMethod
check_grad(f, args; atol::Real=1e-8, verbose::Bool=false, iloss::Int, kwargs...)

Return true if the gradient of f(args..., kwargs...) is reversible.

source
NiLang.AD.hessian_backbackMethod
hessian_backback(f, args; iloss::Int, kwargs...)

Obtain the Hessian matrix of f(args..., kwargs...) by back propagating adjoint program.

source
NiLang.AD.jacobianMethod
jacobian(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.

source
NiLang.AD.jacobian_repeatMethod
jacobian_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.

source
NiLang.AD.NGradType
NGrad{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.

Note

Val(1) is specially optimized, so putting the loss as the first parameter can avoid potential overhead.

```

source