Anyone use Zig for scientific computing

Hi,
I just want to find out anyone using Zig for scientific computing ? I am speaking for the domains of FEM, CFD, numerical methods …
I tried to use, but the syntax seems very unforgiving with full of '@'casting and having to pass allocator everytime and so on.
For example take a look at this

Has anyone succeeded in using it more elegantly ?
Is support for scientific computing considered in this language ?

Thank you,
Ashok

1 Like

I don’t think there’s much out there, but because Zig lends itself towards data-orientated-design I think things like FEM and CFD can be good fits.

You mention having to pass std.mem.Allocator everywhere, but these types of problems tend to have large static data structures that you want to iterate over many many times. Once you’ve set-up your data structures, you don’t need your allocators and all of the code that does the simulation is just working on existing allocations. Knowing there are no allocations in your main hot-loop is a really good thing.

11 Likes

I’ve seen a few people on this forum attempt to build an MPI distribution such as OpenMPI using the zig build system, but IIRC they ran into a lot of issues related to the heavy use of macros that zig/clang didn’t handle in the same way as gcc. Might be worth giving it another go with zig 0.16 though. And honestly, I bet throwing claude at it would give you a working build on your host machine that you could iterate on from there.

Also I’m not sure if anyone is working on CUDA support with Zig; I know zig can compile to SPIR-V, but I’m not familiar with the details and how the performance would compare with native CUDA.

Otherwise, all of the CFD codes I worked on were either in Fortran, C, or C-like C++, so Zig would be a massive improvement in development experience.

At $work I built many numerical programs in zig. I didn’t have much problems other than how to express embarrassingly parallel loops.
As for casting, I find myself only needing to cast from the loop index to a float.

For allocators, I agree with @WeeBull, if in your hot path you’re allocating, that’s probably a performance mistake.

Regarding matrices, I suggest to just roll your own matrix library. It’s not much work to write one, and if you ever need to implement complex operations on it, I suggest linking to an external library. The underlying data layout are few, so most of them are intercompatible, so often you just need to call an external function in the right way.

2 Likes

Yes cuda totally work in Zig.
Arguably better than C++

The ecosystem is balbutiant to say the least

2 Likes

If you do ML there is GitHub - zml/zml: Any model. Any hardware. Zero compromise. Built with @ziglang / @openxla / MLIR / @bazelbuild · GitHub

(Disclaimer I’m working there)

ZML is actually well suited for anything you would do in Numpy.

3 Likes

Thanks for your reply. Personal matrix library makes sense.
Great to hear that you are able to use zig at your work for numerical programs. Can I know some more details about the kind of work you are doing on zig.

Mostly electromagnetic simulations of various kind (eddy current, magneto-static, high frequency), like matrix construction, solvers, post processing, but also a fast multipole library to evaluate potentials with arbitrary precision. Lot’s of work but very rewarding.
Then some code to analyze pcbs, like check self collision, automatically fix the vias, exploring them in 3D.

1 Like

It is also possible to utilize lapacke for matrix operations. It basically works out of the box with zig.

Great work ! Thanks for the pointer to ZML

If it’s of any use, I am building GitHub - srmadrid/zsl: Zig scientific library · GitHub, which includes vectors, matrices, linear algebra and more (still many features missing). It is built to easily allow generic code, so things like adding a f32 matrix to a f64 one (without explicit casting, but only upcasting being allowed, no implicit downcasting), or even defining custom numeric types. Feel free to take a look at it, maybe even contribute if you are interested, or just open an issue if you don’t have the time.

4 Likes

Thank you @srmadrid . Exactly what I am looking for ! Will use it and let you know.
Appreciate for taking the initiative.

1 Like

Is it possible to use as a real a generic type where you define the add subtract etc.. operations as functions?

Like

const Fancy = strict {
  val: f64

  pub fn add(...).... 
} 

Pretty much, although the type must contain a bit more information:

  • It must declare is_custom and is_type as true, and is_real and is_complex as true or false depending on your type. Needed always.
  • It must declare the types Accumulator, for if you want long sums to happen at higher precisions, Real, and Scalar, if you want to expose the type of the elements. All of these can be your type itself. Very commonly needed.
  • The declarations zero: N, one: N and two: N, holding the respective representations, where N is your type. Almost always needed.
  • The function standardUniform(prng: std.Random) N that generates a random number in [0, 1]. Needed only if you plan on generating random numbers with zsl.stats.

Then, for any numeric function you need (if you are missing any, a compile error will tell you which), you need to expose the function, add(x, y), for instance, and a type function, Add(X: type, Y: type) type, that given the input types returns the output type for add. If all operations return the same type you can just do something like:

pub fn Add(X: type, Y: type) type {
    return N; // N is your type
}

I might eventually change this to only need add and infer Add, but for now that’s how it is. For an example of a (mostly) complete “custom” type, you can check out zsl/src/autodiff/dual.zig at main · srmadrid/zsl · GitHub.

1 Like

Loving it I was actually thinking of duals for autodiff.
However I’m going to try and plug in my posit library just for fun.

For the design. Why a number must declare to be complex? Can’t you build a complex number from the basic real type?
Also why declare is_custom? Declarations can exist only in non numerical types so just checking for the existence of custom is a sign of it being a custom type.

You are right, is_custom is not needed and is_complex is redundant, as you would just wrap your real type in zsl.Complex. I just uploaded the changes.

Out of curiosity, what do you plan on using my library for and what functionality do you seek?

I want to eventually implement a cad kernel, and use some automatic diff library to solve the geometric constraints.

1 Like

I am all in for it. I was Learning Geometric modeling with c and found that all real world kernels are in c++. Explored OpenCascade, Solvespace… Was thinking there should be some good alternative for those who what to be free from C++ complexity. Definitely that can’t be Rust. Thats when I started exploring Zig. Great to see similar interests.

1 Like