Config#

Config is the global configuration singleton that controls all precision, calculation, and table-loading settings for a Lactuca session. Changes take effect immediately for all subsequent calculations in the same Python process; call reset_to_defaults() to restore factory defaults (preserving tables_path and config_path). Use reset() only to clear the cached singleton (testing/debug).

Key setting groups:

Group

Settings

Guide

Calculation mode

calculation_mode

Calculation Modes

Decimal precision

decimals.*

Decimal Precision and Rounding

\(l_x\) interpolation

lx_interpolation ("linear" / "exponential")

lx Interpolation

Mortality placement

mortality_placement ("beginning" / "mid" / "end")

Life Insurances

Force of mortality

force_mortality_method

Force of Mortality Methods

Calendar constants

days_per_year, weeks_per_year

Numerical Precision

Date parsing

date_format

Date Utilities Guide

Tables path

tables_path

Using Actuarial Tables

Config file path

config_path (alias: path)

Configuration

Force integer ts

force_integer_ts

Prospective Reserves and the ts Parameter

See also

Configuration — Narrative guide to all Config settings with worked examples.

class lactuca.Config(config_path: str | PathLike[str] | None = None)#

Bases: object

Singleton configuration manager for Lactuca.

Provides a process-global configuration instance with validation and persistence to a TOML file. The first call to Config() constructs and caches the instance; all subsequent calls return the same object.

Parameters:

config_path (str, path-like, or None, optional) – Path to the configuration TOML file. Only respected on the first instantiation; later calls ignore this argument and return the cached singleton.

Notes

  • Thread-safe: all public methods — get(), set(), set_many(), save(), load(), save_if_changed(), and reset() — may be called from multiple threads concurrently without external synchronisation.

  • The pre-constructed singleton is also exposed as the module attribute lactuca.config, so import lactuca as lc; lc.config.set(...) works without explicitly calling Config().

  • The default configuration file name is lactuca_config.toml, placed in the current working directory when no path is provided.

  • This class focuses on configuration I/O and validation. Heavy numeric algorithms and array handling belong in computational modules that use NumPy float64 for precision and performance.

TOML file structure — produced by save() and accepted by load() (default values shown):

[paths]
actuarial_tables = "/abs/path/to/actuarial_tables"

[decimals]
lx = 15
dx = 15
# ... one entry per actuarial column (19 total; see Config.decimals)

[calculation]
lx_interpolation = "linear"        # "linear" | "exponential"
calculation_mode = "discrete_precision"  # discrete_precision | discrete_simplified | continuous_precision | continuous_simplified
force_integer_ts = false

[calendar]
date_format = "ymd"                # "ymd" | "dmy" | "mdy" | "ymd_int"
days_per_year = 365.25
weeks_per_year = 52.1775

[mortality]
mortality_placement = "mid"        # "beginning" | "mid" | "end"
force_mortality_method = "finite_difference"

Examples

>>> from lactuca import Config
>>> cfg = Config()
>>> cfg.calculation_mode
'discrete_precision'
>>> cfg.decimals.lx = 10
>>> cfg.save_if_changed()

See also

Config.reset

Clear the cached singleton (testing / debugging).

Config.reset_to_defaults

Restore default values in the current instance.

classmethod reset() None#

Reset the singleton instance of the Config class.

Clears the cached singleton instance, allowing the next call to Config() to create a fresh instance. Primarily intended for test harnesses and debugging scenarios where a clean configuration state is required.

After calling this method any existing references to the old Config instance become stale and will not reflect the new singleton state. Call Config() again to obtain a reference to the new singleton instance.

Notes

  • Does not persist any changes to disk or modify the configuration file.

  • Clearing the singleton also notifies dependent modules to clear their derived state, so no stale cached values persist after the next Config() call.

  • Use reset_to_defaults() to restore default values while keeping the same singleton instance.

  • Prefer calling as Config.reset() (class method) rather than cfg.reset() (instance method) to avoid confusion.

See also

Config.reset_to_defaults

Restore default values in the current instance.

Examples

Reset and obtain a fresh singleton:

>>> from lactuca import Config
>>> Config.reset()
>>> cfg = Config()
>>> cfg.decimals.lx = 20

