Source code for matrix_toolkit.pde.random_params

"""
Random parameter generation for PDE matrices
"""

import numpy as np
from typing import Dict, List, Tuple, Optional
from dataclasses import dataclass


[docs] @dataclass class ParameterDistribution: """Parameter distribution specification""" name: str distribution: str = "uniform" # uniform, normal, lognormal params: Dict = None # Distribution parameters
[docs] def sample(self, seed: Optional[int] = None) -> float: """Sample from distribution""" if seed is not None: np.random.seed(seed) if self.distribution == "uniform": low = self.params.get('low', 0.0) high = self.params.get('high', 1.0) return np.random.uniform(low, high) elif self.distribution == "normal": mean = self.params.get('mean', 0.0) std = self.params.get('std', 1.0) return np.random.normal(mean, std) elif self.distribution == "lognormal": mean = self.params.get('mean', 0.0) sigma = self.params.get('sigma', 1.0) return np.random.lognormal(mean, sigma) else: raise ValueError(f"Unknown distribution: {self.distribution}")
[docs] class RandomParameterGenerator: """ Generate random parameters for PDE problems Examples: >>> gen = RandomParameterGenerator() >>> gen.add_parameter('diffusion', 'uniform', low=0.1, high=10.0) >>> gen.add_parameter('velocity', 'normal', mean=1.0, std=0.5) >>> params = gen.sample(n=100) """
[docs] def __init__(self): self.parameters: Dict[str, ParameterDistribution] = {}
[docs] def add_parameter( self, name: str, distribution: str = "uniform", **dist_params ): """ Add a parameter to sample Args: name: Parameter name distribution: Distribution type **dist_params: Distribution parameters """ self.parameters[name] = ParameterDistribution( name=name, distribution=distribution, params=dist_params )
[docs] def sample( self, n: int = 1, seed: Optional[int] = None ) -> List[Dict[str, float]]: """ Sample n parameter sets Args: n: Number of samples seed: Random seed Returns: List of parameter dictionaries """ samples = [] for i in range(n): sample_seed = None if seed is None else seed + i params = {} for param_name, param_dist in self.parameters.items(): params[param_name] = param_dist.sample(sample_seed) samples.append(params) return samples
[docs] class LatinHypercubeSampler: """ Latin Hypercube Sampling for better space coverage Examples: >>> sampler = LatinHypercubeSampler() >>> sampler.add_parameter('diffusion', low=0.1, high=10.0) >>> sampler.add_parameter('velocity', low=-1.0, high=1.0) >>> samples = sampler.sample(n=50) """
[docs] def __init__(self): self.parameters: Dict[str, Tuple[float, float]] = {}
[docs] def add_parameter(self, name: str, low: float, high: float): """Add parameter with range""" self.parameters[name] = (low, high)
[docs] def sample( self, n: int, seed: Optional[int] = None ) -> List[Dict[str, float]]: """ Generate Latin Hypercube samples Args: n: Number of samples seed: Random seed Returns: List of parameter dictionaries """ if seed is not None: np.random.seed(seed) n_params = len(self.parameters) param_names = list(self.parameters.keys()) # Generate LHS samples in [0, 1]^d lhs_samples = np.zeros((n, n_params)) for i in range(n_params): # Divide [0, 1] into n intervals intervals = np.arange(n) / n # Sample within each interval samples = intervals + np.random.uniform(0, 1/n, n) # Randomly permute lhs_samples[:, i] = np.random.permutation(samples) # Scale to parameter ranges result = [] for i in range(n): params = {} for j, param_name in enumerate(param_names): low, high = self.parameters[param_name] params[param_name] = low + (high - low) * lhs_samples[i, j] result.append(params) return result