Vektor Flow
A language experiment for keeping array analysis and visual exploration close together.
A different axis. Another transformation. One promising curve. Vektor Flow is an experimental language built around keeping those steps close to the mathematics you want to investigate.
FOLLOW THE EVIDENCE
A smaller gap between an idea and an answer.
Experimental work moves through questions. Which sample changes most? What happens if the same operation runs along another dimension? Does a feature survive a different view?
Those questions can demand memory planning, array reshaping, worker pools and another plotting interface before the investigation resumes. VKF brings dimensions, functions, quantities and views into one language design.
The aim is to let the question set the pace. Arbitrary disk-backed evaluation and automatic CPU/GPU placement remain goals that need their own implementation evidence.
HOW THE DESIGN HELPS
Keep the relationship visible.
Say which axis matters.
Name dimensions and select the ones a function consumes. Keep the remaining axes in the result.
Work with axes →Reuse an idea directly.
Bind one parameter, compose another function, or transform a model into its derivative.
Bind a parameter →Inspect what the numbers mean.
Add coordinate data to a viewport and compare layers through the same visual model.
Build a view →WHAT YOU CAN TRY
Which site has the strongest contrast?
Four generated Lorentzian spectra. Calculate contrast on 257 energy samples per site, then inspect site 2 on a denser 513-sample curve.
Run the program, then change selected_site:2 to selected_site:1 and run again. The output and curve update from the edited source.
Run to plot a generated Lorentzian spectrum. These are synthetic data, not measurements.
Generated, in-memory data; no file access or performance claim. Complete equivalent Python program →
WHERE IT STARTED
Why I started building it.
Vektor Flow is an experiment in building the language I wished I had during my research.
My work in experimental quantum materials meant exploring large measurements, changing algorithms and inspecting the patterns they revealed. I built parallel infrastructure and spent hours writing viewers. I wanted dimensions, functions and views to fit together more naturally.
Existing tools already offer selective reads, dimensional operations, parallel execution and multidimensional viewers. I want to find out how much repeated work can disappear when those ideas are designed together.
The preview demonstrates small array investigations, argument binding and coordinate plots. General disk-backed evaluation and automatic profitable CPU/GPU placement remain goals. Start with the examples: they make the design concrete enough to question, change and judge.
Read the fuller story and existing-tool context
Why I am building Vektor Flow
Vektor Flow is an experiment in building the language I wished I had during my research. I want to see how much of the repeated work around data access, dimensions, parallel execution and plotting can disappear when those ideas are designed together. My background is in experimental quantum materials, experimenting with algorithms to reveal patterns in large measurements. Too often, changing the question meant rebuilding the machinery around it. Those experiences set the aim; how much a language can resolve remains an open question.
1. Follow the question through the data
Imagine a spectral map: an energy trace at each measurement position. Compare the variation in each trace, inspect the spectrum behind an unusual result, then change the transformation or the dimension it acts on. This is an illustrative investigation, grounded in the kind of work that motivated me.
The important continuity is between those steps. A selected trace should retain its relationship to the energy coordinates; reducing the energy dimension should leave the measurement positions available for comparison. Existing libraries can express this workflow. My design preference is to carry that intent through function application, evaluation and visualization without repeatedly translating it between interfaces.
2. Ask for a result before materializing everything
Memory was a recurring frustration. For an ordinary numeric h5py Dataset, np.sin(ds)[idx] converts the dataset to an array before selecting the result; np.sin(ds[idx]) reads the selection first. h5py already supports selective reads. Dask goes further: an elementwise-operation-then-slice graph can exclude unrelated computation blocks. These are useful mechanisms, not missing features. [1]
But computation blocks and physical storage chunks are different. Selecting one value can still require reading and decompressing its storage chunk. Nor can every operation move past a selection: an elementwise sine needs the selected values, a reduction needs its reduction domain, and a neighborhood operation needs surrounding values. [1]
I want lazy evaluation across composed operations and indexing to be the ordinary language model, with optimizations that preserve those dependencies. That requires semantics an optimizer can reason about, beyond a convenient reader API. General disk-backed evaluation remains a goal; the current spectral example is in memory. Lower peak RAM or faster execution would need measurements of identical pipelines.
3. Make the dimension part of the function application
In signal analysis, the same mathematical operation may act along one dimension or several. NumPy broadcasting already handles compatible shapes; generalized ufuncs describe core dimensions, and einsum expresses contractions and permutations. xarray's apply_ufunc names core dimensions, while JAX's vmap maps functions over selected axes. Julia also gives broadcasting and fusion direct language syntax. [2]
These mechanisms solve substantial parts of the problem. Broadcasting alone, however, does not specify which dimensions an arbitrary analysis function consumes. I want that choice visible where the function is applied. The preview's span_u(spectra) applies a defined max-minus-min function along the energy dimension and retains the position dimension. That small example makes the intended relationship inspectable; it does not establish automatic vectorization of every function.
4. Keep execution choices connected to the calculation
I built my own pools and parallel infrastructure. I wanted more of that responsibility to move into the system, so changing an analysis would require fewer surrounding decisions.
Current alternatives already do real work here: Numba supports CPU parallelization, CuPy and JAX provide GPU computation, and Dask supplies schedulers. Their documentation also explains why placement is consequential: compilation, scheduling, transfers and whether data already resides on a device affect the result. A faster kernel need not make the complete investigation faster. [3]
My goal is for Vektor Flow to use the calculation's structure when deciding whether CPU or GPU execution is worthwhile. That is an optimization goal requiring measured decisions, not a current guarantee of profitable automatic placement.
5. Carry the meaning into the view
I spent hours building a viewer to navigate images. That experience matters to the design, but tools such as napari already provide multidimensional navigation and sliders. Matplotlib has a common Axes/Artist model and interactive widgets. Its different plotting functions often encode meaningful geometry: imshow displays a regular raster, whereas pcolormesh can represent cells through corner coordinates. [4]
I want coordinates, scalar fields and stacks to share a consistent view vocabulary while retaining those distinctions. The checked spectral example adds energy and signal coordinates with view.add, sets limits with view.push and displays with view.show. Broader append-based stack navigation is an intended design, not something this example demonstrates. The aim is to preserve the connection between calculation and inspection as the question changes.
6. Why pursue a language?
Argument binding was another small, repeated friction. Python's functools.partial already binds arguments; I wanted the operation directly in the notation. In the preview, G:L[n:0,m:3] binds two named arguments and leaves the remaining argument callable. I also wanted ordinary names available without a long reserved-word list. Python distinguishes reserved words from contextual soft keywords; type is not generally forbidden as a variable name. My preference concerns language design, not a missing Python capability. [5]
In early 2026, a friendly function-parser conversation led me to write a symbolic engine in Rust. Recognizing the parser as part of an abstract syntax tree opened the possibility of a language. AI-assisted development and many remakes followed.
A language lets me explore dimensions, binding, evaluation and views as connected semantics. I want to find out whether designing them together can remove more of the repeated work I encountered. Learning new notation, interoperability and the maturity of established ecosystems are real tradeoffs. The present examples make the experiment concrete enough to judge; its larger goals still have to earn their place through implementation and evidence.
Official documentation references
- h5py datasets and storage chunks · h5py dataset array conversion · Dask slicing
- NumPy universal functions · NumPy einsum · xarray core dimensions · JAX vectorization · Julia vectorized functions
- Numba parallel execution · CuPy arrays and devices · JAX benchmarking · Dask scheduling
- Matplotlib Axes · Matplotlib sliders · Matplotlib imshow · Matplotlib pcolormesh · napari viewer
- Python partial application · Python soft keywords