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 andVarArray()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 themintension§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 andcircuit§3.25. Several of them also have shortcut forms, such asExistorHamming, that are derived fromCountorSum[ch. 9]. - Logical combination.
IfandMatchbuild 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)andmaximize(term)accept a variable, an expression, a sum, a dot product, a generator, or aMinimum,MaximumorNValuesterm §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:
# 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.
| Name | Value | Where | Notes |
|---|---|---|---|
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.py | allowed values complete, fast, none; meaning not stated in the guide |
-force_element_index | off | §3.19 compiler.py | allows an element index that can fall outside the array |
-dont_adjust_indexing | off, so auto-adjustment is on | §3.2 compiler.py | the guide spells it -dontadjustindexing; see Paper vs code |
integer // and % with negative operands | rounded toward zero | §3.1 | the guide strongly discourages this |
| Solver-specific options | not 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 registersdont_adjust_indexingcompiler.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/solversholdsace,choco,cosocoandexternalsolvers. A search for Picat, CP-SAT or OR-Tools inpycsp3/solversfound nothing. I did not readexternalin detail. - Version. The guide is "Version 2.6 (i)", dated 2 September 2026 p. 1.
The code's
version.txtsays 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,orandnotcannot be overloaded. Use|,&and^, and parenthesise each operand §3.1 [ch. 7]. Two separate lists of constraints can be posted instead of anand. - Conditional posting. Put an ordinary
ifoutsidesatisfy(), or writeC if condition else Noneinside 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
elementmust 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_indexis set oroptions.force_element_index = Trueis 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_indexingcompiler.py. - Domains. Only finite integer and symbolic domains are supported §2.2.
- Solver flags with
sols. Passingsols=ALLor an integer above 1 adds solver flags automatically. The stringoptionsis 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
reifiedByon anelement(member) constraint test_constraints_global_count_shortcuts.py. Choco also gives wrong solutions for asumwithnotintest_constraints_global_sum.py, and loses solutions for lex-matrix constraints withgeorgttest_constraints_global_lexincreasing_lexdecreasing.py. ACE raises an error onallDifferentover expressions when the combined domain size equals the number of terms test_variables.py, and it fails onregularwhen 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.