Skip to content

Target Transforms

matgpr.target_transforms

IdentityTargetTransform dataclass

No-op target transform with the same interface as other transforms.

Source code in matgpr/target_transforms.py
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@dataclass
class IdentityTargetTransform:
    """No-op target transform with the same interface as other transforms."""

    def fit(self, y, **kwargs):
        """Validate target values and return the transform."""
        to_1d_finite(y, "y")
        return self

    def transform(self, y, **kwargs) -> np.ndarray:
        """Return target values unchanged."""
        return to_1d_finite(y, "y")

    def fit_transform(self, y, **kwargs) -> np.ndarray:
        """Fit the transform and return transformed target values."""
        self.fit(y, **kwargs)
        return self.transform(y, **kwargs)

    def inverse_transform(self, y_transformed, **kwargs) -> np.ndarray:
        """Return transformed target values unchanged."""
        return to_1d_finite(y_transformed, "y_transformed")

    def inverse_std(self, mean_transformed, std_transformed, **kwargs) -> np.ndarray:
        """Return predictive standard deviations unchanged."""
        to_1d_finite(mean_transformed, "mean_transformed")
        return _to_nonnegative_std(std_transformed)

    def inverse_prediction(self, prediction: GPyTorchPrediction, **kwargs) -> GPyTorchPrediction:
        """Return a prediction object in the original target scale."""
        return _inverse_prediction_with_arrays(self, prediction, **kwargs)

fit(y, **kwargs)

Validate target values and return the transform.

Source code in matgpr/target_transforms.py
33
34
35
36
def fit(self, y, **kwargs):
    """Validate target values and return the transform."""
    to_1d_finite(y, "y")
    return self

transform(y, **kwargs)

Return target values unchanged.

Source code in matgpr/target_transforms.py
38
39
40
def transform(self, y, **kwargs) -> np.ndarray:
    """Return target values unchanged."""
    return to_1d_finite(y, "y")

fit_transform(y, **kwargs)

Fit the transform and return transformed target values.

Source code in matgpr/target_transforms.py
42
43
44
45
def fit_transform(self, y, **kwargs) -> np.ndarray:
    """Fit the transform and return transformed target values."""
    self.fit(y, **kwargs)
    return self.transform(y, **kwargs)

inverse_transform(y_transformed, **kwargs)

Return transformed target values unchanged.

Source code in matgpr/target_transforms.py
47
48
49
def inverse_transform(self, y_transformed, **kwargs) -> np.ndarray:
    """Return transformed target values unchanged."""
    return to_1d_finite(y_transformed, "y_transformed")

inverse_std(mean_transformed, std_transformed, **kwargs)

Return predictive standard deviations unchanged.

Source code in matgpr/target_transforms.py
51
52
53
54
def inverse_std(self, mean_transformed, std_transformed, **kwargs) -> np.ndarray:
    """Return predictive standard deviations unchanged."""
    to_1d_finite(mean_transformed, "mean_transformed")
    return _to_nonnegative_std(std_transformed)

inverse_prediction(prediction, **kwargs)

Return a prediction object in the original target scale.

Source code in matgpr/target_transforms.py
56
57
58
def inverse_prediction(self, prediction: GPyTorchPrediction, **kwargs) -> GPyTorchPrediction:
    """Return a prediction object in the original target scale."""
    return _inverse_prediction_with_arrays(self, prediction, **kwargs)

StandardizedTargetTransform dataclass

Standardize target values using training-set mean and standard deviation.

Source code in matgpr/target_transforms.py
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
@dataclass
class StandardizedTargetTransform:
    """Standardize target values using training-set mean and standard deviation."""

    mean_: float | None = field(default=None, init=False)
    scale_: float | None = field(default=None, init=False)

    def fit(self, y, **kwargs):
        """Estimate the mean and standard deviation from training targets."""
        values = to_1d_finite(y, "y")
        scale = float(np.std(values, ddof=0))
        if scale <= 0:
            raise ValueError("Cannot standardize a constant target")
        self.mean_ = float(np.mean(values))
        self.scale_ = scale
        return self

    def transform(self, y, **kwargs) -> np.ndarray:
        """Return standardized target values."""
        self._require_fitted()
        values = to_1d_finite(y, "y")
        return (values - self.mean_) / self.scale_

    def fit_transform(self, y, **kwargs) -> np.ndarray:
        """Fit the transform and return standardized target values."""
        self.fit(y, **kwargs)
        return self.transform(y, **kwargs)

    def inverse_transform(self, y_transformed, **kwargs) -> np.ndarray:
        """Return standardized values in the original target scale."""
        self._require_fitted()
        values = to_1d_finite(y_transformed, "y_transformed")
        return values * self.scale_ + self.mean_

    def inverse_std(self, mean_transformed, std_transformed, **kwargs) -> np.ndarray:
        """Return predictive standard deviations in the original target scale."""
        self._require_fitted()
        to_1d_finite(mean_transformed, "mean_transformed")
        return _to_nonnegative_std(std_transformed) * self.scale_

    def inverse_prediction(self, prediction: GPyTorchPrediction, **kwargs) -> GPyTorchPrediction:
        """Return a prediction object in the original target scale."""
        return _inverse_prediction_with_arrays(self, prediction, **kwargs)

    def _require_fitted(self) -> None:
        if self.mean_ is None or self.scale_ is None:
            raise ValueError("StandardizedTargetTransform must be fitted before use")

fit(y, **kwargs)

Estimate the mean and standard deviation from training targets.

Source code in matgpr/target_transforms.py
68
69
70
71
72
73
74
75
76
def fit(self, y, **kwargs):
    """Estimate the mean and standard deviation from training targets."""
    values = to_1d_finite(y, "y")
    scale = float(np.std(values, ddof=0))
    if scale <= 0:
        raise ValueError("Cannot standardize a constant target")
    self.mean_ = float(np.mean(values))
    self.scale_ = scale
    return self

transform(y, **kwargs)

Return standardized target values.

Source code in matgpr/target_transforms.py
78
79
80
81
82
def transform(self, y, **kwargs) -> np.ndarray:
    """Return standardized target values."""
    self._require_fitted()
    values = to_1d_finite(y, "y")
    return (values - self.mean_) / self.scale_

fit_transform(y, **kwargs)

Fit the transform and return standardized target values.

Source code in matgpr/target_transforms.py
84
85
86
87
def fit_transform(self, y, **kwargs) -> np.ndarray:
    """Fit the transform and return standardized target values."""
    self.fit(y, **kwargs)
    return self.transform(y, **kwargs)

inverse_transform(y_transformed, **kwargs)

Return standardized values in the original target scale.

Source code in matgpr/target_transforms.py
89
90
91
92
93
def inverse_transform(self, y_transformed, **kwargs) -> np.ndarray:
    """Return standardized values in the original target scale."""
    self._require_fitted()
    values = to_1d_finite(y_transformed, "y_transformed")
    return values * self.scale_ + self.mean_

inverse_std(mean_transformed, std_transformed, **kwargs)

Return predictive standard deviations in the original target scale.

Source code in matgpr/target_transforms.py
95
96
97
98
99
def inverse_std(self, mean_transformed, std_transformed, **kwargs) -> np.ndarray:
    """Return predictive standard deviations in the original target scale."""
    self._require_fitted()
    to_1d_finite(mean_transformed, "mean_transformed")
    return _to_nonnegative_std(std_transformed) * self.scale_

inverse_prediction(prediction, **kwargs)

Return a prediction object in the original target scale.

Source code in matgpr/target_transforms.py
101
102
103
def inverse_prediction(self, prediction: GPyTorchPrediction, **kwargs) -> GPyTorchPrediction:
    """Return a prediction object in the original target scale."""
    return _inverse_prediction_with_arrays(self, prediction, **kwargs)

LogTargetTransform dataclass

Log-transform positive targets and invert Gaussian predictions.

The transform is z = log(y + offset). Predictive means and standard deviations are inverted with log-normal moments, which is more appropriate than simply exponentiating the transformed-space mean.

Source code in matgpr/target_transforms.py
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
@dataclass
class LogTargetTransform:
    """Log-transform positive targets and invert Gaussian predictions.

    The transform is ``z = log(y + offset)``. Predictive means and standard
    deviations are inverted with log-normal moments, which is more appropriate
    than simply exponentiating the transformed-space mean.
    """

    offset: float = 0.0

    def fit(self, y, **kwargs):
        """Validate that all shifted target values are positive."""
        self._validate_shifted_target(y, "y")
        return self

    def transform(self, y, **kwargs) -> np.ndarray:
        """Return log-transformed target values."""
        values = self._validate_shifted_target(y, "y")
        return np.log(values + self.offset)

    def fit_transform(self, y, **kwargs) -> np.ndarray:
        """Fit the transform and return log-transformed target values."""
        self.fit(y, **kwargs)
        return self.transform(y, **kwargs)

    def inverse_transform(self, y_transformed, **kwargs) -> np.ndarray:
        """Return log-scale values in the original target scale."""
        values = to_1d_finite(y_transformed, "y_transformed")
        return np.exp(values) - self.offset

    def inverse_std(self, mean_transformed, std_transformed, **kwargs) -> np.ndarray:
        """Return original-scale standard deviations from log-normal moments."""
        mean = to_1d_finite(mean_transformed, "mean_transformed")
        std = _to_nonnegative_std(std_transformed)
        validate_same_length(mean, std, "mean_transformed", "std_transformed")
        variance = std**2
        return np.sqrt(np.expm1(variance) * np.exp(2.0 * mean + variance))

    def inverse_prediction(self, prediction: GPyTorchPrediction, **kwargs) -> GPyTorchPrediction:
        """Return a log-space prediction object in the original target scale."""
        mean = to_1d_finite(prediction.mean, "prediction.mean")
        std = None
        if prediction.std is not None:
            std = self.inverse_std(mean, prediction.std)

        lower = None
        if prediction.lower is not None:
            lower = self.inverse_transform(prediction.lower)

        upper = None
        if prediction.upper is not None:
            upper = self.inverse_transform(prediction.upper)

        if prediction.std is not None:
            original_mean = (
                np.exp(mean + 0.5 * _to_nonnegative_std(prediction.std) ** 2) - self.offset
            )
        else:
            original_mean = self.inverse_transform(mean)

        return GPyTorchPrediction(mean=original_mean, std=std, lower=lower, upper=upper)

    def _validate_shifted_target(self, y, name: str) -> np.ndarray:
        values = to_1d_finite(y, name)
        if np.any(values + self.offset <= 0):
            raise ValueError(f"{name} + offset must contain only positive values")
        return values

fit(y, **kwargs)

Validate that all shifted target values are positive.

Source code in matgpr/target_transforms.py
121
122
123
124
def fit(self, y, **kwargs):
    """Validate that all shifted target values are positive."""
    self._validate_shifted_target(y, "y")
    return self

transform(y, **kwargs)

Return log-transformed target values.

Source code in matgpr/target_transforms.py
126
127
128
129
def transform(self, y, **kwargs) -> np.ndarray:
    """Return log-transformed target values."""
    values = self._validate_shifted_target(y, "y")
    return np.log(values + self.offset)

fit_transform(y, **kwargs)

Fit the transform and return log-transformed target values.

Source code in matgpr/target_transforms.py
131
132
133
134
def fit_transform(self, y, **kwargs) -> np.ndarray:
    """Fit the transform and return log-transformed target values."""
    self.fit(y, **kwargs)
    return self.transform(y, **kwargs)

inverse_transform(y_transformed, **kwargs)

Return log-scale values in the original target scale.

Source code in matgpr/target_transforms.py
136
137
138
139
def inverse_transform(self, y_transformed, **kwargs) -> np.ndarray:
    """Return log-scale values in the original target scale."""
    values = to_1d_finite(y_transformed, "y_transformed")
    return np.exp(values) - self.offset

inverse_std(mean_transformed, std_transformed, **kwargs)

Return original-scale standard deviations from log-normal moments.

Source code in matgpr/target_transforms.py
141
142
143
144
145
146
147
def inverse_std(self, mean_transformed, std_transformed, **kwargs) -> np.ndarray:
    """Return original-scale standard deviations from log-normal moments."""
    mean = to_1d_finite(mean_transformed, "mean_transformed")
    std = _to_nonnegative_std(std_transformed)
    validate_same_length(mean, std, "mean_transformed", "std_transformed")
    variance = std**2
    return np.sqrt(np.expm1(variance) * np.exp(2.0 * mean + variance))

inverse_prediction(prediction, **kwargs)

Return a log-space prediction object in the original target scale.

Source code in matgpr/target_transforms.py
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
def inverse_prediction(self, prediction: GPyTorchPrediction, **kwargs) -> GPyTorchPrediction:
    """Return a log-space prediction object in the original target scale."""
    mean = to_1d_finite(prediction.mean, "prediction.mean")
    std = None
    if prediction.std is not None:
        std = self.inverse_std(mean, prediction.std)

    lower = None
    if prediction.lower is not None:
        lower = self.inverse_transform(prediction.lower)

    upper = None
    if prediction.upper is not None:
        upper = self.inverse_transform(prediction.upper)

    if prediction.std is not None:
        original_mean = (
            np.exp(mean + 0.5 * _to_nonnegative_std(prediction.std) ** 2) - self.offset
        )
    else:
        original_mean = self.inverse_transform(mean)

    return GPyTorchPrediction(mean=original_mean, std=std, lower=lower, upper=upper)