Pass a custom configuration path on first creation after reset:

>>> from lactuca import Config
>>> Config.reset()
>>> cfg = Config("/project/my_config.toml")
as_dict() dict[str, Any]#

Return a flat dict snapshot of the current configuration.

Returns:

A new dictionary with flat keys and Python primitives suitable for serialization.

Return type:

dict[str, Any]

Notes

  • Keys are the flat (un-nested) field names, identical to those accepted by set() and set_many() (e.g., 'tables_path', 'decimals_lx', 'calculation_mode'). The structure does not mirror the TOML hierarchy — use save() to produce nested TOML output.

  • The returned mapping is a shallow copy: modifying it does not affect the Config state.

See also

Config.set

Validate and set a single configuration value.

Config.set_many

Atomically apply multiple configuration updates.

Config.save

Persist the current configuration to a TOML file.

Examples

>>> from lactuca import Config
>>> cfg = Config()
>>> d = cfg.as_dict()
>>> "decimals_lx" in d
True
get(key: str, default: Any = None) Any#

Return a single configuration value.

Parameters:
  • key (str) – Name of the configuration attribute to retrieve.

  • default (Any, optional) – Value returned for unrecognized keys. When None (the default), a KeyError is raised instead.

Returns:

The current validated value for key or default when provided.

Return type:

Any

Raises:

KeyError – If key is not recognized and default was not supplied or was supplied as None.

Notes

  • Read-only access to the current configuration state.

  • Pass an explicit non-None sentinel (e.g., default=0) when None is a meaningful fallback value.

See also

Config.set

Validate and set a single configuration value.

Config.set_many

Atomically apply multiple configuration updates.

Config.as_dict

Return all configuration values as a flat dict.

Examples

>>> from lactuca import Config
>>> cfg = Config()
>>> cfg.get("decimals_lx")
15
>>> cfg.get("unknown_key", default="fallback")
'fallback'
load(file_path: str | PathLike[str] | None = None) None#

Load configuration from a TOML file and merge it into the current model.

The TOML file is merged into the current configuration: keys present in the file override the corresponding in-memory values; keys absent from the file are left unchanged (loading is additive, not a full replacement). See the class docstring for the accepted TOML structure and section names. If validation fails the current configuration is preserved.

Parameters:

file_path (str, path-like, or None, optional) – Path to a file or a directory. When None the instance’s current config_path is used (default: lactuca_config.toml in the current working directory). Accepts pathlib.Path and other os.PathLike objects.

Raises:
  • FileNotFoundError – If the resolved file does not exist.

  • RuntimeError – If no TOML reader is installed (requires Python 3.11+ or pip install tomli).

  • ValueError – If the merged configuration fails validation — the original instance state is preserved in this case.

  • TypeError – If a field has an incorrect type (e.g. force_integer_ts is not a bool) — the original instance state is preserved.

  • OSError – If the file cannot be opened or read due to a filesystem error.

Notes

  • The in-memory configuration and config_path are updated on all non-exceptional returns.

  • The method is thread-safe.

See also

Config.save

Persist the current configuration to a TOML file.

Config.save_if_changed

Write only when the on-disk file differs from the current state.

Examples

>>> from lactuca import Config
>>> cfg = Config()
>>> cfg.load("/path/to/lactuca_config.toml")
reset_to_defaults() None#

Restore the configuration to library default values.

All configuration settings are reset to their library default values, except tables_path, which is preserved. tables_path is an environment-specific path (where the actuarial table files are installed) and is not considered a “default” calculation parameter; resetting it would cause FileNotFoundError in notebooks or scripts that run from a working directory different from the project root.

The Config object’s identity and the current config_path are also preserved.

Notes

  • This method does not write the default values to the configuration file. To persist the changes, call save() or save_if_changed().

  • Invokes the same cache invalidation hooks as reset(), so derived caches (tables, builders) do not retain stale configuration state. Unlike reset(), the singleton instance itself is preserved.

See also

Config.reset

Clear the cached singleton (testing / debugging).

Examples

Reset the configuration to default values:

