wikillm

Paper · 2020-09-01 · cs.AI

PyCSP3: Modeling Combinatorial Constrained Problems in Python

PyCSP3 is a Python library for declaring constraint satisfaction and optimization models. A model and its JSON data compile into an XCSP3 XML instance, which solvers such as ACE and Choco read. The document is the library's user guide: variables, objectives, about 27 constraint forms, solver piloting and pitfalls. It reports no benchmark results.

Constraint satisfactionGlobal constraintconstraint-programmingmodeling-languagecombinatorial-optimization

arXivPDFCodeMarkdown7 min read

AI-compiled by claude-haiku-5-5 on 2026-10-18, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.

In plain words

PyCSP3 is a Python library for describing puzzles and combinatorial problems as rules. You name the unknowns (variables) and the values each may take. You then state the rules they must obey (constraints), and optionally a quantity to make as small or as large as possible. You do not write the search yourself. The library turns your Python file, plus an optional data file, into a standard problem file, and a solver reads that file and looks for answers.

The guide walks through many worked examples, from a number riddle and Sudoku to warehouse location. It shows how to get one solution, several, or all of them from Python, and how to run an incremental search or ask which rules clash.

TL;DR

  • PyCSP3 is a Python-embedded modeling language for finite-domain constraint satisfaction (CSP) and optimization (COP) problems [intro].
  • Modeling and solving are kept apart: the model and data compile to an XCSP3 XML instance, which a solver such as ACE or Choco then solves [intro] §5.1.
  • The guide is version 2.6 (i), dated 2 September 2026. The code at the commit cited here is 2.6.1 version.txt.
  • The guide reports no benchmark numbers. Its value is the API, the constraint forms and the pitfalls below.

Problem

Constraint solvers each take problems in their own input format, so writing a model directly in one of them is laborious. The guide proposes a Python layer that lets a user write the model once, keep the data separate in JSON, and hand a standard XCSP3 file to any supporting solver [intro].

The scope is narrow. Only integer variables with finite domains, and symbolic variables with finite domains, can be declared §2.2. The library follows the XCSP3-core subset of the format and its popular constraints [intro]. The document introduces no new solving method.

Method

The workflow has four steps: write a Python model, provide an optional JSON data file, compile the model with its data into an XCSP3 instance, and solve that instance with a solver [intro] §5.1.

The library's own concepts map onto these steps:

  • Variables. Var() declares a stand-alone variable and VarArray() a list of them. A domain is given as a range, a set, a list of symbols or a generator §2.2 functions.py functions.py.
  • Constraints. satisfy() posts constraints that every solution must meet. It can be called several times §2.2 functions.py. Chapter 3 covers 27 constraint sections, among them intension §3.1, extension §3.2, regular §3.3, allDifferent §3.5, sum §3.11, count §3.12, element §3.19, cumulative §3.22, binPacking §3.23, knapsack §3.24 and circuit §3.25. Several of them also have shortcut forms, such as Exist or Hamming, that are derived from Count or Sum [ch. 9].
  • Logical combination. If and Match build conditional structures; the guide writes their names with a capital letter §4.1. Tabulation, reformulation, meta-constraints and explicit reification are covered in §4.2 to §4.5.
  • Objectives. minimize(term) and maximize(term) accept a variable, an expression, a sum, a dot product, a generator, or a Minimum, Maximum or NValues term §2.3 functions.py functions.py.
  • Examples. A riddle about four consecutive integers summing to 14 §1.1; a Sudoku §1.3; a warehouse-location problem §1.3; a search for prime-looking numbers below 1000, which enumerates all solutions §6.2.

Compilation is run as python <model_file> [options]. The first argument must be a .py file compiler.py. The options that the code registers include data, dataparser, dataexport, variant, checker, solver, solve and display compiler.py §5.1.

Pseudocode for the pipeline, from declaration to solution:

Pseudocode
# Declare finite-domain variables from a range, set or list  [§2.2]
# [code: pycsp3/functions.py#L137]
variables = declare(domains)

# Post constraints; every posted constraint must hold  [§2.2]
# [code: pycsp3/functions.py#L1041]
satisfy(constraints)

# Optionally state the quantity to minimise or maximise  [§2.3]
# [code: pycsp3/functions.py#L4180]
minimize(objective_term)

# Compile the model and its data into an XCSP3 instance file  [§5.1]
# [code: pycsp3/__init__.py#L156-L173]
instance_file, is_cop = compile(filename)

# Run the chosen solver on the instance; returns a status constant  [§6.1]
# [code: pycsp3/__init__.py#L245-L275]
status = solve(solver=ACE, sols=None)

# Read the values of the first solution, if the status shows one  [§5.2]
# [code: pycsp3/functions.py#L4420]
if status is SAT or status is OPTIMUM:
    answers = values(variables)