BoundedTargetTransform dataclass

Logit-transform targets constrained to a finite physical interval.

The transform is:

z = logit((y - lower_bound) / (upper_bound - lower_bound)).

Predictive means and standard deviations are inverted with Gauss-Hermite quadrature over the implied logistic-normal distribution. This is useful for properties with known physical bounds, such as efficiencies, fractions, probabilities, normalized phase fractions, or bounded scores.

Source code in matgpr/target_transforms.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
@dataclass
class BoundedTargetTransform:
    """Logit-transform targets constrained to a finite physical interval.

    The transform is:

    ``z = logit((y - lower_bound) / (upper_bound - lower_bound))``.

    Predictive means and standard deviations are inverted with Gauss-Hermite
    quadrature over the implied logistic-normal distribution. This is useful
    for properties with known physical bounds, such as efficiencies,
    fractions, probabilities, normalized phase fractions, or bounded scores.
    """

    lower_bound: float
    upper_bound: float
    n_quadrature_points: int = 20

    def __post_init__(self) -> None:
        self.lower_bound = float(self.lower_bound)
        self.upper_bound = float(self.upper_bound)
        if not np.isfinite(self.lower_bound) or not np.isfinite(self.upper_bound):
            raise ValueError("Bounds must be finite")
        if self.upper_bound <= self.lower_bound:
            raise ValueError("upper_bound must be greater than lower_bound")
        if not isinstance(self.n_quadrature_points, int) or self.n_quadrature_points < 3:
            raise ValueError("n_quadrature_points must be an integer greater than or equal to 3")

    def fit(self, y, **kwargs):
        """Validate that targets are strictly inside the physical interval."""
        self._validate_bounded_target(y, "y")
        return self

    def transform(self, y, **kwargs) -> np.ndarray:
        """Return logit-transformed target values."""
        values = self._validate_bounded_target(y, "y")
        scaled = (values - self.lower_bound) / self._width
        return np.log(scaled / (1.0 - scaled))

    def fit_transform(self, y, **kwargs) -> np.ndarray:
        """Fit the transform and return logit-transformed target values."""
        self.fit(y, **kwargs)
        return self.transform(y, **kwargs)

    def inverse_transform(self, y_transformed, **kwargs) -> np.ndarray:
        """Return logit-space values in the original bounded target scale."""
        values = to_1d_finite(y_transformed, "y_transformed")
        return self.lower_bound + self._width * _sigmoid(values)

    def inverse_std(self, mean_transformed, std_transformed, **kwargs) -> np.ndarray:
        """Return original-scale standard deviations from logistic-normal moments."""
        mean = to_1d_finite(mean_transformed, "mean_transformed")
        std = _to_nonnegative_std(std_transformed)
        validate_same_length(mean, std, "mean_transformed", "std_transformed")
        _, original_std = self._logistic_normal_moments(mean, std)
        return original_std

    def inverse_prediction(self, prediction: GPyTorchPrediction, **kwargs) -> GPyTorchPrediction:
        """Return a bounded prediction object in the original target scale."""
        mean = to_1d_finite(prediction.mean, "prediction.mean")

        std = None
        if prediction.std is not None:
            original_mean, std = self._logistic_normal_moments(mean, prediction.std)
        else:
            original_mean = self.inverse_transform(mean)

        lower = None
        if prediction.lower is not None:
            lower = self.inverse_transform(prediction.lower)

        upper = None
        if prediction.upper is not None:
            upper = self.inverse_transform(prediction.upper)

        return GPyTorchPrediction(mean=original_mean, std=std, lower=lower, upper=upper)

    @property
    def _width(self) -> float:
        return self.upper_bound - self.lower_bound

    def _validate_bounded_target(self, y, name: str) -> np.ndarray:
        values = to_1d_finite(y, name)
        if np.any(values <= self.lower_bound) or np.any(values >= self.upper_bound):
            raise ValueError(f"{name} must be strictly between lower_bound and upper_bound")
        return values

    def _logistic_normal_moments(
        self,
        mean_transformed: np.ndarray,
        std_transformed,
    ) -> tuple[np.ndarray, np.ndarray]:
        mean = to_1d_finite(mean_transformed, "mean_transformed")
        std = _to_nonnegative_std(std_transformed)
        validate_same_length(mean, std, "mean_transformed", "std_transformed")

        nodes, weights = np.polynomial.hermite.hermgauss(self.n_quadrature_points)
        samples = mean[:, None] + np.sqrt(2.0) * std[:, None] * nodes[None, :]
        original_samples = self.lower_bound + self._width * _sigmoid(samples)
        normalized_weights = weights / np.sqrt(np.pi)
        original_mean = original_samples @ normalized_weights
        second_moment = (original_samples * original_samples) @ normalized_weights
        variance = np.maximum(second_moment - original_mean * original_mean, 0.0)
        return original_mean, np.sqrt(variance)

