From scratch
Using KnapsackProblem Class and DD Operations
In this tutorial, we will use the knapsack problem as an example to show how to create and export a DD.
Knapsack Problem
The knapsack problem considers:
- A list of items, each with a weight and an associated value.
- Maximum weight capacity of the knapsack.
The goal is to:
- Select a subset of items such that their combined weight does not exceed the knapsack's capacity.
- Maximize the sum of the values of the selected items.
Problem Implementation
1. Setting Up the Environment
Create a Python script (e.g., knapsackProblem.py) in your project directory to run the tutorial steps.
Create a C++ script (e.g., knapsackProblem.cpp and knapsackProblem.h) in your project directory to run the tutorial steps.
2. Import Dependencies
Ensure you import the required modules and classes. Here's an example import section:
3 .Create problem class and implement functions
class KnapsackProblem : public AbstractProblem<int> {
public:
KnapsackProblem(int* initial_state, const vector<pair<string, vector<int>>>& variables,
vector<int> weights, int capacity)
: AbstractProblem(initial_state, variables),
weights(weights),
capacity(capacity) {}
private:
vector<int> weights;
int capacity;
};
3.1. Transition function
Description:
- Function Purpose : Computes the transition to a new state based on the previous state, a variable index, and its assigned value.
- Variables :
previous_stateis the current state,variable_indexidentifies the variable to change,variable_valueis the value assigned to it, andscratch_stateis a pre-allocated buffer where the new state is written. - Return Value : Returns a boolean
is_feasibleindicating if the transition satisfies the problem constraints. The new state is written intoscratch_state. - Code example for knapsack:
3.2. Priority for discard node function
Description:
- Function Purpose : Determines the priority of discarding a node based on its state. Lower-priority nodes are discarded first.
- Return Value : Returns the negation of the state (the knapsack load), so nodes with lower load are discarded first.
- Code example for knapsack:
3.3. Create priority for merge node function
Description:
- Function Purpose : Calculates the priority for merging nodes based on a node ID and state.
- Variables :
idis the identifier of the node, andstateis its current state. - Return Value : Returns the negation of the state (the knapsack load), so nodes with lower load are merged first.
- Code example for knapsack:
3.4. Create merge operator function
Description:
- Function Purpose : Defines how to merge two states (
state_oneandstate_two). - Return Value : Returns the merged state. We keep the minimum load, which yields a valid relaxation (upper bound) for the knapsack.
- Code example for knapsack:
3.5. Implement get as string function
Description:
- 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 an independent copy of a state.
- Return Value : Returns a copy of
state. - Code example for knapsack:
Create a Decision Diagram
Once the Problem class is created and functional, you can proceed to create a DD and work with it.
1. Setting Up the Environment
Create a Python script (e.g., knapsackMain.py) in your project directory to run the tutorial steps.
Create a C++ script (e.g., knapsackMain.cpp) in your project directory to run the tutorial steps.
2. Import Dependencies
Ensure you import the required modules and classes. Here's an example import section:
3. Defining Input Parameters
Set up variables and parameters needed for the problem instance. Modify these based on your specific input data or generation methods.
// Replace with actual data loading logic
int variable_length = 4;
vector<int> weights = {10, 20, 30, 40};
int capacity = 50;
int width = capacity / 2;
int* initial_state = new int(0);
vector<pair<string, vector<int>>> variables = {
make_pair("x_1", vector<int>{0, 1}),
make_pair("x_2", vector<int>{0, 1}),
make_pair("x_3", vector<int>{0, 1}),
make_pair("x_4", vector<int>{0, 1})
};
vector<double> objective_weights = {1, 2, 3, 4};
4. Creating the KnapsackProblem Instance
Instantiate the new instass of the class with the defined parameters.
5. Constructing the Decision Diagram
Create an instance of DD with the KnapsackProblem instance.
6. Constructing the Decision Diagram
Perform various operations on the decision diagram such as creation, reduction, restriction, and relaxation. If it's wanted to verbose, use True instead.
# Exact DD (and reduce it)
dd_instance.create_decision_diagram(verbose=False)
dd_instance.reduce_decision_diagram(verbose=False)
# Or use a restricted/relaxed DD instead
# dd_instance.create_restricted_decision_diagram(max_width=width, verbose=False)
# dd_instance.create_relax_priority_decision_diagram(max_width=width, verbose=False)
7. Exporting Graph Files (Optional)
Export the decision diagram graph to a file for visualization.
8. Solving the Objective Function
Use ShortestLongestPath to compute the optimal solution (longest path = maximization, shortest path = minimization) over the DD.
9. Writing results to file
auto* file = new ofstream(full_file_path, std::ios::app);
auto now = std::chrono::system_clock::now();
std::time_t now_time = std::chrono::system_clock::to_time_t(now);
std::tm* local_time = std::localtime(&now_time);
char buffer[80];
std::strftime(buffer, 80, "%d-%m-%Y %H:%M:%S", local_time);
(*file) << "[" << buffer << "]" << " ";
(*file) << "Solution value: " << longest_path.get_solution().value << "\n";
file->close();
10 .Running the Script
Run your script (knapsackMain.py) to execute all the steps and solve the knapsack problem based on your setup.
Run your script (knapsackMain.cpp) to execute all the steps and solve the knapsack problem based on your setup.