Recursive Problem Implementetion
We now present how to implement a recursive model of a discrete optimization problem. As our running example, we will use the knapsack problem provided in the Examples\KnapsackInstance\
folder.
1. Setting Up the Enviroment
Create a Python file for the new class (e.g., KnapsackProblem.py
) in your project directory.
Create C++ files for the new class (e.g., KnapsackProblem.cpp
and KnapsackProblem.h
) in your project directory.
2. Import Dependencies
Make sure you import the required modules and classes. Here's an example import section:
3. Create problem class and implement functions
We first create a class that inherits from AbstractProblem
and implement its constructor that receives the problem parameters. Note that all problem classes that inherit from AbstractProblem
need a constructor that receives at least the initial state and the list of variables of the recursive model. All other parameters are problem-specific, such as the weights and capacity of the knapsack.
class KnapsackProblem : public AbstractProblem<vector<int>> {
public:
KnapsackProblem::KnapsackProblem(
vector<int>* initial_state,
const vector<pair<string, vector<int>>>& variables,
vector<int> weight,
int capacity)
: AbstractProblem(initial_state, variables), weights(weight), capacity(capacity)
{
}
};
3.1. Transition function
- Function Purpose: Computes the transition to a new state based on the previous state, a variable ID, and its assigned value.
- Variables:
previous_state
represents the current state,variable_id
identifies the variable, andvariable_value
is the value assigned to such variable. - Return Value: Returns
new_state
and a booleanis_feasible
indicating if the transition satisfies the problem constraints. - Code example for knapsack: The state is a two-dimensional vector that stores the minimum and maximum load of the knapsack. The transition function updates the knapsack load and checks if the minimum load exceeds the capacity.
def transition_function(self,
previus_state: 'State',
variable_id: str,
variable_value: int) -> tuple['State', bool]:
is_feasible: bool = False
new_state: 'State' = [0,0]
for i in range(2):
new_state[i] = previus_state[i] + self.weights[int(variable_id[2:]) - 1] * variable_value
if new_state[i] <= self.capacity:
is_feasible = True
return new_state, is_feasible
pair<vector<int>*, bool> KnapsackProblem::transition_function(
const vector<int>* previous_state,
const string& variable_id,
int variable_value) const {
bool isFeasible = false;
pair<vector<int>*, bool> to_return = make_pair(new vector<int>(2), false);
for (int i = 0; i < 2; i++) {
to_return.first->at(i) = previous_state->at(i) + weights[stoi(variable_id.substr(2)) - 1] * variable_value;
if (to_return.first->at(i) <= capacity ) isFeasible = true; //at least one side is feasible
}
to_return.second = isFeasible;
return to_return;
}
3.2. Priority for discard node function
- Function Purpose: Determines the priority of discarding a node based on its state. This function is needed to create a Restricted DD.
- Return Value: Returns the state itself (
state
) as its priority for discarding nodes. - Code example for knapsack: In this case, we prioritize states with higher knapsack load; we prefer to discard nodes with lower knapsack load.
3.3. Create priority for merge node function
- Function Purpose: Calculates the priority for merging nodes based on an ID and state. This function is needed to create a Relaxed DD.
- Variables:
id
is the node's identifier, andstate
is its current state. - Return Value: Returns
-int(id),
which assigns a priority where nodes with smaller IDs have higher priority for merging. - Code example for knapsack: This implementation merges nodes in inverse lexicographic order (i.e., node id). It gives the lowest priority to nodes where the minimum and maximum knapsack loads differ (i.e., relaxed states).
3.4. Create merge operator function
- Function Purpose: Defines how to merge two states (
state_one
andstate_two
). This function is needed to create a Relaxed DD. - Return Value: Returns the merged state.
- Code example for knapsack: The new node includes the two previous nodes' minimum and maximum knapsack load.
3.5. Implement get as string function
- Function Purpose: Converts a state (
state
) into its string representation. - Return Value: Returns the string representation of
state
. - Code example for knapsack:
3.6. Implement get state copy function
Description:
- Function Purpose : Creates a copy of a state
- Return Value : Returns a copy of a state
- Code example for knapsack: