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 |
|
|
Decimal precision |
|
|
\(l_x\) interpolation |
|
|
Mortality placement |
|
|
Force of mortality |
|
|
Calendar constants |
|
|
Date parsing |
|
|
Tables path |
|
|
Config file path |
|
|
Force integer ts |
|
See also
Configuration — Narrative guide to all Config settings with worked examples.
- class lactuca.Config(config_path: str | PathLike[str] | None = None)#
Bases:
objectSingleton 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(), andreset()— may be called from multiple threads concurrently without external synchronisation.The pre-constructed singleton is also exposed as the module attribute
lactuca.config, soimport lactuca as lc; lc.config.set(...)works without explicitly callingConfig().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 byload()(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.resetClear the cached singleton (testing / debugging).
Config.reset_to_defaultsRestore 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
Configinstance become stale and will not reflect the new singleton state. CallConfig()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 thancfg.reset()(instance method) to avoid confusion.
See also
Config.reset_to_defaultsRestore 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
dictsnapshot 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()andset_many()(e.g.,'tables_path','decimals_lx','calculation_mode'). The structure does not mirror the TOML hierarchy — usesave()to produce nested TOML output.The returned mapping is a shallow copy: modifying it does not affect the
Configstate.
See also
Config.setValidate and set a single configuration value.
Config.set_manyAtomically apply multiple configuration updates.
Config.savePersist 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), aKeyErroris raised instead.
- Returns:
The current validated value for
keyordefaultwhen provided.- Return type:
Any
- Raises:
KeyError – If
keyis not recognized anddefaultwas not supplied or was supplied asNone.
Notes
Read-only access to the current configuration state.
Pass an explicit non-
Nonesentinel (e.g.,default=0) whenNoneis a meaningful fallback value.
See also
Config.setValidate and set a single configuration value.
Config.set_manyAtomically apply multiple configuration updates.
Config.as_dictReturn 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
Nonethe instance’s currentconfig_pathis used (default:lactuca_config.tomlin the current working directory). Acceptspathlib.Pathand otheros.PathLikeobjects.- 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_tsis not abool) — 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_pathare updated on all non-exceptional returns.The method is thread-safe.
See also
Config.savePersist the current configuration to a TOML file.
Config.save_if_changedWrite 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_pathis an environment-specific path (where the actuarial table files are installed) and is not considered a “default” calculation parameter; resetting it would causeFileNotFoundErrorin notebooks or scripts that run from a working directory different from the project root.The
Configobject’s identity and the currentconfig_pathare also preserved.Notes
This method does not write the default values to the configuration file. To persist the changes, call
save()orsave_if_changed().Invokes the same cache invalidation hooks as
reset(), so derived caches (tables, builders) do not retain stale configuration state. Unlikereset(), the singleton instance itself is preserved.
See also
Config.resetClear 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 whenfile_pathisNone). 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.Pathand otheros.PathLikeobjects viaos.fspath(). WhenNone, the instance’s configured path is used (default:lactuca_config.tomlin the current working directory). A directory receiveslactuca_config.tomlautomatically; extensionless paths have.tomlappended.overwrite (bool, optional) – When
False(default) aFileExistsErroris raised if the target file exists with different contents. WhenTruethe file is overwritten.
- Raises:
ValueError – If an explicit file extension other than
.tomlis used.RuntimeError – If no TOML writer is installed. Run
pip install tomli-wto resolve.FileExistsError – If the target exists with different contents and
overwriteisFalse.OSError – For filesystem-related errors when creating directories or writing the file. Includes
PermissionError(a subclass ofOSError) 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_pathis updated on all non-exceptional returns.
See also
Config.save_asSave to an explicit new path.
Config.save_if_changedWrite 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,.tomlextension check, and atomic write semantics are identical tosave().- Parameters:
file_path (str or path-like) – Target path for the configuration file. Must have a
.tomlextension or no extension (in which case.tomlis appended automatically). The parent directory is created if it does not exist.overwrite (bool, optional) – When
True, overwrite an existing file atfile_pathwithout error. Defaults toFalse.
- Raises:
ValueError – If
file_pathhas an explicit extension other than.toml.FileExistsError – If
file_pathalready exists with different contents andoverwriteisFalse.RuntimeError – If no TOML writer is installed. Run
pip install tomli-wto resolve.OSError – If the file cannot be written due to a filesystem error.
Notes
The instance’s
config_pathis updated to the normalizedfile_pathon all non-exceptional returns.The previous configuration file at the original
config_pathis not deleted or modified.
See also
Config.saveFull documentation of atomic write semantics and path normalization.
Config.save_if_changedWrite 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
Nonethe instance’s configured path is used. Acceptspathlib.Pathand otheros.PathLikeobjects.- Returns:
Trueif a write took place (file did not exist or contents differed),Falseif the on-disk contents already matched the current configuration.- Return type:
bool
- Raises:
RuntimeError – If no TOML writer is installed. Run
pip install tomli-wto resolve.OSError – If the file cannot be written due to a filesystem error.
Notes
The instance’s
config_pathis 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.saveWrite unconditionally.
Config.save_asSave 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
valueforkeyand 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 givenkey.
- Raises:
KeyError – If
keyis not a known configuration attribute.ValueError – If the provided
valuefails domain validation.TypeError – If the provided
valuehas an incorrect type.
Notes
Thread-safe: callers do not need to synchronize externally.
Call
save()orsave_if_changed()to persist changes.
See also
Config.getReturn a single configuration value.
Config.set_manyAtomically 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
mappingis not a dict, or if a value fails type validation.KeyError – If any key in
mappingis unknown.ValueError – If any value in
mappingfails domain validation.
Notes
Thread-safe: callers do not need to synchronize externally.
Call
save()orsave_if_changed()to persist changes.
See also
Config.setValidate 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-tablelxvalues perdecimals(default; standard production mode).'discrete_simplified'— fast discrete approximations: Woolhouse (2-term UDD) for annuities whenm > 1; linear age interpolation between annual insurance values whenm > 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 viaforce_mortality_methodfor insurance and endowment engines.'continuous_simplified'— product-specific continuous shortcuts (terminal-period interpolation for annuities; arithmetic mean of twocontinuous_precisioninsurance 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_interpolationInterpolation method for the survival function.
Config.force_mortality_methodForce-of-mortality approximation for continuous modes.
lactuca.engine.base.CalculationModeLiteralCanonical 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(), andload()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.pathUnqualified alias for this property.
Config.savePersists 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_yearCompanion calendar conversion constant.
Config.weeks_per_yearCompanion 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_yearCompanion 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 probabilitypx: int — \(p_x\) survival probabilitytpx: int — \({}_t p_x\) multi-year survival probabilitytqx: int — \({}_t q_x\) multi-year mortality probabilityix: int — \(i_x\) disability inception rateox: int — \(o_x\) exit rateLx: int — \(L_x\) person-years livedTx: int — \(T_x\) total future person-yearsex: int — \(e_x\) life expectancyDx,Nx,Sx: int — \(D_x\), \(N_x\), \(S_x\) annuity commutationCx,Mx,Rx: int — \(C_x\), \(M_x\), \(R_x\) insurance commutationannuities: int — life annuity present valuesinsurances: 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.lxreads the same value ascfg.get('decimals_lx')). Assigning a value (e.g.,cfg.decimals.lx = 10) is equivalent to callingcfg.set('decimals_lx', 10)and triggers the same validation; invalid values raiseValueError. The proxy object is cached — repeated access tocfg.decimalsreturns the same instance.See also
Config.setModify individual decimal precision settings by key name.
Config.getRead 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:
Trueif fractionaltsvalues raiseValueError;Falseto allow them (default).- Return type:
bool
- Raises:
TypeError – If the assigned value is not a
bool.
Notes
tsis the temporal shift (in years) applied to the timing of annuity and insurance payments.When
False(default), fractional values such as0.5(half-year shift) are permitted; whenTrue, only integer values are accepted and non-integer inputs raiseValueError.
See also
Config.calculation_modeOverall calculation strategy;
tsis 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_modeSelect 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_placementDeath-benefit payment timing within sub-periods (
discrete_precision/discrete_simplifiedinsurances; independent of this setting).Config.calculation_modeOverall 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_precisionanddiscrete_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 toMORTALITY_OFFSETin discrete insurance engines (form-thly payments, discount times usej/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\) whenm = 1; \(f = 0\)).'mid'— benefit at mid-sub-period (default; \(\delta_m = 1/(2m)\); \(f = 0.5\)). Not the same setting as UDDlx_interpolation.'end'— benefit at end of sub-period / year of death (\(\delta_m = 1/m\) whenm-thly; \(f = 1\)).
- Return type:
MortalityPlacementLiteral
- Raises:
ValueError – If the assigned value is not
'beginning','mid', or'end'.
See also
Config.lx_interpolationFractional-age survival bridge (independent knob).
lactuca.engine.base.MORTALITY_OFFSETFractional offsets used by discrete insurance engines.
Config.decimalsDecimal-place settings;
Cxrounding 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_pathCanonical 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_pathPath 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, aUserWarningis emitted if two or more property setters (sex,cohort,duration,unisex_blend) trigger independent rebuilds between calculations on the same table. The recommended alternative isconfigure()orbatch_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_yearCompanion calendar constant for daily period conversion.
Examples
>>> from lactuca import Config >>> cfg = Config() >>> cfg.weeks_per_year 52.1775