Show HN: CNET – Complex-valued neural networks from scratch in C++/CUDA A developer released CNet, a C++/CUDA framework for building and researching deep complex-valued neural networks and for optimizing complex-valued functions via gradient descent with Wirtinger derivatives, on Hacker News' Show HN. CNet ships a CPU reference and a CUDA kernel for every built-in layer, requires hand-written kernels wired into GPU dispatch through `GpuMapping` (`gpu/gpumapping.h`) and the `GPU_LAYER_TABLE` registration macro (`gpu/gpu_data.h`) with no automatic CPU-to-GPU codegen, and defines class probabilities with the Born rule p_k = |z_k|^2 / |z|^2 and loss L(z, y) = -sum_k y_k log(|z_k|^2 / |z|^2). The author positions CNet not as a competitor to PyTorch and JAX, which both support complex tensors and complex-aware autodiff, but as a complex-first, auditable substrate for research on complex-valued networks in signal communications, medical imaging and ECG, deepfake detection and acoustic analysis. CNet is a C++/CUDA framework for building and researching deep complex valued neural networks, as well as for optimization of complex valued functions using gradient descent with Wirtinger derivatives. https://en.wikipedia.org/wiki/Wirtinger derivatives Every built-in layer ships both a CPU reference and a CUDA kernel. Adding a new operator means writing its CPU forward/backward class and a CUDA kernel, then wiring the kernel into the GPU dispatch via GpuMapping gpu/gpumapping.h and the GPU LAYER TABLE registration macro gpu/gpu data.h — there is no automatic CPU→GPU codegen; the kernel is written once, by hand. Why are the complex valued neural network CVNN interesting? For one, it is a research area that is currently neglected due to the huge commercial success of the real valued ones. This Medium article https://machine-learning-made-simple.medium.com/complex-valued-neural-networks-might-be-the-future-of-deep-learning-c51f71f4c835 summarizes well why CVNN might be the future of Deep Learning: "complex numbers work very well for phasic data... The fields where these can be implemented most readily are: Signal Communications; Healthcare both medical image and ECG ; Deep Fake Detections; and Acoustic Analysis for Industrial Maintenance and expansion." Despite the fact that mathematically the field of complex numbers "in spite of the well-known and obvious identification of with these two are entirely different as far as their vector space structure is concerned." PyTorch and JAX both support complex tensors and complex-aware autodiff, and for mainstream work they are the right tools — far more mature, faster at scale, multi-GPU, and backed by large ecosystems. CNet does not compete on that axis. It exists because, for research on complex-valued networks specifically , a complex-first, transparent substrate has concrete advantages: - Complex is the default, not a bolted-on dtype. In the mainstream frameworks complex coverage is uneven: many layers, normalizations, activations, initializers and optimizers assume real tensors, so a CVNN is assembled from real building blocks plus hand-written patches whose gradient conventions must be checked case by case. In CNet every variable, layer, loss and the optimizer are complex by construction . - Wirtinger gradients are explicit, first-class objects. Each layer manipulates the pair$ \partial L/\partial z,\ \partial L/\partial \bar z $ directly, and the non-holomorphic case e.g.$|z|^2$ is handled in the open rather than hidden behind a conjugation convention. Every layer ships a CPU reference and a GPU kernel checked against each other and against finite differences, so the complex gradient is auditable , not trusted. - Spectral structure is built in. The DFT, the Hadamard spectral product, the inverse DFT and the$\mathrm{conv} = \mathrm{IFFT} \mathrm{FFT} \cdot \mathrm{FFT} $ equivalence are first-class layers, so problems whose natural domain is the frequency plane map onto the framework directly. - A physics-native model and loss — the Born rule. This is the sharpest departure from the mainstream frameworks. CNet reads a network's output as a complex amplitude vector $z$ a state and defines the class probabilities by the quantum-measurement Born rule$p k = |z k|^2 / |z|^2$ , with loss$\mathcal{L} z, y = -\sum k y k \log\big |z k|^2 / |z|^2\big $ . Classification is a measurement on an amplitude produced by a cascade of complex — often unitary — operations, rather than real logits passed through a softmax. This inductive bias is natural for physical and wave/field data: an IQ baseband sample is the complex envelope of a real passband signal, an MRI$k$ -space sample is a complex Fourier coefficient, and a wavefunction is a complex amplitude. PyTorch and JAX provide no such head or loss out of the box. - A small, auditable substrate. The full forward/backward of every layer and its CUDA kernel is explicit and readable, and the GPU execution model — a depth-ordered graph cloned across the batch — is visible and controllable. In short: use PyTorch or JAX to ship; use CNet to study complex-valued learning itself — to model data in its native complex / amplitude form with a Born-rule measurement head, to prototype operators whose complex gradients you need to see and control, and to work natively in the spectral domain. For sequence models text, bytes, Go moves or any other token stream you write only the model graph and the data; impl/cnet app.h https://github.com/crasmarum/CNet/blob/main/impl/cnet app.h supplies training on one GPU or many NCCL , Adam with warm-up and cosine decay, gradient clipping, validation with best-checkpoint saving, and inference. include "../impl/cnet app.h" struct MyData : app::DataSource { // vocab , context , sample heldout, rng, in, tgt ... }; struct MyModel : app::ModelDef { // build net, vocab, context - app::Heads embedding, loss ... }; int main int argc, char argv { FLAGS::Parse argc, argv ; MyData data ... ; MyModel model ... ; app::Trainer trainer model, data ; // options from standard flags: -steps -lr -batch -save path ... return trainer.run ; } Inference on a saved model: app::Infer inf; inf.loadModel "model.mod" ; std::vector