2
2
3
3
import numpy as np
4
4
from numpy .typing import NDArray
5
+ from ropt .enums import ConstraintType
5
6
from ropt .transforms import OptModelTransforms
6
- from ropt .transforms .base import ObjectiveTransform , VariableTransform
7
-
8
- from everest .config import ControlConfig , ObjectiveFunctionConfig
7
+ from ropt .transforms .base import (
8
+ NonLinearConstraintTransform ,
9
+ ObjectiveTransform ,
10
+ VariableTransform ,
11
+ )
12
+
13
+ from everest .config import (
14
+ ControlConfig ,
15
+ ObjectiveFunctionConfig ,
16
+ OutputConstraintConfig ,
17
+ )
9
18
from everest .config .utils import FlattenedControls
10
19
11
20
@@ -79,9 +88,58 @@ def has_auto_scale(self) -> bool:
79
88
return bool (np .any (self ._auto_scales ))
80
89
81
90
91
+ class ConstraintScaler (NonLinearConstraintTransform ):
92
+ def __init__ (
93
+ self , scales : list [float ], auto_scales : list [bool ], weights : list [float ]
94
+ ) -> None :
95
+ self ._scales = np .asarray (scales , dtype = np .float64 )
96
+ self ._auto_scales = np .asarray (auto_scales , dtype = np .bool_ )
97
+ self ._weights = np .asarray (weights , dtype = np .float64 )
98
+
99
+ def transform_rhs_values (
100
+ self , rhs_values : NDArray [np .float64 ], types : NDArray [np .ubyte ]
101
+ ) -> tuple [NDArray [np .float64 ], NDArray [np .ubyte ]]:
102
+ def flip_type (constraint_type : ConstraintType ) -> ConstraintType :
103
+ match constraint_type :
104
+ case ConstraintType .GE :
105
+ return ConstraintType .LE
106
+ case ConstraintType .LE :
107
+ return ConstraintType .GE
108
+ case _:
109
+ return constraint_type
110
+
111
+ rhs_values = rhs_values / self ._scales # noqa: PLR6104
112
+ # Flip inequality types if self._scales < 0 in the division above:
113
+ types = np .fromiter (
114
+ (
115
+ flip_type (type_ ) if scale < 0 else type_
116
+ for type_ , scale in zip (types , self ._scales , strict = False )
117
+ ),
118
+ np .ubyte ,
119
+ )
120
+ return rhs_values , types
121
+
122
+ def forward (self , constraints : NDArray [np .float64 ]) -> NDArray [np .float64 ]:
123
+ return constraints / self ._scales
124
+
125
+ def backward (self , constraints : NDArray [np .float64 ]) -> NDArray [np .float64 ]:
126
+ return constraints * self ._scales
127
+
128
+ def calculate_auto_scales (self , constraints : NDArray [np .float64 ]) -> None :
129
+ auto_scales = np .abs (
130
+ np .nansum (constraints * self ._weights [:, np .newaxis ], axis = 0 )
131
+ )
132
+ self ._scales [self ._auto_scales ] *= auto_scales [self ._auto_scales ]
133
+
134
+ @property
135
+ def has_auto_scale (self ) -> bool :
136
+ return bool (np .any (self ._auto_scales ))
137
+
138
+
82
139
def get_optimization_domain_transforms (
83
140
controls : list [ControlConfig ],
84
141
objectives : list [ObjectiveFunctionConfig ],
142
+ constraints : list [OutputConstraintConfig ] | None ,
85
143
weights : list [float ],
86
144
) -> OptModelTransforms :
87
145
flattened_controls = FlattenedControls (controls )
@@ -107,4 +165,19 @@ def get_optimization_domain_transforms(
107
165
],
108
166
weights ,
109
167
),
168
+ nonlinear_constraints = (
169
+ ConstraintScaler (
170
+ [
171
+ 1.0 if constraint .scale is None else constraint .scale
172
+ for constraint in constraints
173
+ ],
174
+ [
175
+ False if constraint .auto_scale is None else constraint .auto_scale
176
+ for constraint in constraints
177
+ ],
178
+ weights ,
179
+ )
180
+ if constraints
181
+ else None
182
+ ),
110
183
)
0 commit comments