Solving

Solving

Once your variables are defined and constraints are applied, the final step is to solve the model. In the new Qaekwy API, this process is streamlined: the Model class itself handles the interaction with the solver engine. You simply invoke the solve methods directly on your model instance.

There are two primary methods for solving:

  • solve_one(): Ideal for standard usage where finding the first valid solution is sufficient.
  • solve(): Used when you need to explore multiple solutions or exhaustively enumerate the search space.

Basic Solving

Finding a Single Solution

For most Constraint Satisfaction Problems (CSPs), you are interested in a valid assignment of variables. The solve_one() method is the most efficient way to achieve this. It returns a single Solution object if successful, or None if no solution satisfies the constraints.

import qaekwy as qw

m = Model()

# ...

# Returns the first found solution or None
solution = m.solve_one()

if solution:
    solution.pretty_print()
else:
    print("No solution found.")

Finding Multiple Solutions

To find more than one solution, use the solve() method. You can control the maximum number of results using the solution_limit parameter.

# Returns a list of up to 5 solutions
solutions = m.solve(solution_limit=5)

if solutions:
    for s in solutions:
        s.pretty_print()

Search Strategies (Searchers)

The Searcher is the algorithm the solver uses to navigate the solution space. While Qaekwy defaults to Depth-First Search (dfs), choosing the right strategy can significantly impact performance depending on your problem type (e.g., optimization vs. satisfaction).

The different searcher strategies available are:

  • "dfs": Depth-First Search (default strategy)
  • "bab": Branch-and-Bound
  • "lds": Limited Discrepancy Search algorithm.
  • "pbs": Portfolio-Based Search algorithm.
  • "rbs": Restart-based Search algorithm.
# Use Branch and Bound for an optimization problem
best_solution = m.solve_one(searcher="bab")

Cutoffs: Knowing When to Stop

For really hard problems, a full search could take days or even weeks. A cutoff is like setting an actions counter for your search.

Qaekwy has a bunch of different ways to set a time limit on your search:

  • CutoffConstant: Stop after a fixed number of steps.
  • CutoffFibonacci: The step limit increases like the Fibonacci sequence.
  • CutoffGeometric: The step limit grows exponentially.
  • CutoffLuby: A special sequence that’s good for some problems.
  • CutoffLinear: The step limit increases in a straight line.
  • CutoffRandom: A random step limit.

You can also combine cutoffs using Meta Cutoffs like MetaCutoffAppender, MetaCutoffMerger, and MetaCutoffRepeater.

To set a cutoff, proceed like following:

import qaekwy as qw

m = qw.Model()

# ...

# Stop searching after 1000 steps
solution = m.solve_one(cutoff=qw.CutoffConstant(1000))

Error Handling

The solve() methods abstract away the raw engine response, but they will raise a SolverError if the engine encounters a critical failure (e.g., malformed constraints or server-side issues).

import qaekwy as qw

m = qw.Model()

# ...

try:
    solutions = m.solve()
    # ...
except qw.SolverError as e:
    print(f"Solver failed with status {e.status}: {e.message}")