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 ?
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.
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.
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.
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.
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
}
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.
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.