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,57 @@ 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
+ types = np .fromiter (
113
+ (
114
+ flip_type (type_ ) if scale < 0 else type_
115
+ for type_ , scale in zip (types , self ._scales , strict = False )
116
+ ),
117
+ np .ubyte ,
118
+ )
119
+ return rhs_values , types
120
+
121
+ def forward (self , constraints : NDArray [np .float64 ]) -> NDArray [np .float64 ]:
122
+ return constraints / self ._scales
123
+
124
+ def backward (self , constraints : NDArray [np .float64 ]) -> NDArray [np .float64 ]:
125
+ return constraints * self ._scales
126
+
127
+ def calculate_auto_scales (self , constraints : NDArray [np .float64 ]) -> None :
128
+ auto_scales = np .abs (
129
+ np .nansum (constraints * self ._weights [:, np .newaxis ], axis = 0 )
130
+ )
131
+ self ._scales [self ._auto_scales ] *= auto_scales [self ._auto_scales ]
132
+
133
+ @property
134
+ def has_auto_scale (self ) -> bool :
135
+ return bool (np .any (self ._auto_scales ))
136
+
137
+
82
138
def get_opt_model_transforms (
83
139
controls : list [ControlConfig ],
84
140
objectives : list [ObjectiveFunctionConfig ],
141
+ constraints : list [OutputConstraintConfig ] | None ,
85
142
weights : list [float ],
86
143
) -> OptModelTransforms :
87
144
flattened_controls = FlattenedControls (controls )
@@ -107,4 +164,19 @@ def get_opt_model_transforms(
107
164
],
108
165
weights ,
109
166
),
167
+ nonlinear_constraints = (
168
+ ConstraintScaler (
169
+ [
170
+ 1.0 if constraint .scale is None else constraint .scale
171
+ for constraint in constraints
172
+ ],
173
+ [
174
+ False if constraint .auto_scale is None else constraint .auto_scale
175
+ for constraint in constraints
176
+ ],
177
+ weights ,
178
+ )
179
+ if constraints
180
+ else None
181
+ ),
110
182
)
0 commit comments