@@ -734,8 +734,8 @@ def __init__(self, center, half_lengths):
734
734
raise AttributeError ("Vector of half-lengths must be real-valued and numeric." )
735
735
if not all (isinstance (elem , (int , float )) for elem in center ):
736
736
raise AttributeError ("Vector center must be real-valued and numeric." )
737
- if any (elem <= 0 for elem in half_lengths ):
738
- raise AttributeError ("Half length values must be > 0 ." )
737
+ if any (elem < 0 for elem in half_lengths ):
738
+ raise AttributeError ("Half length values must be nonnegative ." )
739
739
# === Valid variance dimensions
740
740
if not len (center ) == len (half_lengths ):
741
741
raise AttributeError ("Half lengths and center of ellipsoid must have same dimensions." )
@@ -765,33 +765,52 @@ def parameter_bounds(self):
765
765
parameter_bounds = [(nom_value [i ] - half_length [i ], nom_value [i ] + half_length [i ]) for i in range (len (nom_value ))]
766
766
return parameter_bounds
767
767
768
- def set_as_constraint (self , uncertain_params , ** kwargs ):
768
+ def set_as_constraint (self , uncertain_params , model = None , config = None ):
769
769
"""
770
- Function to generate constraints for the AxisAlignedEllipsoid uncertainty set.
770
+ Generate constraint(s) for the `AxisAlignedEllipsoidSet`
771
+ class.
771
772
772
773
Args:
773
- uncertain_params: uncertain parameter objects for writing constraint objects
774
- """
775
- if len (uncertain_params ) != len (self .center ):
776
- raise AttributeError ("Center of ellipsoid must be same dimensions as vector of uncertain parameters." )
777
- # square and invert half lengths
778
- inverse_squared_half_lengths = list (1.0 / (a ** 2 ) for a in self .half_lengths )
779
- # Calculate row vector of differences
780
- diff_squared = []
781
- # === Assume VarList uncertain_param_vars
782
- for idx , i in enumerate (uncertain_params ):
783
- if uncertain_params [idx ].is_indexed ():
784
- for index in uncertain_params [idx ]:
785
- diff_squared .append ((uncertain_params [idx ][index ] - self .center [idx ])** 2 )
786
- else :
787
- diff_squared .append ((uncertain_params [idx ] - self .center [idx ])** 2 )
788
-
789
- # Calculate inner product of difference vector and variance matrix
790
- constraint = sum ([x * y for x , y in zip (inverse_squared_half_lengths , diff_squared )])
791
-
774
+ uncertain_params: uncertain parameter objects for writing
775
+ constraint objects. Indexed parameters are accepted, and
776
+ are unpacked for constraint generation.
777
+ """
778
+ all_params = list ()
779
+
780
+ # expand all uncertain parameters to a list.
781
+ # this accounts for the cases in which `uncertain_params`
782
+ # consists of indexed model components,
783
+ # or is itself a single indexed component
784
+ if not isinstance (uncertain_params , (tuple , list )):
785
+ uncertain_params = [uncertain_params ]
786
+
787
+ all_params = []
788
+ for uparam in uncertain_params :
789
+ all_params .extend (uparam .values ())
790
+
791
+ if len (all_params ) != len (self .center ):
792
+ raise AttributeError (
793
+ f"Center of ellipsoid is of dimension { len (self .center )} ,"
794
+ f" but vector of uncertain parameters is of dimension"
795
+ f" { len (all_params )} "
796
+ )
797
+
798
+ zip_all = zip (all_params , self .center , self .half_lengths )
799
+ diffs_squared = list ()
800
+
801
+ # now construct the constraints
792
802
conlist = ConstraintList ()
793
803
conlist .construct ()
794
- conlist .add (constraint <= 1 )
804
+ for param , ctr , half_len in zip_all :
805
+ if half_len > 0 :
806
+ diffs_squared .append ((param - ctr ) ** 2 / (half_len ) ** 2 )
807
+ else :
808
+ # equality constraints for parameters corresponding to
809
+ # half-lengths of zero
810
+ conlist .add (param == ctr )
811
+
812
+ conlist .add (sum (diffs_squared ) <= 1 )
813
+
795
814
return conlist
796
815
797
816
@@ -802,13 +821,19 @@ class EllipsoidalSet(UncertaintySet):
802
821
803
822
def __init__ (self , center , shape_matrix , scale = 1 ):
804
823
"""
805
- EllipsoidalSet constructor
824
+ EllipsoidalSet constructor.
806
825
807
- Args:
808
- center: Vector (``list``) of uncertain parameter values around which deviations are restrained.
809
- shape_matrix: Positive semi-definite matrix, effectively a covariance matrix for
810
- constraint and bounds determination.
811
- scale: Right-hand side value for the ellipsoid.
826
+ Parameters
827
+ ----------
828
+ center : (N,) array-like
829
+ Center of the ellipsoid.
830
+ shape_matrix : (N, N) array-like
831
+ A positive definite matrix characterizing the shape
832
+ and orientation of the ellipsoid.
833
+ scale : float
834
+ Square of the factor by which to scale the semi-axes
835
+ of the ellipsoid (i.e. the eigenvectors of the covariance
836
+ matrix).
812
837
"""
813
838
814
839
# === Valid data in lists/matrixes
@@ -875,8 +900,8 @@ def parameter_bounds(self):
875
900
scale = self .scale
876
901
nom_value = self .center
877
902
P = self .shape_matrix
878
- parameter_bounds = [(nom_value [i ] - np .power (P [i ][i ], 0.5 ) * scale ,
879
- nom_value [i ] + np .power (P [i ][i ], 0.5 ) * scale ) for i in range (self .dim )]
903
+ parameter_bounds = [(nom_value [i ] - np .power (P [i ][i ] * scale , 0.5 ),
904
+ nom_value [i ] + np .power (P [i ][i ] * scale , 0.5 )) for i in range (self .dim )]
880
905
return parameter_bounds
881
906
882
907
def set_as_constraint (self , uncertain_params , ** kwargs ):
0 commit comments