Skip to content

End-to-end tutorial: wrap an OLS regression library #35

Description

@timholy

The docs need a coherent, beginning-to-end tutorial: write a Julia library, run `build_library`, install the generated Python package, call it from Python. `abi_stress` is the wrong subject (it exists to exercise corner cases, not to teach), and welford alone exercises too little of the API now that JLWStatus / CMatrix / CString / bundling have landed.

Subject: small OLS regression library

struct FitResult
    status::JLWStatus
    coeffs::CVector{Float64}
    r_squared::Float64
end

@ccallable fit(X::CMatrix{Float64}, y::CVector{Float64},
               coeffs_buf::CVector{Float64})::FitResult
@ccallable predict(coeffs::CVector{Float64}, X::CMatrix{Float64},
                   out::CVector{Float64})::JLWStatus
@ccallable summary_report(result::FitResult, buf::CString)::JLWStatus

Why this subject

Exercises the full recognized vocabulary in one demo: `CMatrix` (design matrix), `CVector` (response, coefficients, predictions), scalar (`r_squared`), `CString` (report buffer), `JLWStatus` both as direct return and embedded in a result struct. The algorithm is one line (`X \\ y`), so the reader's attention goes to the wrapping story, not numerical methods. Rank-deficient / shape-mismatched `X` is a natural `jlw_error` path. Multi-entrypoint shape (`fit` + `predict`) shows users how multiple `@ccallable`s coexist. Python side reads like scikit-learn, which is the right cultural reference.

What does not belong

Opaque-handle / stateful patterns and parametric structs hit `# TODO: hand-wrap` and belong in a different (advanced) tutorial. Keep this one a clean welcome mat.

Tutorial outline

  1. The Julia source — `mylib.jl` with the three `@ccallable`s above, using `JLWInterop`.
  2. `Project.toml` for the entry project (note: absolute `[sources]` paths only).
  3. `build_library(...; bundle = true)` from a REPL using `JuliaC.jl`.
  4. `pip install ./out/mylib_py` in a clean virtualenv with no system Julia.
  5. Use it from Python: numpy arrays in, numpy arrays out, exceptions for bad input.
  6. "What to edit when you add an entrypoint" — regenerate `_lowlevel.py`, leave `_facade.py`, or delete it to regenerate the starter.

Real `jldoctest` for the `read_abi_info` / `write_wrapper` shape-checking steps where feasible. The full `build_library` invocation is too slow for doctest; show it as a non-tested code block and rely on the example being kept buildable manually.

Prerequisites

Verified end-to-end against `build_library` + JuliaC on Julia 1.13-rc1; success requires three pieces:

  • JuliaC.jl PR #147 (NTuple → "array" in ABI export)
  • This repo's PR Recognize "array" type (homogeneous tuples) #31 / `teh/array` branch (emitter consumes "array" kind), plus a small JLWInterop `ntuple(f, Val(N))` fix that will be bundled into the same PR

Until #31 ships, the tutorial cannot build end-to-end. (`X \\ y` itself under `--trim=safe` is verified clean — LinearAlgebra works.)

Implementation depends on #34 (docs restructure) only loosely; the tutorial can land into the existing `docs/src/` layout and move when #34 reorganizes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions