Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CSP functionality to the library #2

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions examples/csp_example.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include "definitions.h"
#include <iostream>
#include <unordered_map>

int main() {
// Create a CSP problem
CSPProblem csp;

// Add variables and their domains
csp.add_variable("X", {1, 2, 3});
csp.add_variable("Y", {1, 2, 3});
csp.add_variable("Z", {1, 2, 3});

// Add constraints
csp.add_constraint([](const std::unordered_map<std::string, int> &assignment) {
return assignment.at("X") != assignment.at("Y");
});
csp.add_constraint([](const std::unordered_map<std::string, int> &assignment) {
return assignment.at("Y") != assignment.at("Z");
});
csp.add_constraint([](const std::unordered_map<std::string, int> &assignment) {
return assignment.at("X") != assignment.at("Z");
});

// Solve the CSP using backtracking search
BacktrackingSearch search(&csp);
std::unordered_map<std::string, int> solution = search.search();

// Print the solution
if (!solution.empty()) {
std::cout << "Solution found:" << std::endl;
for (const auto &pair : solution) {
std::cout << pair.first << " = " << pair.second << std::endl;
}
} else {
std::cout << "No solution found." << std::endl;
}

return 0;
}
138 changes: 138 additions & 0 deletions include/definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#include <string>
#include <vector>
#include <memory>
#include <unordered_map>
#include <unordered_set>

/**
* @brief Represents an abstract state in a problem.
Expand Down Expand Up @@ -113,4 +115,140 @@ class Problem {
State *initial_state_;
};

/**
* @brief Represents a constraint satisfaction problem (CSP).
*
* This class defines the structure of a CSP, including variables, domains, and constraints.
* It should be extended by the user to implement specific CSP logic.
*/
class CSPProblem : public Problem {
public:
/**
* @brief Constructor for the CSPProblem class.
*/
CSPProblem() {}

/**
* @brief Virtual destructor for the CSPProblem class.
*/
virtual ~CSPProblem() {}

/**
* @brief Adds a variable to the CSP.
*
* @param variable The name of the variable.
* @param domain The domain of the variable.
*/
void add_variable(const std::string &variable, const std::vector<int> &domain) {
variables_[variable] = domain;
}

/**
* @brief Adds a constraint to the CSP.
*
* @param constraint The constraint function.
*/
void add_constraint(const std::function<bool(const std::unordered_map<std::string, int> &)> &constraint) {
constraints_.push_back(constraint);
}

/**
* @brief Tests if a given assignment satisfies all constraints.
*
* @param assignment The assignment to test.
* @return True if the assignment satisfies all constraints, false otherwise.
*/
bool is_consistent(const std::unordered_map<std::string, int> &assignment) const {
for (const auto &constraint : constraints_) {
if (!constraint(assignment)) {
return false;
}
}
return true;
}

/**
* @brief Retrieves the variables of the CSP.
*
* @return A reference to the variables of the CSP.
*/
const std::unordered_map<std::string, std::vector<int>> &variables() const {
return variables_;
}

private:
std::unordered_map<std::string, std::vector<int>> variables_; ///< Variables of the CSP.
std::vector<std::function<bool(const std::unordered_map<std::string, int> &)>> constraints_; ///< Constraints of the CSP.
};

/**
* @brief Represents a backtracking search algorithm for solving CSPs.
*/
class BacktrackingSearch {
public:
/**
* @brief Constructor for the BacktrackingSearch class.
*
* @param problem The CSP problem to solve.
*/
BacktrackingSearch(CSPProblem *problem) : problem_(problem) {}

/**
* @brief Solves the CSP using backtracking search.
*
* @return A solution to the CSP, or an empty assignment if no solution is found.
*/
std::unordered_map<std::string, int> search() {
std::unordered_map<std::string, int> assignment;
if (backtrack(assignment)) {
return assignment;
} else {
return {};
}
}

private:
/**
* @brief Performs backtracking search to find a solution to the CSP.
*
* @param assignment The current assignment.
* @return True if a solution is found, false otherwise.
*/
bool backtrack(std::unordered_map<std::string, int> &assignment) {
if (assignment.size() == problem_->variables().size()) {
return true;
}

std::string variable = select_unassigned_variable(assignment);
for (int value : problem_->variables().at(variable)) {
assignment[variable] = value;
if (problem_->is_consistent(assignment)) {
if (backtrack(assignment)) {
return true;
}
}
assignment.erase(variable);
}

return false;
}

/**
* @brief Selects an unassigned variable from the CSP.
*
* @param assignment The current assignment.
* @return The name of an unassigned variable.
*/
std::string select_unassigned_variable(const std::unordered_map<std::string, int> &assignment) const {
for (const auto &variable : problem_->variables()) {
if (assignment.find(variable.first) == assignment.end()) {
return variable.first;
}
}
return "";
}

CSPProblem *problem_; ///< The CSP problem to solve.
};

