From 0b3f0300c7b2214a13900ffa910f9c2004b47291 Mon Sep 17 00:00:00 2001 From: Michaela Tarpley Date: Wed, 16 Sep 2026 09:42:34 -0500 Subject: [PATCH 1/6] Added validate_input_sizes function to Model class --- src/condor/models.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/condor/models.py b/src/condor/models.py index eff72506..4421b91f 100644 --- a/src/condor/models.py +++ b/src/condor/models.py @@ -1,6 +1,8 @@ import dataclasses as dc import logging +import numpy as np + from condor import backend, implementations # TODO: figure out how to make this an option/setting like django? @@ -1382,6 +1384,8 @@ def __init__(self, *args, name="", **kwargs): self.bind_input_fields(*args, **kwargs) + self.validate_input_sizes() + implementation_class = cls.get_implementation_class(cls) self.implementation = implementation_class(self) # self, list(self.input_kwargs.values()) @@ -1398,6 +1402,34 @@ def __init__(self, *args, name="", **kwargs): self.bind_embedded_models() + def validate_input_sizes(self): + cls = self.__class__ + err_flag = 0 + err_string = "" + + for input_field in cls._meta.input_fields: + for name in list(input_field.keys()): + desired_shape = getattr(cls, name).shape + input_value = getattr(self, name) + + if isinstance(input_value, (int, float, np.ndarray, list)): + input_value = np.atleast_2d(input_value) + # want all row vectors to be made column vectors + if input_value.shape[0] == 1: + input_value = input_value.reshape(-1, 1) + + input_shape = input_value.shape + + if input_shape != desired_shape: + err_flag = 1 + err_string += ( + f"{input_field._name} {name} was assigned with shape " + f"{input_shape}, but should be shape {desired_shape}\n" + ) + + if err_flag: + raise ValueError(err_string) + def bind_input_fields(self, *args, **kwargs): cls = self.__class__ self.input_kwargs = {} From d98d2a2e22d2400a6778fb199688257a60a99404 Mon Sep 17 00:00:00 2001 From: Michaela Tarpley Date: Wed, 16 Sep 2026 14:05:08 -0500 Subject: [PATCH 2/6] Ensure casadi version less than 3.8 for testing in pipeline --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 20a7196f..d479d873 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ classifiers = [ dependencies = [ "numpy", "scipy", - "casadi>=3.7.2", + "casadi>=3.7.2,<3.8", "ndsplines>=0.2.0post0", ] From 47a25def131c0b18472084b2b6180dbe32c92139 Mon Sep 17 00:00:00 2001 From: Michaela Tarpley Date: Wed, 16 Sep 2026 15:59:05 -0500 Subject: [PATCH 3/6] Fix to allow input vectors to be row or column vectors since condor can still run with either. --- src/condor/models.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/condor/models.py b/src/condor/models.py index 4421b91f..5a08de4f 100644 --- a/src/condor/models.py +++ b/src/condor/models.py @@ -1414,13 +1414,28 @@ def validate_input_sizes(self): if isinstance(input_value, (int, float, np.ndarray, list)): input_value = np.atleast_2d(input_value) - # want all row vectors to be made column vectors - if input_value.shape[0] == 1: - input_value = input_value.reshape(-1, 1) input_shape = input_value.shape - if input_shape != desired_shape: + # if input is not a vector, then it has to + # match desired_shape exactly + # if desired_input is a vector, then + # input can be row or column vector + + base_shape_correct = input_shape == desired_shape + input_is_vector = len(input_shape) == 2 and ( + input_shape[0] == 1 or input_shape[1] == 1 + ) + vector_input_incorrect_size = ( + input_is_vector + and not base_shape_correct + and (input_value.T.shape != desired_shape) + ) + non_vector_input_incorrect_size = ( + not input_is_vector and not base_shape_correct + ) + + if non_vector_input_incorrect_size or vector_input_incorrect_size: err_flag = 1 err_string += ( f"{input_field._name} {name} was assigned with shape " From 5575996678c66df8a0a574f4455e88eb6f0711f0 Mon Sep 17 00:00:00 2001 From: Michaela Tarpley Date: Wed, 16 Sep 2026 16:00:08 -0500 Subject: [PATCH 4/6] restore pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d479d873..20a7196f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ classifiers = [ dependencies = [ "numpy", "scipy", - "casadi>=3.7.2,<3.8", + "casadi>=3.7.2", "ndsplines>=0.2.0post0", ] From 03d2f01af0510fdbf7386c8be29a17b207468815 Mon Sep 17 00:00:00 2001 From: Michaela Tarpley Date: Fri, 25 Sep 2026 11:38:16 -0500 Subject: [PATCH 5/6] Updated the validate_input_sizes function in Model to handle tuple inputs as well. Added test_invalid_size_check to ensure the validate_input_sizes function is working correctly. --- src/condor/models.py | 2 +- tests/test_model_api.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/condor/models.py b/src/condor/models.py index 5a08de4f..806669cd 100644 --- a/src/condor/models.py +++ b/src/condor/models.py @@ -1412,7 +1412,7 @@ def validate_input_sizes(self): desired_shape = getattr(cls, name).shape input_value = getattr(self, name) - if isinstance(input_value, (int, float, np.ndarray, list)): + if not isinstance(input_value, backend.symbol_class): input_value = np.atleast_2d(input_value) input_shape = input_value.shape diff --git a/tests/test_model_api.py b/tests/test_model_api.py index 1c7d3262..26d4380d 100644 --- a/tests/test_model_api.py +++ b/tests/test_model_api.py @@ -146,3 +146,41 @@ class Sys(co.ExplicitSystem): out = Sys(x=x) assert out.y == 2 * x[1, 0] + + +def test_invalid_size_check(): + class MySys(co.ExplicitSystem): + x = input(shape=(2, 1)) + y = input(shape=(2, 2)) + output.z = y @ x + + sim1 = MySys( + x=(0, 1), + y=[[1, 2], [3, 4]], + ) + sim2 = MySys( + x=np.array([0, 1]), + y=np.zeros((2, 2)), + ) + sim3 = MySys( + x=np.ones((1, 2)), + y=co.backend.operators.zeros((2, 2)), + ) + + with pytest.raises(ValueError, match="was assigned with shape"): + sim4 = MySys( + x=(0.1, 0.1, 0.1), + y=0, + ) + + with pytest.raises(ValueError, match="was assigned with shape"): + sim5 = MySys( + x=np.array([0]), + y=(1, 2, 3), + ) + + with pytest.raises(ValueError, match="was assigned with shape"): + sim5 = MySys( + x=co.backend.operators.zeros((3, 3)), + y=co.backend.operators.zeros((2, 1)), + ) From f77235d5f754a8daa4a00ca77b1da59ec699ade8 Mon Sep 17 00:00:00 2001 From: Michaela Tarpley Date: Wed, 30 Sep 2026 12:03:04 -0500 Subject: [PATCH 6/6] refactored test_invalid_input_check to use pytest parametrize; added assertion on the z output shape --- tests/test_model_api.py | 55 ++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/tests/test_model_api.py b/tests/test_model_api.py index 26d4380d..53909ecc 100644 --- a/tests/test_model_api.py +++ b/tests/test_model_api.py @@ -148,39 +148,44 @@ class Sys(co.ExplicitSystem): assert out.y == 2 * x[1, 0] -def test_invalid_size_check(): +@pytest.mark.parametrize( + ("x_input", "y_input"), + [ + (((0, 1), [[1, 2], [3, 4]])), + (np.array([0, 1]), np.zeros((2, 2))), + (np.ones((1, 2)), co.backend.operators.zeros((2, 2))), + ], +) +def test_valid_size_check(x_input, y_input): class MySys(co.ExplicitSystem): x = input(shape=(2, 1)) y = input(shape=(2, 2)) output.z = y @ x - sim1 = MySys( - x=(0, 1), - y=[[1, 2], [3, 4]], - ) - sim2 = MySys( - x=np.array([0, 1]), - y=np.zeros((2, 2)), - ) - sim3 = MySys( - x=np.ones((1, 2)), - y=co.backend.operators.zeros((2, 2)), + sim = MySys( + x=x_input, + y=y_input, ) - with pytest.raises(ValueError, match="was assigned with shape"): - sim4 = MySys( - x=(0.1, 0.1, 0.1), - y=0, - ) + assert sim.z.shape == (2, 1) - with pytest.raises(ValueError, match="was assigned with shape"): - sim5 = MySys( - x=np.array([0]), - y=(1, 2, 3), - ) + +@pytest.mark.parametrize( + ("x_input", "y_input"), + [ + ((0.1, 0.1, 0.1), 0), + (np.array([0]), (1, 2, 3)), + (co.backend.operators.zeros((3, 3)), co.backend.operators.zeros((2, 1))), + ], +) +def test_invalid_size_check(x_input, y_input): + class MySys(co.ExplicitSystem): + x = input(shape=(2, 1)) + y = input(shape=(2, 2)) + output.z = y @ x with pytest.raises(ValueError, match="was assigned with shape"): - sim5 = MySys( - x=co.backend.operators.zeros((3, 3)), - y=co.backend.operators.zeros((2, 1)), + sim = MySys( + x=x_input, + y=y_input, )