Skip to content

Commit

Permalink
Potential fix for infeasible models.
Browse files Browse the repository at this point in the history
  • Loading branch information
sukritkalra committed Oct 31, 2023
1 parent 5ce7bab commit 146bf51
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 33 deletions.
20 changes: 19 additions & 1 deletion schedulers/tetrisched/src/Expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1179,6 +1179,8 @@ ParseResultPtr MaxExpression::parse(SolverModelPtr solverModel,
// Parse each of the children and constrain the MaxExpression's start time,
// end time and utility as a function of the children's start time, end time
// and utility.
Time maxEndTimeOfChildren = 0;
bool anyChildrenWithUtilities = false;
for (int i = 0; i < numChildren; i++) {
auto childParsedResult = children[i]->parse(
solverModel, availablePartitions, capacityConstraints, currentTime);
Expand All @@ -1188,6 +1190,7 @@ ParseResultPtr MaxExpression::parse(SolverModelPtr solverModel,
" is not an Expression with utility. Skipping.");
continue;
}
anyChildrenWithUtilities = true;

// Check that the MaxExpression's childrens were specified correctly.
if (!childParsedResult->startTime ||
Expand Down Expand Up @@ -1225,14 +1228,29 @@ ParseResultPtr MaxExpression::parse(SolverModelPtr solverModel,
maxStartTimeConstraint->addTerm(childStartTime, childIndicator);

// Add the end time of the child to the MaxExpression's end time.
if (childEndTime > maxEndTimeOfChildren) {
maxEndTimeOfChildren = childEndTime;
}
maxEndTimeConstraint->addTerm(childEndTime, childIndicator);

// Add the utility of the child to the MaxExpression's utility.
(*maxObjectiveFunction) += (*childUtility);
}

if (!anyChildrenWithUtilities) {
throw tetrisched::exceptions::ExpressionConstructionException(
name + " must have at least one child with utility.");
}

// Constrain the MaxExpression's start time to be less than or equal to the
// start time of the chosen child.
// start time of the chosen child. However, if the MaxExpression is not placed
// i.e., the sum of its childrens indicator is 0, then we have to let the
// start time be free. We do this by adding the maximum end time of the
// children as a constant that is activated if the children cannot be placed.
maxStartTimeConstraint->addTerm(maxEndTimeOfChildren);
maxStartTimeConstraint->addTerm(
-1 * static_cast<TETRISCHED_ILP_TYPE>(maxEndTimeOfChildren),
maxIndicator);
maxStartTimeConstraint->addTerm(-1, maxStartTime);

// Constrain the MaxExpression's end time to be greater than or equal to the
Expand Down
100 changes: 68 additions & 32 deletions schedulers/tetrisched_scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,26 @@ def schedule(
objective_strl.addChild(task_graph_strl)

# Register the STRL expression with the scheduler and solve it.
self._scheduler.registerSTRL(objective_strl, partitions, sim_time.time)
solver_start_time = time.time()
self._scheduler.schedule(sim_time.time)
solver_end_time = time.time()
solver_time = EventTime(
int((solver_end_time - solver_start_time) * 1e6), EventTime.Unit.US
)
try:
self._scheduler.registerSTRL(objective_strl, partitions, sim_time.time)
solver_start_time = time.time()
self._scheduler.schedule(sim_time.time)
solver_end_time = time.time()
solver_time = EventTime(
int((solver_end_time - solver_start_time) * 1e6), EventTime.Unit.US
)
except RuntimeError as e:
self._logger.error(
f'[{sim_time.time}] Received error with description: "{e}" '
f"while invoking the STRL-based Scheduler. Dumping the model to "
f"tetrisched_error_{sim_time.time}.lp."
)
self._scheduler.exportLastSolverModel(
f"tetrisched_error_{sim_time.time}.lp"
)
raise e

# If requested, log the model to a file.
if self._log_to_file or sim_time.time in self._log_times:
self._scheduler.exportLastSolverModel(f"tetrisched_{sim_time.time}.lp")
self._logger.debug(
Expand Down Expand Up @@ -315,17 +328,6 @@ def construct_task_strl(
"TetrischedScheduler currently only supports Slot resources."
)

# Construct the STRL MAX expression for this Task.
# This enforces the choice of only one placement for this Task.
self._logger.debug(
f"[{current_time.time}] Constructing a STRL expression tree for "
f"{task.name} (runtime={execution_strategy.runtime}, "
f"deadline={task.deadline}) with name: {task.unique_name}_placement."
)
chooseOneFromSet = tetrisched.strl.MaxExpression(
f"{task.unique_name}_placement"
)

# Construct the STRL ChooseExpressions for this Task.
# This expression represents a particular placement choice for this Task.
num_slots_required = execution_strategy.resources.get_total_quantity(
Expand All @@ -335,27 +337,54 @@ def construct_task_strl(
time_discretizations = self._get_time_discretizations_until(
current_time, task.deadline - execution_strategy.runtime
)

task_choose_expressions = []
for placement_time in time_discretizations:
if placement_time < current_time and task.state != TaskState.RUNNING:
# If the placement time is in the past, then we cannot place the task
# unless it is already running.
continue

# Construct a ChooseExpression for placement at this time.
# TODO (Sukrit): We just assume for now that all Slots are the same and
# thus the task can be placed on any Slot. This is not true in general.
chooseAtTime = tetrisched.strl.ChooseExpression(
task.unique_name,
partitions,
num_slots_required,
placement_time.time,
execution_strategy.runtime.to(EventTime.Unit.US).time,
task_choose_expressions.append(
tetrisched.strl.ChooseExpression(
task.unique_name,
partitions,
num_slots_required,
placement_time.time,
execution_strategy.runtime.to(EventTime.Unit.US).time,
)
)

# Register this expression with the MAX expression.
chooseOneFromSet.addChild(chooseAtTime)
if len(task_choose_expressions) == 0:
self._logger.warn(
f"[{current_time.time}] No ChooseExpressions were generated for "
f"{task.unique_name} with deadline {task.deadline}."
)
return None

self._logger.debug(
f"[{current_time.time}] Generated {len(time_discretizations)} "
f"ChooseExpressions for {task.unique_name} from {time_discretizations[0]} "
f"to {time_discretizations[-1]} for {num_slots_required} slots for "
f"{execution_strategy.runtime}."
f"[{current_time.time}] Generated {len(task_choose_expressions)} "
f"ChooseExpressions for {task.unique_name} for times "
f"{[str(t) for t in time_discretizations]} for {num_slots_required} slots "
f"for {execution_strategy.runtime}."
)

# Construct the STRL MAX expression for this Task.
# This enforces the choice of only one placement for this Task.
self._logger.debug(
f"[{current_time.time}] Constructing a STRL expression tree for "
f"{task.name} (runtime={execution_strategy.runtime}, "
f"deadline={task.deadline}) with name: {task.unique_name}_placement."
)
chooseOneFromSet = tetrisched.strl.MaxExpression(
f"{task.unique_name}_placement"
)
for choose_expression in task_choose_expressions:
chooseOneFromSet.addChild(choose_expression)

return chooseOneFromSet

def _construct_task_graph_strl(
Expand Down Expand Up @@ -397,7 +426,6 @@ def _construct_task_graph_strl(
f"graph {task_graph.name} rooted at {task.unique_name}."
)
task_expression = self.construct_task_strl(current_time, task, partitions)
task_strls[task.id] = task_expression
else:
# If this Task is not in the set of Tasks that we are required to schedule,
# then we just return a None expression.
Expand All @@ -421,8 +449,9 @@ def _construct_task_graph_strl(
if child_expression:
child_expressions.append(child_expression)

# If there are no children, return the expression for this Task.
# If there are no children, cache and return the expression for this Task.
if len(child_expressions) == 0:
task_strls[task.id] = task_expression
return task_expression

# Construct the subtree for the children of this Task.
Expand Down Expand Up @@ -462,6 +491,8 @@ def _construct_task_graph_strl(
else:
task_graph_expression = child_expression

# Cache and return the expression for this Task.
task_strls[task.id] = task_graph_expression
return task_graph_expression

def construct_task_graph_strl(
Expand Down Expand Up @@ -516,6 +547,11 @@ def construct_task_graph_strl(
return root_task_strls[0]
else:
# Construct a MinExpression to order the roots of the TaskGraph.
self._logger.debug(
f"[{current_time.time}] Collecting {len(root_task_strls)} STRLs "
f"for {task_graph.name} into a MinExpression "
f"{task_graph.name}_min_expression."
)
min_expression_task_graph = tetrisched.strl.MinExpression(
f"{task_graph.name}_min_expression"
)
Expand Down

0 comments on commit 146bf51

Please sign in to comment.