diff --git a/schedulers/tetrisched/src/Expression.cpp b/schedulers/tetrisched/src/Expression.cpp index bf898c1c..fccbe16a 100644 --- a/schedulers/tetrisched/src/Expression.cpp +++ b/schedulers/tetrisched/src/Expression.cpp @@ -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); @@ -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 || @@ -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(maxEndTimeOfChildren), + maxIndicator); maxStartTimeConstraint->addTerm(-1, maxStartTime); // Constrain the MaxExpression's end time to be greater than or equal to the diff --git a/schedulers/tetrisched_scheduler.py b/schedulers/tetrisched_scheduler.py index a904ed2a..f0c5f844 100644 --- a/schedulers/tetrisched_scheduler.py +++ b/schedulers/tetrisched_scheduler.py @@ -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( @@ -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( @@ -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( @@ -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. @@ -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. @@ -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( @@ -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" )