@@ -188,13 +188,89 @@ def create_cvxpy_problem(self):
188188 (cp .sum ((cp .multiply (st .get_opt_voxels_volume_cc (org ),
189189 A [st .get_opt_voxels_idx (org ), :] @ x ))))
190190 <= limit / num_fractions ]
191+
192+ elif constraint_def [i ]['type' ] == 'CVaR_Upper' :
193+
194+ '''
195+ Json example:
196+ {
197+ "type": "CVaR_Upper",
198+ "parameters": {
199+ "structure_name": "HEART",
200+ "alpha": 0.90
201+ },
202+ "constraints": {
203+ "limit": 5
204+ }
205+ }
206+ '''
207+
208+ alpha = float (constraint_def [i ]['parameters' ]['alpha' ])
209+ if not (0.0 < alpha < 1.0 ):
210+ raise ValueError (f"CVaR_Upper: alpha must be in (0,1), got { alpha } " )
211+ struct = constraint_def [i ]['parameters' ]['structure_name' ]
212+ limit = constraint_def [i ]['constraints' ]['limit' ]
213+ if struct in self .my_plan .structures .get_structures ():
214+ voxel_idx = st .get_opt_voxels_idx (struct )
215+ if len (voxel_idx ) > 0 :
216+ dose_1d_list = A [voxel_idx , :] @ x * num_fractions # dose per voxel
217+
218+ label = f"constraint_{ struct } _a{ alpha :.4f} "
219+
220+ zeta = cp .Variable (name = f"zeta_{ label } " )
221+ w = cp .Variable (len (voxel_idx ), name = f"w_{ label } " )
222+
223+ # Store variables in self.vars using label
224+ self .vars [f"zeta_{ label } " ] = zeta
225+ self .vars [f"w_{ label } " ] = w
226+
227+ # Add CVaR constraint
228+ constraints += [
229+ zeta + (1 / ((1 - alpha ) * len (voxel_idx ))) * cp .sum (w ) <= limit ,
230+ w >= dose_1d_list - zeta ,
231+ w >= 0
232+ ]
233+
191234 mask = np .isfinite (d_max )
192235 # Create index mask arrays
193236 indices = np .arange (len (mask )) # assumes mask is 1D and corresponds to voxel indices
194237 all_d_max_vox_ind = indices [mask ]
195238 constraints += [A [all_d_max_vox_ind , :] @ x <= d_max [all_d_max_vox_ind ]] # Add constraint for all d_max voxels at once
196239 print ('Problem created' )
197240
241+ def add_CVaR_Upper (self , alpha : float , limit : float , struct : str ):
242+ """
243+ add CVaR+ to the problem as a constraint
244+
245+ """
246+ constraints = self .constraints
247+ x = self .vars ["x" ]
248+ st = self .inf_matrix
249+ A = self .inf_matrix .A
250+ num_fractions = self .clinical_criteria .get_num_of_fractions ()
251+
252+ if struct in self .my_plan .structures .get_structures ():
253+ voxel_idx = st .get_opt_voxels_idx (struct )
254+
255+ if len (voxel_idx ) > 0 :
256+ dose_1d_list = A [voxel_idx , :] @ x * num_fractions # dose per voxel
257+
258+ label = f"constraint_{ struct } _a{ alpha :.4f} "
259+
260+ zeta = cp .Variable (name = f"zeta_{ label } " )
261+ w = cp .Variable (len (voxel_idx ), name = f"w_{ label } " )
262+
263+ # Store variables in self.vars using label
264+ self .vars [f"zeta_{ label } " ] = zeta
265+ self .vars [f"w_{ label } " ] = w
266+
267+ # Add CVaR constraint
268+ constraints += [
269+ zeta + (1 / ((1 - alpha ) * len (voxel_idx ))) * cp .sum (w ) <= limit ,
270+ w >= dose_1d_list - zeta ,
271+ w >= 0
272+ ]
273+
198274 def add_max (self , struct : str , dose_gy : float ):
199275 """
200276 Add max constraints to the problem
0 commit comments