Skip to content

Commit 96c8ffb

Browse files
committed
New script to generate task-asset matrices (and wordy help)
- updated all of Ryan's old 'wordy' sections to print more clearly what the constraints are - the task_asset_generator script is not meant for a permanent solution; it's meant to provide some kind of example of what we will need to do to generate task-asset matrix information based on certain task and asset inputs TaskAssetGroupGenerator - A new class to help organize task and asset input info in creating the proper inputs to the scheduler WORKFLOW 0) Start with a list of tasks and assets in a certain format - The user can input a certain 'strategy' (like a LineDesign configuration) to specify what tasks are needed - Meaning, the parameterization of tasks should be defined by the user (or at least the strategy of how the tasks should be parameterized) 1) 'Validate' the task and asset definitions - makes sure the input format is how it should be and all entries are standardized 2) Generate feasible asset groups - make 'groups' of each individual asset (like normal), and then build combinations of each asset (up to a maximum group size) and evaluate whether that group is operationally feasible - operationally feasible is based off of weather, capabilities, overlapping capabilities, total cost, etc. - if feasible, then we can calculate their group properties (to match the dictionary format of asset groups) - - sum costs, minimum weather, etc. - (!! we can change these later !!) 3) Match tasks to asset groups - determine if asset group has the capabilities to perform each task (and works with its weather rating) - if they're compatible, calculate the cost and duration (!! can also be updated later !!) - creates a list of feasible groups per task that get stored in 'task_asset_matches' 4) Build task-asset matrix - builds the matrix off of 'task_asset_matches' with -1's as defaults - the 'sparseness' is a pre-processing step in evaluating the feasibility of each task-asset combo
1 parent 6356ba4 commit 96c8ffb

File tree

2 files changed

+648
-2
lines changed

2 files changed

+648
-2
lines changed

famodel/irma/scheduler.py

Lines changed: 162 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import numpy as np
2525
import os
2626

27-
wordy = 1 # level of verbosity for print statements
27+
wordy = 2 # level of verbosity for print statements
2828

2929
class Scheduler:
3030

@@ -387,13 +387,21 @@ def set_up_optimizer(self, goal : str = "cost"):
387387
self.b_eq_1 = np.zeros(self.A_eq_1.shape[0], dtype=int)
388388

389389
if wordy > 1:
390+
'''
390391
print("A_eq_1^T:")
391392
for i in range(self.Xta_start,self.Xta_end):
392393
pstring = str(self.X_indices[i])
393394
for column in self.A_eq_1.transpose()[i]:
394395
pstring += f"{ column:5}"
395396
print(pstring)
396397
print("b_eq_1: ", self.b_eq_1)
398+
'''
399+
print("Constraint 1 details:")
400+
for i, row in enumerate(self.A_eq_1):
401+
xta_idx = np.where(row == 1)[0][0] - self.Xta_start
402+
t = xta_idx // self.A
403+
a = xta_idx % self.A
404+
print(f" Invalid pairing: Xta[{t},{a}] = 0")
397405

398406
A_eq_list.append(self.A_eq_1)
399407
b_eq_list.append(self.b_eq_1)
@@ -582,6 +590,52 @@ def set_up_optimizer(self, goal : str = "cost"):
582590
A_ub_list.append(self.A_ub_2)
583591
b_ub_list.append(self.b_ub_2)
584592

