Skip to content

Configuration

import linox

original = linox.config.get_max_dense_n()
linox.config.set_max_dense_n(1000)
assert linox.config.get_max_dense_n() == 1000
linox.config.set_max_dense_n(original)

Settings

Setting Default Effect
max_dense_n 2000 Size above which method="auto" prefers approximate routes
debug off Enables densification performance warnings
warn_on_densify Whether todense emits a warning
import linox

original = linox.is_debug()
linox.set_debug(True)
assert linox.is_debug()
linox.set_debug(original)

Debug mode also honours the LINOX_DEBUG environment variable.

Method defaults

set_default_method pins the choice for one operation, overriding the size heuristic but not an explicit method= argument:

not-executed
linox.config.set_default_method("solve", "cg")

Valid method names per operation are declared in linox.config.VALID_METHODS, and anything outside them is rejected:

import linox

assert "cg" in linox.config.VALID_METHODS["solve"]
assert "lanczos" in linox.config.VALID_METHODS["sqrt"]

Debug events

import jax.numpy as jnp
import linox
import linox.config as config

seen = []
config.set_debug_hook(lambda event: seen.append(event.kind))
try:
    linox.Diagonal(jnp.arange(1.0, 5.0)).todense()
finally:
    config.set_debug_hook(None)

assert "densify" in seen

A DebugEvent carries kind, msg, op_type, shape, dtype and timing. Kinds include densify, matmul, warn, init and the profiling kinds emitted around solves and decompositions.

See Avoiding densification for the caveats — in particular, linox.todense() the function does not emit a densify event.

API reference

Global configuration for linox warnings and debug behavior.

Usage: - Toggle debug prints (e.g., densification warnings): from linox.config import set_debug set_debug(True)

  • Or via environment variable: export LINOX_DEBUG=1

DebugEvent dataclass

A single debug or profiling event emitted by the library.

Source code in linox/config.py
@dataclass(frozen=True)
class DebugEvent:
    """A single debug or profiling event emitted by the library."""

    kind: str  # e.g. "densify", "solve_fallback", "eigh_dense"
    msg: str
    op_type: str | None = None
    op_id: int | None = None
    shape: AnyType = None
    dtype: AnyType = None
    meta: dict[str, AnyType] | None = None
    t: float = 0.0
    duration: float | None = None
    phase: str | None = None  # "start" or "end"

emit(event: DebugEvent) -> None

Emit a structured debug event to the hook (if any).

Source code in linox/config.py
def emit(event: DebugEvent) -> None:
    """Emit a structured debug event to the hook (if any)."""
    if _DEBUG_HOOK is not None:
        # set timestamp lazily
        if event.t == 0.0:
            object.__setattr__(event, "t", time.time())  # dataclass frozen workaround
        _DEBUG_HOOK(event)

get_max_dense_n() -> int

Get the maximum size for automatic densification.

Source code in linox/config.py
def get_max_dense_n() -> int:
    """Get the maximum size for automatic densification."""
    return _MAX_DENSE_N

get_warn_on_densify() -> bool

Return whether densification warnings are enabled.

Source code in linox/config.py
def get_warn_on_densify() -> bool:
    """Return whether densification warnings are enabled."""
    return _WARN_ON_DENSIFY

is_debug() -> bool

Return whether debug mode is enabled.

Source code in linox/config.py
def is_debug() -> bool:
    """Return whether debug mode is enabled."""
    return _DEBUG

profile(kind: str, msg: str, **kwargs) -> None

Context manager to profile an operation time.

Source code in linox/config.py
@contextlib.contextmanager
def profile(kind: str, msg: str, **kwargs) -> None:
    """Context manager to profile an operation time."""
    t0 = time.time()
    # emit start event
    emit(DebugEvent(kind=kind, msg=msg, phase="start", t=t0, **kwargs))
    try:
        yield
    finally:
        t1 = time.time()
        # emit end event with duration
        emit(DebugEvent(kind=kind, msg=msg, phase="end", t=t1, duration=t1 - t0, **kwargs))

resolve_method(operation: str, op: AnyType, requested_method: str) -> str

Resolve the execution method based on request, config, and operator properties.

Priority: 1. Explicitly requested method (if not 'auto') 2. Configured default for this operation 3. 'auto' heuristics (based on size, structure, etc.)

Args: operation: Name of the operation ('solve', 'eigh', 'sqrt', etc.) op: The linear operator involved requested_method: The method argument provided by the user

Returns:

Type Description
The resolved method name (e.g. 'exact', 'lanczos', 'cg').

Raises:

Type Description
ValueError

If requested_method is not valid for operation.

