Solving Constraint Problems with Prolog Backtracking
Learn how Prolog's backtracking and unification allow you to solve constraint-satisfaction problems by describing the goal rather than the search algorithm.
06 Oct 2026, 14:42 UTC

When solving constraint-satisfaction problems (CSPs), writing explicit nested loops or manual stack management to explore possible states is tedious and error-prone. In Prolog, the useful takeaway is that you can describe what a valid solution looks like using logic rules, and the engine's built-in depth-first search and backtracking will handle the how of finding it.
This capability relies on unification—a process of making two terms identical by binding variables—and choice points. A choice point is a marker the engine places whenever a goal can be satisfied in multiple ways. If a subsequent goal fails, Prolog automatically reverts to the most recent choice point, undoes any variable bindings made since then, and tries the next available alternative.
Defining the Knowledge Base
A Prolog program consists of a knowledge base of facts (ground assertions) and rules (logical implications). For a map-coloring problem, where no two adjacent regions can share the same color, you first define the topology of the map.
% Run in an ISO-compliant interpreter like SWI-Prolog
% No elevated permissions required; load as a .pl file
region(a). region(b). region(c). region(d).
% Define adjacency (undirected edges)
adjacent(a, b).
adjacent(a, c).
adjacent(b, d).
adjacent(c, d).
% Ensure symmetry: if A is adjacent to B, B is adjacent to A
is_adjacent(X, Y) :- adjacent(X, Y).
is_adjacent(X, Y) :- adjacent(Y, X).
Before running a complex solver, verify the knowledge base by querying is_adjacent(b, a). to ensure the symmetry rule works as expected.
Implementing the Search Logic
To solve the CSP, you create a predicate that assigns colors to regions while checking for conflicts. The engine will backtrack whenever it hits a conflict, effectively pruning that branch of the search tree.
% colors(ListofRegions, ListofColors, Assignment)
color_map([], _, []).
color_map([R|Rs], Colors, [R-Color|Rest]) :-
member(Color, Colors), % This creates a choice point
\+ (member(Adj-AdjColor, Rest), is_adjacent(R, Adj), AdjColor = Color),
color_map(Rs, Colors, Rest).
In this example, member(Color, Colors) is the engine's primary mechanism for exploration. If the subsequent check for adjacent colors fails (indicated by \+, the "not" operator), Prolog backtracks to member/2 to try the next color in the list. This happens recursively across the entire region list.
Diagnostic Verification
To visualize this process, use the trace command in the interpreter before running your query. The trace will show exactly when the engine creates a choice point and when it "fails" and backtracks. This is the most practical way to identify if your search space is too large or if your rules are causing unnecessary iterations.
Optimizing with the Cut Operator
Unconstrained backtracking can lead to performance degradation if the engine continues searching for alternatives after a satisfactory solution is found. The cut operator (!) tells Prolog to commit to the choices made so far and discard all other choice points for the current predicate.
For instance, if you only need one valid coloring, you can wrap the call in a predicate that cuts after the first success:
solve_once(Regions, Colors, Assignment) :-
color_map(Regions, Colors, Assignment),
!.
Trade-offs and Limitations
While powerful, this model has specific risks:
- Rule Order: Prolog processes rules from top to bottom. Placing a recursive call before a base case can lead to infinite recursion (stack overflow).
- Logical Purity: The cut operator is a control tool, not a logical one. Overusing it makes code "directional," meaning it may work when searching for an assignment but fail if you try to use the same predicate to verify an existing one.
- Complexity: For very large CSPs, simple backtracking is inefficient. In those cases, using a Constraint Logic Programming (CLP) library, such as
clpfdin SWI-Prolog, is recommended to prune the search space more aggressively.
To verify your result, check the final Assignment list to ensure no two regions linked by is_adjacent/2 share the same color value.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.