593+
if wordy > 1:
594+
print("Constraint 2 details:")
595+
if hasattr(self, 'A_ub_2'):
596+
for i, row in enumerate(self.A_ub_2):
597+
# Find the variables that are non-zero in this constraint
598+
xta_indices = np.where(row[self.Xta_start:self.Xta_start + self.T * self.A] != 0)[0]
599+
xts_indices = np.where(row[self.Xts_start:self.Xts_start + self.T * self.S] != 0)[0]
600+
601+
if len(xta_indices) > 0 or len(xts_indices) > 0:
602+
constraint_parts = []
603+
604+
# Add Xta terms
605+
for xta_idx in xta_indices:
606+
coeff = row[self.Xta_start + xta_idx]
607+
t = xta_idx // self.A
608+
a = xta_idx % self.A
609+
if coeff == 1:
610+
constraint_parts.append(f"Xta[{t},{a}]")
611+
elif coeff == -1:
612+
constraint_parts.append(f"-Xta[{t},{a}]")
613+
else:
614+
constraint_parts.append(f"{coeff}*Xta[{t},{a}]")
615+
616+
# Add Xts terms
617+
for xts_idx in xts_indices:
618+
coeff = row[self.Xts_start + xts_idx]
619+
t = xts_idx // self.S
620+
s = xts_idx % self.S
621+
if coeff == 1:
622+
constraint_parts.append(f"Xts[{t},{s}]")
623+
elif coeff == -1:
624+
constraint_parts.append(f"-Xts[{t},{s}]")
625+
else:
626+
constraint_parts.append(f"{coeff}*Xts[{t},{s}]")
627+
628+
if constraint_parts:
629+
constraint_eq = " + ".join(constraint_parts).replace("+ -", "- ")
630+
bound = self.b_ub_2[i]
631+
print(f" Dependency constraint: {constraint_eq}{bound}")
632+
633+
if i >= 4: # Limit output to avoid too much detail
634+
remaining = len(self.A_ub_2) - i - 1
635+
if remaining > 0:
636+
print(f" ... and {remaining} more dependency constraints")
637+
break
638+
585639
if wordy > 0:
586640
print("Constraint 2 built.")
587641

@@ -605,6 +659,7 @@ def set_up_optimizer(self, goal : str = "cost"):
605659
self.A_eq_3[t, (self.Xta_start + t * self.A):(self.Xta_start + t * self.A + self.A)] = 1 # Set the coefficients for the Xta variables to 1 for each task t
606660

607661
if wordy > 1:
662+
'''
608663
print("A_eq_3^T:")
609664
print(" T1 T2") # Header for 2 tasks
610665
for i in range(self.Xta_start,self.Xta_end):
@@ -613,6 +668,11 @@ def set_up_optimizer(self, goal : str = "cost"):
613668
pstring += f"{ column:5}"
614669
print(pstring)
615670
print("b_eq_3: ", self.b_eq_3)
671+
'''
672+
print("Constraint 3 details:")
673+
for t in range(self.T):
674+
asset_vars = [f"Xta[{t},{a}]" for a in range(self.A)]
675+
print(f" Task {t} assignment: {' + '.join(asset_vars)} = 1")
616676

617677
A_eq_list.append(self.A_eq_3)
618678
b_eq_list.append(self.b_eq_3)
@@ -643,6 +703,9 @@ def set_up_optimizer(self, goal : str = "cost"):
643703

644704
rows_4 = []
645705
bounds_4 = []
706+
707+
if wordy > 1:
708+
print('Constraint 4 details:')
646709

647710
# For each individual asset, create constraints to prevent conflicts
648711
for individual_asset_idx in range(len(self.assets)):
@@ -689,7 +752,7 @@ def set_up_optimizer(self, goal : str = "cost"):
689752
bounds_4.append(3) # Sum ≤ 3 prevents all 4 from being 1 simultaneously
690753

691754
if wordy > 1:
692-
print(f" Conflict constraint for {individual_asset_name} in period {period_idx}:")
755+
#print(f" Conflict constraint for {individual_asset_name} in period {period_idx}:")
693756
print(f" Xta[{task1},{ag1}] + Xta[{task2},{ag2}] + Xtp[{task1},{period_idx}] + Xtp[{task2},{period_idx}] ≤ 3")
694757

695758
# Create constraint matrix
@@ -701,6 +764,7 @@ def set_up_optimizer(self, goal : str = "cost"):
701764
self.A_ub_4 = np.zeros((0, num_variables), dtype=int)
702765
self.b_ub_4 = np.array([], dtype=int)
703766

767+
'''
704768
if wordy > 1:
705769
print("A_ub_4^T:")
706770
print(" P1 P2 P3 P4 P5") # Header for 5 periods
@@ -710,6 +774,7 @@ def set_up_optimizer(self, goal : str = "cost"):
710774
pstring += f"{ column:5}"
711775
print(pstring)
712776
print("b_ub_4: ", self.b_ub_4)
777+
'''
713778

714779
A_ub_list.append(self.A_ub_4)
715780
b_ub_list.append(self.b_ub_4)
@@ -740,6 +805,7 @@ def set_up_optimizer(self, goal : str = "cost"):
740805
self.b_ub_10 = np.ones(self.A_ub_10.shape[0], dtype=int) # Each infeasible combination: Xta + Xts <= 1
741806

