I’d like to give students in my research group a function to optimize…but I’d like to keep the functional form secret, for pedagogical purposes. Is there some way to “compile” a function or obfuscate it somehow so that another user can use it but not have access to the actual code? Or maybe someone has an even better idea to meet the teaching goal…
Have the function value served by a web page on your server?
Maybe write the function in C and compile it into a shared library and have them call it via @ccall
(or try StaticCompiler.jl)?
Actually, just providing the C source code is perhaps enough obsfucation
I’d have to remember C…it’s been decades…
Put the source code in a separate file and tell them not to look at it?
Isn’t it enough to manually obfuscate it by changing all symbol names to random names? Perhaps together with some manual code transforms purely for obfuscating purposes. If the obfuscation is only for pedagogical purposes, maybe this is enough?
Suppose my function was f(x,y) = x^2+sin(y)
. Can you use this as an example to tell me how to use IRTools to generate some intermediate representation that is directly callable? I looked at the README for IRTools; I could see how code was turned into some “lower level” julia, but it wasn’t clear to me how to call/use that lower level stuff. In the README, I didn’t fully understand
julia> f = IRTools.func(ir); # Turn the new IR into a lambda
julia> f(nothing, 10, 5)
though that seems to be what I want to do: just cut and paste the output of @code…except that only works if you give arguments to it. Can I not define a function with the intermediate representation? If I gave that to a student, they’d be able to use it but not really understand it (i.e., black box).
As a MWE, I did this:
julia> f(x,y) = x^2 + sin(y)
f (generic function with 2 methods)
julia> ir = @code_ir f(1,1)
1: (%1, %2, %3)
%4 = Core.apply_type(Base.Val, 2)
%5 = (%4)()
%6 = Base.literal_pow(Main.:^, %2, %5)
%7 = Main.sin(%3)
%8 = %6 + %7
return %8
julia> g = IRTools.func(ir)
##254 (generic function with 1 method)
julia> g(nothing,2,1)
4.841470984807897
julia> f(2,1)
4.841470984807897
Seems to do the trick. But how would I copy the output of ir = @code...
into a function definition? And why is nothing
needed as the first argument in g
? (Probably these are simple questions, but IRs are outside of my experience…)
Interesting approach. I don’t know how to call @code_ir
, but if you use @code_lowered
, and make some adjustments, it works. The result is hard to understand if the function is complex enough:
julia> f(x,y) = x^2 + sin(y)
f (generic function with 1 method)
julia> @code_lowered f(1.0,1.0)
CodeInfo(
1 ─ %1 = Core.apply_type(Base.Val, 2)
│ %2 = (%1)()
│ %3 = Base.literal_pow(Main.:^, x, %2)
│ %4 = Main.sin(y)
│ %5 = %3 + %4
└── return %5
)
I removed the tabbing and replaced the %
with var
, to get:
julia> function g(x,y)
var1 = Core.apply_type(Base.Val, 2)
var2 = (var1)()
var3 = Base.literal_pow(Main.:^, x, var2)
var4 = Main.sin(y)
var5 = var3 + var4
return var5
end
g (generic function with 1 method)
Which is the same function as f
. Unless your students are used to this and/or your function is very simple, probably that is good enough. I will use it.
The premise is that if C code is sufficiently obfuscated by its low-level nature, as someone proposed earlier, then even lower-level IR code would be even better obfuscated. I think the approach would be that you supply Julia code that includes some representation of the IR code of your function, and that Julia code would expose the function generated by IRTools.func. I expect you can probably persist that IR code as text or a binary blob. JLD2 maybe?
I don’t know, but you could wrap g with another function that calls g
passing nothing
as the first argument.
Is there some way to define a function that evals the @code_native
output? That is really obfuscated.
julia> f(x,y) = x + y
f (generic function with 1 method)
julia> @code_native f(1,1)
.text
; ┌ @ REPL[51]:1 within `f'
; │┌ @ int.jl:87 within `+'
leaq (%rdi,%rsi), %rax
; │└
retq
nopw %cs:(%rax,%rax)
; └
This might be a stupid question, but does
(v1.1) pkg> add https://github.com/JuliaLang/Example.jl
work if the Github repo is a private repo? My guess is that it does not…
If it would, the repo wouldn’t be private
Don’t know about native but I’d naively guess that @code_llvm
should be possible (don’t ask me how though…). At least there are things like Base.llvmcall
and such.
Here is a simple obfuscation scheme that is probably adequate for students who aren’t very familiar with Julia…
Contents of save_obfuscated.jl
:
using JLD2
obfuscate_string(str) = [Int(c) for c in str]
funvec = obfuscate_string(
"""
function obfuscated_function(x,y)
x^2 + 2x*y + y^2
end
""")
@save "function_archive.jld2" {compress=true} funvec
Contents of load_obfuscated.jl
:
using JLD2
@load "function_archive.jld2" funvec
include_string(@__MODULE__, string(Char.(funvec)...))
Sample usage…
First Julia session:
julia> include("save_obfuscated.jl")
A later Julia session run by students for whom you did not supply a copy of save_obfuscated.jl
:
julia> include("load_obfuscated.jl")
obfuscated_function (generic function with 1 method)
julia> obfuscated_function(3,4.0)
49.0
The file function_archive.jld2
(which you do supply to your students along with load_obfuscated.jl
) is not human-readable.
One way is to approximate the function they should approximate yourself with a different basis/package as they are using and give them the approximation as target.
If you have a simple function like f(x, y) = x^2 + sin(y) in a given domain and you want it as a black box you can learn it in a neural network. It is black box because of its complexity.
Then you can give to your students a function which encapsulate the network.
@code_native might not be portable enough