Skip to content

Commit 218224f

Browse files
committed
Add CVaR upper constraint helper in Optimization
1 parent fef433d commit 218224f

File tree

1 file changed

+76
-0
lines changed

1 file changed

+76
-0
lines changed

portpy/photon/optimization.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)