Skip to content

Commit

Permalink
Implement methods for retrieving solution value.
Browse files Browse the repository at this point in the history
  • Loading branch information
sukritkalra committed Sep 27, 2023
1 parent e41d842 commit 319fef1
Show file tree
Hide file tree
Showing 6 changed files with 242 additions and 31 deletions.
90 changes: 77 additions & 13 deletions schedulers/tetrisched/include/tetrisched/Expression.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,32 @@ enum ParseResultType {
/// the relevant start and finish times.
EXPRESSION_UTILITY = 2,
};
using ParseResultType = enum ParseResultType;
using SolutionResultType = enum ParseResultType;

template <typename X>
class XOrVariableT {
private:
std::variant<std::monostate, X, VariablePtr> value;

public:
/// Constructors and operators.
XOrVariableT() = default;
XOrVariableT(const X& newValue);
XOrVariableT(const VariablePtr& newValue);
XOrVariableT(const XOrVariableT& newValue) = default;
XOrVariableT(XOrVariableT&& newValue) = default;
XOrVariableT& operator=(const X& newValue);
XOrVariableT& operator=(const VariablePtr& newValue);

/// Resolves the value inside this class.
X resolve() const;
};

/// A `ParseResult` class represents the result of parsing an expression.
struct ParseResult {
using TimeOrVariableT = std::variant<Time, VariablePtr>;
using IndicatorT = std::variant<uint32_t, VariablePtr>;
using TimeOrVariableT = XOrVariableT<Time>;
using IndicatorT = XOrVariableT<uint32_t>;
/// The type of the result.
ParseResultType type;
/// The start time associated with the parsed result.
Expand All @@ -56,6 +77,23 @@ struct ParseResult {
/// The utility associated with the parsed result.
std::optional<ObjectiveFunctionPtr> utility;
};
using ParseResultPtr = std::shared_ptr<ParseResult>;

/// A `SolutionResult` class represents the solution attributed to an
/// expression.
struct SolutionResult {
/// The type of the result.
SolutionResultType type;
/// The start time associated with the result.
std::optional<Time> startTime;
/// The end time associated with the result.
std::optional<Time> endTime;
/// The value of the indicator associated with the result.
std::optional<uint32_t> indicator;
/// The utility associated with the result.
std::optional<TETRISCHED_ILP_TYPE> utility;
};
using SolutionResultPtr = std::shared_ptr<SolutionResult>;

struct PartitionTimePairHasher {
size_t operator()(const std::pair<uint32_t, Time>& pair) const {
Expand Down Expand Up @@ -92,14 +130,38 @@ class CapacityConstraintMap {
Time granularity = 1);
};

/// An Abstract Base Class for all expressions in the STRL language.
/// A Base Class for all expressions in the STRL language.
class Expression {
protected:
/// The parsed result from the Expression.
/// Used for retrieving the solution from the solver.
ParseResultPtr parsedResult;

public:
/// Default construct the base class.
Expression() = default;

/// Adds a child to this epxression.
/// May throw tetrisched::excpetions::ExpressionConstructionException
/// if an incorrect number of children are registered.
virtual void addChild(ExpressionPtr child) = 0;
virtual ParseResult parse(SolverModelPtr solverModel,
Partitions availablePartitions,
CapacityConstraintMap& capacityConstraints,
Time currentTime) = 0;

/// Parses the expression into a set of variables and constraints for the
/// Solver. Returns a ParseResult that contains the utility of the expression,
/// an indicator specifying if the expression was satisfied and variables that
/// provide a start and end time bound on this Expression.
virtual ParseResultPtr parse(SolverModelPtr solverModel,
Partitions availablePartitions,
CapacityConstraintMap& capacityConstraints,
Time currentTime) = 0;

/// Solves the subtree rooted at this Expression and returns the solution.
/// It assumes that the SolverModelPtr has been populated with values for
/// unknown variables and throws a
/// tetrisched::exceptions::ExpressionSolutionException if the SolverModelPtr
/// is not populated. This method returns the actual values for the variables
/// specified in the ParseResult.
SolutionResultPtr solve(SolverModelPtr solverModel);
};

/// A `ChooseExpression` represents a choice of a required number of machines
Expand All @@ -126,9 +188,10 @@ class ChooseExpression : public Expression {
ChooseExpression(TaskPtr associatedTask, Partitions resourcePartitions,
uint32_t numRequiredMachines, Time startTime, Time duration);
void addChild(ExpressionPtr child) override;
ParseResult parse(SolverModelPtr solverModel, Partitions availablePartitions,
CapacityConstraintMap& capacityConstraints,
Time currentTime) override;
ParseResultPtr parse(SolverModelPtr solverModel,
Partitions availablePartitions,
CapacityConstraintMap& capacityConstraints,
Time currentTime) override;
};

/// An `ObjectiveExpression` collates the objectives from its children and
Expand All @@ -143,9 +206,10 @@ class ObjectiveExpression : public Expression {
public:
ObjectiveExpression(ObjectiveType objectiveType);
void addChild(ExpressionPtr child) override;
ParseResult parse(SolverModelPtr solverModel, Partitions availablePartitions,
CapacityConstraintMap& capacityConstraints,
Time currentTime) override;
ParseResultPtr parse(SolverModelPtr solverModel,
Partitions availablePartitions,
CapacityConstraintMap& capacityConstraints,
Time currentTime) override;
};

} // namespace tetrisched
Expand Down
7 changes: 7 additions & 0 deletions schedulers/tetrisched/include/tetrisched/SolverModel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ class ObjectiveFunctionT {
/// Merges this utility with another utility.
void merge(const ObjectiveFunctionT<T>& other);

/// Retrieves the value of the utility of this ObjectiveFunction.
T getValue() const;

/// Annotate friend classes for Solvers so that they have access to internals.
friend tetrisched::CPLEXSolver;
friend tetrisched::GurobiSolver;
Expand Down Expand Up @@ -248,6 +251,10 @@ class SolverModelT {
/// Retrieve the number of constraints in this SolverModel.
size_t numConstraints() const;

/// Retrieves the value of the objective function of this SolverModel.
/// Throws an error if the model was not solved first.
T getObjectiveValue() const;

/// All the Solver implementations should be a friend of the SolverModel.
/// This allows Solver implementations to construct the model to pass
/// back to the user.
Expand Down
10 changes: 10 additions & 0 deletions schedulers/tetrisched/include/tetrisched/Types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ class ExpressionConstructionException : public std::exception {
const char* what() const noexcept override { return message.c_str(); }
};

/// An exception that is thrown when solving of an Expression fails.
class ExpressionSolutionException : public std::exception {
private:
std::string message;

public:
ExpressionSolutionException(std::string message) : message(message) {}
const char* what() const noexcept override { return message.c_str(); }
};

/// An exception that is thrown when the Solver is used incorrectly.
class SolverException : public std::exception {
private:
Expand Down
133 changes: 116 additions & 17 deletions schedulers/tetrisched/src/Expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,46 @@

namespace tetrisched {

/* Method definitions for XOrVariableT. */
template <typename X>
XOrVariableT<X>::XOrVariableT(const X& value) : value(value) {}

template <typename X>
XOrVariableT<X>::XOrVariableT(const VariablePtr& variable) : value(variable) {}

template <typename X>
XOrVariableT<X>& XOrVariableT<X>::operator=(const X& newValue) {
value = newValue;
return *this;
}

template <typename X>
XOrVariableT<X>& XOrVariableT<X>::operator=(const VariablePtr& newValue) {
value = newValue;
return *this;
}

template <typename X>
X XOrVariableT<X>::resolve() const {
// If the value is the provided type, then return it.
if (std::holds_alternative<X>(value)) {
return std::get<X>(value);
} else if (std::holds_alternative<VariablePtr>(value)) {
// If the value is a VariablePtr, then return the value of the variable.
auto variable = std::get<VariablePtr>(value);
auto variableValue = variable->getValue();
if (!variableValue) {
throw tetrisched::exceptions::ExpressionSolutionException(
"No solution was found for the variable name: " +
variable->getName());
}
return variableValue.value();
} else {
throw tetrisched::exceptions::ExpressionSolutionException(
"XOrVariableT was resolved with an invalid type.");
}
}

void CapacityConstraintMap::registerUsageAtTime(const Partition& partition,
Time time,
VariablePtr variable) {
Expand Down Expand Up @@ -61,6 +101,59 @@ void CapacityConstraintMap::registerUsageForDuration(const Partition& partition,
}
}

SolutionResultPtr Expression::solve(SolverModelPtr solverModel) {
// Check that the Expression was parsed before.
if (!parsedResult) {
throw tetrisched::exceptions::ExpressionSolutionException(
"Expression was not parsed before solve.");
}

// Construct the SolutionResult.
SolutionResultPtr solutionResult = std::make_shared<SolutionResult>();
switch (parsedResult->type) {
case ParseResultType::EXPRESSION_PRUNE:
solutionResult->type = SolutionResultType::EXPRESSION_PRUNE;
return solutionResult;
case ParseResultType::EXPRESSION_NO_UTILITY:
solutionResult->type = SolutionResultType::EXPRESSION_NO_UTILITY;
return solutionResult;
case ParseResultType::EXPRESSION_UTILITY:
solutionResult->type = SolutionResultType::EXPRESSION_UTILITY;
break;
default:
throw tetrisched::exceptions::ExpressionSolutionException(
"Expression was parsed with an invalid ParseResultType: " +
std::to_string(static_cast<int>(parsedResult->type)));
}

// Retrieve the start, end times and the indicator from the SolverModel.
if (!parsedResult->startTime) {
throw tetrisched::exceptions::ExpressionSolutionException(
"Expression with a utility was parsed without a start time.");
}
solutionResult->startTime = parsedResult->startTime->resolve();

if (!parsedResult->endTime) {
throw tetrisched::exceptions::ExpressionSolutionException(
"Expression with a utility was parsed without an end time.");
}
solutionResult->endTime = parsedResult->endTime->resolve();

if (!parsedResult->indicator) {
throw tetrisched::exceptions::ExpressionSolutionException(
"Expression with a utility was parsed without an indicator.");
}
solutionResult->indicator = parsedResult->indicator->resolve();

if (!parsedResult->utility) {
throw tetrisched::exceptions::ExpressionSolutionException(
"Expression with a utility was parsed without a utility.");
}
solutionResult->utility = parsedResult->utility.value()->getValue();

return solutionResult;
}

ChooseExpression::ChooseExpression(TaskPtr associatedTask,
Partitions resourcePartitions,
uint32_t numRequiredMachines, Time startTime,
Expand All @@ -77,17 +170,21 @@ void ChooseExpression::addChild(ExpressionPtr child) {
"ChooseExpression cannot have a child.");
}

ParseResult ChooseExpression::parse(SolverModelPtr solverModel,
Partitions availablePartitions,
CapacityConstraintMap& capacityConstraints,
Time currentTime) {
ParseResultPtr ChooseExpression::parse(
SolverModelPtr solverModel, Partitions availablePartitions,
CapacityConstraintMap& capacityConstraints, Time currentTime) {
// Create and save the ParseResult.
ParseResultPtr parseResult = std::make_shared<ParseResult>();
parsedResult = parseResult;

if (currentTime > startTime) {
TETRISCHED_DEBUG("Pruning Choose expression for "
<< associatedTask->getTaskName()
<< " to be placed starting at time " << startTime
<< " and ending at " << endTime
<< " because it is in the past.");
return ParseResult{.type = ParseResultType::EXPRESSION_PRUNE};
parseResult->type = ParseResultType::EXPRESSION_PRUNE;
return parseResult;
}
TETRISCHED_DEBUG("Parsing Choose expression for "
<< associatedTask->getTaskName()
Expand All @@ -105,7 +202,8 @@ ParseResult ChooseExpression::parse(SolverModelPtr solverModel,
if (schedulablePartitions.size() == 0) {
// There are no schedulable partitions, this expression cannot be satisfied.
// and should provide 0 utility.
return ParseResult{.type = ParseResultType::EXPRESSION_NO_UTILITY};
parseResult->type = ParseResultType::EXPRESSION_NO_UTILITY;
return parseResult;
}

// This Choose expression needs to be passed to the Solver.
Expand Down Expand Up @@ -150,13 +248,12 @@ ParseResult ChooseExpression::parse(SolverModelPtr solverModel,
utility->addTerm(1, isSatisfiedVar);

// Construct the return value.
return ParseResult{
.type = ParseResultType::EXPRESSION_UTILITY,
.startTime = startTime,
.endTime = endTime,
.indicator = isSatisfiedVar,
.utility = std::move(utility),
};
parseResult->type = ParseResultType::EXPRESSION_UTILITY;
parseResult->startTime = startTime;
parseResult->endTime = endTime;
parseResult->indicator = isSatisfiedVar;
parseResult->utility = std::move(utility);
return parseResult;
}

ObjectiveExpression::ObjectiveExpression(ObjectiveType objectiveType)
Expand All @@ -166,7 +263,7 @@ void ObjectiveExpression::addChild(ExpressionPtr child) {
children.push_back(std::move(child));
}

ParseResult ObjectiveExpression::parse(
ParseResultPtr ObjectiveExpression::parse(
SolverModelPtr solverModel, Partitions availablePartitions,
CapacityConstraintMap& capacityConstraints, Time currentTime) {
// Construct the overall utility of this expression.
Expand All @@ -176,16 +273,18 @@ ParseResult ObjectiveExpression::parse(
for (auto& child : children) {
auto result = child->parse(solverModel, availablePartitions,
capacityConstraints, currentTime);
if (result.type == ParseResultType::EXPRESSION_UTILITY) {
utility->merge(*result.utility.value());
if (result->type == ParseResultType::EXPRESSION_UTILITY) {
utility->merge(*(result->utility.value()));
}
}

// Add the utility to the SolverModel.
solverModel->setObjectiveFunction(std::move(utility));

return ParseResult{.type = ParseResultType::EXPRESSION_NO_UTILITY};
return std::make_shared<ParseResult>(
ParseResult{.type = ParseResultType::EXPRESSION_NO_UTILITY});
}

} // namespace tetrisched

// // standard C/C++ libraries
Expand Down
Loading

0 comments on commit 319fef1

Please sign in to comment.