>>> from lactuca import Config
>>> cfg = Config()
>>> cfg.set("decimals_lx", 5)
>>> cfg.reset_to_defaults()
>>> cfg.get("decimals_lx")
15
save(file_path: str | PathLike[str] | None = None, overwrite: bool = False) None#

Persist the current configuration to a TOML file.

Serialize the current configuration into a nested TOML layout and write it atomically to file_path (or the instance’s configured path when file_path is None). The write is skipped when the on-disk contents already match the serialized text to reduce unnecessary I/O.

Parameters:
  • file_path (str, path-like, or None, optional) – Target file path or directory. Accepts pathlib.Path and other os.PathLike objects via os.fspath(). When None, the instance’s configured path is used (default: lactuca_config.toml in the current working directory). A directory receives lactuca_config.toml automatically; extensionless paths have .toml appended.

  • overwrite (bool, optional) – When False (default) a FileExistsError is raised if the target file exists with different contents. When True the file is overwritten.

Raises:
  • ValueError – If an explicit file extension other than .toml is used.

  • RuntimeError – If no TOML writer is installed. Run pip install tomli-w to resolve.

  • FileExistsError – If the target exists with different contents and overwrite is False.

  • OSError – For filesystem-related errors when creating directories or writing the file. Includes PermissionError (a subclass of OSError) for access-denied scenarios.

Notes

  • The file is written in the nested TOML structure described in the class docstring.

  • Two files are considered equal only when their full contents match exactly; files with identical settings but differing whitespace are treated as different.

  • Writes are atomic; readers never observe a partial file.

  • The instance’s config_path is updated on all non-exceptional returns.

See also

Config.save_as

Save to an explicit new path.

Config.save_if_changed

Write only when the on-disk file differs from the current state.

Examples

>>> from lactuca import Config
>>> cfg = Config()
>>> cfg.set("decimals_lx", 10)
>>> cfg.save()
save_as(file_path: str | PathLike[str], overwrite: bool = False) None#

Save the configuration to a new file path.

Convenience wrapper around save() that targets an explicit path. Path normalization, .toml extension check, and atomic write semantics are identical to save().

Parameters:
  • file_path (str or path-like) – Target path for the configuration file. Must have a .toml extension or no extension (in which case .toml is appended automatically). The parent directory is created if it does not exist.

  • overwrite (bool, optional) – When True, overwrite an existing file at file_path without error. Defaults to False.

Raises:
  • ValueError – If file_path has an explicit extension other than .toml.

  • FileExistsError – If file_path already exists with different contents and overwrite is False.

  • RuntimeError – If no TOML writer is installed. Run pip install tomli-w to resolve.

  • OSError – If the file cannot be written due to a filesystem error.

Notes

  • The instance’s config_path is updated to the normalized file_path on all non-exceptional returns.

  • The previous configuration file at the original config_path is not deleted or modified.

See also

Config.save

Full documentation of atomic write semantics and path normalization.

Config.save_if_changed

Write only when the on-disk file differs from the current state.

Examples

>>> from lactuca import Config
>>> cfg = Config()
>>> cfg.save_as("/tmp/backup.toml", overwrite=True)
save_if_changed(file_path: str | PathLike[str] | None = None) bool#

Write the configuration only when the on-disk file differs.

Determines whether the current configuration differs from the on-disk file (when present) and performs an atomic write only when the contents differ. Uses the same atomic write strategy as save().

Parameters:

file_path (str, path-like, or None, optional) – Target path; when None the instance’s configured path is used. Accepts pathlib.Path and other os.PathLike objects.

Returns:

True if a write took place (file did not exist or contents differed), False if the on-disk contents already matched the current configuration.

Return type:

bool

Raises:
  • RuntimeError – If no TOML writer is installed. Run pip install tomli-w to resolve.

  • OSError – If the file cannot be written due to a filesystem error.

Notes

  • The instance’s config_path is updated to the resolved path on all non-exceptional returns.

  • Two files are considered different when their exact contents do not match; identical settings with differing whitespace are treated as different.

See also

Config.save

Write unconditionally.

Config.save_as

Save to an explicit new path.

Examples

>>> from lactuca import Config
>>> cfg = Config()
>>> cfg.set("decimals_lx", 10)
>>> cfg.save_if_changed()
True
set(key: str, value: Any) None#

