-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Is it possible to generate Python from code quotations? #52
Comments
One more thing: Tensor code is mostly calling just:
|
@pkese This looks interesting, but I'm not sure about the state of quotation support in Fable. We will need to investigate fable-compiler/Fable#1839 |
I don't think that full quotation support (as in in Fable runtime) would be necessary, because the hosting app is just normal dotnet CLR with all F# core libraries being accessible. It's just quotations that need to be converted into something that could be pushed into Fable compiler. I'm experimenting with mapping let logPoisson =
<@
fun (k:torch.Tensor) (mu:torch.Tensor) ->
let bcast = torch.broadcast_tensors(k, mu)
let k = bcast[0]
let mu = bcast[1]
let logPmf = k.xlogy(mu) - (k + torch.tensor 1).lgamma() - mu
-logPmf
@> ... and manage to Fable-compile the above into Python code below (I haven't implemented Fable's DeclaredTypes yet, so generated types are all Any): def arrow_1(k: Any=None) -> Callable[[Any], Any]:
def arrow_0(mu: Any=None) -> Any:
bcast : Any = torch.broadcast_tensors([k, mu])
k : Any = bcast[0]
mu : Any = bcast[1]
logPmf : Any = (k.xlogy(mu) - k + torch.tensor(1, None, False).lgamma()) - mu
return -logPmf
return arrow_0 However I'm not sure this is the best approach. It seems some AST conversion gets done in FSharp2Fable code, which is operating on Problem is that typed FSharp.Quotations.Compiler made a work-around by converting |
Btw, I've noticed that Python generated parentheses are wrong: logPmf : Any = (k.xlogy(mu) - k + torch.tensor(1, None, False).lgamma()) - mu should be logPmf : Any = k.xlogy(mu) - (k + torch.tensor(1, None, False)).lgamma() - mu I can probably fix that on my side with using witnesses and generating proper |
What did the original F# code for this line look like? Or could you make a minimal repro without the deps? |
FYI: @alfonsogarciacaro for info about quotation handling. He might provide more input here. |
@dbrattli here's a repro: let s = "Py_" + ("str" + "ing").ToUpper() + ";" compiles into s : str = ("Py_" + "str" + "ing".ToUpper()) + ";" ... and it looks like I probably have something wrong with how I'm calling compiler, because F#'s string But otherwise, yeah, I'd appreciate any hints or directions from folks that have been here before me. |
@pkese What version of Fable are you running? When I try the example I get: s : str = ("Py_" + "string".upper()) + ";" ❯ dotnet fable-py --version
4.0.0-alpha-032 Try: |
I'm using the There's no official way (or at least I didn't find it) to make Fable start compilation from some provided AST ... Fable assumes there's a project with .fs files and wants to parse that project and read those files. So I initialized |
@alfonsogarciacaro Do you have any ideas about this? |
So for the context (after spending a few days on this), I'm trying to evaluate my options. Currently I'm:
This appears to be working for simple cases and it gets me quite far - CUDA kernel code probably won't need full F# language support anyway. However I'm skipping FSharp2Fable step which I assume I'll have to partially re-implement manually (not being familiar enough with Fable internals, I don't know which transforms get applied on which step, so these are just my assumptions). A thing that I haven't yet figured out is also how to plug in replacements. In few places, I'll have to translate between F#'s TorchSharp library calls and their PyTorch counterparts (differences are minimal). The other option would be to replicate what FSharp.Quotations.Compiler is doing:
The problem here is that this would need a fork of FCS, because FCS doesn't expose any APIs that would digest untyped SynExpr AST and spit out typed FSharpExpr AST. The third option would be to compile the whole project source code with Fable and then cherry-pick and extract just bits and pieces that need to be passed to CUDA. The issue with that is that this would probably make it difficult to work with dynamic Jupyter Notebook environments, which - for many - is the standard way to develop AI code. Also my issue is that I'm just getting familiar with the whole FCS / Fable compiler infrastructure - and the learning curve is quite steep. Especially because I'm trying to plug my AST into the Fable workflow at locations, where it probably wasn't intended (and I'm wondering if any of this can maybe be made more approachable with Fable 4?). I'll appreciate any hints or directions. |
Sorry for the late reply. I need to check to understand the problem in full but from a quick read this looks like a good use case for Fable plugins. As mentioned above quotations are not supported by Fable yet, the problem is not quotation itself but in order to make them useful you need almost "full" reflection support which prevents tree shaking and produces much bigger bundle sizes. This is way #1839 is not merged yet. But quotations are only necessary if you need to manipulate the AST in runtime. If everything can be resolved at compile time a plugin should be enough. The plugin receives the AST for the decorated method (it can also inspect the AST of the full file) and/or calls to that method, together with a helper from the compiler and returns the transformed AST. If it's enough for you to do the transformations in the Fable AST, Fable2Python can later transform that to Python. But if you need to generate your custom code you can just build a string an return The problem with plugins in |
Here's an actual real world problem that Fable.Python can solve...
PyTorch has a JIT mode, that can make neural network models run much faster: Speeding up model training with PyTorch JIT
The way JIT works is:
Instead of interpreting Python neural network code and pushing tensor ops to the GPU line by line, you produce TorchScript code containing neural network model's AST.
TorchScript is restricted subset of Python that is simple enough, that it can be parsed by some C++ code inside Torch and then compiled into a CUDA kernel. And then that CUDA kernel code gets executed in a single call to the GPU making everything much faster (hence JIT).
Problem is that TorchScript (in Python world) is pain to produce. There are two ways:
F# solves this: unlike Python, F# makes it easy to produce ASTs without tracing.
So the idea is to use F# code quotations / computational expressions / reflected definitions of the F# AST code, then convert the AST into the TorchScript subset of Python, load that it into Torch to render fast CUDA kernel and then call that kernel directly.
Most of the infrastructure is already present: TorchSharp project contains the dotnet bindings to C++ Torch libraries (the same stuff that PyTorch and Lua Torch are built upon).
C++ Torch can load
.pt
files containing TorchScript models.TorchSharp could easily load
.pt
files as well (the API is there), but unfortunately the C# code is lacking the Python parser and unpickler to extract Python method signatures from TorchScript, so that part is not supported yet. But if we generated the Python code ourselves from F#, we wouldn't need to parse that Python anyway, because we knew the method signatures all along from F# AST.So the question is, which parts of Fable.Python can be reused for this?
At the minimum, I assume that Python AST parts can come handy, but I hope one can start somewhere closer to F# AST and reuse more of Fable infrastructure.
I'm just assuming that code quotations would be preferred here, because the tensor model hot-spot code that needs to get compiled into CUDA is usually just a few lines of code compared to whole project, that includes data loading, training loop, setting up the optimizers, etc... i.e. stuff that runs just fine in F# TorchSharp and can't be put on the GPU anyway. We could potentially parse the whole source file using FCS and throw everything else but the model code away.
Any thoughts?
The text was updated successfully, but these errors were encountered: