pycsamt.ai.domain_gap.simulator#
Reproducible corruption of canonical surveys for domain-gap training.
Every function in this module maps a validated
SurveyData to a new, independently
validated SurveyData. Corruptions are
composed from a single integer seed so a training run can be reproduced
exactly from its CorruptionConfig and seed alone.
Two families of corruption are distinguished:
- Systematic (bias-like)
apply_static_shift()andapply_galvanic_distortion()model near-surface, frequency-independent effects that multiply the true impedance by a real matrix. They change the signal, not its declared uncertainty, beyond the linear scaling of that uncertainty.- Random (noise-like)
add_heteroscedastic_noise(),apply_error_floor(),apply_dropout(), andinject_outliers()model acquisition noise, missing data, and undetected bad readings. These updateimpedance_errorandvalidso that masks and errors stay consistent with the injected corruption, per the M3 acceptance gate.
apply_corruption_suite() composes all of the above in a fixed,
documented order using seeds spawned from a single parent seed, and returns
a CorruptionRecord describing exactly what was sampled.
Module Attributes
|
Named corruption suites for M3's clean/in-distribution/severe/OOD split. |
Functions
|
Add complex, per-observation heteroscedastic Gaussian noise. |
|
Apply the full, ordered M3 corruption pipeline from a single seed. |
|
Mark stations, frequencies, or individual observations as missing. |
|
Clamp the declared error to a minimum fraction of |
|
Inject a per-station real Groom & Bailey-style distortion matrix. |
|
Multiply every present component by a per-station real factor. |
|
Perturb a random fraction of valid observations by a large factor. |
|
Add Gaussian noise to station coordinates and elevation. |
Classes
|
Parameter ranges for one corruption pass over a |
|
Provenance of one applied |
- class pycsamt.ai.domain_gap.simulator.CorruptionConfig(noise_level_range=(0.0, 0.0), error_floor_fraction=0.0, static_shift_log10_sigma=0.0, distortion_gain_log10_sigma=0.0, distortion_twist_deg_sigma=0.0, distortion_shear_sigma=0.0, distortion_anisotropy_sigma=0.0, station_dropout_rate=0.0, frequency_dropout_rate=0.0, random_dropout_rate=0.0, outlier_rate=0.0, outlier_log10_shift_range=(0.5, 1.5), coordinate_sigma_m=0.0, elevation_sigma_m=0.0)[source]
Bases:
objectParameter ranges for one corruption pass over a
SurveyData.All defaults are zero/no-op so
CorruptionConfig()is the clean synthetic control set required by the M3 gate.- Parameters:
noise_level_range ((float, float), default=(0.0, 0.0)) – Bounds on the relative heteroscedastic noise standard deviation sampled independently per station/frequency observation.
error_floor_fraction (float, default=0.0) – Minimum declared
impedance_erroras a fraction of|Z|.static_shift_log10_sigma (float, default=0.0) – Std. dev. of the log10 per-station static-shift factor applied identically across all frequencies.
distortion_gain_log10_sigma (float, default=0.0) – Std. dev. of the per-station Groom-Bailey-style gain, twist, shear, and anisotropy parameters of the injected galvanic distortion.
distortion_twist_deg_sigma (float, default=0.0) – Std. dev. of the per-station Groom-Bailey-style gain, twist, shear, and anisotropy parameters of the injected galvanic distortion.
distortion_shear_sigma (float, default=0.0) – Std. dev. of the per-station Groom-Bailey-style gain, twist, shear, and anisotropy parameters of the injected galvanic distortion.
distortion_anisotropy_sigma (float, default=0.0) – Std. dev. of the per-station Groom-Bailey-style gain, twist, shear, and anisotropy parameters of the injected galvanic distortion.
station_dropout_rate (float, default=0.0) – Probability that an entire station, an entire frequency (across all stations), or an individual observation is marked missing.
frequency_dropout_rate (float, default=0.0) – Probability that an entire station, an entire frequency (across all stations), or an individual observation is marked missing.
random_dropout_rate (float, default=0.0) – Probability that an entire station, an entire frequency (across all stations), or an individual observation is marked missing.
outlier_rate (float, default=0.0) – Fraction of remaining valid observations perturbed by a large, undetected multiplicative shift.
outlier_log10_shift_range ((float, float), default=(0.5, 1.5)) – Bounds on the magnitude (in log10 decades) of injected outliers; the sign is randomized.
coordinate_sigma_m (float, default=0.0) – Std. dev. of Gaussian perturbation applied to station horizontal coordinates and elevation, respectively.
elevation_sigma_m (float, default=0.0) – Std. dev. of Gaussian perturbation applied to station horizontal coordinates and elevation, respectively.
Examples
>>> config = CorruptionConfig(noise_level_range=(0.01, 0.05)) >>> config.config_hash() == CorruptionConfig( ... noise_level_range=(0.01, 0.05) ... ).config_hash() True
- error_floor_fraction: float = 0.0
- static_shift_log10_sigma: float = 0.0
- distortion_gain_log10_sigma: float = 0.0
- distortion_twist_deg_sigma: float = 0.0
- distortion_shear_sigma: float = 0.0
- distortion_anisotropy_sigma: float = 0.0
- station_dropout_rate: float = 0.0
- frequency_dropout_rate: float = 0.0
- random_dropout_rate: float = 0.0
- outlier_rate: float = 0.0
- coordinate_sigma_m: float = 0.0
- elevation_sigma_m: float = 0.0
- to_dict()[source]
Return a JSON-serializable, order-stable representation.
- Returns:
Field values with a schema discriminator.
- Return type:
Examples
>>> CorruptionConfig().to_dict()["schema_version"] 1
- classmethod from_dict(data)[source]
Restore and validate a serialized configuration.
- Parameters:
data (mapping) – State previously returned by
to_dict().- Returns:
Validated immutable configuration.
- Return type:
Examples
>>> state = CorruptionConfig(error_floor_fraction=0.02).to_dict() >>> CorruptionConfig.from_dict(state).error_floor_fraction 0.02
- class pycsamt.ai.domain_gap.simulator.CorruptionRecord(config, seed, sampled=<factory>, severity=None)[source]
Bases:
objectProvenance of one applied
apply_corruption_suite()call.- Parameters:
config (CorruptionConfig) – Configuration the sampled parameters were drawn from.
seed (int) – Parent seed used to spawn every corruption step’s generator.
severity (str or None) – Name of the severity preset used, when applicable.
sampled (mapping) – Concrete, JSON-serializable per-step summary statistics (e.g. the number of dropped stations, or the mean sampled distortion gain).
- config: CorruptionConfig
- seed: int
- pycsamt.ai.domain_gap.simulator.SEVERITY_PRESETS: Mapping[str, CorruptionConfig] = {'clean': CorruptionConfig(noise_level_range=(0.0, 0.0), error_floor_fraction=0.0, static_shift_log10_sigma=0.0, distortion_gain_log10_sigma=0.0, distortion_twist_deg_sigma=0.0, distortion_shear_sigma=0.0, distortion_anisotropy_sigma=0.0, station_dropout_rate=0.0, frequency_dropout_rate=0.0, random_dropout_rate=0.0, outlier_rate=0.0, outlier_log10_shift_range=(0.5, 1.5), coordinate_sigma_m=0.0, elevation_sigma_m=0.0), 'held_out_corruption': CorruptionConfig(noise_level_range=(0.1, 0.25), error_floor_fraction=0.05, static_shift_log10_sigma=0.3, distortion_gain_log10_sigma=0.1, distortion_twist_deg_sigma=25.0, distortion_shear_sigma=0.4, distortion_anisotropy_sigma=0.3, station_dropout_rate=0.15, frequency_dropout_rate=0.15, random_dropout_rate=0.1, outlier_rate=0.08, outlier_log10_shift_range=(1.0, 2.5), coordinate_sigma_m=15.0, elevation_sigma_m=5.0), 'in_distribution': CorruptionConfig(noise_level_range=(0.01, 0.03), error_floor_fraction=0.02, static_shift_log10_sigma=0.05, distortion_gain_log10_sigma=0.02, distortion_twist_deg_sigma=3.0, distortion_shear_sigma=0.05, distortion_anisotropy_sigma=0.0, station_dropout_rate=0.0, frequency_dropout_rate=0.0, random_dropout_rate=0.02, outlier_rate=0.0, outlier_log10_shift_range=(0.5, 1.5), coordinate_sigma_m=1.0, elevation_sigma_m=0.0), 'severe': CorruptionConfig(noise_level_range=(0.05, 0.15), error_floor_fraction=0.05, static_shift_log10_sigma=0.15, distortion_gain_log10_sigma=0.05, distortion_twist_deg_sigma=10.0, distortion_shear_sigma=0.15, distortion_anisotropy_sigma=0.0, station_dropout_rate=0.05, frequency_dropout_rate=0.05, random_dropout_rate=0.05, outlier_rate=0.02, outlier_log10_shift_range=(0.5, 1.5), coordinate_sigma_m=5.0, elevation_sigma_m=2.0)}
Named corruption suites for M3’s clean/in-distribution/severe/OOD split.
- pycsamt.ai.domain_gap.simulator.add_heteroscedastic_noise(survey, *, level_range=(0.02, 0.05), rng)[source]
Add complex, per-observation heteroscedastic Gaussian noise.
- Parameters:
survey (SurveyData) – Clean or already-corrupted survey.
level_range ((float, float), default=(0.02, 0.05)) – Bounds on the relative noise standard deviation sampled independently for every
(station, frequency)pair and shared across components at that pair, mimicking correlated instrument noise.rng (numpy.random.Generator) – Source of randomness; callers control reproducibility.
- Returns:
New survey with perturbed impedance and an
impedance_errorthat combines any pre-existing error with the injected noise in quadrature.- Return type:
Examples
>>> import numpy as np >>> from pycsamt.ai.data.contracts import SurveyData >>> z = np.full((1, 2, 1), 100 + 0j) >>> survey = SurveyData(z, [10.0, 1.0], ["S"], ["xy"], [[0.0, 0.0]]) >>> noisy = add_heteroscedastic_noise( ... survey, level_range=(0.05, 0.05), rng=np.random.default_rng(0) ... ) >>> noisy.impedance_error is not None True
- pycsamt.ai.domain_gap.simulator.apply_error_floor(survey, *, floor_fraction)[source]
Clamp the declared error to a minimum fraction of
|Z|.- Parameters:
survey (SurveyData) – Survey whose error floor should be enforced.
floor_fraction (float) – Minimum
impedance_erroras a fraction of|Z|. Zero is a no-op.
- Returns:
New survey with an error array at least as large as
floor_fraction * |Z|on valid observations.- Return type:
Examples
>>> import numpy as np >>> from pycsamt.ai.data.contracts import SurveyData >>> z = np.full((1, 1, 1), 100 + 0j) >>> survey = SurveyData( ... z, ... [1.0], ... ["S"], ... ["xy"], ... [[0.0, 0.0]], ... impedance_error=np.ones((1, 1, 1)), ... ) >>> floored = apply_error_floor(survey, floor_fraction=0.5) >>> floored.impedance_error[0, 0, 0] 50.0
- pycsamt.ai.domain_gap.simulator.apply_static_shift(survey, *, log10_sigma, rng, return_info=False)[source]
Multiply every present component by a per-station real factor.
Static shift is modelled as a frequency-independent real scalar
c_s = 10 ** N(0, log10_sigma)per station, applied identically to every impedance component and to the declared error, consistent with the linear scaling of a real multiplicative distortion.- Parameters:
survey (SurveyData) – Survey to distort.
log10_sigma (float) – Std. dev. of the log10 static-shift factor. Zero is a no-op.
rng (numpy.random.Generator) – Source of randomness.
return_info (bool)
- Returns:
New survey with static shift applied.
- Return type:
Examples
>>> import numpy as np >>> from pycsamt.ai.data.contracts import SurveyData >>> z = np.full((2, 1, 1), 100 + 0j) >>> survey = SurveyData(z, [1.0], ["A", "B"], ["xy"], [[0, 0], [1, 0]]) >>> shifted = apply_static_shift( ... survey, log10_sigma=0.1, rng=np.random.default_rng(0) ... ) >>> shifted.shape == survey.shape True
- pycsamt.ai.domain_gap.simulator.apply_galvanic_distortion(survey, *, gain_log10_sigma=0.0, twist_deg_sigma=0.0, shear_sigma=0.0, anisotropy_sigma=0.0, rng, return_info=False)[source]
Inject a per-station real Groom & Bailey-style distortion matrix.
- Parameters:
survey (SurveyData) – Survey to distort. Must expose at least one impedance component; any of
xx, xy, yx, yyabsent fromSurveyData.componentsis treated as zero for the purpose of building the dense 2x2 impedance used internally, which is an approximation for surveys that only store off-diagonal components.gain_log10_sigma (float) – Std. dev. of the per-station gain (log10), twist (degrees), shear, and anisotropy parameters. Zero for every parameter is a no-op.
twist_deg_sigma (float) – Std. dev. of the per-station gain (log10), twist (degrees), shear, and anisotropy parameters. Zero for every parameter is a no-op.
shear_sigma (float) – Std. dev. of the per-station gain (log10), twist (degrees), shear, and anisotropy parameters. Zero for every parameter is a no-op.
anisotropy_sigma (float) – Std. dev. of the per-station gain (log10), twist (degrees), shear, and anisotropy parameters. Zero for every parameter is a no-op.
rng (numpy.random.Generator) – Source of randomness.
return_info (bool)
- Returns:
New survey with distorted impedance; declared error, if any, is scaled by the sampled gain as a first-order approximation.
- Return type:
Examples
>>> import numpy as np >>> from pycsamt.ai.data.contracts import SurveyData >>> z = np.ones((1, 1, 2), dtype=complex) * (1 + 1j) >>> survey = SurveyData(z, [1.0], ["S"], ["xy", "yx"], [[0, 0]]) >>> distorted = apply_galvanic_distortion( ... survey, twist_deg_sigma=10.0, rng=np.random.default_rng(0) ... ) >>> distorted.shape == survey.shape True
- pycsamt.ai.domain_gap.simulator.apply_dropout(survey, *, station_rate=0.0, frequency_rate=0.0, random_rate=0.0, rng, return_info=False)[source]
Mark stations, frequencies, or individual observations as missing.
Dropped observations are invalidated by setting the impedance to
NaN;SurveyDataconstruction then recomputesvalidfrom finiteness, so masks stay authoritative automatically.- Parameters:
survey (SurveyData) – Survey to thin out.
station_rate (float, default=0.0) – Independent probabilities that a whole station (all frequencies and components), a whole frequency (all stations and components), or an individual observation is dropped. Effects are combined (a station or frequency dropout wins over a random one at the same cell).
frequency_rate (float, default=0.0) – Independent probabilities that a whole station (all frequencies and components), a whole frequency (all stations and components), or an individual observation is dropped. Effects are combined (a station or frequency dropout wins over a random one at the same cell).
random_rate (float, default=0.0) – Independent probabilities that a whole station (all frequencies and components), a whole frequency (all stations and components), or an individual observation is dropped. Effects are combined (a station or frequency dropout wins over a random one at the same cell).
rng (numpy.random.Generator) – Source of randomness.
return_info (bool)
- Returns:
New survey with additional invalid observations.
- Return type:
Examples
>>> import numpy as np >>> from pycsamt.ai.data.contracts import SurveyData >>> z = np.ones((4, 4, 1), dtype=complex) >>> survey = SurveyData( ... z, ... [4.0, 3.0, 2.0, 1.0], ... ["A", "B", "C", "D"], ... ["xy"], ... np.zeros((4, 2)), ... ) >>> thinned = apply_dropout( ... survey, station_rate=1.0, rng=np.random.default_rng(0) ... ) >>> thinned.n_valid 0
- pycsamt.ai.domain_gap.simulator.inject_outliers(survey, *, rate=0.0, log10_shift_range=(0.5, 1.5), rng, return_info=False)[source]
Perturb a random fraction of valid observations by a large factor.
Outliers remain marked
validand keep their existing declared error, simulating a bad reading that quality control failed to flag — the case a robust inverter must tolerate.- Parameters:
survey (SurveyData) – Survey to perturb.
rate (float, default=0.0) – Fraction of currently valid observations perturbed. Zero is a no-op.
log10_shift_range ((float, float), default=(0.5, 1.5)) – Bounds on the outlier magnitude in log10 decades; the sign is randomized per outlier.
rng (numpy.random.Generator) – Source of randomness.
return_info (bool)
- Returns:
New survey with a subset of valid impedance values shifted by
10 ** (+/- shift).- Return type:
Examples
>>> import numpy as np >>> from pycsamt.ai.data.contracts import SurveyData >>> z = np.full((1, 10, 1), 100 + 0j) >>> survey = SurveyData( ... z, np.arange(10.0, 0.0, -1.0), ["S"], ["xy"], [[0, 0]] ... ) >>> corrupted = inject_outliers( ... survey, rate=0.5, rng=np.random.default_rng(0) ... ) >>> corrupted.shape == survey.shape True
- pycsamt.ai.domain_gap.simulator.perturb_coordinates(survey, *, coordinate_sigma_m=0.0, elevation_sigma_m=0.0, rng)[source]
Add Gaussian noise to station coordinates and elevation.
- Parameters:
survey (SurveyData) – Survey whose station geometry should be perturbed.
coordinate_sigma_m (float, default=0.0) – Std. dev. of Gaussian noise added to the horizontal (x, y) coordinates and to elevation, respectively. Elevation entries that are
NaN(unknown) stayNaN.elevation_sigma_m (float, default=0.0) – Std. dev. of Gaussian noise added to the horizontal (x, y) coordinates and to elevation, respectively. Elevation entries that are
NaN(unknown) stayNaN.rng (numpy.random.Generator) – Source of randomness.
- Returns:
New survey with perturbed
SurveyData.coordinates_m.- Return type:
Examples
>>> import numpy as np >>> from pycsamt.ai.data.contracts import SurveyData >>> z = np.ones((1, 1, 1), dtype=complex) >>> survey = SurveyData(z, [1.0], ["S"], ["xy"], [[0.0, 0.0, 100.0]]) >>> moved = perturb_coordinates( ... survey, coordinate_sigma_m=5.0, rng=np.random.default_rng(0) ... ) >>> moved.coordinates_m.shape (1, 3)
- pycsamt.ai.domain_gap.simulator.apply_corruption_suite(survey, config=None, *, severity=None, seed)[source]
Apply the full, ordered M3 corruption pipeline from a single seed.
Steps run in this fixed order: static shift, galvanic distortion, heteroscedastic noise, error floor, dropout, outliers, coordinate perturbation. Systematic distortions are applied to the clean signal before random noise and missingness, matching how these effects compose physically.
- Parameters:
survey (SurveyData) – Clean survey to corrupt.
config (CorruptionConfig, optional) – Explicit configuration. Mutually exclusive with
severity.severity (str, optional) – Name of an entry in
SEVERITY_PRESETSto use asconfig.seed (int) – Parent seed. Each step draws from an independently spawned child generator so adding a new step does not change earlier steps’ draws.
- Returns:
survey (SurveyData) – Corrupted survey.
record (CorruptionRecord) – Provenance of the applied configuration and sampled parameters.
- Raises:
ValueError – If both or neither of
config/severityare given, orseverityis unknown.- Return type:
Examples
>>> import numpy as np >>> from pycsamt.ai.data.contracts import SurveyData >>> z = np.full((3, 5, 2), 100 + 50j) >>> survey = SurveyData( ... z, ... np.linspace(100, 1, 5), ... ["A", "B", "C"], ... ["xy", "yx"], ... np.zeros((3, 2)), ... ) >>> corrupted, record = apply_corruption_suite( ... survey, severity="in_distribution", seed=0 ... ) >>> record.severity 'in_distribution'