Create a simulation nop
A “nop” (or “no op”) is an operation that passes its inputs through unmodified. A simulation nop is a function that is not traced by the BitSAD simulator. Instead, that call stack is “passed through” unmodified. Let’s look at a concrete example.
using BitSAD
using LinearAlgebra
f(x) = reverse(x)
g(v) = norm(v)
h(x) = g(f(x))
v = SBitstream.(rand(3) ./ 10)
h(v)
SBitstream{Float64}(value = 0.15149575607935242)
with 0 bits.
If we look at the transformed simulation code for h
, we see that BitSAD recurses into the call stack of f
.
BitSAD.show_simulatable(h, v)
:(function var"##tape_h#281"(x1::typeof(Main.anonymous.h), x2::Vector{SBitstream{Float64}})
x3 = (BitSAD.getbit)(x2)
x4 = (reverse)(x2)
x5 = (LinearAlgebra.norm)(x4)
x6 = (BitSAD.getbit)(x4)
x7 = (SL2Normer(...))(x6)
x8 = (BitSAD.setbit!)(x5, x7)
return x5
end)
Suppose instead of unwrapping the call to f
, we want the simulator to call f
itself on the plain arguments. We can do this with BitSAD.@nosim
.
BitSAD.@nosim f(x)
By declaring f(x)
as a simulation nop, we prevent BitSAD from tracing into any call matching f(::Any)
. Instead, BitSAD will call f(x)
.
Tip
You can use type signatures to restrict which methods of a function are marked as simulation nops. For example, f(x::SBitstream)
will only prevent tracing into f
when typeof(x) <: SBitstream
. When no type signature is given, the argument type defaults to Any
.
BitSAD.show_simulatable(h, v)
:(function var"##tape_h#282"(x1::typeof(Main.anonymous.h), x2::Vector{SBitstream{Float64}})
x3 = (BitSAD.getbit)(x2)
x4 = (Main.anonymous.f)(x2)
x5 = (LinearAlgebra.norm)(x4)
x6 = (BitSAD.getbit)(x4)
x7 = (SL2Normer(...))(x6)
x8 = (BitSAD.setbit!)(x5, x7)
return x5
end)
Warning
Notice that f
is called on x2
directly (i.e. there is no call to getbit
). Marking a call as a simulation nop bypasses the getbit
/setbit!
calls inserted by the simulator for all arguments.