fit(y, **kwargs)

Validate that targets are strictly inside the physical interval.

Source code in matgpr/target_transforms.py
208
209
210
211
def fit(self, y, **kwargs):
    """Validate that targets are strictly inside the physical interval."""
    self._validate_bounded_target(y, "y")
    return self

transform(y, **kwargs)

Return logit-transformed target values.

Source code in matgpr/target_transforms.py
213
214
215
216
217
def transform(self, y, **kwargs) -> np.ndarray:
    """Return logit-transformed target values."""
    values = self._validate_bounded_target(y, "y")
    scaled = (values - self.lower_bound) / self._width
    return np.log(scaled / (1.0 - scaled))

fit_transform(y, **kwargs)

Fit the transform and return logit-transformed target values.

Source code in matgpr/target_transforms.py
219
220
221
222
def fit_transform(self, y, **kwargs) -> np.ndarray:
    """Fit the transform and return logit-transformed target values."""
    self.fit(y, **kwargs)
    return self.transform(y, **kwargs)

inverse_transform(y_transformed, **kwargs)

Return logit-space values in the original bounded target scale.

Source code in matgpr/target_transforms.py
224
225
226
227
def inverse_transform(self, y_transformed, **kwargs) -> np.ndarray:
    """Return logit-space values in the original bounded target scale."""
    values = to_1d_finite(y_transformed, "y_transformed")
    return self.lower_bound + self._width * _sigmoid(values)

inverse_std(mean_transformed, std_transformed, **kwargs)

Return original-scale standard deviations from logistic-normal moments.

Source code in matgpr/target_transforms.py
229
230
231
232
233
234
235
def inverse_std(self, mean_transformed, std_transformed, **kwargs) -> np.ndarray:
    """Return original-scale standard deviations from logistic-normal moments."""
    mean = to_1d_finite(mean_transformed, "mean_transformed")
    std = _to_nonnegative_std(std_transformed)
    validate_same_length(mean, std, "mean_transformed", "std_transformed")
    _, original_std = self._logistic_normal_moments(mean, std)
    return original_std

inverse_prediction(prediction, **kwargs)

Return a bounded prediction object in the original target scale.

Source code in matgpr/target_transforms.py
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
def inverse_prediction(self, prediction: GPyTorchPrediction, **kwargs) -> GPyTorchPrediction:
    """Return a bounded prediction object in the original target scale."""
    mean = to_1d_finite(prediction.mean, "prediction.mean")

    std = None
    if prediction.std is not None:
        original_mean, std = self._logistic_normal_moments(mean, prediction.std)
    else:
        original_mean = self.inverse_transform(mean)

    lower = None
    if prediction.lower is not None:
        lower = self.inverse_transform(prediction.lower)

    upper = None
    if prediction.upper is not None:
        upper = self.inverse_transform(prediction.upper)

    return GPyTorchPrediction(mean=original_mean, std=std, lower=lower, upper=upper)

PhysicsResidualTransform dataclass

Model residuals relative to a physics baseline.

This transform supports workflows where a simple physics model provides a baseline prediction and GPR learns the residual: residual = observed_target - physics_baseline.