Validate and set a single configuration value.

Validates value for key and updates the configuration in a thread-safe manner.

Parameters:
  • key (str) – Configuration attribute name to update (must be a model field).

  • value (Any) – New value to assign to key. Must satisfy the validation rules for the given key.

Raises:
  • KeyError – If key is not a known configuration attribute.

  • ValueError – If the provided value fails domain validation.

  • TypeError – If the provided value has an incorrect type.

Notes

See also

Config.get

Return a single configuration value.

Config.set_many

Atomically apply multiple configuration updates.

Examples

>>> from lactuca import Config
>>> cfg = Config()
>>> cfg.set("decimals_lx", 10)
>>> cfg.get("decimals_lx")
10
set_many(mapping: dict[str, Any]) None#

Atomically apply multiple configuration updates.

All updates are validated atomically: either all changes are applied or a validation exception is raised and no state is changed.

Parameters:

mapping (dict[str, Any]) – Mapping of configuration keys to new values.

Raises:
  • TypeError – If mapping is not a dict, or if a value fails type validation.

  • KeyError – If any key in mapping is unknown.

  • ValueError – If any value in mapping fails domain validation.

Notes

See also

Config.set

Validate and set a single configuration value.

Examples

>>> from lactuca import Config
>>> cfg = Config()
>>> cfg.set_many({"decimals_lx": 10, "decimals_qx": 10})
property calculation_mode: Literal['discrete_precision', 'discrete_simplified', 'continuous_precision', 'continuous_simplified']#

Calculation strategy for actuarial computations.

Returns:

Current calculation mode. Allowed values:

  • 'discrete_precision' — exact discrete calculations using rounded life-table lx values per decimals (default; standard production mode).

  • 'discrete_simplified' — fast discrete approximations: Woolhouse (2-term UDD) for annuities when m > 1; linear age interpolation between annual insurance values when m > 1 (Lactuca-specific — not Woolhouse); UDD (Uniform Distribution of Deaths) for endowments.

  • 'continuous_precision' — continuous-time valuation via numerical integration; force-of-mortality approximation configurable via force_mortality_method for insurance and endowment engines.

  • 'continuous_simplified' — product-specific continuous shortcuts (terminal-period interpolation for annuities; arithmetic mean of two continuous_precision insurance legs; average-force survival for endowments).

All four modes value the same actuarial present value under shared global conventions; they are actuarially coherent but not required to return numerically identical results. See Calculation Modes (Actuarial coherence across modes).

Return type:

CalculationModeLiteral

Raises:

ValueError – If the assigned value is not one of the four allowed modes.

See also

Config.lx_interpolation

Interpolation method for the survival function.

Config.force_mortality_method

Force-of-mortality approximation for continuous modes.

lactuca.engine.base.CalculationModeLiteral

Canonical mode definitions.

Examples

>>> from lactuca import Config
>>> cfg = Config()
>>> cfg.calculation_mode
'discrete_precision'
property config_path: str#

Absolute path of the configuration file used by this instance.

Returns:

Absolute path to the current TOML configuration file.

Return type:

str

Notes

  • Updated automatically by save(), save_as(), save_if_changed(), and load() on all non-exceptional returns.

  • The initial value is determined by the working directory at the time of the first Config() call, not at import time.

See also

Config.path

Unqualified alias for this property.

Config.save

Persists configuration and updates this path.

Examples

>>> from lactuca import Config
>>> cfg = Config()
>>> cfg.config_path
'/path/to/lactuca_config.toml'
property date_format: str#

Current parsing order for ambiguous string dates.

Returns:

Current date parsing order. Allowed values:

  • 'ymd' — year–month–day order (ISO 8601 style, default).

  • 'dmy' — day–month–year order (European convention).

  • 'mdy' — month–day–year order (North American convention).

  • 'ymd_int' — compact integer format (YYYYMMDD).

Return type:

str

Raises:

ValueError – If the assigned value is not in the allowed set.

See also

Config.days_per_year

Companion calendar conversion constant.

Config.weeks_per_year

Companion calendar conversion constant.

Examples

>>> from lactuca import Config
>>> cfg = Config()
>>> cfg.date_format
'ymd'
property days_per_year: float#

