homodyne.config¶
The homodyne.config package provides the configuration infrastructure:
YAML/JSON file loading (ConfigManager), parameter bounds and prior
distributions (ParameterSpace), and the underlying typed registries
(ParameterManager, ParameterRegistry).
ConfigManager¶
Minimal, API-compatible configuration manager. Loads YAML or JSON files and
exposes the parsed dictionary through the .config attribute.
- class homodyne.config.manager.ConfigManager[source]
Bases:
objectMinimal configuration manager for homodyne v2 scattering analysis.
Provides simplified configuration loading with preserved API compatibility.
Key Features: - YAML/JSON configuration file loading - Compatible .config attribute access - Preserved constructor signature - Graceful fallback to defaults - CPU-only execution (GPU support removed in v2.3.0)
- Usage:
config_manager = ConfigManager(‘my_config.yaml’) data = config_manager.config
- __init__(config_file='homodyne_config.yaml', config_override=None)[source]
Initialize configuration manager.
- load_config()[source]
Load and parse YAML/JSON configuration file.
Supports both YAML and JSON formats with graceful fallback to default configuration if loading fails.
- Return type:
- get_config()[source]
Get the current configuration dictionary.
- update_config(key, value)[source]
Update a configuration value using dot notation.
- get_parameter_bounds(parameter_names=None)[source]
Get parameter bounds from configuration (cached).
Uses cached ParameterManager internally for improved performance.
- Parameters:
parameter_names (
list[str] |None) – List of parameter names to get bounds for. If None, returns bounds for all parameters in the current analysis mode.- Returns:
List of bound dictionaries with keys: ‘name’, ‘min’, ‘max’, ‘type’
- Return type:
Examples
>>> config_mgr = ConfigManager("config.yaml") >>> bounds = config_mgr.get_parameter_bounds(["D0", "alpha"]) >>> bounds[0] {'min': 1.0, 'max': 1000000.0, 'name': 'D0', 'type': 'Normal'}
Notes
This method uses a cached ParameterManager for ~14x speedup on repeated calls.
- get_active_parameters()[source]
Get list of active (physical) parameters from configuration (cached).
Uses cached ParameterManager internally for improved performance.
- Returns:
List of parameter names to be optimized. Falls back to mode-appropriate parameters if not specified in config.
- Return type:
Examples
>>> config_mgr = ConfigManager("config.yaml") >>> config_mgr.get_active_parameters() ['D0', 'alpha', 'D_offset', 'gamma_dot_t0', 'beta', 'gamma_dot_t_offset', 'phi0']
Notes
This method uses a cached ParameterManager for ~14x speedup on repeated calls.
- get_initial_parameters(use_midpoint_defaults=True)[source]
Get initial parameter values from configuration.
Loads initial parameter values from the initial_parameters.values section of the configuration. If values are null or missing, calculates mid-point defaults from parameter bounds.
- Parameters:
use_midpoint_defaults (
bool) – If True (default), calculate mid-point defaults when values are null. If False, raise an error when values are missing.- Returns:
Dictionary mapping parameter names (canonical) to initial values. Only includes active parameters (excludes fixed parameters).
- Return type:
- Raises:
ValueError – If values are null and use_midpoint_defaults is False. If number of values doesn’t match number of parameter names.
Examples
>>> # With explicit values in config >>> config = { ... 'initial_parameters': { ... 'parameter_names': ['D0', 'alpha', 'D_offset'], ... 'values': [1000.0, 0.5, 10.0] ... } ... } >>> config_mgr = ConfigManager(config_override=config) >>> config_mgr.get_initial_parameters() {'D0': 1000.0, 'alpha': 0.5, 'D_offset': 10.0}
>>> # With null values (mid-point defaults) >>> config = { ... 'initial_parameters': { ... 'parameter_names': ['D0', 'alpha'], ... 'values': null ... } ... } >>> config_mgr = ConfigManager(config_override=config) >>> params = config_mgr.get_initial_parameters() >>> # params['D0'] will be mid-point of bounds: (min + max) / 2
Notes
Uses ParameterManager for name mapping (gamma_dot_0 → gamma_dot_t0)
Respects active_parameters and fixed_parameters from config
Logs when using mid-point defaults
Returns only active parameters (fixed parameters excluded)
- validate_per_angle_scaling(n_phi)[source]
Validate per-angle scaling array lengths against number of phi angles.
This method should be called after loading phi angles from data to verify that the per_angle_scaling arrays in the config match the actual number of angles in the data.
- Parameters:
n_phi (
int) – Number of phi angles in the loaded data.- Returns:
List of validation warnings (empty if all valid).
- Return type:
- Raises:
ValueError – If per-angle scaling arrays have incorrect length and cannot be used.
Examples
>>> config_mgr = ConfigManager("config.yaml") >>> warnings = config_mgr.validate_per_angle_scaling(n_phi=5) >>> if warnings: ... for w in warnings: ... logger.warning(w)
- get_cmc_config()[source]
Get CMC (Consensus Monte Carlo) configuration with validation and defaults.
Extracts and validates the CMC configuration section from the optimization settings. Applies default values for missing fields and validates ranges and backend compatibility.
- Returns:
CMC configuration dictionary with validated settings including: - enable: bool or “auto” - min_points_for_cmc: int - sharding: dict with strategy, num_shards, max_points_per_shard - backend: dict with name, checkpoint settings - combination: dict with method, validation settings - per_shard_mcmc: dict with num_warmup, num_samples, etc. - validation: dict with convergence criteria
- Return type:
- Raises:
ValueError – If required CMC fields are invalid or incompatible with hardware
Examples
>>> config_mgr = ConfigManager("cmc_config.yaml") >>> cmc_config = config_mgr.get_cmc_config() >>> print(cmc_config["sharding"]["strategy"]) 'stratified'
Notes
Automatically applies sensible defaults for missing fields
Validates value ranges (e.g., num_shards > 0)
Checks backend compatibility with detected hardware
Logs migration warnings for deprecated settings
Usage Example¶
from homodyne.config.manager import ConfigManager
# Load from file
config_manager = ConfigManager("my_config.yaml")
config = config_manager.config
# Access nested values
analysis_mode = config["analysis"]["mode"] # "static" or "laminar_flow"
num_warmup = config["optimization"]["mcmc"]["num_warmup"]
# Override specific keys programmatically
config_manager2 = ConfigManager(
config_override={"analysis": {"mode": "static"}}
)
ParameterSpace¶
Loads parameter bounds and prior distributions from the parameter_space
section of the YAML config. Used by both NLSQ (for bounds) and CMC (for
prior construction).
- class homodyne.config.parameter_space.ParameterSpace[source]
Bases:
objectParameter space definition with bounds and prior distributions.
This class encapsulates all information needed to define the parameter space for MCMC/CMC optimization, including parameter bounds and prior distributions loaded from configuration files.
- model_type
Model type: ‘static’ or ‘laminar_flow’
- Type:
Examples
>>> # From config dict >>> config = { ... 'parameter_space': { ... 'model': 'static', ... 'bounds': [ ... {'name': 'D0', 'min': 100.0, 'max': 1e5, ... 'prior_mu': 1000.0, 'prior_sigma': 1000.0, 'type': 'TruncatedNormal'}, ... {'name': 'alpha', 'min': -2.0, 'max': 2.0, ... 'prior_mu': -1.2, 'prior_sigma': 0.3, 'type': 'Normal'} ... ] ... } ... } >>> param_space = ParameterSpace.from_config(config) >>> param_space.get_bounds('D0') (100.0, 100000.0) >>> prior = param_space.get_prior('D0') >>> prior.dist_type 'TruncatedNormal'
- model_type: str
- classmethod from_config(config_dict, analysis_mode=None)[source]
Load ParameterSpace from configuration dictionary.
This class method constructs a ParameterSpace instance from a YAML configuration dict, handling missing values gracefully and integrating with the existing ParameterManager for name mapping and defaults.
- Parameters:
- Returns:
Configured parameter space instance
- Return type:
ParameterSpace- Raises:
ValueError – If parameter_space section is malformed or missing required fields
Examples
>>> config = {'parameter_space': {'model': 'static', 'bounds': [...]}} >>> param_space = ParameterSpace.from_config(config) >>> param_space.model_type 'static'
Notes
Uses ParameterManager for name mapping (gamma_dot_0 → gamma_dot_t0)
Falls back to package defaults if config is incomplete
Validates all bounds and prior distribution parameters
Logs warnings for missing or invalid config values
- classmethod from_defaults(analysis_mode='laminar_flow')[source]
Create ParameterSpace with package defaults (no config file).
This method creates a ParameterSpace using only the hardcoded defaults from ParameterManager, useful when no config file is available or for testing.
- Parameters:
analysis_mode (
str) – Analysis mode: ‘static’ or ‘laminar_flow’- Returns:
Parameter space with default bounds and wide priors
- Return type:
ParameterSpace
Examples
>>> param_space = ParameterSpace.from_defaults('static') >>> param_space.parameter_names ['D0', 'alpha', 'D_offset']
- copy()[source]
Return a shallow copy safe for localized mutations.
- Return type:
ParameterSpace
- drop_parameters(names)[source]
Return a copy with specific parameters removed.
- Return type:
ParameterSpace
- with_prior_overrides(overrides)[source]
Return a copy with select priors replaced.
- Return type:
ParameterSpace
- get_bounds(param_name)[source]
Get bounds for a specific parameter.
- get_prior(param_name)[source]
Get prior distribution for a specific parameter.
- get_bounds_array()[source]
Get bounds as numpy arrays (for optimization).
- Return type:
- Returns:
lower_bounds (np.ndarray) – Array of lower bounds (in parameter_names order)
upper_bounds (np.ndarray) – Array of upper bounds (in parameter_names order)
Examples
>>> param_space = ParameterSpace.from_defaults('static') >>> lower, upper = param_space.get_bounds_array() >>> lower.shape (3,)
- get_prior_means()[source]
Get prior means as numpy array (for initialization).
- Returns:
Array of prior means (in parameter_names order)
- Return type:
Examples
>>> param_space = ParameterSpace.from_defaults('static') >>> means = param_space.get_prior_means() >>> means.shape (3,)
- validate_values(values, tolerance=1e-10)[source]
Validate parameter values against bounds.
- Parameters:
- Return type:
- Returns:
is_valid (bool) – True if all values are within bounds
violations (list[str]) – List of violation messages (empty if valid)
Examples
>>> param_space = ParameterSpace.from_defaults('static') >>> values = {'D0': 1000.0, 'alpha': -1.2, 'D_offset': 0.0} >>> is_valid, violations = param_space.validate_values(values) >>> is_valid True
- convert_to_beta_scaled_priors()[source]
Return a copy of the ParameterSpace with BetaScaled priors on bounded params.
- Return type:
ParameterSpace
- convert_to_beta_priors()[source]
Backward compatible alias for BetaScaled conversion.
- Return type:
ParameterSpace
- get_single_angle_fallback_prior(param_name)[source]
Return a gentle BetaScaled prior for single-angle stabilization.
- Parameters:
param_name (
str) – Target parameter (e.g., ‘D0’, ‘alpha’, ‘D_offset’).- Returns:
BetaScaled prior centered within the configured bounds.
- Return type:
PriorDistribution
- with_single_angle_stabilization(*, enable_beta_fallback=False)[source]
Return a copy with priors tightened for the single-angle regime.
- Return type:
ParameterSpace
- get_single_angle_geometry_config()[source]
Return heuristic priors for single-angle diffusion reparameterization.
- clamp_to_open_interval(param_name, value, epsilon=1e-6)[source]
Clamp parameter value to strictly inside bounds (open interval).
NumPyro’s TruncatedNormal transform requires values strictly inside (min, max) - not equal to the boundaries. This method ensures values are at least epsilon away from both bounds.
- Parameters:
- Returns:
Clamped value strictly inside (min + epsilon, max - epsilon)
- Return type:
Examples
>>> param_space = ParameterSpace.from_defaults('static') >>> # If offset bounds are [0.5, 1.5] and value equals 0.5 (boundary violation) >>> clamped = param_space.clamp_to_open_interval('offset', 0.5) >>> # Returns 0.500001 (0.5 + 1e-6), strictly inside bounds >>> clamped > 0.5 and clamped < 1.5 True
- __init__(model_type, parameter_names=<factory>, bounds=<factory>, priors=<factory>, units=<factory>)
Usage Example¶
from homodyne.config.manager import ConfigManager
from homodyne.config.parameter_space import ParameterSpace
config_manager = ConfigManager("config.yaml")
param_space = ParameterSpace.from_config(config_manager.config)
# Inspect bounds for D0
d0_min, d0_max = param_space.get_bounds("D0")
print(f"D0 bounds: [{d0_min:.2g}, {d0_max:.2g}]")
# Get ordered bounds tuple for NLSQ
lower_bounds, upper_bounds = param_space.get_bounds_array("laminar_flow")
YAML Configuration Schema¶
A complete YAML configuration file for laminar_flow analysis:
# ============================================================
# Homodyne YAML Configuration Schema (laminar_flow mode)
# ============================================================
analysis:
mode: "laminar_flow" # "static" | "laminar_flow"
q: 0.01 # scattering wavevector [Å⁻¹]
L: 1000.0 # gap length [Å]
data:
hdf5_file: "sample.h5"
use_cache: true
cache_dir: null
validate: true
optimization:
method: "nlsq" # "nlsq" | "cmc"
nlsq:
anti_degeneracy:
per_angle_mode: "auto" # "auto" | "constant" | "individual" | "fourier"
cmaes:
enable: false
preset: "cmaes-global"
refine_with_nlsq: true
memory_fraction: 0.75
mcmc:
num_warmup: 500
num_samples: 1500
num_chains: 4
cmc:
enable: true
sharding:
strategy: "stratified"
max_points_per_shard: "auto"
backend_config:
name: "auto"
per_angle_mode: "auto"
combination:
method: "consensus_mc"
min_success_rate: 0.90
per_shard_mcmc:
num_warmup: 500
num_samples: 1500
num_chains: 4
chain_method: "parallel"
target_accept_prob: 0.85
max_tree_depth: 10
adaptive_sampling: true
min_warmup: 100
min_samples: 200
enable_jax_profiling: false
jax_profile_dir: "./profiles/jax"
validation:
max_divergence_rate: 0.10
require_nlsq_warmstart: false
max_r_hat: 1.1
min_ess: 400
parameter_space:
D0:
bounds: [1.0, 1.0e6]
prior: {type: "lognormal", mean: 100.0, std: 50.0}
alpha:
bounds: [0.0, 2.0]
prior: {type: "normal", mean: 0.5, std: 0.3}
D_offset:
bounds: [-1.0e4, 1.0e4]
prior: {type: "normal", mean: 0.01, std: 0.1}
gamma_dot_t0:
bounds: [1.0e-6, 10.0]
prior: {type: "lognormal", mean: 1.0e-3, std: 5.0e-4}
beta:
bounds: [-2.0, 2.0]
prior: {type: "normal", mean: 0.0, std: 0.5}
gamma_dot_t_offset:
bounds: [-10.0, 10.0]
prior: {type: "normal", mean: 0.0, std: 0.1}
phi0:
bounds: [0.0, 180.0]
prior: {type: "uniform", low: 0.0, high: 180.0}
output:
dir: "./results"
save_plots: true
save_npz: true
save_json: true
logging:
level: "INFO"
file: "homodyne.log"
Default Parameter Values¶
Parameter |
Min |
Max |
Physical meaning |
|---|---|---|---|
\(D_0\) |
1.0 |
1e6 |
Diffusion coefficient prefactor (Ų/s) |
\(\alpha\) |
0.0 |
2.0 |
Diffusion time exponent |
\(D_\text{offset}\) |
−1e4 |
1e4 |
Background diffusion (Ų/s) |
\(\dot\gamma_0\) |
1e−6 |
10.0 |
Shear rate prefactor (s-1) |
\(\beta\) |
−2.0 |
2.0 |
Shear rate time exponent |
\(\dot\gamma_\text{offset}\) |
−10.0 |
10.0 |
Background shear rate (s-1) |
\(\varphi_0\) |
0.0 |
180.0 |
Flow orientation angle (degrees) |
ParameterRegistry¶
Centralized parameter registry that provides the single source of truth for parameter metadata (names, types, bounds, defaults, descriptions). Used by NLSQ (bounds), CMC (prior construction), and configuration validation.
- class homodyne.config.parameter_registry.ParameterInfo[source]
Bases:
objectMetadata for a single parameter.
- name
Canonical parameter name
- Type:
- description
Human-readable description
- Type:
- dtype
Python/numpy type (float, int, etc.)
- Type:
- default
Default value for initialization
- Type:
- lower_bound
Lower bound for optimization/MCMC
- Type:
- upper_bound
Upper bound for optimization/MCMC
- Type:
- prior_mean
Prior mean for Bayesian inference (None for uniform prior)
- Type:
float | None
- prior_std
Prior standard deviation (None for uniform prior)
- Type:
float | None
- units
Physical units (e.g., ‘Ų/s’, ‘radians’)
- Type:
- is_scaling
True if this is a per-angle scaling parameter
- Type:
- is_physical
True if this is a physical model parameter
- Type:
- is_flow
True if this is a flow-specific parameter
- Type:
- log_space
True if parameter should be sampled in log space (e.g., D0)
- Type:
- name: str
- description: str
- dtype
alias of
float
- default: float = 1.0
- lower_bound: float = 0.0
- upper_bound: float = inf
- units: str = ''
- is_scaling: bool = False
- is_physical: bool = False
- is_flow: bool = False
- log_space: bool = False
- __init__(name, description, dtype=<class 'float'>, default=1.0, lower_bound=0.0, upper_bound=inf, prior_mean=None, prior_std=None, units='', is_scaling=False, is_physical=False, is_flow=False, log_space=False)
- class homodyne.config.parameter_registry.ParameterRegistry[source]
Bases:
objectCentralized registry of all parameter definitions.
This class provides a single source of truth for parameter metadata, eliminating duplication across the codebase.
Examples
>>> registry = ParameterRegistry() >>> registry.get_param_names("static") ['D0', 'alpha', 'D_offset']
>>> registry.get_all_param_names("static", n_angles=3, include_scaling=True) ['contrast_0', 'contrast_1', 'contrast_2', 'offset_0', 'offset_1', 'offset_2', 'D0', 'alpha', 'D_offset']
>>> registry.get_bounds("D0") (100.0, 100000.0)
- static __new__(cls)[source]
Singleton pattern - return existing instance if available.
- Return type:
ParameterRegistry
- property scaling_names: tuple[str, ...]
Base names of all scaling parameters (derived from
is_scalingflag).Returns a tuple in registration order (contrast, offset) so that downstream consumers produce deterministic parameter orderings. Cached after first access since
_PARAMETERSis immutable.
- get_param_info(name)[source]
Get parameter metadata.
- get_param_names(analysis_mode)[source]
Get physical parameter names for analysis mode.
- get_all_param_names(analysis_mode, n_angles=1, include_scaling=True)[source]
Get all parameter names including per-angle scaling.
- Parameters:
- Returns:
Complete parameter names in NumPyro sampling order: [contrast_0..n, offset_0..n, D0, alpha, …]
- Return type:
Notes
NumPyro requires parameters in EXACT order as model.sample(). This method returns parameters in the correct order for init_to_value().
- get_bounds(name)[source]
Get parameter bounds.
- get_all_bounds(analysis_mode, n_angles=1, include_scaling=True)[source]
Get bounds for all parameters.
T055: Logs parameter bounds at DEBUG level.
- get_defaults(analysis_mode, n_angles=1, include_scaling=True)[source]
Get default values for all parameters.
T055: Logs parameter initial values at DEBUG level.
- get_num_params(analysis_mode, n_angles=1, include_scaling=True)[source]
Get total number of parameters.
- validate_param_values(values, analysis_mode, n_angles=1, include_scaling=True)[source]
Validate parameter values against bounds.
- Parameters:
- Raises:
ValueError – If any value is out of bounds
- Return type:
- expand_initial_values(initial_values, n_angles)[source]
Expand scalar scaling values to per-angle parameters.
- Parameters:
- Returns:
Expanded values with contrast_i and offset_i
- Return type:
Examples
>>> registry = ParameterRegistry() >>> registry.expand_initial_values({'contrast': 0.5, 'offset': 1.0, 'D0': 1000}, n_angles=3) {'contrast_0': 0.5, 'contrast_1': 0.5, 'contrast_2': 0.5, 'offset_0': 1.0, 'offset_1': 1.0, 'offset_2': 1.0, 'D0': 1000}
- homodyne.config.parameter_registry.get_registry()[source]
Get the global ParameterRegistry instance.
- Returns:
Singleton registry instance (guaranteed by ParameterRegistry.__new__)
- Return type:
ParameterRegistry
- homodyne.config.parameter_registry.get_param_names(analysis_mode)[source]
Get physical parameter names for analysis mode.
- homodyne.config.parameter_registry.get_all_param_names(analysis_mode, n_angles=1, include_scaling=True)[source]
Get all parameter names including per-angle scaling.
- homodyne.config.parameter_registry.get_bounds(name)[source]
Get parameter bounds.
- homodyne.config.parameter_registry.get_defaults(analysis_mode, n_angles=1, include_scaling=True)[source]
Get default values for all parameters.
Minimal Configuration Management for Homodyne¶
Simplified configuration system with preserved API compatibility. Provides essential YAML/JSON loading with the same interface as the original ConfigManager while removing complex features not needed for core functionality.
Note: GPU support removed in v2.3.0 - CPU-only execution.
- homodyne.config.manager.load_xpcs_config(config_path)[source]
Load XPCS configuration from file.
Convenience function for loading configuration files.
Parameter Space Configuration for MCMC/CMC¶
Defines the ParameterSpace class for loading parameter bounds and prior distributions from YAML configuration files. This enables config-driven MCMC initialization without hardcoded priors.
This module is part of the v2.1.0 MCMC simplification implementation. See: /home/wei/Documents/GitHub/homodyne/agent-os/specs/2025-10-31-mcmc-simplification/spec.md
- class homodyne.config.parameter_space.PriorDistribution[source]
Bases:
objectPrior distribution specification for a parameter.
- dist_type
Distribution type: ‘Normal’, ‘TruncatedNormal’, ‘Uniform’, ‘LogNormal’
- Type:
- mu
Mean (location parameter)
- Type:
- sigma
Standard deviation (scale parameter)
- Type:
- min_val
Minimum bound (for truncated distributions)
- Type:
- max_val
Maximum bound (for truncated distributions)
- Type:
- dist_type: str
- mu: float = 0.0
- sigma: float = 1.0
- min_val: float = -inf
- max_val: float = inf
- to_numpyro_kwargs()[source]
Convert to NumPyro distribution kwargs.
- __init__(dist_type, mu=0.0, sigma=1.0, min_val=-inf, max_val=inf, alpha=None, beta=None)