Status constants are UNSAT, SAT, OPTIMUM, UNKNOWN and CORE §5.2 [code: pycsp3/init.py#L36-L48]. solve() returns the status and keeps the result available through solution(), value(), values(), bound(), and n_solutions() §5.2 §6.2.

Piloting the solver from Python covers several solutions or all of them §6.2, incremental solving that blocks found solutions, and simulated optimization §6.3. Unsatisfiable cores are extracted when extraction=True §6.4.

Hyperparameters

The library defines these defaults. The guide documents the options but does not state a tuning recipe.

NameValueWhereNotes
solve(solver=)ACE§6.1 [code: pycsp3/init.py#L245]CHOCO is the other solver constant
solve(options=)""[code: pycsp3/init.py#L245]solver-specific flags passed through
solve(filename=)None[code: pycsp3/init.py#L245]file of the compiled instance
solve(verbose=)-1[code: pycsp3/init.py#L245]range -1 to 2
solve(sols=)None[code: pycsp3/init.py#L245]number of solutions; ALL for no limit. Behaviour for None not stated
solve(extraction=)False§6.4 [code: pycsp3/init.py#L245]True seeks an unsatisfiable core
compile(verbose=)0[code: pycsp3/init.py#L156]
checker option"fast"compiler.pyallowed values complete, fast, none; meaning not stated in the guide
-force_element_indexoff§3.19 compiler.pyallows an element index that can fall outside the array
-dont_adjust_indexingoff, so auto-adjustment is on§3.2 compiler.pythe guide spells it -dontadjustindexing; see Paper vs code
integer // and % with negative operandsrounded toward zero§3.1the guide strongly discourages this
Solver-specific optionsnot repeated here[ch. 8]options for ACE in §8.1, for Choco in §8.2

When sols is ALL, or an integer greater than 1, the library appends -xe -xc=false for ACE and -a for Choco [code: pycsp3/init.py#L269-L273].

Results

Not applicable as a method paper. The guide reports no benchmark numbers and no author-reported results. It says that more models are available in competition papers, cited as compet22 to compet26, and does not reproduce their results [intro]. Its illustrative models, which the guide counts at more than 60 in the introduction, are in chapter 1 §1.

Paper vs code

The code at commit 523f9957d2c8d4efdd11226da83d313e5624f8a6 was cloned from the repository the paper links [intro] LICENSE. Checked and matching: solve(), solver() and compile() exist with the signatures and purposes the guide gives §5.2 §6.1 [code: pycsp3/init.py#L140] [code: pycsp3/init.py#L156] [code: pycsp3/init.py#L245]. The instance-file snippet instance = compile() followed by solver(ACE).solve(instance) is consistent with the solver class §6.2 solver.py.

Differences:

  • Indexing flag name. The guide says -dontadjustindexing §3.2. The code registers dont_adjust_indexing compiler.py. I did not run the compiler, so I cannot say whether the guide's spelling is accepted.
  • Solver backends. The introduction names Picat and CP-SAT as solvers that consume the XCSP3 instance [intro]. The repository's pycsp3/solvers holds ace, choco, cosoco and external solvers. A search for Picat, CP-SAT or OR-Tools in pycsp3/solvers found nothing. I did not read external in detail.
  • Version. The guide is "Version 2.6 (i)", dated 2 September 2026 p. 1. The code's version.txt says 2.6.1 version.txt.
  • Model count. The arXiv abstract says "more than 50" illustrative models. The guide's own abstract says "more than 60" [intro]. This card follows the guide.

Pitfalls

  • Equality. Write == to post equality. A single = is Python assignment, which cannot be redefined §1.1 §3.1.
  • Logical operators. Python's and, or and not cannot be overloaded. Use |, & and ^, and parenthesise each operand §3.1 [ch. 7]. Two separate lists of constraints can be posted instead of an and.
  • Conditional posting. Put an ordinary if outside satisfy(), or write C if condition else None inside it [ch. 7].
  • Integer division. With a negative operand, // and % round toward zero, as in C or Java. The guide strongly discourages such expressions §3.1.
  • Element index. The index of element must be a variable, not a constant §3.19. An index whose domain reaches outside the array, such as a domain from -1 to 9 on an array of 10, does not compile unless -force_element_index is set or options.force_element_index = True is written at the top of the model §3.19. The guide also shows a workaround with an auxiliary variable §3.19.
  • Silent index adjustment. For arrays declared in the model (ListVar) and integer arrays from the data (ListInt), an index at or beyond the length is taken modulo the length, with a warning. A model that relies on an out-of-range error will not get one §3.2. Disable it with -dont_adjust_indexing compiler.py.
  • Domains. Only finite integer and symbolic domains are supported §2.2.
  • Solver flags with sols. Passing sols=ALL or an integer above 1 adds solver flags automatically. The string options is extended, not replaced, so custom flags can conflict with these [code: pycsp3/init.py#L269-L273].
  • Known backend bugs. The repository's tests record bugs that the solver backends had when each test was written. Choco ignores reifiedBy on an element (member) constraint test_constraints_global_count_shortcuts.py. Choco also gives wrong solutions for a sum with notin test_constraints_global_sum.py, and loses solutions for lex-matrix constraints with ge or gt test_constraints_global_lexincreasing_lexdecreasing.py. ACE raises an error on allDifferent over expressions when the combined domain size equals the number of terms test_variables.py, and it fails on regular when the variables have different domains test_constraints_global_regular.py. Check these against the current solver versions before relying on a backend for one of these forms.
  • Remaining questions. The guide does not state how long the solvers take on any model. It also does not say which solver to prefer for which constraint.

Lineage

  • Builds on: the XCSP3 intermediate format, which the guide cites as its reference for instances [intro]. No card for it exists in this wiki.
  • Inspired by: JvCSP3, Numberjack and CPpy [intro]. Named here only; no cards for them in this wiki.
  • Compared against: none in the guide.
  • Superseded by: none stated. Its changelog runs from version 1.3 to 2.6 [ch. 9].

Source and license

Adapted from “PyCSP3: Modeling Combinatorial Constrained Problems in Python” by Christophe Lecoutre, Nicolas Szczepanski (arXiv:2009.00326), used under CC BY-SA 4.0. Changes: condensed and restructured for implementation, with tables, equations and algorithms taken from the paper and its code, each cited to its source. Not affiliated with or endorsed by the authors. This page is licensed under CC BY-SA 4.0. Code citations refer to https://github.com/xcsp3team/pycsp3 at commit 523f9957d2c8 (MIT). Any quoted code is shown under that license.