The core package for reversible eDSL NiLang.
Warning Requires Julia version >= 1.3.
- Define a pair of dual instructions
julia> using NiLangCore
julia> function ADD(a!::Number, b::Number)
a! + b, b
end
ADD (generic function with 3 methods)
julia> function SUB(a!::Number, b::Number)
a! - b, b
end
SUB (generic function with 3 methods)
julia> @dual ADD SUB
- Define a reversible function
julia> @i function test(a, b)
SUB(a, b)
end
julia> using NiLangCore
julia> @code_reverse x += f(y)
:(x -= f(y))
julia> @code_reverse x .+= f.(y)
:(x .-= f.(y))
julia> @code_reverse x ⊻= f(y)
:(x ⊻= f(y))
julia> @code_reverse x ← zero(T)
:(x → zero(T))
julia> @code_reverse begin y += f(x) end
quote
#= /home/leo/.julia/dev/NiLangCore/src/dualcode.jl:82 =#
y -= f(x)
#= REPL[52]:1 =#
end
julia> julia> @code_reverse if (precond, postcond) y += f(x) else y += g(x) end
:(if (postcond, precond)
#= /home/leo/.julia/dev/NiLangCore/src/dualcode.jl:69 =#
y -= f(x)
#= REPL[48]:1 =#
else
#= /home/leo/.julia/dev/NiLangCore/src/dualcode.jl:69 =#
y -= g(x)
#= REPL[48]:1 =#
end)
julia> @code_reverse while (precond, postcond) y += f(x) end
:(@from !postcond while precond
#= /home/leo/.julia/dev/NiLangCore/src/dualcode.jl:72 =#
y -= f(x)
#= REPL[49]:1 =#
end)
julia> @code_reverse for i=start:step:stop y += f(x) end
:(for i = stop:-step:start
#= /home/leo/.julia/dev/NiLangCore/src/dualcode.jl:76 =#
y -= f(x)
#= REPL[50]:1 =#
end)
julia> @code_reverse @safe println(x)
:(#= /home/leo/.julia/dev/NiLangCore/src/dualcode.jl:81 =# @safe println(x))
The ←
(\leftarrow + TAB) operation copies B to A, its inverse is →
(\rightarrow + TAB)
- push into a stack,
A[end+1] ← B
=>[A..., B], B
- add a key-value pair into a dict,
A[i] ← B
=>{A..., i=>B}, B
- allocate a new ancilla,
(A = ∅) ← B
=>(A = B), B
The ↔
(\leftrightarrow + TAB) operation swaps B and A, it is self reversible
- swap two variables,
A ↔ B
=>B, A
- transfer into a stack,
A[end+1] ↔ B
=>[A..., B], ∅
- transfer a key-value pair into a dict,
A[i] ↔ B
=>haskey ? {(A\A[i])..., i=>B}, A[i] : {A..., i=>B}, ∅
- transfer the value of two variables,
(A = ∅) ↔ B
=>(A = B), ∅
One can use var::∅
to annotate var
as a fresh new variable (only new variables can be allocated), use var[end+1]
to represent stack top for push and var[end]
for stack top for pop.