feat(kcsolve): pybind11 bindings and Python solver support #298

Merged
forbes merged 3 commits from feat/solver-api-types into main 2026-02-20 01:10:02 +00:00
Owner

Summary

Phase 2 of the pluggable solver system (#288): exposes the KCSolve C++ API to Python via pybind11, enabling scripted solver access and pure-Python solver backends.

Builds on Phase 1 (PR #297) which introduced the IKCSolver interface, SolverRegistry, and OndselAdapter.

Changes

New files

  • Solver/bindings/PyIKCSolver.h — pybind11 trampoline class enabling Python IKCSolver subclasses. Uses PYBIND11_OVERRIDE_PURE for 3 pure virtuals (name, supported_joints, solve) and PYBIND11_OVERRIDE for 10 defaulted virtuals.

  • Solver/bindings/kcsolve_py.cpp — Complete pybind11 module (import kcsolve):

    • 5 enums: BaseJointKind (24 values), SolveStatus, DiagnosticKind, MotionKind, LimitKind
    • 10 struct bindings: Transform, Part, Constraint + Limit, MotionDef, SimulationParams, SolveContext, ConstraintDiagnostic, SolveResult + PartResult
    • 2 class bindings: IKCSolver (with trampoline), OndselAdapter
    • 6 module functions wrapping SolverRegistry: available(), load(), joints_for(), set_default(), get_default(), register_solver()
    • PySolverHolder — forwards all virtual calls with GIL acquisition, prevents Python GC of solver instances
    • API_VERSION_MAJOR = 1
  • Solver/bindings/CMakeLists.txt — pybind11 build target following the tsp_solver pattern. Output placed in Mod/Assembly/ as kcsolve.so.

  • AssemblyTests/TestKCSolvePy.py — 16 Python tests across 4 classes:

    • TestKCSolveImport — module loads, key symbols exist, API version
    • TestKCSolveTypes — Transform identity, Part defaults, SolveContext construction, enum values, Constraint/SolveResult fields
    • TestKCSolveRegistry — available(), load(), unknown solver handling, default management
    • TestPySolver — Python subclass instantiation, solve round-trip, register/load/solve full cycle, default virtual methods

Modified files

  • IKCSolver.h — Constructor moved from protected to public for pybind11 trampoline access. Class remains abstract (3 pure virtuals prevent direct instantiation).
  • Solver/CMakeLists.txtadd_subdirectory(bindings) gated on FREECAD_USE_PYBIND11
  • CMakeLists.txt — Added TestKCSolvePy.py to AssemblyTests_SRCS
  • TestAssemblyWorkbench.py — Registered 4 new test classes

Usage

import kcsolve

# List registered solvers
print(kcsolve.available())

# Load and use the Ondsel solver
solver = kcsolve.load("ondsel")
ctx = kcsolve.SolveContext()
result = solver.solve(ctx)

# Create a Python solver backend
class MySolver(kcsolve.IKCSolver):
    def name(self):
        return "MySolver"
    def supported_joints(self):
        return [kcsolve.BaseJointKind.Fixed]
    def solve(self, ctx):
        r = kcsolve.SolveResult()
        r.status = kcsolve.SolveStatus.Success
        return r

kcsolve.register_solver("my_solver", MySolver)

Testing

  • 18 C++ tests pass (KCSolve_tests_run) — no regressions
  • 16 new Python tests pass (TestKCSolvePy)

Closes #288

## Summary Phase 2 of the pluggable solver system (#288): exposes the KCSolve C++ API to Python via pybind11, enabling scripted solver access and pure-Python solver backends. Builds on Phase 1 (PR #297) which introduced the `IKCSolver` interface, `SolverRegistry`, and `OndselAdapter`. ## Changes ### New files - **`Solver/bindings/PyIKCSolver.h`** — pybind11 trampoline class enabling Python `IKCSolver` subclasses. Uses `PYBIND11_OVERRIDE_PURE` for 3 pure virtuals (`name`, `supported_joints`, `solve`) and `PYBIND11_OVERRIDE` for 10 defaulted virtuals. - **`Solver/bindings/kcsolve_py.cpp`** — Complete pybind11 module (`import kcsolve`): - 5 enums: `BaseJointKind` (24 values), `SolveStatus`, `DiagnosticKind`, `MotionKind`, `LimitKind` - 10 struct bindings: `Transform`, `Part`, `Constraint` + `Limit`, `MotionDef`, `SimulationParams`, `SolveContext`, `ConstraintDiagnostic`, `SolveResult` + `PartResult` - 2 class bindings: `IKCSolver` (with trampoline), `OndselAdapter` - 6 module functions wrapping `SolverRegistry`: `available()`, `load()`, `joints_for()`, `set_default()`, `get_default()`, `register_solver()` - `PySolverHolder` — forwards all virtual calls with GIL acquisition, prevents Python GC of solver instances - `API_VERSION_MAJOR = 1` - **`Solver/bindings/CMakeLists.txt`** — pybind11 build target following the `tsp_solver` pattern. Output placed in `Mod/Assembly/` as `kcsolve.so`. - **`AssemblyTests/TestKCSolvePy.py`** — 16 Python tests across 4 classes: - `TestKCSolveImport` — module loads, key symbols exist, API version - `TestKCSolveTypes` — Transform identity, Part defaults, SolveContext construction, enum values, Constraint/SolveResult fields - `TestKCSolveRegistry` — available(), load(), unknown solver handling, default management - `TestPySolver` — Python subclass instantiation, solve round-trip, register/load/solve full cycle, default virtual methods ### Modified files - **`IKCSolver.h`** — Constructor moved from `protected` to `public` for pybind11 trampoline access. Class remains abstract (3 pure virtuals prevent direct instantiation). - **`Solver/CMakeLists.txt`** — `add_subdirectory(bindings)` gated on `FREECAD_USE_PYBIND11` - **`CMakeLists.txt`** — Added `TestKCSolvePy.py` to `AssemblyTests_SRCS` - **`TestAssemblyWorkbench.py`** — Registered 4 new test classes ## Usage ```python import kcsolve # List registered solvers print(kcsolve.available()) # Load and use the Ondsel solver solver = kcsolve.load("ondsel") ctx = kcsolve.SolveContext() result = solver.solve(ctx) # Create a Python solver backend class MySolver(kcsolve.IKCSolver): def name(self): return "MySolver" def supported_joints(self): return [kcsolve.BaseJointKind.Fixed] def solve(self, ctx): r = kcsolve.SolveResult() r.status = kcsolve.SolveStatus.Success return r kcsolve.register_solver("my_solver", MySolver) ``` ## Testing - 18 C++ tests pass (`KCSolve_tests_run`) — no regressions - 16 new Python tests pass (`TestKCSolvePy`) Closes #288
forbes added 1 commit 2026-02-20 00:05:23 +00:00
feat(kcsolve): pybind11 bindings and Python solver support
All checks were successful
Build and Test / build (pull_request) Successful in 29m19s
7ea0078ba3
Add the kcsolve pybind11 module exposing the KCSolve C++ API to Python:

- PyIKCSolver trampoline enabling Python IKCSolver subclasses
- Bindings for all 5 enums, 10 structs, IKCSolver, and OndselAdapter
- Module functions wrapping SolverRegistry (available, load, joints_for,
  set_default, get_default, register_solver)
- PySolverHolder class forwarding virtual calls with GIL acquisition
- register_solver() for runtime Python solver registration

IKCSolver constructor moved from protected to public for pybind11
trampoline access (class remains abstract via 3 pure virtuals).

Includes 16 Python tests covering module import, type bindings, enum
values, registry functions, Python solver subclassing, and full
register/load/solve round-trip.

Closes #288
forbes added 1 commit 2026-02-20 00:59:13 +00:00
docs: KCSolve architecture and Python API reference
All checks were successful
Build and Test / build (pull_request) Successful in 28m58s
406e120180
- Replace OndselSolver architecture doc with KCSolve pluggable solver
  architecture covering IKCSolver interface, SolverRegistry, OndselAdapter,
  Python bindings, file layout, and testing
- Add kcsolve Python API reference with full type documentation, module
  functions, usage examples, and pybind11 vector-copy caveat
- Add INTER_SOLVER.md spec (previously untracked) with Phase 1 and Phase 2
  marked as complete
- Update SUMMARY.md with new page links
forbes added 1 commit 2026-02-20 01:06:10 +00:00
docs(kcsolve): expand Python API reference with full method docs
All checks were successful
Build and Test / build (pull_request) Successful in 29m49s
bd43e62822
Expand SolveContext field descriptions (motions, simulation, bundle_fixed),
Constraint params table, marker explanations, Constraint.Limit descriptions,
MotionDef field descriptions, SimulationParams field descriptions, and all
optional IKCSolver methods with signatures, parameter docs, and usage
examples (interactive drag protocol, kinematic simulation, diagnostics,
export_native, capability queries).
forbes merged commit b02bcbfe46 into main 2026-02-20 01:10:02 +00:00
forbes deleted branch feat/solver-api-types 2026-02-20 01:10:06 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: kindred/create#298