Calendar basis used to convert days to actuarial year fractions.

Returns:

Current days-per-year constant. Allowed values: 360 (30/360 convention), 365 (Actual/365), 365.25 (default, mean Gregorian year), 365.2425 (exact Gregorian mean), 366 (leap year).

Return type:

float

Raises:

ValueError – If the assigned value is not in the allowed set.

See also

Config.weeks_per_year

Companion calendar constant for weekly period conversion.

Examples

>>> from lactuca import Config
>>> cfg = Config()
>>> cfg.days_per_year
365.25
property decimals: _DecimalsConfig#

Grouped access to decimal precision settings with editor autocompletion.

Returns:

Decimal-precision proxy with one integer read/write attribute per actuarial column (19 total). Key attributes:

  • lx : int — \(\ell_x\) survivors at age \(x\)

  • dx : int — \(d_x\) deaths between age \(x\) and \(x+1\)

  • qx : int — \(q_x\) mortality probability

  • px : int — \(p_x\) survival probability

  • tpx : int — \({}_t p_x\) multi-year survival probability

  • tqx : int — \({}_t q_x\) multi-year mortality probability

  • ix : int — \(i_x\) disability inception rate

  • ox : int — \(o_x\) exit rate

  • Lx : int — \(L_x\) person-years lived

  • Tx : int — \(T_x\) total future person-years

  • ex : int — \(e_x\) life expectancy

  • Dx, Nx, Sx : int — \(D_x\), \(N_x\), \(S_x\) annuity commutation

  • Cx, Mx, Rx : int — \(C_x\), \(M_x\), \(R_x\) insurance commutation

  • annuities : int — life annuity present values

  • insurances : int — life insurance present values

Return type:

object

Notes

Each attribute name corresponds to the flat configuration key with its decimals_ prefix removed (e.g., cfg.decimals.lx reads the same value as cfg.get('decimals_lx')). Assigning a value (e.g., cfg.decimals.lx = 10) is equivalent to calling cfg.set('decimals_lx', 10) and triggers the same validation; invalid values raise ValueError. The proxy object is cached — repeated access to cfg.decimals returns the same instance.

See also

Config.set

Modify individual decimal precision settings by key name.

Config.get

Read individual decimal precision settings by key name.

Examples

>>> from lactuca import Config
>>> cfg = Config()
>>> cfg.decimals.lx
15
>>> cfg.decimals.lx = 10
property force_integer_ts: bool#

Whether to reject fractional ts (shift) values.

Returns:

True if fractional ts values raise ValueError; False to allow them (default).

Return type:

bool

Raises:

TypeError – If the assigned value is not a bool.

Notes

  • ts is the temporal shift (in years) applied to the timing of annuity and insurance payments.

  • When False (default), fractional values such as 0.5 (half-year shift) are permitted; when True, only integer values are accepted and non-integer inputs raise ValueError.

See also

Config.calculation_mode

Overall calculation strategy; ts is used in all modes.

Examples

>>> from lactuca import Config
>>> cfg = Config()
>>> cfg.force_integer_ts
False
property force_mortality_method: Literal['finite_difference', 'spline', 'kernel']#

Approximation method for the force of mortality \(\mu_x\) in continuous calculations.

Returns:

Current force of mortality approximation method. Allowed values:

  • 'finite_difference' — central finite differences on \(\ell_x\) (default; fast, sufficient for most tables).

  • 'spline' — cubic-spline fit to \(\ell_x\) (smoother curve, more accurate on coarse tables).

  • 'kernel' — kernel-smoothing estimate (non-parametric; use when the table has irregular spacing or noise).

Return type:

ForceMortalityMethodLiteral

Raises:

ValueError – If the assigned value is not 'finite_difference', 'spline', or 'kernel'.

See also

Config.calculation_mode

Select continuous vs. discrete calculation strategy.

Examples

>>> from lactuca import Config
>>> cfg = Config()
>>> cfg.force_mortality_method
'finite_difference'
property lx_interpolation: Literal['linear', 'exponential']#

Interpolation method for fractional-age survival (\(\ell_x\) bridge).

