Skip to content

Commit

Permalink
Translate objective function and introduce ObjectiveExpression
Browse files Browse the repository at this point in the history
  • Loading branch information
sukritkalra committed Sep 25, 2023
1 parent 2c80cbe commit d0f1cc4
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 23 deletions.
4 changes: 4 additions & 0 deletions schedulers/tetrisched/include/tetrisched/CPLEXSolver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ class CPLEXSolver : public Solver {
/// Translates the Constraint into a CPLEX expression.
IloRange translateConstraint(const ConstraintPtr& constraint) const;

/// Translates the ObjectiveFunction into a CPLEX expression.
IloObjective translateObjectiveFunction(
const ObjectiveFunctionPtr& objectiveFunction) const;

public:
/// Create a new CPLEXSolver.
CPLEXSolver();
Expand Down
35 changes: 29 additions & 6 deletions schedulers/tetrisched/include/tetrisched/Expression.hpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#ifndef _TETRISCHED_EXPRESSION_HPP_
#define _TETRISCHED_EXPRESSION_HPP_

#include <functional>
#include <optional>
#include <unordered_map>
#include <variant>
#include <functional>

#include "tetrisched/Partition.hpp"
#include "tetrisched/SolverModel.hpp"
Expand Down Expand Up @@ -49,9 +49,12 @@ struct ParseResult {
std::optional<TimeOrVariableT> endTime;
/// The indicator associated with the parsed result.
/// This indicator denotes if the expression was satisfied or not.
/// The indicator can return an integer if it is trivially satisfied during parsing.
/// Note that the startTime and endTime are only valid if the indicator is 1.
/// The indicator can return an integer if it is trivially satisfied during
/// parsing. Note that the startTime and endTime are only valid if the
/// indicator is 1.
std::optional<IndicatorT> indicator;
/// The utility associated with the parsed result.
std::optional<ObjectiveFunctionPtr> utility;
};

struct PartitionTimePairHasher {
Expand Down Expand Up @@ -99,6 +102,9 @@ class Expression {
Time currentTime) = 0;
};

/// A `ChooseExpression` represents a choice of a required number of machines
/// from the set of resource partitions for the given duration starting at the
/// provided start_time.
class ChooseExpression : public Expression {
private:
/// The Task instance that this ChooseExpression is being inserted into
Expand All @@ -108,7 +114,7 @@ class ChooseExpression : public Expression {
/// choose resources from.
Partitions resourcePartitions;
/// The number of partitions that this ChooseExpression needs to choose.
uint32_t numRequiredPartitions;
uint32_t numRequiredMachines;
/// The start time of the choice represented by this Expression.
Time startTime;
/// The duration of the choice represented by this Expression.
Expand All @@ -118,13 +124,30 @@ class ChooseExpression : public Expression {

public:
ChooseExpression(TaskPtr associatedTask, Partitions resourcePartitions,
uint32_t numRequiredPartitions, Time startTime,
Time duration);
uint32_t numRequiredMachines, Time startTime, Time duration);
void addChild(ExpressionPtr child) override;
ParseResult parse(SolverModelPtr solverModel, Partitions availablePartitions,
CapacityConstraintMap& capacityConstraints,
Time currentTime) override;
};

/// An `ObjectiveExpression` collates the objectives from its children and
/// informs the SolverModel of the objective function.
class ObjectiveExpression : public Expression {
private:
/// The sense of this Expression.
ObjectiveType objectiveType;
/// The children of this Expression.
std::vector<ExpressionPtr> children;

public:
ObjectiveExpression(ObjectiveType objectiveType);
void addChild(ExpressionPtr child) override;
ParseResult parse(SolverModelPtr solverModel, Partitions availablePartitions,
CapacityConstraintMap& capacityConstraints,
Time currentTime) override;
};

} // namespace tetrisched
#endif // _TETRISCHED_EXPRESSION_HPP_

Expand Down
10 changes: 8 additions & 2 deletions schedulers/tetrisched/include/tetrisched/SolverModel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include <unordered_map>
#include <vector>

#include "tetrisched/Types.hpp"

Expand Down Expand Up @@ -182,6 +182,12 @@ class ObjectiveFunctionT {

/// Retrieve the number of terms in this ObjectiveFunction.
size_t size() const;

/// Merges this utility with another utility.
void merge(const ObjectiveFunctionT<T>& other);

/// Annotate friend classes for Solvers so that they have access to internals.
friend tetrisched::CPLEXSolver;
};

// Specialize the ObjectiveFunction class for Integer.
Expand All @@ -197,7 +203,7 @@ class SolverModelT {
/// The constraints in this model.
std::unordered_map<uint32_t, std::unique_ptr<ConstraintT<T>>> constraints;
/// The objective function in this model.
std::shared_ptr<ObjectiveFunctionT<T>> objectiveFunction;
std::unique_ptr<ObjectiveFunctionT<T>> objectiveFunction;

/// Generate a new solver model.
/// Construct a Solver to get an instance of the Model.
Expand Down
59 changes: 59 additions & 0 deletions schedulers/tetrisched/src/CPLEXSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,62 @@ IloRange CPLEXSolver::translateConstraint(
return rangeConstraint;
}

IloObjective CPLEXSolver::translateObjectiveFunction(
const ObjectiveFunctionPtr& objectiveFunction) const {
IloExpr objectiveExpr(cplexEnv);

// Construct all the terms.
for (auto& term : objectiveFunction->terms) {
if (term.second) {
// If the variable has not been translated, throw an error.
if (cplexVariables.find(term.second->getId()) == cplexVariables.end()) {
throw tetrisched::exceptions::SolverException(
"Variable " + term.second->getName() +
" not found in CPLEX model.");
}
// Call the relevant function to add the term to the constraint.
switch (term.second->variableType) {
case tetrisched::VariableType::VAR_INTEGER:
objectiveExpr +=
term.first *
std::get<IloIntVar>(cplexVariables.at(term.second->getId()));
break;
case tetrisched::VariableType::VAR_CONTINUOUS:
objectiveExpr +=
term.first *
std::get<IloNumVar>(cplexVariables.at(term.second->getId()));
break;
case tetrisched::VariableType::VAR_INDICATOR:
objectiveExpr +=
term.first *
std::get<IloBoolVar>(cplexVariables.at(term.second->getId()));
break;
default:
throw tetrisched::exceptions::SolverException(
"Unsupported variable type: " + term.second->variableType);
}
} else {
objectiveExpr += term.first;
}
}

// Construct the Sense of the Constraint.
IloObjective objectiveConstraint;
switch (objectiveFunction->objectiveType) {
case tetrisched::ObjectiveType::OBJ_MAXIMIZE:
objectiveConstraint = IloMaximize(cplexEnv, objectiveExpr);
break;
case tetrisched::ObjectiveType::OBJ_MINIMIZE:
objectiveConstraint = IloMinimize(cplexEnv, objectiveExpr);
break;
default:
throw tetrisched::exceptions::SolverException(
"Unsupported objective type: " + objectiveFunction->objectiveType);
}

return objectiveConstraint;
}

void CPLEXSolver::translateModel() {
if (!solverModel) {
throw tetrisched::exceptions::SolverException(
Expand All @@ -132,6 +188,9 @@ void CPLEXSolver::translateModel() {
cplexModel.add(translateConstraint(constraint.second));
}

// Translate the objective function.
cplexModel.add(translateObjectiveFunction(solverModel->objectiveFunction));

// Extract the model to the CPLEX instance.
cplexInstance.extract(cplexModel);
}
Expand Down
51 changes: 40 additions & 11 deletions schedulers/tetrisched/src/Expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,14 @@ void CapacityConstraintMap::registerUsageForDuration(const Partition& partition,

ChooseExpression::ChooseExpression(TaskPtr associatedTask,
Partitions resourcePartitions,
uint32_t numRequiredPartitions,
Time startTime, Time duration)
uint32_t numRequiredMachines, Time startTime,
Time duration)
: associatedTask(associatedTask),
resourcePartitions(resourcePartitions),
numRequiredPartitions(numRequiredPartitions),
numRequiredMachines(numRequiredMachines),
startTime(startTime),
duration(duration),
endTime(startTime + duration) {
if (numRequiredPartitions > resourcePartitions.size()) {
throw tetrisched::exceptions::ExpressionConstructionException(
"ChooseExpression requires more partitions than are available.");
}
}
endTime(startTime + duration) {}

void ChooseExpression::addChild(ExpressionPtr child) {
throw tetrisched::exceptions::ExpressionConstructionException(
Expand Down Expand Up @@ -134,7 +129,7 @@ ParseResult ChooseExpression::parse(SolverModelPtr solverModel,
std::to_string(startTime),
0,
std::min(static_cast<uint32_t>(partition->size()),
numRequiredPartitions));
numRequiredMachines));

// Add the variable to the demand constraint.
fulfillsDemandConstraint->addTerm(1, allocationVar);
Expand All @@ -146,17 +141,51 @@ ParseResult ChooseExpression::parse(SolverModelPtr solverModel,
}
// Ensure that if the Choose expression is satisfied, it fulfills the
// demand for this expression. Pass the constraint to the model.
fulfillsDemandConstraint->addTerm(-1 * numRequiredPartitions, isSatisfiedVar);
fulfillsDemandConstraint->addTerm(-1 * numRequiredMachines, isSatisfiedVar);
solverModel->addConstraint(std::move(fulfillsDemandConstraint));

// Construct the Utility function for this Choose expression.
auto utility =
std::make_unique<ObjectiveFunction>(ObjectiveType::OBJ_MAXIMIZE);
utility->addTerm(1, isSatisfiedVar);

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

ObjectiveExpression::ObjectiveExpression(ObjectiveType objectiveType)
: objectiveType(objectiveType) {}

void ObjectiveExpression::addChild(ExpressionPtr child) {
children.push_back(std::move(child));
}

ParseResult ObjectiveExpression::parse(
SolverModelPtr solverModel, Partitions availablePartitions,
CapacityConstraintMap& capacityConstraints, Time currentTime) {
// Construct the overall utility of this expression.
auto utility = std::make_unique<ObjectiveFunction>(objectiveType);

// Parse the children and collect the utiltiies.
for (auto& child : children) {
auto result = child->parse(solverModel, availablePartitions,
capacityConstraints, currentTime);
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};
}
} // namespace tetrisched

// // standard C/C++ libraries
Expand Down
7 changes: 7 additions & 0 deletions schedulers/tetrisched/src/SolverModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,13 @@ size_t ObjectiveFunctionT<T>::size() const {
return terms.size();
}

template <typename T>
void ObjectiveFunctionT<T>::merge(const ObjectiveFunctionT<T> &other) {
for (auto &term : other.terms) {
terms.push_back(term);
}
}

/*
* Methods for SolverModel.
* These methods provide an implementation of the Constraint class.
Expand Down
12 changes: 8 additions & 4 deletions schedulers/tetrisched/test/test_solver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,17 @@ TEST(SolverModel, TestCPLEXSolverTranslation) {
constraint->addTerm(2, intVar);
constraint->addTerm(5);
solverModelPtr->addConstraint(std::move(constraint));
auto objectiveFunction = std::make_unique<tetrisched::ObjectiveFunction>(
tetrisched::ObjectiveType::OBJ_MAXIMIZE);
objectiveFunction->addTerm(1, intVar);
solverModelPtr->setObjectiveFunction(std::move(objectiveFunction));
solverModelPtr->exportModel("test_solvermodel.lp");
EXPECT_TRUE(std::filesystem::exists("test_solvermodel.lp"))
<< "The file test_solvermodel.lp was not created.";
std::filesystem::remove("test_solvermodel.lp");
<< "The file test_solvermodel.lp was not created.";
// std::filesystem::remove("test_solvermodel.lp");
cplexSolver.translateModel();
cplexSolver.exportModel("test_cplexmodel.lp");
EXPECT_TRUE(std::filesystem::exists("test_cplexmodel.lp"))
<< "The file test_cplexmodel.lp was not created.";
std::filesystem::remove("test_cplexmodel.lp");
<< "The file test_cplexmodel.lp was not created.";
// std::filesystem::remove("test_cplexmodel.lp");
}

0 comments on commit d0f1cc4

Please sign in to comment.