742807
if wordy > 1:
808+
'''
743809
print("A_ub_10^T:")
744810
print(" T1A1 T1A2 T2A1") # Header for 3 task-asset pairs example with T2A2 invalid
745811
for i in range(self.Xta_start,self.Xta_end):
@@ -753,6 +819,22 @@ def set_up_optimizer(self, goal : str = "cost"):
753819
pstring += f"{ column:5}"
754820
print(pstring)
755821
print("b_ub_10: ", self.b_ub_10)
822+
'''
823+
print("Constraint 10 details:")
824+
for i, row in enumerate(self.A_ub_10):
825+
# Find the Xta and Xts variables that are 1 in this row
826+
xta_indices = np.where(row[self.Xta_start:self.Xta_start + self.T * self.A] == 1)[0]
827+
xts_indices = np.where(row[self.Xts_start:self.Xts_start + self.T * self.S] == 1)[0]
828+
829+
if len(xta_indices) > 0 and len(xts_indices) > 0:
830+
xta_idx = xta_indices[0]
831+
xts_idx = xts_indices[0]
832+
t_ta = xta_idx // self.A
833+
a = xta_idx % self.A
834+
t_ts = xts_idx // self.S
835+
s = xts_idx % self.S
836+
duration = self.task_asset_matrix[t_ta, a, 1]
837+
print(f" Task {t_ta} exceeds period limit: Xta[{t_ta},{a}] + Xts[{t_ts},{s}] ≤ 1 (start {s} + duration {duration} > {self.P})")
756838

757839
A_ub_list.append(self.A_ub_10)
758840
b_ub_list.append(self.b_ub_10)
@@ -938,6 +1020,7 @@ def set_up_optimizer(self, goal : str = "cost"):
9381020
b_ub_list.append(self.b_ub_14b)
9391021

9401022
if wordy > 1:
1023+
'''
9411024
print("A_lb_14^T:")
9421025
print(" T1A1S1 T1A2S1 ...") # Header for 3 task-asset pairs example with T2A2 invalid
9431026
for i in range(self.Xta_start,self.Xta_end):
@@ -956,6 +1039,39 @@ def set_up_optimizer(self, goal : str = "cost"):
9561039
pstring += f"{ column:5}"
9571040
print(pstring)
9581041
print("b_lb_14: ", self.b_ub_14)
1042+
'''
1043+
print("Constraint 14a details:")
1044+
if hasattr(self, 'A_ub_14a'):
1045+
for i, row in enumerate(self.A_ub_14a):
1046+
xts_indices = np.where(row[self.Xts_start:self.Xts_start + self.T * self.S] == 1)[0]
1047+
xtp_indices = np.where(row[self.Xtp_start:self.Xtp_start + self.T * self.P] == -1)[0]
1048+
if len(xts_indices) > 0 and len(xtp_indices) > 0:
1049+
xts_idx = xts_indices[0]
1050+
xtp_idx = xtp_indices[0]
1051+
t_ts = xts_idx // self.S
1052+
s = xts_idx % self.S
1053+
t_tp = xtp_idx // self.P
1054+
p = xtp_idx % self.P
1055+
print(f" Start-period mapping: Xts[{t_ts},{s}] - Xtp[{t_tp},{p}] ≤ 0")
1056+
1057+
print("Constraint 14b details:")
1058+
if hasattr(self, 'A_ub_14b'):
1059+
for i, row in enumerate(self.A_ub_14b):
1060+
xta_indices = np.where(row[self.Xta_start:self.Xta_start + self.T * self.A] == 1)[0]
1061+
xts_indices = np.where(row[self.Xts_start:self.Xts_start + self.T * self.S] == 1)[0]
1062+
xtp_indices = np.where(row[self.Xtp_start:self.Xtp_start + self.T * self.P] == -1)[0]
1063+
1064+
if len(xta_indices) > 0 and len(xts_indices) > 0 and len(xtp_indices) > 0:
1065+
xta_idx = xta_indices[0]
1066+
xts_idx = xts_indices[0]
1067+
xtp_idx = xtp_indices[0]
1068+
t_ta = xta_idx // self.A
1069+
a = xta_idx % self.A
1070+
t_ts = xts_idx // self.S
1071+
s = xts_idx % self.S
1072+
t_tp = xtp_idx // self.P
1073+
p = xtp_idx % self.P
1074+
print(f" Duration enforcement: Xta[{t_ta},{a}] + Xts[{t_ts},{s}] - Xtp[{t_tp},{p}] ≤ 1")
9591075

