Skip to main content
CPMpy logo

Github Version PyPI version PyPI downloads Tests Licence


CPMpy: a Constraint Programming and Modeling library in Python, based on numpy, with direct solver access.

Documentation: https://cpmpy.readthedocs.io/


Constraint solving at your finger tips

For combinatorial problems with Boolean and integer variables. With many high-level constraints that are automatically decomposed when not natively supported by the solver.

Lightweight, well-documented, used in research and industry.

Install simply with pip install cpmpy

🔑 Key Features

  • Solver-agnostic: use and compare CP, ILP, SMT, PB and SAT solvers
  • ML-friendly: decision variables are numpy arrays, with vectorized operations and constraints
  • Incremental solving: assumption variables, adding constraints and updating objectives
  • Extensively tested: large test-suite and actively fuzz-tested
  • Tools: for parameter-tuning, debugging, explanation generation and XCSP3 benchmarking
  • Flexible: easy to add constraints or solvers, also direct solver access

🔩 Solvers

CPMpy can translate to a wide variety of constraint solving paradigms, including both commercial and open-source solvers.

  • CP Solvers: OR-Tools (default), IBM CP Optimizer (license required), Choco, Glasgow GCS, Pumpkin, MiniZinc+solvers
  • ILP Solvers: SCIP, HiGHS, Gurobi (license required), CPLEX (license required)
  • GO Solvers: Hexaly (license required)
  • SMT Solvers: Z3
  • PB Solvers: Exact
  • SAT Encoders and Solvers: PySAT+solvers, Pindakaas
  • Decision Diagrams: PySDD

</> Example: flexible jobshop scheduling

An example that also demonstrates CPMpy's seamless integration into the scientific Python ecosystem:

# Parallel machine scheduling: a set of jobs must be scheduled, each can be run on compatible machines,
# with different duration and energy consumption. Minimize makespan and total energy consumption
import cpmpy as cp
import pandas as pd
import random; random.seed(1)

# --- Data definition ---
num_jobs = 15
num_machines = 3
# Generate some data: [job_id, machine_id, duration, energy]
data = [[jobid, machid, random.randint(2, 8), random.randint(5, 15)]
        for jobid in range(num_jobs) for machid in range(num_machines)]
df_data = pd.DataFrame(data, columns=['job_id', 'machine_id', 'duration', 'energy'])
num_compatible = len(df_data)

# Compute maximal horizon (sum of longest duration per job)
horizon = df_data.groupby("job_id")["duration"].max().sum()

# Decision `start[j]`: integer start time for each job `j`
start = cp.intvar(0, horizon, shape=num_jobs, name="start")
# Decision `end[j]`: integer end time for each job `j`
end = cp.intvar(0, horizon, shape=num_jobs, name="end")
# Decision `active[c]`: Per compatible job/machine combination `c`, Boolean indicating if it is used
active = cp.boolvar(shape=num_compatible, name="active")

model = cp.Model()

# Mandatory jobs: each job must be assigned to exactly one compatible machine
for _, job_rows in df_data.groupby("job_id"):
    model.add(cp.sum(active[job_rows.index]) == 1)

# Machine capacity: each machine can only process one job at a time
# also enforces Matching duration: the end time of a job is the start time + the duration on its assigned machine
for _, mach_rows in df_data.groupby("machine_id", sort=True):
    model.add(cp.NoOverlapOptional(
        start = start[mach_rows["job_id"]],
        end = end[mach_rows["job_id"]],
        duration = mach_rows["duration"].values,
        is_present = active[mach_rows.index],
    ))

# Metric Makespan: end time of the latest job
makespan = cp.max(end)

# Metric Total energy: total energy consumed by the machines
total_energy = cp.sum(active * df_data["energy"])

# Objective: minimize makespan, and to a lesser extend also total energy consumption
model.minimize(100 * makespan + total_energy)


