← Back to Fixed Effects
Fixed Effects

CLINEAR: a slope with hard bounds

When a single coefficient β must satisfy a hard constraint (positive, negative, or inside [a, b]), use 'clinear' instead of a plain linear term. pyINLA enforces the bounds smoothly by reparameterizing β through an unconstrained internal parameter θ.

1. What it does

One slope, plus a guarantee on its sign or range

A normal linear term contributes β zi to the linear predictor, with β free on the entire real line. A clinear term keeps the same contribution but constrains β to a user-specified interval:

ηi = … + β zi + …,   with β ∈ (low, high)

Typical uses: a slope you know must be non-negative (a rate, a price, a dose effect), a probability-like coefficient bounded in (0, 1), or any case where domain knowledge gives a hard range you do not want the optimizer to leave.

2. The trick

An unconstrained θ maps to a bounded β

Inference is done on an unconstrained parameter θ; the visible slope β is recovered from θ through one of two transformations, picked automatically from the range tuple you pass:

AOne-sided

β = low + eθ

Use when β must exceed a known floor (commonly 0). range = (low, float('inf')).

θ β low

BTwo-sided

β = low + (high − low)·σ(θ)

Use when β lives in a known interval. σ is the logistic, σ(θ) = eθ/(1+eθ). range = (low, high).

θ β high low
For an unconstrained slope (no lower or upper bound), use model='linear' instead of 'clinear'. The clinear reparameterization is meant for genuinely bounded slopes.
3. Example

Force a positive slope in a Poisson rate model

import numpy as np import pandas as pd from pyinla import pyinla # synthetic Poisson rate data with a positive slope for z rng = np.random.default_rng(2026) n = 200 z = rng.uniform(size=n) expo = rng.uniform(0.5, 2.0, size=n) eta = -1.0 + 0.7 * z y = rng.poisson(expo * np.exp(eta)) df = pd.DataFrame({"y": y, "z": z, "expo": expo}) formula = { "response": "y", # intercept is included by default "random": [ { "model": "clinear", "covariate": "z", "range": (0.0, float("inf")), # forces beta >= 0 "hyper": [{"prior": "normal", "param": [0.0, 0.001]}], } ], "offset": np.log(df["expo"]), } res = pyinla(formula=formula, family="poisson", data=df) print(res.summary_fixed) # intercept print(res.summary_hyperpar) # row 'Beta for z' is the back-transformed slope
4. Spec keys

Parameters of the clinear dict

KeyRequiredDescription
modelYesMust be 'clinear'.
covariateYesColumn name in the dataframe, or an explicit 1D array of values.
rangeYesTuple (low, high). Picks the transformation: see Section 2. Requires low < high. Supported shapes: (low, high) both finite, or (low, float('inf')) with finite low.
hyperNoList with exactly one dict, [{prior, param, initial, fixed}], placing a prior on the internal θ. Default: [{"prior": "normal", "param": [1, 10], "initial": 1, "fixed": False}].
idNoLabel used in summary_random / summary_hyperpar. Defaults to the value of covariate when it is a column name.
weightsNoPer-observation weight vector multiplied into the covariate contribution. Length must equal the number of rows.
diagonalNoSmall constant added to the precision-matrix diagonal for numerical stability. Default 0.0; rarely needed.
5. The prior is on θ, not on β

Watch what the default prior implies for your slope

The hyper block places a prior on the unconstrained internal parameter θ. The prior on the actual slope β is induced by the transformation. Three things to keep straight:

Default prior on θ
θ ~ N(μ=1, τ=10)
τ is precision: variance = 1/10 = 0.1, sd ≈ 0.316. Not N(1, 10²).
Implied on β for (0, inf)
β = eθ
Log-normal: median e ≈ 2.72, 95% interval ≈ [1.46, 5.05]. Concentrated above zero.
Implied on β for (0, 10)
β = 10·σ(θ)
Median ≈ 7.31, 95% interval ≈ [5.94, 8.35]. Skewed toward the upper bound.
The default is informative, not flat. The mean of θ is 1, not 0, and the precision is moderately tight. If you want a near-flat induced prior on β, pass a vague prior on θ centered at 0 instead:
"hyper": [{"prior": "normal", "param": [0.0, 0.001]}] # theta ~ N(0, sd~31.6)

Keys inside the hyper dict

KeyDescription
priorPrior family for θ: "normal", "loggamma", "flat", "pc", and others.
paramParameters of the prior, in that family's order. For "normal": [mean, precision].
initialStarting value for θ in the optimizer. Default 1.
fixedIf True, pin θ at initial instead of inferring it. Default False.
6. Reading results

Where to find the slope after fitting

7. When not to use

If you only need an informative prior (no hard bounds)

Prefer a standard fixed effect with a prior set through control['fixed']: simpler, no reparameterization, and you specify the prior on β directly. See Linear slope priors. Reach for clinear only when the bound is a hard requirement (sign, physical limits, identifiability).