Source code in matgpr/target_transforms.py
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
@dataclass
class PhysicsResidualTransform:
    """Model residuals relative to a physics baseline.

    This transform supports workflows where a simple physics model provides a
    baseline prediction and GPR learns the residual:
    ``residual = observed_target - physics_baseline``.
    """

    baseline_name: str = "physics_baseline"

    def fit(self, y, *, baseline):
        """Validate target and baseline arrays."""
        values = to_1d_finite(y, "y")
        baseline = to_1d_finite(baseline, self.baseline_name)
        validate_same_length(values, baseline, "y", self.baseline_name)
        return self

    def transform(self, y, *, baseline) -> np.ndarray:
        """Return residual target values."""
        values = to_1d_finite(y, "y")
        baseline = to_1d_finite(baseline, self.baseline_name)
        validate_same_length(values, baseline, "y", self.baseline_name)
        return values - baseline

    def fit_transform(self, y, *, baseline) -> np.ndarray:
        """Fit the transform and return residual target values."""
        self.fit(y, baseline=baseline)
        return self.transform(y, baseline=baseline)

    def inverse_transform(self, y_transformed, *, baseline) -> np.ndarray:
        """Return residual predictions in the original target scale."""
        residual = to_1d_finite(y_transformed, "y_transformed")
        baseline = to_1d_finite(baseline, self.baseline_name)
        validate_same_length(residual, baseline, "y_transformed", self.baseline_name)
        return residual + baseline

    def inverse_std(self, mean_transformed, std_transformed, **kwargs) -> np.ndarray:
        """Return residual predictive standard deviations unchanged."""
        to_1d_finite(mean_transformed, "mean_transformed")
        return _to_nonnegative_std(std_transformed)

    def inverse_prediction(self, prediction: GPyTorchPrediction, *, baseline) -> GPyTorchPrediction:
        """Add a physics baseline back to a residual prediction object."""
        return _inverse_prediction_with_arrays(self, prediction, baseline=baseline)

fit(y, *, baseline)

Validate target and baseline arrays.

Source code in matgpr/target_transforms.py
297
298
299
300
301
302
def fit(self, y, *, baseline):
    """Validate target and baseline arrays."""
    values = to_1d_finite(y, "y")
    baseline = to_1d_finite(baseline, self.baseline_name)
    validate_same_length(values, baseline, "y", self.baseline_name)
    return self

transform(y, *, baseline)

Return residual target values.

Source code in matgpr/target_transforms.py
304
305
306
307
308
309
def transform(self, y, *, baseline) -> np.ndarray:
    """Return residual target values."""
    values = to_1d_finite(y, "y")
    baseline = to_1d_finite(baseline, self.baseline_name)
    validate_same_length(values, baseline, "y", self.baseline_name)
    return values - baseline

fit_transform(y, *, baseline)

Fit the transform and return residual target values.

Source code in matgpr/target_transforms.py
311
312
313
314
def fit_transform(self, y, *, baseline) -> np.ndarray:
    """Fit the transform and return residual target values."""
    self.fit(y, baseline=baseline)
    return self.transform(y, baseline=baseline)

inverse_transform(y_transformed, *, baseline)

Return residual predictions in the original target scale.

Source code in matgpr/target_transforms.py
316
317
318
319
320
321
def inverse_transform(self, y_transformed, *, baseline) -> np.ndarray:
    """Return residual predictions in the original target scale."""
    residual = to_1d_finite(y_transformed, "y_transformed")
    baseline = to_1d_finite(baseline, self.baseline_name)
    validate_same_length(residual, baseline, "y_transformed", self.baseline_name)
    return residual + baseline

inverse_std(mean_transformed, std_transformed, **kwargs)

Return residual predictive standard deviations unchanged.

Source code in matgpr/target_transforms.py
323
324
325
326
def inverse_std(self, mean_transformed, std_transformed, **kwargs) -> np.ndarray:
    """Return residual predictive standard deviations unchanged."""
    to_1d_finite(mean_transformed, "mean_transformed")
    return _to_nonnegative_std(std_transformed)

inverse_prediction(prediction, *, baseline)

Add a physics baseline back to a residual prediction object.

Source code in matgpr/target_transforms.py
328
329
330
def inverse_prediction(self, prediction: GPyTorchPrediction, *, baseline) -> GPyTorchPrediction:
    """Add a physics baseline back to a residual prediction object."""
    return _inverse_prediction_with_arrays(self, prediction, baseline=baseline)

TargetTransformSpec dataclass

Metadata for a reusable materials-property target-transform preset.

The spec documents which transform is usually appropriate for a property type, stores default transform keyword arguments, and builds the concrete transform when requested.

