Tutorial
Understanding Constraint Satisfaction Problems in ProblemReductions
This tutorial explains how to work with constraint satisfaction problems using the ProblemReductions package in Julia.
Basic Setup
First, let's import the required packages and create a simple problem:
julia> using ProblemReductions, Graphs
julia> problem = IndependentSet(smallgraph(:diamond))
IndependentSet{SimpleGraph{Int64}, Int64, UnitWeight}(SimpleGraph{Int64}(5, [[2, 3], [1, 3, 4], [1, 2, 4], [2, 3]]), [1, 1, 1, 1])
This creates an Independent Set problem based on a diamond-shaped graph.
Problem Properties
You can query various properties of the problem:
1. Basic properties
julia> num_variables(problem) # number of variables
4
julia> num_flavors(problem) # number of flavors of each variable
2
julia> problem_size(problem) # size of the problem
(num_vertices = 4, num_edges = 5)
2. Weights (for weighted problems)
julia> ProblemReductions.weights(problem) # weights of the problem
4-element UnitWeight: 1 1 1 1
julia> ProblemReductions.set_weights(problem, [1, 2, 2, -1, -1, -2]) # set the weights of the problem
IndependentSet{SimpleGraph{Int64}, Int64, Vector{Int64}}(SimpleGraph{Int64}(5, [[2, 3], [1, 3, 4], [1, 2, 4], [2, 3]]), [1, 2, 2, -1, -1, -2])
3. Objectives and Constraints
julia> ProblemReductions.objectives(problem) # View the objective functions
4-element Vector{ProblemReductions.LocalSolutionSize{Int64}}: LocalSolutionSize{Int64} on [1] ┌───────────────┬──────┐ │ Configuration │ Size │ ├───────────────┼──────┤ │ [0] │ 0 │ │ [1] │ 1 │ └───────────────┴──────┘ LocalSolutionSize{Int64} on [2] ┌───────────────┬──────┐ │ Configuration │ Size │ ├───────────────┼──────┤ │ [0] │ 0 │ │ [1] │ 1 │ └───────────────┴──────┘ LocalSolutionSize{Int64} on [3] ┌───────────────┬──────┐ │ Configuration │ Size │ ├───────────────┼──────┤ │ [0] │ 0 │ │ [1] │ 1 │ └───────────────┴──────┘ LocalSolutionSize{Int64} on [4] ┌───────────────┬──────┐ │ Configuration │ Size │ ├───────────────┼──────┤ │ [0] │ 0 │ │ [1] │ 1 │ └───────────────┴──────┘
julia> ProblemReductions.constraints(problem) # View the constraints
5-element Vector{ProblemReductions.LocalConstraint}: LocalConstraint on [1, 2] ┌───────────────┬───────┐ │ Configuration │ Valid │ ├───────────────┼───────┤ │ [0, 0] │ true │ │ [1, 0] │ true │ │ [0, 1] │ true │ │ [1, 1] │ false │ └───────────────┴───────┘ LocalConstraint on [1, 3] ┌───────────────┬───────┐ │ Configuration │ Valid │ ├───────────────┼───────┤ │ [0, 0] │ true │ │ [1, 0] │ true │ │ [0, 1] │ true │ │ [1, 1] │ false │ └───────────────┴───────┘ LocalConstraint on [2, 3] ┌───────────────┬───────┐ │ Configuration │ Valid │ ├───────────────┼───────┤ │ [0, 0] │ true │ │ [1, 0] │ true │ │ [0, 1] │ true │ │ [1, 1] │ false │ └───────────────┴───────┘ LocalConstraint on [2, 4] ┌───────────────┬───────┐ │ Configuration │ Valid │ ├───────────────┼───────┤ │ [0, 0] │ true │ │ [1, 0] │ true │ │ [0, 1] │ true │ │ [1, 1] │ false │ └───────────────┴───────┘ LocalConstraint on [3, 4] ┌───────────────┬───────┐ │ Configuration │ Valid │ ├───────────────┼───────┤ │ [0, 0] │ true │ │ [1, 0] │ true │ │ [0, 1] │ true │ │ [1, 1] │ false │ └───────────────┴───────┘
julia> ProblemReductions.energy_mode(problem) # View the energy mode of the problem
LargerSizeIsBetter()
Remarks
- All
ConstraintSatisfactionProblem
s useobjectives
andconstraints
to define the problem. - The
constraints
returns a vector of local constraints. Each local constraint is a boolean function of the variables.true
means the constraint is satisfied by the current assignment to local variables. - The energy is defined as
-solution_size
ifenergy_mode(problem) == LargerSizeIsBetter()
andsolution_size
ifenergy_mode(problem) == SmallerSizeIsBetter()
. The energy is infinity if the solution is invalid.
Working with Solutions
You can evaluate different solutions (configurations) using these methods:
1. Single Solution Evaluation
julia> solution_size(problem, [1, 0, 1, 0]) # Calculate solution size
SolutionSize{Int64}(2, false)
julia> energy(problem, [1, 0, 1, 0]) # Calculate energy
3037000500
The solution size is defined as the sum of the sizes of the variables. It returns a SolutionSize
object, which has two fields:
size
: the size of the solutionis_valid
: whether the solution satisfies the constraints
2. Multiple Solutions Evaluation (faster than multiple single evaluations)
julia> solution_size_multiple(problem, [[1, 0, 0, 0], [0, 1, 0, 1]]) # Evaluate multiple configurations
2-element Vector{SolutionSize{Int64}}: SolutionSize{Int64}(1, true) SolutionSize{Int64}(2, false)
3. Finding the Optimal Solution
julia> ProblemReductions.findbest(problem, BruteForce()) # Find the best configuration using brute force
1-element Vector{Vector{Int64}}: [1, 0, 0, 1]
Remarks
- Configuration Format: Solutions are represented as vectors of integers in the range of
0:num_flavors(problem)-1
, where each element represents a variable's state. - Optimization: The
findbest
method allows you to find optimal solutions using different algorithms (likeBruteForce
in this example). Alternatively, you can use theGTNSolver
in theGenericTensorNetworks
package to find optimal solutions using tensor network methods.
This API provides a comprehensive toolkit for defining, manipulating, and solving constraint satisfaction problems in Julia.