# --- solving and graphical visualisation ---
if model.solve():
    print(model.status())
    print("Total makespan:", makespan.value(), "energy:", total_energy.value())

    # Visualize with Plotly's excellent Gantt chart support
    import plotly.express as px
    df_solution = df_data[active.value() == True].copy()  # Select rows where active is True
    df_solution["start"] = pd.to_datetime(start.value(), unit="m")
    df_solution["end"] = pd.to_datetime(end.value(), unit="m")
    import plotly.io as pio; pio.renderers.default = "browser"
    px.timeline(df_solution, x_start="start", x_end="end", y="machine_id", color="job_id", text="energy").show()
else:
    print("No solution found.")

You can then compare the runtime of all installed solvers, or much more...

for solvername in cp.SolverLookup.solvernames():
    try:
        model.solve(solver=solvername, time_limit=10)  # max 10 seconds
        print(f"{solvername}: {model.status()}")
    except Exception as e:
        print(f"{solvername}: Not run -- {str(e)}")

🌳 Ecosystem

CPMpy is part of the scientific Python ecosystem, making it easy to use in Jupyter notebooks, to add visualisations, or to use it in machine learning pipelines.

Other projects that build on CPMpy:

  • XCP-explain: a library for explainable constraint programming
  • PyConA: a library for constraint acquisition
  • Fuzz-Test: fuzz testing of constraint solvers
  • Sudoku Assistant: an Android app for sudoku scanning, solving and intelligent hints
  • CHAT-Opt demonstrator: translates natural language problem descriptions into CPMpy models

Also, CPMpy participated in both the 2024 and 2025 XCSP3 competition, twice making its solvers win 3 gold and 1 silver medal.

🔧 Library development

CPMpy has the open-source Apache 2.0 license and is run as an open-source project. All discussions happen on Github, even between direct colleagues, and all changes are reviewed through pull requests.

Join us! We welcome any feedback and contributions. You are also free to reuse any parts in your own project. A good starting point to contribute is to add your models to the examples/ folder.

Are you a solver developer? We are keen to integrate solvers that have a Python API and are on pip. Check out our adding solvers documentation and contact us!

🙏 Acknowledgments

Part of the development received funding through Prof. Tias Guns' European Research Council (ERC) Consolidator grant, under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 101002802, CHAT-Opt).

You can cite CPMpy as follows: "Guns, T. (2019). Increasing modeling language convenience with a universal n-dimensional array, CPpy as python-embedded example. The 18th workshop on Constraint Modelling and Reformulation at CP (ModRef 2019).

@inproceedings{guns2019increasing,
    title={Increasing modeling language convenience with a universal n-dimensional array, CPpy as python-embedded example},
    author={Guns, Tias},
    booktitle={Proceedings of the 18th workshop on Constraint Modelling and Reformulation at CP (Modref 2019)},
    volume={19},
    year={2019}
}

If you work in academia, please cite us. If you work in industry, we'd love to hear how you are using it. The lab of Prof. Guns is open to collaborations and contract research.

Metadata

Release files for cpmpy 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cpmpy 1.1.0
File Size Uploaded
cpmpy-1.1.0.tar.gz 430.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for cpmpy 1.1.0
File Interpreter ABI Platform
cpmpy-1.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 835.6 kB

Release files / cpmpy-1.1.0.tar.gz

Download URL cpmpy-1.1.0.tar.gz
Size 430.0 kB
Tags Source
SHA-256 checksum
How to use checksums
862355ed1ff328576e19354fd592fd90d7b4e7f9a371aeadb735c3946aa4ce4a
BLAKE2b-256 checksum
How to use checksums
ebcaeef0c7eda99586348ce4d7a4a6ec701ea0557f22f4d250b7b99664c7a29f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.25

Release files / cpmpy-1.1.0-py3-none-any.whl

Download URL cpmpy-1.1.0-py3-none-any.whl
Size 405.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2071a7144f950a4736c02c96409bbf70778af13daffb3cfb85c484d9dc2ea301
BLAKE2b-256 checksum
How to use checksums
45b9666c5dd8dcbf11441b797e6f97e3eaf97c726404b61321d855909a0b2715
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.25
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page