From 0b0cc6ae79e834c2980907b95b46cc72b2479b70 Mon Sep 17 00:00:00 2001
From: Pantelis Sopasakis
Date: Mon, 30 Mar 2026 17:42:12 +0100
Subject: [PATCH 1/7] add dimension() to constraints
---
python/opengen/constraints/affine_space.py | 3 +++
python/opengen/constraints/ball1.py | 3 +++
python/opengen/constraints/ball2.py | 3 +++
python/opengen/constraints/ball_inf.py | 3 +++
python/opengen/constraints/cartesian.py | 3 +++
python/opengen/constraints/constraint.py | 9 +++++++++
python/opengen/constraints/finite_set.py | 2 +-
python/opengen/constraints/no_constraints.py | 3 +++
python/opengen/constraints/simplex.py | 3 +++
python/opengen/constraints/soc.py | 3 +++
python/opengen/constraints/zero.py | 1 +
11 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/python/opengen/constraints/affine_space.py b/python/opengen/constraints/affine_space.py
index 1eb054c6..ae38bfea 100644
--- a/python/opengen/constraints/affine_space.py
+++ b/python/opengen/constraints/affine_space.py
@@ -54,3 +54,6 @@ def is_compact(self):
"""Affine spaces are not compact sets
"""
return False
+
+ def dimension(self):
+ return super().dimension()
diff --git a/python/opengen/constraints/ball1.py b/python/opengen/constraints/ball1.py
index 7ca05849..56082eb7 100644
--- a/python/opengen/constraints/ball1.py
+++ b/python/opengen/constraints/ball1.py
@@ -73,3 +73,6 @@ def is_convex(self):
def is_compact(self):
return True
+
+ def dimension(self):
+ return super().dimension()
\ No newline at end of file
diff --git a/python/opengen/constraints/ball2.py b/python/opengen/constraints/ball2.py
index f0bb43be..a993d613 100644
--- a/python/opengen/constraints/ball2.py
+++ b/python/opengen/constraints/ball2.py
@@ -91,3 +91,6 @@ def is_convex(self):
def is_compact(self):
return True
+
+ def dimension(self):
+ return super().dimension()
\ No newline at end of file
diff --git a/python/opengen/constraints/ball_inf.py b/python/opengen/constraints/ball_inf.py
index 738eaf4f..72c28dbc 100644
--- a/python/opengen/constraints/ball_inf.py
+++ b/python/opengen/constraints/ball_inf.py
@@ -88,3 +88,6 @@ def is_convex(self):
def is_compact(self):
return True
+
+ def dimension(self):
+ return super().dimension()
\ No newline at end of file
diff --git a/python/opengen/constraints/cartesian.py b/python/opengen/constraints/cartesian.py
index c4b1d662..a5b8e40a 100644
--- a/python/opengen/constraints/cartesian.py
+++ b/python/opengen/constraints/cartesian.py
@@ -124,3 +124,6 @@ def is_compact(self):
if not set_i.is_compact():
return False
return True
+
+ def dimension(self):
+ return super().dimension()
\ No newline at end of file
diff --git a/python/opengen/constraints/constraint.py b/python/opengen/constraints/constraint.py
index 6076f223..8fee3aa8 100644
--- a/python/opengen/constraints/constraint.py
+++ b/python/opengen/constraints/constraint.py
@@ -32,3 +32,12 @@ def is_compact(self):
Whether the set is compact
"""
return False
+
+ def dimension(self):
+ """
+ Constraint dimension
+
+ Derived classes can override this method to return the dimension of the
+ constraint, where possible, or return `None` if the constraint does not
+ have a fixed dimension.
+ """
\ No newline at end of file
diff --git a/python/opengen/constraints/finite_set.py b/python/opengen/constraints/finite_set.py
index b590caab..37908a64 100644
--- a/python/opengen/constraints/finite_set.py
+++ b/python/opengen/constraints/finite_set.py
@@ -7,7 +7,7 @@
class FiniteSet(Constraint):
"""Finite set
- A set of the form :math:`A = \{a_1, a_2, \ldots, a_K\}`
+ A set of the form :math:`A = \\{a_1, a_2, \\ldots, a_K\\}`
"""
def __init__(self, points=None):
diff --git a/python/opengen/constraints/no_constraints.py b/python/opengen/constraints/no_constraints.py
index a81722d4..e16507dc 100644
--- a/python/opengen/constraints/no_constraints.py
+++ b/python/opengen/constraints/no_constraints.py
@@ -21,3 +21,6 @@ def is_convex(self):
def is_compact(self):
return False
+
+ def dimension(self):
+ return super().dimension()
\ No newline at end of file
diff --git a/python/opengen/constraints/simplex.py b/python/opengen/constraints/simplex.py
index 1a07fa93..763ed883 100644
--- a/python/opengen/constraints/simplex.py
+++ b/python/opengen/constraints/simplex.py
@@ -103,3 +103,6 @@ def is_convex(self):
def is_compact(self):
"""Whether the set is compact (`True`)"""
return True
+
+ def dimension(self):
+ return super().dimension()
\ No newline at end of file
diff --git a/python/opengen/constraints/soc.py b/python/opengen/constraints/soc.py
index eb1279e5..49c76579 100644
--- a/python/opengen/constraints/soc.py
+++ b/python/opengen/constraints/soc.py
@@ -85,3 +85,6 @@ def is_convex(self):
def is_compact(self):
return False
+
+ def dimension(self):
+ return super().dimension()
\ No newline at end of file
diff --git a/python/opengen/constraints/zero.py b/python/opengen/constraints/zero.py
index 8205445c..95761bab 100644
--- a/python/opengen/constraints/zero.py
+++ b/python/opengen/constraints/zero.py
@@ -28,3 +28,4 @@ def is_convex(self):
def is_compact(self):
return True
+
From 80567941e058b55ea8bf5eee8a6bbc957290ae76 Mon Sep 17 00:00:00 2001
From: Pantelis Sopasakis
Date: Mon, 30 Mar 2026 18:06:56 +0100
Subject: [PATCH 2/7] dimension() implemented in constraints
---
python/opengen/constraints/ball1.py | 4 +-
python/opengen/constraints/ball2.py | 6 +-
python/opengen/constraints/ball_inf.py | 4 +-
python/opengen/constraints/cartesian.py | 2 +-
python/opengen/constraints/no_constraints.py | 2 +-
python/opengen/constraints/rectangle.py | 2 +-
python/opengen/constraints/simplex.py | 2 +-
python/opengen/constraints/soc.py | 2 +-
python/opengen/constraints/sphere2.py | 5 ++
python/opengen/constraints/zero.py | 7 +-
python/test/test_constraints.py | 72 +++++++++++++++++++-
11 files changed, 95 insertions(+), 13 deletions(-)
diff --git a/python/opengen/constraints/ball1.py b/python/opengen/constraints/ball1.py
index 56082eb7..deee203d 100644
--- a/python/opengen/constraints/ball1.py
+++ b/python/opengen/constraints/ball1.py
@@ -75,4 +75,6 @@ def is_compact(self):
return True
def dimension(self):
- return super().dimension()
\ No newline at end of file
+ if self.center is None:
+ return None
+ return len(self.center)
\ No newline at end of file
diff --git a/python/opengen/constraints/ball2.py b/python/opengen/constraints/ball2.py
index a993d613..70fdffd0 100644
--- a/python/opengen/constraints/ball2.py
+++ b/python/opengen/constraints/ball2.py
@@ -7,7 +7,7 @@
class Ball2(Constraint):
"""A Euclidean ball constraint
- A constraint of the form :math:`\|u-u_0\| \leq r`, where :math:`u_0` is the center
+ A constraint of the form :math:`\Vert u-u_0 \Vert \leq r`, where :math:`u_0` is the center
of the ball and `r` is its radius
"""
@@ -93,4 +93,6 @@ def is_compact(self):
return True
def dimension(self):
- return super().dimension()
\ No newline at end of file
+ if self.center is None:
+ return None
+ return len(self.center)
\ No newline at end of file
diff --git a/python/opengen/constraints/ball_inf.py b/python/opengen/constraints/ball_inf.py
index 72c28dbc..4f4ea4ec 100644
--- a/python/opengen/constraints/ball_inf.py
+++ b/python/opengen/constraints/ball_inf.py
@@ -90,4 +90,6 @@ def is_compact(self):
return True
def dimension(self):
- return super().dimension()
\ No newline at end of file
+ if self.center is None:
+ return None
+ return len(self.center)
\ No newline at end of file
diff --git a/python/opengen/constraints/cartesian.py b/python/opengen/constraints/cartesian.py
index a5b8e40a..448ebb16 100644
--- a/python/opengen/constraints/cartesian.py
+++ b/python/opengen/constraints/cartesian.py
@@ -126,4 +126,4 @@ def is_compact(self):
return True
def dimension(self):
- return super().dimension()
\ No newline at end of file
+ return self.segments[-1] + 1
\ No newline at end of file
diff --git a/python/opengen/constraints/no_constraints.py b/python/opengen/constraints/no_constraints.py
index e16507dc..7dd72a18 100644
--- a/python/opengen/constraints/no_constraints.py
+++ b/python/opengen/constraints/no_constraints.py
@@ -23,4 +23,4 @@ def is_compact(self):
return False
def dimension(self):
- return super().dimension()
\ No newline at end of file
+ return None
\ No newline at end of file
diff --git a/python/opengen/constraints/rectangle.py b/python/opengen/constraints/rectangle.py
index c6340d09..f77e92b8 100644
--- a/python/opengen/constraints/rectangle.py
+++ b/python/opengen/constraints/rectangle.py
@@ -31,7 +31,7 @@ def __check_xmin_xmax(cls, xmin, xmax):
if xmin_element > xmax_element:
raise Exception("xmin must be <= xmax")
- def __init__(self, xmin, xmax):
+ def __init__(self, xmin=None, xmax=None):
"""Construct a new instance of Rectangle
:param xmin: minimum bounds (can be ``None``)
diff --git a/python/opengen/constraints/simplex.py b/python/opengen/constraints/simplex.py
index 763ed883..113767e3 100644
--- a/python/opengen/constraints/simplex.py
+++ b/python/opengen/constraints/simplex.py
@@ -105,4 +105,4 @@ def is_compact(self):
return True
def dimension(self):
- return super().dimension()
\ No newline at end of file
+ return None
\ No newline at end of file
diff --git a/python/opengen/constraints/soc.py b/python/opengen/constraints/soc.py
index 49c76579..466058df 100644
--- a/python/opengen/constraints/soc.py
+++ b/python/opengen/constraints/soc.py
@@ -87,4 +87,4 @@ def is_compact(self):
return False
def dimension(self):
- return super().dimension()
\ No newline at end of file
+ return None
\ No newline at end of file
diff --git a/python/opengen/constraints/sphere2.py b/python/opengen/constraints/sphere2.py
index f8fca5f2..5283da53 100644
--- a/python/opengen/constraints/sphere2.py
+++ b/python/opengen/constraints/sphere2.py
@@ -75,3 +75,8 @@ def is_convex(self):
def is_compact(self):
return True
+
+ def dimension(self):
+ if self.center is None:
+ return None
+ return len(self.center)
\ No newline at end of file
diff --git a/python/opengen/constraints/zero.py b/python/opengen/constraints/zero.py
index 95761bab..96cb357e 100644
--- a/python/opengen/constraints/zero.py
+++ b/python/opengen/constraints/zero.py
@@ -7,15 +7,16 @@
class Zero(Constraint):
"""A set that contains only the origin
- The singleton :math:`\{0\}`
+ The singleton :math:`\\{0\\}`
"""
def __init__(self):
"""
- Constructor for set :math:`Z = \{0\}`
+ Constructor for set :math:`Z = \\{0\\}`
"""
+ pass
def distance_squared(self, u):
return fn.norm2_squared(u)
@@ -29,3 +30,5 @@ def is_convex(self):
def is_compact(self):
return True
+ def dimension(self):
+ return None
\ No newline at end of file
diff --git a/python/test/test_constraints.py b/python/test/test_constraints.py
index f080d518..53135eaa 100644
--- a/python/test/test_constraints.py
+++ b/python/test/test_constraints.py
@@ -11,7 +11,7 @@ class ConstraintsTestCase(unittest.TestCase):
# Infinity Ball
# -----------------------------------------------------------------------
- def test_ball_inf_origin(self):
+ def test_ball_inf_origin(self):
ball = og.constraints.BallInf(None, 1)
x = np.array([3, 2])
x_sym = cs.SX.sym("x", 2)
@@ -58,6 +58,13 @@ def test_ball_inf_origin_compact(self):
ball = og.constraints.BallInf()
self.assertTrue(ball.is_compact())
+ def test_dimension_ballInf(self):
+ ball = og.constraints.BallInf()
+ self.assertIsNone(ball.dimension())
+
+ ball = og.constraints.BallInf(center=[1., 2., -3.])
+ self.assertEqual(3, ball.dimension())
+
# -----------------------------------------------------------------------
# Euclidean Ball
# -----------------------------------------------------------------------
@@ -122,6 +129,13 @@ def test_ball_euclidean_origin_compact(self):
ball = og.constraints.Ball2()
self.assertTrue(ball.is_compact())
+ def test_dimension_ball2(self):
+ ball = og.constraints.Ball2()
+ self.assertIsNone(ball.dimension())
+
+ ball = og.constraints.Ball2(center=[1., 2., -3.])
+ self.assertEqual(3, ball.dimension())
+
# -----------------------------------------------------------------------
# Rectangle
# -----------------------------------------------------------------------
@@ -194,6 +208,14 @@ def test_rectangle_is_orthant(self):
self.assertFalse(rect.is_orthant())
rect = og.constraints.Rectangle([-1.0, float('-inf')], [10.0, 3.0])
self.assertFalse(rect.is_orthant())
+
+ def test_rectangle_dimension(self):
+ rec_only_xmin = og.constraints.Rectangle(xmin=[1])
+ rec_only_xmax = og.constraints.Rectangle(xmax=[5, 6])
+ rec_xmin_and_xmax = og.constraints.Rectangle(xmin=[0, -1, 2], xmax=[1, 0, 2])
+ self.assertEqual(1, rec_only_xmin.dimension())
+ self.assertEqual(2, rec_only_xmax.dimension())
+ self.assertEqual(3, rec_xmin_and_xmax.dimension())
# -----------------------------------------------------------------------
# Second-Order Cone (SOC)
@@ -264,6 +286,10 @@ def test_second_order_cone_convex(self):
def test_second_order_cone_convex(self):
soc = og.constraints.SecondOrderCone()
self.assertFalse(soc.is_compact())
+
+ def test_soc_dimension(self):
+ soc = og.constraints.SecondOrderCone()
+ self.assertIsNone(soc.dimension())
# -----------------------------------------------------------------------
# No Constraints
@@ -282,6 +308,10 @@ def test_no_constraints_convex(self):
def test_no_constraints_compact(self):
whole_rn = og.constraints.NoConstraints()
self.assertFalse(whole_rn.is_compact())
+
+ def test_no_constraints_dimension(self):
+ whole_rn = og.constraints.NoConstraints()
+ self.assertIsNone(whole_rn.dimension())
# -----------------------------------------------------------------------
# Cartesian product of constraints
@@ -377,6 +407,18 @@ def test_cartesian_convex(self):
[5, 10, 11], [ball_inf, ball_eucl, free])
self.assertFalse(cartesian.is_compact())
+ def test_cartesian_dimension(self):
+ inf = float('inf')
+ ball_inf = og.constraints.BallInf(None, 1)
+ ball_eucl = og.constraints.Ball2(None, 1)
+ rect = og.constraints.Rectangle(xmin=[0.0, 1.0, -inf, 2.0],
+ xmax=[1.0, inf, 10.0, 10.0])
+ cartesian = og.constraints.CartesianProduct(
+ [1, 4, 8], [ball_inf, ball_eucl, rect])
+ self.assertEqual(9, cartesian.dimension())
+
+
+
# -----------------------------------------------------------------------
# Finite Set
# -----------------------------------------------------------------------
@@ -476,6 +518,10 @@ def test_simplex_projection_random_optimality(self):
self.assertLessEqual(
np.dot(x-x_star, z-x_star), 1e-10, "Simplex optimality conditions failed")
+ def test_simplex_dimension(self):
+ simplex = og.constraints.Simplex(alpha=1.0)
+ self.assertIsNone(simplex.dimension())
+
# -----------------------------------------------------------------------
# Ball1
# -----------------------------------------------------------------------
@@ -531,6 +577,13 @@ def test_ball1_project_random_points_center(self):
self.assertLessEqual(
np.dot(e-x_star, x-x_star), 1e-10, "Ball1 optimality conditions failed (2)")
+ def test_dimension_ball1(self):
+ ball = og.constraints.Ball1()
+ self.assertIsNone(ball.dimension())
+
+ ball = og.constraints.Ball1(center=[1., 2., -3.])
+ self.assertEqual(3, ball.dimension())
+
# -----------------------------------------------------------------------
# Sphere2
# -----------------------------------------------------------------------
@@ -555,8 +608,23 @@ def test_sphere2_no_center(self):
sphere = og.constraints.Sphere2(radius=0.5)
u = [0, 0, 0, 0]
dist = sphere.distance_squared(u)
- self.assertAlmostEqual(0.25, dist, places=12)
+ self.assertAlmostEqual(0.25, dist, places=12)
+
+ def test_dimension_sphere2(self):
+ sphere = og.constraints.Sphere2()
+ self.assertIsNone(sphere.dimension())
+
+ sphere = og.constraints.Sphere2(center=[1., 2., -3.])
+ self.assertEqual(3, sphere.dimension())
+
+
+ # -----------------------------------------------------------------------
+ # Zero
+ # -----------------------------------------------------------------------
+ def test_zero_dimension(self):
+ z = og.opengen.constraints.Zero()
+ self.assertIsNone(z.dimension())
if __name__ == '__main__':
unittest.main()
From 38f7049c59ec6418ec8307d71efd2d949289ea1b Mon Sep 17 00:00:00 2001
From: Pantelis Sopasakis
Date: Mon, 30 Mar 2026 18:33:14 +0100
Subject: [PATCH 3/7] catch inconsistent inputs
- checking if u and U are consistent
- solve issue with __init__ making code completionts impossible
---
python/opengen/__init__.py | 26 +++++++++++++++------
python/opengen/builder/optimizer_builder.py | 9 +++++++
python/test/test_constraints.py | 2 +-
3 files changed, 29 insertions(+), 8 deletions(-)
diff --git a/python/opengen/__init__.py b/python/opengen/__init__.py
index d7cedb79..58cb9a85 100644
--- a/python/opengen/__init__.py
+++ b/python/opengen/__init__.py
@@ -1,7 +1,19 @@
-import opengen.definitions
-import opengen.builder
-import opengen.config
-import opengen.functions
-import opengen.constraints
-import opengen.tcp
-import opengen.ocp
+from . import (
+ definitions,
+ builder,
+ config,
+ functions,
+ constraints,
+ tcp,
+ ocp,
+)
+
+__all__ = [
+ "definitions",
+ "builder",
+ "config",
+ "functions",
+ "constraints",
+ "tcp",
+ "ocp",
+]
diff --git a/python/opengen/builder/optimizer_builder.py b/python/opengen/builder/optimizer_builder.py
index 51500422..841d3662 100644
--- a/python/opengen/builder/optimizer_builder.py
+++ b/python/opengen/builder/optimizer_builder.py
@@ -645,6 +645,15 @@ def __initialize(self):
def __check_user_provided_parameters(self):
self.__logger.info("Checking user parameters")
+
+ # Check constraints dimensions
+ dim_constraints = self.__problem.constraints.dimension()
+ dim_decision_variables = self.__problem.dim_decision_variables()
+ if dim_constraints is not None and dim_decision_variables != dim_constraints:
+ raise ValueError(f"Inconsistent dimensions - decision variables: {dim_decision_variables}",
+ f"set of constraints: {dim_constraints}")
+
+ # Preconditioning...
if self.__solver_config.preconditioning:
# Preconditioning is not allowed when we have general ALM-type constraints of the form
# F1(u, p) in C, unless C is {0} or an orthant (special case of rectangle).
diff --git a/python/test/test_constraints.py b/python/test/test_constraints.py
index 53135eaa..fa1fcc87 100644
--- a/python/test/test_constraints.py
+++ b/python/test/test_constraints.py
@@ -622,7 +622,7 @@ def test_dimension_sphere2(self):
# Zero
# -----------------------------------------------------------------------
- def test_zero_dimension(self):
+ def test_zero_dimension(self):
z = og.opengen.constraints.Zero()
self.assertIsNone(z.dimension())
From 1bcde5894ffd9bc48414bff416f9780f51e80c3e Mon Sep 17 00:00:00 2001
From: Pantelis Sopasakis
Date: Mon, 30 Mar 2026 18:37:14 +0100
Subject: [PATCH 4/7] overhaul of __init__.py
---
python/opengen/builder/__init__.py | 15 +++++--
python/opengen/builder/optimizer_builder.py | 11 ++---
python/opengen/config/__init__.py | 20 ++++++---
python/opengen/constraints/__init__.py | 45 ++++++++++++++-------
python/opengen/functions/__init__.py | 30 +++++++++-----
python/opengen/tcp/__init__.py | 15 +++++--
python/test/__init__.py | 3 ++
7 files changed, 99 insertions(+), 40 deletions(-)
diff --git a/python/opengen/builder/__init__.py b/python/opengen/builder/__init__.py
index ceb1edb6..7f127ba1 100644
--- a/python/opengen/builder/__init__.py
+++ b/python/opengen/builder/__init__.py
@@ -1,3 +1,12 @@
-from .optimizer_builder import *
-from .problem import *
-from .set_y_calculator import *
+from .optimizer_builder import OpEnOptimizerBuilder
+from .problem import Problem
+from .set_y_calculator import SetYCalculator
+from .ros_builder import RosBuilder, ROS2Builder
+
+__all__ = [
+ "OpEnOptimizerBuilder",
+ "Problem",
+ "SetYCalculator",
+ "RosBuilder",
+ "ROS2Builder",
+]
diff --git a/python/opengen/builder/optimizer_builder.py b/python/opengen/builder/optimizer_builder.py
index 841d3662..4993764d 100644
--- a/python/opengen/builder/optimizer_builder.py
+++ b/python/opengen/builder/optimizer_builder.py
@@ -2,6 +2,7 @@
import shutil
import yaml
+import opengen as og
import opengen.config as og_cfg
import opengen.definitions as og_dfn
import opengen.constraints as og_cstr
@@ -43,10 +44,10 @@ class OpEnOptimizerBuilder:
"""
def __init__(self,
- problem,
- metadata=og_cfg.OptimizerMeta(),
- build_configuration=og_cfg.BuildConfiguration(),
- solver_configuration=og_cfg.SolverConfiguration()):
+ problem: og.builder.Problem,
+ metadata: og_cfg.OptimizerMeta =og_cfg.OptimizerMeta(),
+ build_configuration: og_cfg.BuildConfiguration =og_cfg.BuildConfiguration(),
+ solver_configuration: og_cfg.SolverConfiguration=og_cfg.SolverConfiguration()):
"""Constructor of OpEnOptimizerBuilder
:param problem: instance of :class:`~opengen.builder.problem.Problem`
@@ -645,7 +646,7 @@ def __initialize(self):
def __check_user_provided_parameters(self):
self.__logger.info("Checking user parameters")
-
+
# Check constraints dimensions
dim_constraints = self.__problem.constraints.dimension()
dim_decision_variables = self.__problem.dim_decision_variables()
diff --git a/python/opengen/config/__init__.py b/python/opengen/config/__init__.py
index 3b81ad6b..74b924eb 100644
--- a/python/opengen/config/__init__.py
+++ b/python/opengen/config/__init__.py
@@ -1,5 +1,15 @@
-from .meta import *
-from .solver_config import *
-from .build_config import *
-from .tcp_server_config import *
-from .ros_config import *
+from .meta import OptimizerMeta, SEMVER_PATTERN
+from .solver_config import SolverConfiguration
+from .build_config import BuildConfiguration, RustAllocator
+from .tcp_server_config import TcpServerConfiguration
+from .ros_config import RosConfiguration
+
+__all__ = [
+ "OptimizerMeta",
+ "SEMVER_PATTERN",
+ "SolverConfiguration",
+ "BuildConfiguration",
+ "RustAllocator",
+ "TcpServerConfiguration",
+ "RosConfiguration",
+]
diff --git a/python/opengen/constraints/__init__.py b/python/opengen/constraints/__init__.py
index e361c496..ce529003 100644
--- a/python/opengen/constraints/__init__.py
+++ b/python/opengen/constraints/__init__.py
@@ -1,14 +1,31 @@
-from .ball1 import *
-from .ball2 import *
-from .sphere2 import *
-from .rectangle import *
-from .constraint import *
-from .ball_inf import *
-from .soc import *
-from .no_constraints import *
-from .cartesian import *
-from .zero import *
-from .finite_set import *
-from .halfspace import *
-from .simplex import *
-from .affine_space import *
+from .ball1 import Ball1
+from .ball2 import Ball2
+from .sphere2 import Sphere2
+from .rectangle import Rectangle
+from .constraint import Constraint
+from .ball_inf import BallInf
+from .soc import SecondOrderCone
+from .no_constraints import NoConstraints
+from .cartesian import CartesianProduct
+from .zero import Zero
+from .finite_set import FiniteSet
+from .halfspace import Halfspace
+from .simplex import Simplex
+from .affine_space import AffineSpace
+
+__all__ = [
+ "Ball1",
+ "Ball2",
+ "Sphere2",
+ "Rectangle",
+ "Constraint",
+ "BallInf",
+ "SecondOrderCone",
+ "NoConstraints",
+ "CartesianProduct",
+ "Zero",
+ "FiniteSet",
+ "Halfspace",
+ "Simplex",
+ "AffineSpace",
+]
diff --git a/python/opengen/functions/__init__.py b/python/opengen/functions/__init__.py
index 2f8b3339..11508d8b 100644
--- a/python/opengen/functions/__init__.py
+++ b/python/opengen/functions/__init__.py
@@ -1,9 +1,21 @@
-from .rosenbrock import *
-from .fmin import *
-from .fmax import *
-from .is_symbolic import *
-from .is_numeric import *
-from .sign import *
-from .norm2 import *
-from .fabs import *
-from .norm2_squared import *
+from .rosenbrock import rosenbrock
+from .fmin import fmin
+from .fmax import fmax
+from .is_symbolic import is_symbolic
+from .is_numeric import is_numeric
+from .sign import sign
+from .norm2 import norm2
+from .fabs import fabs
+from .norm2_squared import norm2_squared
+
+__all__ = [
+ "rosenbrock",
+ "fmin",
+ "fmax",
+ "is_symbolic",
+ "is_numeric",
+ "sign",
+ "norm2",
+ "fabs",
+ "norm2_squared",
+]
diff --git a/python/opengen/tcp/__init__.py b/python/opengen/tcp/__init__.py
index 49783d98..17d36335 100644
--- a/python/opengen/tcp/__init__.py
+++ b/python/opengen/tcp/__init__.py
@@ -1,4 +1,11 @@
-from .optimizer_tcp_manager import *
-from .solver_status import *
-from .solver_error import *
-from .solver_response import *
+from .optimizer_tcp_manager import OptimizerTcpManager
+from .solver_status import SolverStatus
+from .solver_error import SolverError
+from .solver_response import SolverResponse
+
+__all__ = [
+ "OptimizerTcpManager",
+ "SolverStatus",
+ "SolverError",
+ "SolverResponse",
+]
diff --git a/python/test/__init__.py b/python/test/__init__.py
index e69de29b..fbd4941c 100644
--- a/python/test/__init__.py
+++ b/python/test/__init__.py
@@ -0,0 +1,3 @@
+"""Test package for the project."""
+
+__all__ = []
From 9feab7d072f6b49a634dbfa4463838a7d2b2a3d3 Mon Sep 17 00:00:00 2001
From: Pantelis Sopasakis
Date: Mon, 30 Mar 2026 18:46:38 +0100
Subject: [PATCH 5/7] lazy imports
---
python/opengen/__init__.py | 43 ++++++++++++++++-----
python/opengen/builder/optimizer_builder.py | 2 +
python/test/test_constraints.py | 2 +-
3 files changed, 37 insertions(+), 10 deletions(-)
diff --git a/python/opengen/__init__.py b/python/opengen/__init__.py
index 58cb9a85..daf6cf89 100644
--- a/python/opengen/__init__.py
+++ b/python/opengen/__init__.py
@@ -1,12 +1,16 @@
-from . import (
- definitions,
- builder,
- config,
- functions,
- constraints,
- tcp,
- ocp,
-)
+"""Top-level package for OpEn with lazy submodule imports.
+
+This module defers importing heavy subpackages to attribute access
+to avoid circular import problems during package initialization.
+
+Lazy submodule imports defer the loading of Python modules and their
+attributes until they are first accessed, reducing startup time and
+memory usage. This is achieved using PEP 562 (__getattr__ and __dir__)
+to intercept attribute access and load the underlying code only when
+necessary.
+"""
+
+from importlib import import_module
__all__ = [
"definitions",
@@ -17,3 +21,24 @@
"tcp",
"ocp",
]
+
+
+def __getattr__(name):
+ """Lazily import submodules on attribute access.
+
+ Example: accessing ``opengen.builder`` will import
+ ``opengen.builder`` and cache it on the package module.
+
+ This defers importing heavy subpackages until they're actually used
+ (lazy imports), reducing startup cost and helping avoid import-time
+ circular dependencies.
+ """
+ if name in __all__:
+ module = import_module(f"{__name__}.{name}")
+ globals()[name] = module
+ return module
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+
+
+def __dir__():
+ return sorted(list(__all__) + list(globals().keys()))
diff --git a/python/opengen/builder/optimizer_builder.py b/python/opengen/builder/optimizer_builder.py
index 4993764d..d4e2c2b8 100644
--- a/python/opengen/builder/optimizer_builder.py
+++ b/python/opengen/builder/optimizer_builder.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
import subprocess
import shutil
import yaml
diff --git a/python/test/test_constraints.py b/python/test/test_constraints.py
index fa1fcc87..2f58acc1 100644
--- a/python/test/test_constraints.py
+++ b/python/test/test_constraints.py
@@ -623,7 +623,7 @@ def test_dimension_sphere2(self):
# -----------------------------------------------------------------------
def test_zero_dimension(self):
- z = og.opengen.constraints.Zero()
+ z = og.constraints.Zero()
self.assertIsNone(z.dimension())
if __name__ == '__main__':
From 4165e33ae7c94746c94943f65d7c8164ee956133 Mon Sep 17 00:00:00 2001
From: Pantelis Sopasakis
Date: Mon, 30 Mar 2026 18:51:35 +0100
Subject: [PATCH 6/7] update CHANGELOG
---
python/CHANGELOG.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/python/CHANGELOG.md b/python/CHANGELOG.md
index ab870a1d..cbda879e 100644
--- a/python/CHANGELOG.md
+++ b/python/CHANGELOG.md
@@ -37,6 +37,8 @@ Note: This is the Changelog file of `opengen` - the Python interface of OpEn
- ROS2 generated packages now use `uint16` for `error_code`, matching the current positive error-code range while keeping the wire format compact
- MATLAB-related Python docs now reference the new `python/` package layout instead of the removed `open-codegen/` path
- ROS2 integration tests now clean stale nested `colcon` build artifacts and preserve the active shell environment more reliably in micromamba/conda setups
+- Fixed issues in `__init__.py`: lazy imports and discoverability
+- Now checking whether the constraints have the correct dimension before attempting to build an optimizer
## [0.10.1] - 2026-03-25
From 618ce38267cf2e6e0b3bc1e4a05fb4a907e54aac Mon Sep 17 00:00:00 2001
From: Pantelis Sopasakis
Date: Mon, 30 Mar 2026 19:04:21 +0100
Subject: [PATCH 7/7] testing
---
python/test/test.py | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/python/test/test.py b/python/test/test.py
index 229a0917..4122e55b 100644
--- a/python/test/test.py
+++ b/python/test/test.py
@@ -68,7 +68,25 @@ def test_with_open_version_rejects_invalid_version(self):
"invalid OpEn version",
):
build_config.with_open_version("^1.2")
+
+
+class SmokeTests(unittest.TestCase):
+ def test_incompatible_constraints_dimensions(self):
+ u = cs.SX.sym("u", 5)
+ p = cs.SX.sym("p", 2)
+ phi = og.functions.rosenbrock(u, p)
+ bounds = og.constraints.Rectangle(xmin=[1, 2], xmax=[3, 4])
+ problem = og.builder.Problem(u, p, phi) \
+ .with_constraints(bounds)
+ meta = og.config.OptimizerMeta().with_optimizer_name("abcd")
+ build_config = og.config.BuildConfiguration() \
+ .with_open_version(local_path=os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "rust"))) \
+ .with_build_directory(".whatever")
+ solver_cfg = og.config.SolverConfiguration()
+ builder = og.builder.OpEnOptimizerBuilder(problem, meta, build_config, solver_cfg)
+ with self.assertRaises(ValueError):
+ builder.build()
class OcpSolutionTestCase(unittest.TestCase):