Skip to content

Commit a633c9f

Browse files
committed
abs - more consistent reshaping of inputs and outputs
1 parent a7dfb35 commit a633c9f

3 files changed

Lines changed: 74 additions & 4 deletions

File tree

modules/performUQ/common/gp_ab_algorithm.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,18 @@ def __init__( # noqa: PLR0913
189189
self.num_recalibration_experiments = 0
190190
self.recalibration_ratio = recalibration_ratio
191191

192-
self.sample_transformation_function = sample_transformation_function
193-
self.model_evaluation_function = model_evaluation_function
192+
self.sample_transformation_function = uq_utilities.Ensure2DOutputShape(
193+
sample_transformation_function,
194+
expected_dim=self.input_dimension,
195+
label='sample_transformation_function',
196+
)
197+
198+
self.model_evaluation_function = uq_utilities.Ensure2DOutputShape(
199+
model_evaluation_function,
200+
expected_dim=self.output_dimension,
201+
label='model_evaluation_function',
202+
)
203+
194204
self.run_type = run_type
195205
self.parallel_pool = uq_utilities.get_parallel_pool_instance(run_type)
196206
self.parallel_evaluation_function = self.parallel_pool.pool.starmap # type: ignore
@@ -267,7 +277,14 @@ def _evaluate_in_parallel(
267277
outputs = np.atleast_2d(
268278
list(self.parallel_evaluation_function(func, iterable))
269279
)
270-
return outputs # noqa: RET504
280+
# Fix scalar outputs → (n, 1)
281+
if outputs.ndim == 1:
282+
outputs = outputs.reshape(-1, 1)
283+
284+
# Fix shape (n, 1, d) → (n, d)
285+
elif outputs.ndim == 3 and outputs.shape[1] == 1: # noqa: PLR2004
286+
outputs = np.squeeze(outputs, axis=1)
287+
return outputs
271288

272289
def _perform_space_filling_doe(self, n_samples):
273290
"""
@@ -302,7 +319,7 @@ def _get_initial_training_set(self, n_samples):
302319
self._perform_space_filling_doe(n_samples)
303320
)
304321
self.loginfo(
305-
f'Generated {inputs.shape[0]} samples for initial training set.'
322+
f'Generated {inputs.shape[0]} samples for initial training set. Evaluating the model at these samples.'
306323
)
307324
outputs = self._evaluate_in_parallel(self.model_evaluation_function, inputs)
308325
return inputs, outputs

modules/performUQ/common/tmcmc.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ def get_scaled_proposal_covariance(
205205

206206
# Step 2: Ensure symmetry
207207
cov = 0.5 * (cov + cov.T)
208+
cov = np.atleast_2d(cov)
208209

209210
# Step 3: Try eigendecomposition with clipping
210211
try:

modules/performUQ/common/uq_utilities.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,3 +514,55 @@ def _get_tabular_results_file_name_for_dataset(
514514
def _write_to_tabular_results_file(tabular_results_file, string_to_write):
515515
with tabular_results_file.open('a') as f:
516516
f.write(string_to_write)
517+
518+
519+
class Ensure2DOutputShape:
520+
def __init__(self, func, expected_dim, label="wrapped_function"):
521+
self.func = func
522+
self.expected_dim = expected_dim
523+
self.label = label
524+
self._last_input_shape = None
525+
self._last_output_shape = None
526+
527+
def __call__(self, *args):
528+
result = self.func(*args)
529+
result = np.asarray(result)
530+
531+
# Handle scalar return (e.g., float) → reshape to (1, 1)
532+
if result.ndim == 0:
533+
result = result.reshape(1, 1)
534+
535+
# Treating the last argument of the function call as the sample
536+
x = np.asarray(args[-1])
537+
n_samples = x.shape[0] if x.ndim > 1 else 1
538+
539+
input_shape = x.shape
540+
output_shape = result.shape
541+
if input_shape == self._last_input_shape and output_shape == self._last_output_shape:
542+
return result
543+
544+
if result.ndim == 1:
545+
if n_samples == 1 and result.shape[0] == self.expected_dim:
546+
result = result.reshape(1, self.expected_dim)
547+
elif self.expected_dim == 1 and result.shape[0] == n_samples:
548+
result = result.reshape(n_samples, 1)
549+
else:
550+
raise ValueError(
551+
f"[{self.label}] 1D output shape {result.shape} is incompatible with input {x.shape}. "
552+
f"Expected ({n_samples}, {self.expected_dim})"
553+
)
554+
555+
elif result.ndim == 2:
556+
if result.shape != (n_samples, self.expected_dim):
557+
raise ValueError(
558+
f"[{self.label}] 2D output shape {result.shape} does not match expected ({n_samples}, {self.expected_dim})"
559+
)
560+
561+
else:
562+
raise ValueError(
563+
f"[{self.label}] Output has {result.ndim} dimensions; expected 1D or 2D"
564+
)
565+
566+
self._last_input_shape = input_shape
567+
self._last_output_shape = result.shape
568+
return result

0 commit comments

Comments
 (0)