Selects how integer-age life-table values are extended to fractional ages when computing \({}_s p_x\), \({}_{1/m} q_x\), and survival factors on m-thly payment grids (via table interval helpers). One of 'linear' (UDD; default) or 'exponential' (constant force of mortality).

Does not control insurance benefit discount timing; that is mortality_placement.

Returns:

Current interpolation method. One of 'linear' (default) or 'exponential'.

Return type:

LxInterpolationLiteral

Raises:

ValueError – If the assigned value is not 'linear' or 'exponential'.

See also

Config.mortality_placement

Death-benefit payment timing within sub-periods (discrete_precision / discrete_simplified insurances; independent of this setting).

Config.calculation_mode

Overall calculation strategy (discrete vs. continuous).

Examples

>>> from lactuca import Config
>>> cfg = Config()
>>> cfg.lx_interpolation
'linear'
property mortality_placement: Literal['beginning', 'mid', 'end']#

Timing of death-benefit payment within each sub-annual period.

Controls the discount offset \(\delta_m\) applied to death benefits in discrete insurance present values (discrete_precision and discrete_simplified). Continuous insurance modes ignore this setting (benefit timing is implicit in the \(\mu\) integrand). Corresponds to the offset \(f\) in \(C_x = d_x \cdot v^{x+f}\) and to MORTALITY_OFFSET in discrete insurance engines (for m-thly payments, discount times use j/m + f/m).

Annuities and pure endowments do not read this setting. It is independent of lx_interpolation, which governs fractional-age survival probabilities (UDD vs constant force), not benefit payment timing.

Returns:

Current mortality placement. Allowed values:

  • 'beginning' — benefit at the start of the sub-period (\(\delta_m = 0\) when m = 1; \(f = 0\)).

  • 'mid' — benefit at mid-sub-period (default; \(\delta_m = 1/(2m)\); \(f = 0.5\)). Not the same setting as UDD lx_interpolation.

  • 'end' — benefit at end of sub-period / year of death (\(\delta_m = 1/m\) when m-thly; \(f = 1\)).

Return type:

MortalityPlacementLiteral

Raises:

ValueError – If the assigned value is not 'beginning', 'mid', or 'end'.

See also

Config.lx_interpolation

Fractional-age survival bridge (independent knob).

lactuca.engine.base.MORTALITY_OFFSET

Fractional offsets used by discrete insurance engines.

Config.decimals

Decimal-place settings; Cx rounding uses this placement.

Examples

>>> from lactuca import Config
>>> cfg = Config()
>>> cfg.mortality_placement
'mid'
property path: str#

Alias for config_path.

Returns:

Absolute path to the current TOML configuration file.

Return type:

str

See also

Config.config_path

Canonical property with full documentation.

Examples

>>> from lactuca import Config
>>> cfg = Config()
>>> cfg.path
'/path/to/lactuca_config.toml'
property tables_path: str#

Path to the directory containing actuarial table files (.ltk).

This path is searched when loading actuarial tables. Changing it takes effect immediately for all subsequent table-load operations.

Returns:

Current actuarial tables directory path.

Return type:

str

Raises:

TypeError – If the assigned value is not a path-like object or string.

Notes

The default value is resolved from the working directory at the time of the first Config() call, not at import time.

See also

Config.config_path

Path to the TOML configuration file.

Examples

>>> from lactuca import Config
>>> cfg = Config()
>>> cfg.tables_path
'/path/to/actuarial_tables'
property warn_on_chained_table_rebuild: bool#

Opt-in warning for chained setter rebuilds.

When True, a UserWarning is emitted if two or more property setters (sex, cohort, duration, unisex_blend) trigger independent rebuilds between calculations on the same table. The recommended alternative is configure() or batch_update().

Default: False (opt-in only).

property weeks_per_year: float#

Calendar basis used to convert weeks to actuarial year fractions.

Returns:

Current weeks-per-year constant. Allowed values: 52, 52.1429 (≈ 365/7), 52.1775 (default, ≈ 365.2425/7).

Return type:

float

Raises:

ValueError – If the assigned value is not in the allowed set.

See also

Config.days_per_year

Companion calendar constant for daily period conversion.

Examples

>>> from lactuca import Config
>>> cfg = Config()
>>> cfg.weeks_per_year
52.1775