# Register Custom Distributions `nemora` ships with a core registry (Weibull, Gamma, Johnson SB, Birnbaum-Saunders, and the generalized beta and generalized secant families). Generalised secant entries are available as `gsmN` for `N >= 2` (e.g. `gsm3`, `gsm5`) and can be extended via the same plugin hooks. The registry can be extended at runtime. You can plug in additional probability density functions either by calling the Python API directly, by installing a plugin that exposes an entry point, or by pointing the toolkit at a YAML configuration file. The helper `nemora.distributions.default_parameter_bounds()` exposes Nemora’s canonical parameter constraints (e.g. enforcing positive shape/scale parameters or simplex-constrained `omega` weights). CLI and fitting helpers fall back to these defaults whenever an individual `Distribution` does not override the `bounds` field. ## Python API ```python from typing import Mapping import numpy as np from nemora.distributions import Distribution, register_distribution def truncated_normal_pdf(x: np.ndarray, params: Mapping[str, float]) -> np.ndarray: mean = params["mu"] sigma = params["sigma"] scale = params.get("s", 1.0) z = (x - mean) / sigma return scale * np.exp(-0.5 * z**2) / (sigma * np.sqrt(2 * np.pi)) register_distribution( Distribution( name="truncnorm", parameters=("mu", "sigma", "s"), pdf=truncated_normal_pdf, notes="Example plugin distribution.", bounds={"sigma": (1e-6, None)}, extras={"source": "python-api"}, ), overwrite=True, ) ``` Once registered, the new distribution appears in `nemora registry` and is available to the HPS workflow. ## Entry points (recommended for plugins) Third-party packages can expose a callable via the `nemora.distributions` entry-point group. The callable should return either a single `Distribution` or an iterable of them. Example `pyproject.toml` snippet: ```toml [project.entry-points."nemora.distributions"] truncnorm = "my_plugin.distributions:create_truncnorm" ``` Within `my_plugin/distributions.py`: ```python from nemora.distributions import Distribution from ._pdf import truncated_normal_pdf def create_truncnorm() -> Distribution: return Distribution( name="truncnorm", parameters=("mu", "sigma", "s"), pdf=truncated_normal_pdf, notes="Truncated Normal distribution shipped by my_plugin.", bounds={"sigma": (1e-6, None)}, extras={"source": "entry-point"}, ) ``` `nemora` discovers the entry point at import time and registers the distribution automatically. ## YAML configuration Supply a YAML file that references callables or defines distributions inline. Point the environment variable `DBHDISTFIT_DISTRIBUTIONS` at one or more files separated by the OS path separator, or copy files into `config/distributions/` inside a source checkout. ```yaml metadata: title: "Custom additions" version: "1.0" distributions: - name: truncnorm parameters: ["mu", "sigma", "s"] pdf: my_plugin.pdf:truncated_normal_pdf notes: "Truncated normal PDF from my_plugin." bounds: sigma: [1e-6, null] - callable: my_plugin.factory:create_mixture overwrite: true args: [2] ``` Each item either provides a `callable` (invoked with optional `args`/`kwargs`) or an inline definition with `name`, `parameters`, and a `pdf` import path. If the callable returns multiple `Distribution` instances they are all registered. The loader skips invalid entries but reports warnings via the logger to aid debugging. ## Inspecting the registry ```bash nemora registry ``` The CLI lists built-in and plugin distributions in alphabetical order. Use `nemora --verbose distribution-name` (planned) to inspect parameters and metadata. To inspect parameter bounds and extras for a specific entry: ```bash nemora registry --describe weibull --json ``` Pass `--show-metadata` to print all distributions in a human-readable table. Programmatic workflows can call `nemora.distributions.list_registry_metadata()` to inspect bounds and extras without parsing CLI output: ```python from nemora.distributions import list_registry_metadata for entry in list_registry_metadata(): print(entry["name"], entry["parameters"], entry["bounds"].get("a")) ``` CLI users can run `nemora registry --describe gamma --show-metadata` (or `--json`) to print the same information without writing Python. ## Testing custom distributions When adding a new distribution, unit tests should exercise the PDF across representative DBH vectors and ensure parameters remain valid for grouped tallies. The helper `register_distribution` accepts `overwrite=True` so test suites can inject temporary definitions without polluting the global registry.