9601076
if wordy > 0:
9611077
print("Constraint 14 built.")
@@ -979,13 +1095,19 @@ def set_up_optimizer(self, goal : str = "cost"):
9791095
self.A_eq_15[t, (self.Xts_start + t * self.S):(self.Xts_start + t * self.S + self.S)] = 1
9801096

9811097
if wordy > 1:
1098+
'''
9821099
print("A_eq_15^T:")
9831100
for i in range(self.Xts_start,self.Xts_end):
9841101
pstring = str(self.X_indices[i])
9851102
for column in self.A_eq_15.transpose()[i]:
9861103
pstring += f"{ column:5}"
9871104
print(pstring)
9881105
print("b_eq_15: ", self.b_eq_15)
1106+
'''
1107+
print("Constraint 15 details:")
1108+
for t in range(self.T):
1109+
start_vars = [f"Xts[{t},{s}]" for s in range(self.S)]
1110+
print(f" Task {t} start assignment: {' + '.join(start_vars)} = 1")
9891111

9901112
A_eq_list.append(self.A_eq_15)
9911113
b_eq_list.append(self.b_eq_15)
@@ -1025,6 +1147,18 @@ def set_up_optimizer(self, goal : str = "cost"):
10251147
A_eq_list.append(self.A_eq_16)
10261148
b_eq_list.append(self.b_eq_16)
10271149

1150+
if wordy > 1:
1151+
print("Constraint 16 details:")
1152+
for t in range(self.T):
1153+
period_vars = [f"Xtp[{t},{p}]" for p in range(self.P)]
1154+
asset_terms = []
1155+
for a in range(self.A):
1156+
duration = self.task_asset_matrix[t, a, 1]
1157+
if duration > 0:
1158+
asset_terms.append(f"{duration}*Xta[{t},{a}]")
1159+
if asset_terms:
1160+
print(f" Task {t} duration: {' + '.join(period_vars)} = {' + '.join(asset_terms)}")
1161+
10281162
if wordy > 0:
10291163
print("Constraint 16 built.")
10301164

@@ -1080,6 +1214,32 @@ def set_up_optimizer(self, goal : str = "cost"):
10801214
A_ub_list.append(self.A_ub_17)
10811215
b_ub_list.append(self.b_ub_17)
10821216

1217+
if wordy > 1:
1218+
print("Constraint 17 details:")
1219+
for i, row in enumerate(self.A_ub_17):
1220+
xta_indices = np.where(row[self.Xta_start:self.Xta_start + self.T * self.A] == 1)[0]
1221+
xtp_indices = np.where(row[self.Xtp_start:self.Xtp_start + self.T * self.P] == 1)[0]
1222+
1223+
if len(xta_indices) > 0 and len(xtp_indices) > 0:
1224+
xta_idx = xta_indices[0]
1225+
xtp_idx = xtp_indices[0]
1226+
t_ta = xta_idx // self.A
1227+
a = xta_idx % self.A
1228+
t_tp = xtp_idx // self.P
1229+
p = xtp_idx % self.P
1230+
1231+
# Get weather info
1232+
period_weather = self.weather[p] if p < len(self.weather) else 0
1233+
asset_max_weather = self.asset_groups[a].get('max_weather', float('inf'))
1234+
1235+
print(f" Weather constraint: Xta[{t_ta},{a}] + Xtp[{t_tp},{p}] ≤ 1 (weather {period_weather} > max {asset_max_weather})")
1236+
1237+
if i >= 4: # Limit output to avoid too much detail
1238+
remaining = len(rows_17) - i - 1
1239+
if remaining > 0:
1240+
print(f" ... and {remaining} more weather constraints")
1241+
break
1242+
10831243
if wordy > 0:
10841244
print(f"Constraint 17 built with {len(rows_17)} weather restrictions.")
10851245
else:

0 commit comments

Comments
 (0)