Correct process to use julia code from C# or VB .NET

I am currently working on a projet involving julia as a base language for mathematical computations, and .NET (C# or VB) for the GUI part.
I want to emphasize the fact that I am quite new to dll handling, julia and .NET (not general programming though) so sorry in advance if I same dumb things.

I developed a julia package for my project where everything works fine. Now I am facing the part where I need to call this julia code from .NET.

With the information provided in the documentation regarding julia code embedding in C/C++ and this thread Embedding Julia in Csharp or VB.net, I managed to get a working code calling the libjulia.dll and parse sqrt(2) in a .NET application.

Of course, my actual code is much more complex than this and I am stuck at this point in terms of strategy.

The aforementioned examples call (and do AFAICT):

  • jl_init() to spawn a julia process
  • jl_eval_string(<julia expression>) to parse a julianic expression
  • jl_atextit_hook(<error code>) to clean up the julia process and close it.

This process, from the viewpoint of .NET seems to start julia, execute one expression and close.

My concern here is that in my project, I will have to:

  • Define some parameters in .NET
  • Call a julia function with these parameters as arguments
  • Plot or use the outputs of the julia function in .NET
  • Reuse these results in other julia functions a bunch of times
  • Close julia when the .NET application is closed.

What I can’t figure at the moment is :

  • I will need to be able to use and reuse results of julia functions in both julia and .NET, is that a problem or will it lead to problems
  • I would like to avoid restarting julia at every function call
  • Do I have to call jl files from jl_eval_string or should I make my julia code a dll and if so how ? I’ve seen PackageCompiler.jl, but I don’t see how it could help here.

Thanks for the help !

5 Likes

Ok, so diving a bit further in the problem (and into the documentation describing how to use julia from C) it seems that the major issue is to declare everything in C#.

For instance, evaluating a julia expression in C requires e.g

jl_eval_string("print(sqrt(2.0))");

To call the same C code in C#, one needs to declare jl_eval_string first with

[DllImport(<libjulia_path>, CallingConvention = CallingConvention.Cdecl)] 
public static extern IntPtr jl_eval_string(string str);

then actually call it in the main.

For each new function of libjulia.dll it feels like one should wrap individually each of these functions …
Can anyone confirm this, this seems like big trouble !

2 Likes

Continuing my lonely path wandering in the insides of C# and julia, I managed to get some more things working, with however little efficiency.

For the record, this project by oheil helped me a lot with this.

In order to get references to the julia modules, either Base, Main or else, the simplest way to do this seems to be:

IntPtr <CSharp pointertomodule> = jl_eval_string("<name of module>")

for instance, for the Main module

IntPtr jl_main = jl_eval_string("Main")

Such a declaration can then be used in a subsequent jl_get_function to retrieve a function from a given module. Please note that jl_get_function is actually not expoter by the libjulia.dll dll, so one has to rewrite this function in C# using e.g.

[DllImport(libjulia_path, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr jl_symbol(string str);
[DllImport(libjulia_path, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr jl_get_global(IntPtr mod, IntPtr func);

public static IntPtr jl_get_function(IntPtr mod, string fun_str)
        {
            IntPtr fun_ptr;
            try
            {
                IntPtr fun_sym = jl_symbol(fun_str);
                fun_ptr = jl_get_global(mod, fun_sym);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
                fun_ptr = (IntPtr)0x0;
            }
            return fun_ptr;
        }

This subsequent call can be fr instance the one in the julia embedding documentation, and takes the form

 IntPtr jl_sin = jl_get_function(jl_base_module, "sin");
 IntPtr arg = jl_box_float64(4.0);
 IntPtr ret = jl_call1(jl_sin, arg);
 double ret_unbox = jl_unbox_float64(ret);

Where the jl_l... functions are defined using the appropriate [Dll....] ... definitions.

Using this approach, one can send and retrieve scalar data from and to C# / Julia.

Great ! But it only works with scalar data ! What about structures and arrays (or worse, structs containing arrays) ???

This is where I am stuck now : I can build structure in C# and box their pointer to julia, but I can’t figure how to use them correctly in julia…

[StructLayout(LayoutKind.Sequential)] unsafe public struct TestStruct
        {
            public int x;
        }

And then instantiate one in my main

namespace App
{
      class Program
      {
      // Declaration in C#
      var test = new TestStruct();
      test.x = 1;

      //Allocation of unmanaged memory
      IntPtr testPtr = Marshal.AllocHGlobal(Marshal.SizeOf<TestStruct>());
      Marshal.StructureToPtr<TestStruct>(test, testPtr, false);

      //Julia startup
      jl_init__threading();
     
      //Boxing of test pointer to julia pointer
      IntPtr box_testPtr = JuliaCall.jl_box_voidpointer(testPtr); // Works fine
      
      //What to do next if I want to e.g retrieve x in a C# variable ????
      // 
      }
}

Stay tuned for more episodes :slight_smile:

4 Likes

Waiting for new episodes… Where you able to access strings or arrays returned by julia functions in C#?

No, I had to abandon this path, at least for a while. It was simpler (yet quite compicated too) to use a messaging protocol like MsgPack than to do this. I may come back to this later… if more competent people help a bit :slight_smile:

2 Likes

I guess your Julia package mainly does numerical computing, right? In that case, I believe that it is eaiser “to use a messaging protocol” through some communication mechanism like inter-process communication (IPC). Preferably, you may try the ZeroMQ library. You can find more discussion in this thread Running Julia from JAVA? What is crazier?. Just replace Java with C#.

In short, your Julia code may set up a server and listen for requests from the C# client, e.g., the request “svd” (along with data) may require the Julia code to perform some SVD on the given data. Then, the Julia server sends the solution back to C# client. MsgPack can be used to encode the message communicated by ZeroMQ.

2 Likes

Actually, I implemented a server-client logic using BSON.jl and Json.NET - Newtonsoft with some hacks to serialize-deserialize to the correct julia format, so you guessed right.

I leave this question open in case someone is interested in this.

PS : I also tested MsgPack, but the specification is a bit too restrictive for generic arrays. It works fine for simple arrays like vectors but other than that and it can get very tricky.

1 Like

Yes, you are correct. I posted a question regarding 2D arrays to the MessagePack-CSharp package. There is no specification for multidimenstional arrays for now. Different implementations usually have different standards.

So finally did you adopt the JSON string format for communcation between Julia and .NET? I guess you still have to define the array protocol by yourself, since C or C# follows the row major while Julia is column major.

Similarly, I think we can also transfer multi-dim arrays as a 1D array, but both the client and the server need to follow our own custom pack/unpack protocol. I will try it later and post the code to GitHub.

Actually, there is some discussion about ND array storage spec for MsgPack in https://github.com/msgpack/msgpack/pull/267, but this is still an open topic.
After discussing myself with the MsgPack-C# guys on their gitter, their point of view seems to be that they provide everything to go beyond the spec, but they wont implement such features in their package as long as the new spec is not merged (it has some drawbacks apparently).

Basically, I implemented a custom resolver / formatter that follows the Julia BSON format spec. I have to admit this was quite a bummer at first because whatever the serialization protocol used, you always get implementation specific features that you need to cope with.
JSON/BSON is very versatile, but you have to re-implement the formatter/resolver
while MsgPack is much more agnostic, but not yet ready for complex structured arrays.

In the end, I chose the solution where it was easier to adapt the code. Even though MsgPack.jl and MsgPack-CSharp are quite well, it felt easier (not much more however) to implement the required code in JSON.net because inferring the (de-)coding algorithm is easier when you don’t need to translate from binary to meaning of some property.

In my personal case, the messaging protocol is very much the tip of the iceberg. The core functionality of my package is really to

  1. receive parameters
  2. compute some complex math stuff that would be painful to implement in C# and that would have missed julia speed and ecosystem
  3. send results back to C# GUI

Overall, the heavy stuff is handled by Julia, bulky data is sent almost only once and results are reduced to their minimal. Therefore, performance / storage improvements of MsgPack, though probably crucial for some other stuff, is not that important here.

If you are interested in what I implemented, I will soon be working on the optimization / refactorization of my implementation of the C# part, which is fairly reduced to the bare minimals I need… I asked some questions on StackOverflow about potential improvements, but with little success so far:

2 Likes

Regarding c# - (De)serialization of BSON data of potentially nested objects with specific structure - Stack Overflow did you want to pass a complicated structure between Julia and C#? If that is the case, I am afraid the paradigm may not be an ideal one. My personal feeling is that the ZeroMQ and MsgPack (or JSON etc.) mechanism is suitable if

  • Julia mainly does scientific computing;
  • The interaction between Julia and C# is infrequent
  • The data communicated are mainly arrays (1d or Nd) rather than complicated custom structures (like a full-fledged class in C#); otherwise, it is difficult to implement the pack/unpack operations in two different languages.

I just started working on a small project in which C# implements the UI (in WPF) and Julia provides the computing core (mainly optimization with JuMP). Your sharing helps a lot.

1 Like

Do you know this thread: Interoperability with .NET ?

It looks cool. Could you please explain the main idea under the interoperation between Julia and C#? It seems that the workhorse is jl_eval_string, but how can I pass an array to it (which may be built by the C# part via interaction with the user)?

Besides, I heard that eval in Julia is of low efficiency. Does it matter here?

Yes, actually, I started everything by reading what you did (you project doTimetable is cite above), though I did not see anything about pointers and other complex stuff…

@Shuhua In my current code, I duplicate all the custom structure definitions between the two implementations, and I create a custom JsonConverter that acts recusrively.
I will try to refactor a bit and post it here, or maybe create a github repo, I have to see…

I’ve been thinking of attempting this kind of thing for a while also (C# for front-end GUI and graphics and Julia for a back-end “computation and analytical engine”) but I’m really just starting out and nowhere close to where you have already gotten and/or oheil got with his doTimeTable application. There’s just not a lot out there on this so I applaud both of your persistence!

Just throwing this out there and not knowing too much about it myself yet, but what about leveraging the Arrow binary format to pass data back and forth? I believe the idea is it was designed exactly for this kind of passing binary data around and be an independent, language-agnostic format whereby binary Arrow data created in language A should “just work” to be read in by language B. It even looks to support streaming.

Although it looks like not every language fully supports all defined features yet according to this status page, Julia and C# do both appear to already support many of the most basic data types as well as Arrow file and stream formats.

Cheers,
Tom

Yep, that’s pretty much my conclusions too, there is nothing much out there about C# and julia. Thanks for the applause !

I personally checked Arrow myself, but currently neither the C# nor the Julia implementations support tensors so no vector or no matrix storage, which was my main concern.
I also tested MsgPack that worked fine until ND-arrays / tensors were involved.
The BSON was also a bummer at first but at least I found sources to develop my own C# formatter/resolver.

The overall message is JSON/BSON is very generic but only defines the grammar, understand only separators and basic syntax, but the specification does not consider specific objects such as tensors in a unified manner between implementations.
On the other side, Arrow or MsgPack degine very specific objects, but just do not currently support ND arrays as far as I can tell.

Is there some workaround possible for tensors/matrix, at least vectors with e.g. “List” that Julia and C# support (note “Large List” and “Fixed Size Binary” not supported by Julia, but not C#)?

At Implementation Status — Apache Arrow v4.0.1

I see Julia is the only language with full data type support for Arrow, even with one more than C++ (and @quinnj should be proud of making Julia (official) Arrow work). But (Sparse) Tensor, that neither language support is in another section “IPC Format” (where Julia still has next best support after C++, and C# least). I do see “the Python, R, Ruby and C/GLib libraries follow the C++ Arrow library.” Does that mean e.g. tensors too (for e.g. Python)? [Then for other sections after that one, Julia support drops off, I suppose all those also apply to e.g. Python.]

Often Julia prefers Julia-only code, and there was maybe a good reason for Arrow.jl. I suppose the C++ Arrow library could have been wrapper in Julia (and C#?), and still could, for as good support as for C++ and Python. Curious why wasn’t done.

I’m not sure if there’s anything to learn from e.g. this package with “The aim is to serialize only the numeric weights”, i.e. a vector, using Arrow.jl:

Hi, @BambOoxX and @tdierickx.

As inspired by the Interoperability with .NET, I have made some attempt to embed Julia inside a C# (or .NET in general) program. Though the raw C API is a little verbose, it seems feasible. I have prepared a series of brief tutorials for a beginner and for my own reference. Please check JuliaCSharp/Embedding at main · ShuhuaGao/JuliaCSharp · GitHub. Hope it helps. Any feedback is welcome. :grinning:

The plan next is to craft a .NET library that provides some wrapper classes to hide all these C API details. I have found a preliminary attempt here: GitHub - WangyuHello/JuliaSharp: C# Interop for Julia , which may serve as an initial reference.

5 Likes

Awesome! I’m looking forward to taking a closer look at these!

Interesting thread, I was just thinking about this. Especially since Microsoft is taking C# UIs cross-platform with MAUI. Also not a big fan of Julia-native UI solutions available right now. I’d like to replace a MATLAB-created GUI.

Hi @Shuhua

Perhaps we could merge some of our work together?

Here is my interop library: Interoperability with .NET - #21 by HyperSphereStudio

It provides interop from C# to Julia and Julia to C# (Julia → C# is far more primitive atm though).

I see you have some C interface backend, I have been working on the frontend as well if you want to take a look. Mind if I use some of your work for my JLArray etc?

1 Like