Source code in linox/config.py
def resolve_method(operation: str, op: AnyType, requested_method: str) -> str:
    """Resolve the execution method based on request, config, and operator properties.

    Priority:
    1. Explicitly requested method (if not 'auto')
    2. Configured default for this operation
    3. 'auto' heuristics (based on size, structure, etc.)

    Args:
        operation: Name of the operation ('solve', 'eigh', 'sqrt', etc.)
        op: The linear operator involved
        requested_method: The method argument provided by the user

    Returns
    -------
        The resolved method name (e.g. 'exact', 'lanczos', 'cg').

    Raises
    ------
    ValueError
        If ``requested_method`` is not valid for ``operation``.
    """
    validate_method(operation, requested_method)

    if requested_method != "auto":
        return requested_method

    # Check config defaults
    if operation in _DEFAULT_METHODS:
        return _DEFAULT_METHODS[operation]

    # 'auto' heuristics
    # Basic logic: use exact if small enough, otherwise approx
    n = op.shape[-1]
    if n <= _MAX_DENSE_N:
        return "exact"

    # Default approx fallbacks for large operators
    if operation == "trace":
        # For large operators, default to Hutchinson
        return "hutchinson"
    if operation == "slogdet":
        # For large operators, default to SLQ (if implemented) or fallback
        # Currently we might not have SLQ hooked up everywhere, so be careful.
        # But 'slq' is the intended approx backend.
        return "slq"
    if operation == "inverse":
        return "lsmr"  # Approx inverse for large scale
    if operation == "solve":
        return "lsmr"
    if operation == "sqrt":
        return "lanczos"
    if operation == "eigh":
        return "lanczos"

    # Fallback to exact (which might fail or be slow if dense)
    return "exact"

set_debug(value: bool) -> None

Enable or disable debug mode (controls warning prints).

Source code in linox/config.py
def set_debug(value: bool) -> None:
    """Enable or disable debug mode (controls warning prints)."""
    global _DEBUG
    _DEBUG = bool(value)

set_debug_hook(hook: CallableType[[DebugEvent], None] | None) -> None

Register/unregister a debug hook that receives DebugEvent objects.

Source code in linox/config.py
def set_debug_hook(hook: CallableType[[DebugEvent], None] | None) -> None:
    """Register/unregister a debug hook that receives DebugEvent objects."""
    global _DEBUG_HOOK
    _DEBUG_HOOK = hook

set_default_method(operation: str, method: str) -> None

Set the default method for a specific operation (e.g. 'eigh', 'solve').

Source code in linox/config.py
def set_default_method(operation: str, method: str) -> None:
    """Set the default method for a specific operation (e.g. 'eigh', 'solve')."""
    _DEFAULT_METHODS[operation] = method

set_max_dense_n(n: int) -> None

Set the maximum size for automatic densification.

Source code in linox/config.py
def set_max_dense_n(n: int) -> None:
    """Set the maximum size for automatic densification."""
    global _MAX_DENSE_N
    _MAX_DENSE_N = int(n)

set_warn_on_densify(value: bool) -> None

Enable or disable warnings when operations trigger densification.

Source code in linox/config.py
def set_warn_on_densify(value: bool) -> None:
    """Enable or disable warnings when operations trigger densification."""
    global _WARN_ON_DENSIFY
    _WARN_ON_DENSIFY = bool(value)

validate_method(operation: str, requested_method: str) -> str

Check requested_method against the methods operation supports.

Raises:

Type Description
ValueError

If the method is not one this operation understands.

Source code in linox/config.py
def validate_method(operation: str, requested_method: str) -> str:
    """Check ``requested_method`` against the methods ``operation`` supports.

    Raises
    ------
    ValueError
        If the method is not one this operation understands.
    """
    valid = VALID_METHODS.get(operation)
    if valid is not None and requested_method not in valid:
        msg = f"Unknown method {requested_method!r} for operation {operation!r}. Valid methods are: {', '.join(sorted(valid))}."
        raise ValueError(msg)
    return requested_method

warn(msg: str, *, prefix: str = 'Warning') -> None

Conditionally print a warning message if debug is enabled.

Args: msg: Message to print. prefix: Optional prefix for the message, defaults to 'Warning'.

Source code in linox/config.py
def warn(msg: str, *, prefix: str = "Warning") -> None:
    """Conditionally print a warning message if debug is enabled.

    Args:
        msg: Message to print.
        prefix: Optional prefix for the message, defaults to 'Warning'.
    """
    emit(DebugEvent(kind="warn", msg=f"{prefix}: {msg}"))
    if _DEBUG:
        pass