#endif // DEFINITIONS_H
62 changes: 62 additions & 0 deletions src/search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,65 @@ std::shared_ptr<Node> AStarSearch::search() {
// Return nullptr if no solution is found
return nullptr;
}

// Implement CSPProblem class methods for handling variables, domains, and constraints
void CSPProblem::add_variable(const std::string &variable, const std::vector<int> &domain) {
variables_[variable] = domain;
}

void CSPProblem::add_constraint(const std::function<bool(const std::unordered_map<std::string, int> &)> &constraint) {
constraints_.push_back(constraint);
}

bool CSPProblem::is_consistent(const std::unordered_map<std::string, int> &assignment) const {
for (const auto &constraint : constraints_) {
if (!constraint(assignment)) {
return false;
}
}
return true;
}

const std::unordered_map<std::string, std::vector<int>> &CSPProblem::variables() const {
return variables_;
}

// Implement BacktrackingSearch class methods for solving CSPs using backtracking search algorithm
BacktrackingSearch::BacktrackingSearch(CSPProblem *problem) : problem_(problem) {}

std::unordered_map<std::string, int> BacktrackingSearch::search() {
std::unordered_map<std::string, int> assignment;
if (backtrack(assignment)) {
return assignment;
} else {
return {};
}
}

bool BacktrackingSearch::backtrack(std::unordered_map<std::string, int> &assignment) {
if (assignment.size() == problem_->variables().size()) {
return true;
}

std::string variable = select_unassigned_variable(assignment);
for (int value : problem_->variables().at(variable)) {
assignment[variable] = value;
if (problem_->is_consistent(assignment)) {
if (backtrack(assignment)) {
return true;
}
}
assignment.erase(variable);
}

return false;
}

std::string BacktrackingSearch::select_unassigned_variable(const std::unordered_map<std::string, int> &assignment) const {
for (const auto &variable : problem_->variables()) {
if (assignment.find(variable.first) == assignment.end()) {
return variable.first;
}
}
return "";
}
46 changes: 46 additions & 0 deletions tests/tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,52 @@ TEST(Search, AStarSearch) {
delete search;
}

// Test case for CSPProblem class
TEST(CSPProblem, AddVariable) {
CSPProblem csp;
csp.add_variable("X", {1, 2, 3});
EXPECT_EQ(csp.variables().size(), 1);
EXPECT_EQ(csp.variables().at("X").size(), 3);
}

TEST(CSPProblem, AddConstraint) {
CSPProblem csp;
csp.add_variable("X", {1, 2, 3});
csp.add_variable("Y", {1, 2, 3});
csp.add_constraint([](const std::unordered_map<std::string, int> &assignment) {
return assignment.at("X") != assignment.at("Y");
});
std::unordered_map<std::string, int> assignment = {{"X", 1}, {"Y", 2}};
EXPECT_TRUE(csp.is_consistent(assignment));
assignment = {{"X", 1}, {"Y", 1}};
EXPECT_FALSE(csp.is_consistent(assignment));
}

// Test case for BacktrackingSearch class
TEST(BacktrackingSearch, Search) {
CSPProblem csp;
csp.add_variable("X", {1, 2, 3});
csp.add_variable("Y", {1, 2, 3});
csp.add_variable("Z", {1, 2, 3});
csp.add_constraint([](const std::unordered_map<std::string, int> &assignment) {
return assignment.at("X") != assignment.at("Y");
});
csp.add_constraint([](const std::unordered_map<std::string, int> &assignment) {
return assignment.at("Y") != assignment.at("Z");
});
csp.add_constraint([](const std::unordered_map<std::string, int> &assignment) {
return assignment.at("X") != assignment.at("Z");
});

BacktrackingSearch search(&csp);
std::unordered_map<std::string, int> solution = search.search();

EXPECT_FALSE(solution.empty());
EXPECT_NE(solution["X"], solution["Y"]);
EXPECT_NE(solution["Y"], solution["Z"]);
EXPECT_NE(solution["X"], solution["Z"]);
}

int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
Expand Down
Loading