Source code in matgpr/target_transforms.py
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
@dataclass(frozen=True)
class TargetTransformSpec:
    """Metadata for a reusable materials-property target-transform preset.

    The spec documents which transform is usually appropriate for a property
    type, stores default transform keyword arguments, and builds the concrete
    transform when requested.
    """

    name: str
    transform_name: str
    transform_kwargs: Mapping[str, object] = field(default_factory=dict)
    aliases: Sequence[str] = ()
    category: str = ""
    description: str = ""
    target_units: str | None = None
    examples: Sequence[str] = ()
    assumptions: Sequence[str] = ()
    notes: Sequence[str] = ()
    tags: Sequence[str] = ()

    def __post_init__(self) -> None:
        if not str(self.name).strip():
            raise ValueError("name must be non-empty")
        if not str(self.transform_name).strip():
            raise ValueError("transform_name must be non-empty")
        object.__setattr__(self, "name", str(self.name))
        object.__setattr__(self, "transform_name", str(self.transform_name))
        object.__setattr__(self, "transform_kwargs", dict(self.transform_kwargs))
        object.__setattr__(self, "aliases", tuple(self.aliases))
        object.__setattr__(self, "examples", tuple(self.examples))
        object.__setattr__(self, "assumptions", tuple(self.assumptions))
        object.__setattr__(self, "notes", tuple(self.notes))
        object.__setattr__(self, "tags", tuple(self.tags))

    def build_transform(self, **overrides):
        """Build the concrete transform for this property preset."""
        transform_kwargs = dict(self.transform_kwargs)
        transform_kwargs.update(overrides)
        return make_target_transform(self.transform_name, **transform_kwargs)

    def summary(self) -> dict[str, object]:
        """Return a compact serializable summary for reports."""
        return {
            "name": self.name,
            "aliases": list(self.aliases),
            "category": self.category,
            "transform_name": self.transform_name,
            "transform_kwargs": dict(self.transform_kwargs),
            "description": self.description,
            "target_units": self.target_units,
            "examples": list(self.examples),
            "assumptions": list(self.assumptions),
            "tags": list(self.tags),
        }

    def discovery_record(self) -> dict[str, object]:
        """Return complete metadata for one transform preset."""
        return {
            **self.summary(),
            "notes": list(self.notes),
        }

build_transform(**overrides)

Build the concrete transform for this property preset.

Source code in matgpr/target_transforms.py
368
369
370
371
372
def build_transform(self, **overrides):
    """Build the concrete transform for this property preset."""
    transform_kwargs = dict(self.transform_kwargs)
    transform_kwargs.update(overrides)
    return make_target_transform(self.transform_name, **transform_kwargs)

summary()

Return a compact serializable summary for reports.

Source code in matgpr/target_transforms.py
374
375
376
377
378
379
380
381
382
383
384
385
386
387
def summary(self) -> dict[str, object]:
    """Return a compact serializable summary for reports."""
    return {
        "name": self.name,
        "aliases": list(self.aliases),
        "category": self.category,
        "transform_name": self.transform_name,
        "transform_kwargs": dict(self.transform_kwargs),
        "description": self.description,
        "target_units": self.target_units,
        "examples": list(self.examples),
        "assumptions": list(self.assumptions),
        "tags": list(self.tags),
    }

discovery_record()

Return complete metadata for one transform preset.

Source code in matgpr/target_transforms.py
389
390
391
392
393
394
def discovery_record(self) -> dict[str, object]:
    """Return complete metadata for one transform preset."""
    return {
        **self.summary(),
        "notes": list(self.notes),
    }

make_target_transform(name, **kwargs)

Create a target transform by name.

Parameters:

Name Type Description Default
name str

One of "identity", "standard", "standardized", "log", "positive", "bounded", "logit", "residual", or "physics_residual". Registered materials-property presets such as "diffusivity", "efficiency_percent", "band_gap_ev", and "formation_energy" are also accepted.

required
Source code in matgpr/target_transforms.py
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
def make_target_transform(name: str, **kwargs):
    """Create a target transform by name.

    Parameters
    ----------
    name
        One of ``"identity"``, ``"standard"``, ``"standardized"``, ``"log"``,
        ``"positive"``, ``"bounded"``, ``"logit"``, ``"residual"``, or
        ``"physics_residual"``. Registered materials-property presets such as
        ``"diffusivity"``, ``"efficiency_percent"``, ``"band_gap_ev"``, and
        ``"formation_energy"`` are also accepted.
    """
    normalized = _normalize_spec_name(name)
    if normalized in {"identity", "none", "passthrough"}:
        return IdentityTargetTransform(**kwargs)
    if normalized in {"standard", "standardized", "zscore", "z_score"}:
        return StandardizedTargetTransform(**kwargs)
    if normalized in {"log", "logarithmic", "positive", "positivity"}:
        return LogTargetTransform(**kwargs)
    if normalized in {"bounded", "bound", "logit"}:
        return BoundedTargetTransform(**kwargs)
    if normalized in {"residual", "physics_residual"}:
        return PhysicsResidualTransform(**kwargs)

    lookup = _target_transform_spec_lookup()
    if normalized in lookup:
        return lookup[normalized].build_transform(**kwargs)

    available = ", ".join(
        [
            "identity",
            "standard",
            "log",
            "bounded",
            "residual",
            *available_target_transform_specs(include_aliases=True),
        ]
    )
    raise ValueError(f"Unknown target transform '{name}'. Available transforms: {available}")

make_materials_target_transform(property_name, **overrides)

Build a target transform from a materials-property preset.

Source code in matgpr/target_transforms.py
438
439
440
def make_materials_target_transform(property_name: str, **overrides):
    """Build a target transform from a materials-property preset."""
    return get_target_transform_spec(property_name).build_transform(**overrides)

list_target_transform_specs()

Return registered materials-property target-transform presets.

Source code in matgpr/target_transforms.py
443
444
445
def list_target_transform_specs() -> tuple[TargetTransformSpec, ...]:
    """Return registered materials-property target-transform presets."""
    return _TARGET_TRANSFORM_SPECS

available_target_transform_specs(*, include_aliases=False)

Return available target-transform preset names, optionally with aliases.

Source code in matgpr/target_transforms.py
448
449
450
451
452
453
def available_target_transform_specs(*, include_aliases: bool = False) -> tuple[str, ...]:
    """Return available target-transform preset names, optionally with aliases."""
    names = [spec.name for spec in _TARGET_TRANSFORM_SPECS]
    if include_aliases:
        names.extend(alias for spec in _TARGET_TRANSFORM_SPECS for alias in spec.aliases)
    return tuple(names)

get_target_transform_spec(name)

Return a target-transform preset by name or alias.

Source code in matgpr/target_transforms.py
456
457
458
459
460
461
462
463
464
465
def get_target_transform_spec(name: str) -> TargetTransformSpec:
    """Return a target-transform preset by name or alias."""
    key = _normalize_spec_name(name)
    lookup = _target_transform_spec_lookup()
    if key not in lookup:
        available = ", ".join(available_target_transform_specs(include_aliases=True))
        raise ValueError(
            f"Unknown target-transform preset '{name}'. Available presets: {available}"
        )
    return lookup[key]

describe_target_transform_spec(name)

Return complete registry metadata for one target-transform preset.

Source code in matgpr/target_transforms.py
468
469
470
def describe_target_transform_spec(name: str) -> dict[str, object]:
    """Return complete registry metadata for one target-transform preset."""
    return get_target_transform_spec(name).discovery_record()

search_target_transform_specs(query=None, *, category=None, tag=None, transform_name=None)

Search registered target-transform presets by text and metadata.

Source code in matgpr/target_transforms.py
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
def search_target_transform_specs(
    query: str | None = None,
    *,
    category: str | None = None,
    tag: str | None = None,
    transform_name: str | None = None,
) -> tuple[TargetTransformSpec, ...]:
    """Search registered target-transform presets by text and metadata."""
    normalized_transform = (
        _normalize_spec_name(transform_name) if transform_name is not None else None
    )
    matches: list[TargetTransformSpec] = []
    for spec in _TARGET_TRANSFORM_SPECS:
        if query is not None and _normalize_spec_name(query) not in _target_spec_search_text(spec):
            continue
        if category is not None and _normalize_spec_name(category) != _normalize_spec_name(
            spec.category
        ):
            continue
        if tag is not None and _normalize_spec_name(tag) not in {
            _normalize_spec_name(item) for item in spec.tags
        }:
            continue
        if normalized_transform is not None and normalized_transform != _normalize_spec_name(
            spec.transform_name
        ):
            continue
        matches.append(spec)
    return tuple(matches)

summarize_target_transform_specs(*, include_aliases=True)

Return a dataframe summary of registered target-transform presets.

Source code in matgpr/target_transforms.py
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
def summarize_target_transform_specs(*, include_aliases: bool = True) -> pd.DataFrame:
    """Return a dataframe summary of registered target-transform presets."""
    rows = []
    for spec in _TARGET_TRANSFORM_SPECS:
        rows.append(
            {
                "name": spec.name,
                "aliases": ", ".join(spec.aliases) if include_aliases else "",
                "category": spec.category,
                "transform_name": spec.transform_name,
                "transform_kwargs": dict(spec.transform_kwargs),
                "description": spec.description,
                "target_units": spec.target_units,
                "examples": ", ".join(spec.examples),
                "assumptions": " ".join(spec.assumptions),
                "tags": ", ".join(spec.tags),
            }
        )
    return pd.DataFrame(rows)