Interfaces ========== Representation and model ------------------------ **Decision.** The representation (``ssm_rep_t``) is plain data with public components, separate from the model with parameters (``ssm_model_t``), as statsmodels separates ``Representation`` and ``KalmanFilter`` from ``MLEModel``. **Why.** The algorithms need only the matrices; the separation lets other languages fill the matrices directly, and lets one model produce many representations (at different parameters) without copying its logic. **In Python.** Models own their representation. A ``MappedModel`` or ``MLEModel`` is set up on the model itself, with ``mod[name] = value`` and the ``initialize_*`` methods, which pass through to it; a user building a model need not handle a ``Representation``. It stays public as the system at given parameter values, on which the algorithms of DK Part I run (``model.representation(params)``). A ``MappedModel`` also accepts one in place of the data, to start from existing matrices. The C interface --------------- **Decision.** The library exports ``bind(C)`` routines with opaque handles, copies inputs in, copies outputs into caller-allocated buffers, returns a status code from every routine, and uses 1-based indices. **Why.** Opaque handles keep Fortran's derived types out of the interface. Copying in and out makes ownership plain: the caller owns its buffers, the library owns its objects, and nothing is shared across the boundary. Status codes, not ``stop``, leave error handling to the caller. **Alternatives.** Exposing pointers into the library's arrays would save copies, but ties the caller to the library's memory layout and lifetime. The copies cost little next to a filter run. **Where.** ``statespace_capi``, ``statespace_capi_models``, ``statespace_capi_extras``; :doc:`../reference/c_api`. Python binding and build ------------------------ **Decision.** The Python package calls the C interface through ctypes. CMake builds the shared library and scikit-build-core packages it into a wheel; fpm remains the build for Fortran users and for the tests. **Why.** ctypes is part of the standard library, needs no compiled extension, and releases the GIL during calls, which ``fit_many`` relies on. The wheel is then independent of the Python version. fpm does not build shared libraries; CMake does. Both compile the copy of L-BFGS-B bundled in ``third_party/lbfgsb``, so a build needs no network access. **Alternatives.** cffi adds a dependency whose wheels can lag new Python versions; Cython or f2py add a compiled extension. **Scope.** The package returns numpy arrays. pandas indexes and plotting were added and then removed as outside the library's scope. Three kinds of user model ------------------------- **Decision.** Python users define models in three ways: built-in components (``StructuralModel``), a declared map from parameters to matrix entries (``MappedModel``), or Python code that sets the matrices (``MLEModel``). **Why.** Speed depends on whether Python runs inside the optimization. The first two run entirely in Fortran and can be fitted in parallel. The third is the most general and familiar to statsmodels users, but calls Python at every likelihood evaluation, and 2k more times per gradient because the score differentiates ``update`` numerically. **Callback failures.** An exception in ``update`` must not leave the model broken. The library then sets H to NaN, so the evaluation fails and the optimizer backs off; it saves H first and restores it at the next call. Python stores the exception and raises it when the library returns. Callback models cannot run on ``fit_many``'s threads; both the Python layer and the library reject them. **Where.** ``statespace_mapped``, ``statespace_callback``; ``python/src/ssfortran/models.py``.