<div id="logo" align="center">
<img width="25%" src="https://www.cril.univ-artois.fr/~lecoutre/logoPyCSP3.gif" alt="logo"/>
</div>
<br />
<h2> PyCSP3 v2.4 <sub><sup>(August 28, 2024)</sup></sub> </h2>
<!-- PyCSP3 is inspired from both [JvCSP3](https://github.com/xcsp3team/XCSP3-Java-Tools/blob/master/doc/JvCSP3v1-1.pdf) (a Java-based API) and [Numberjack](https://github.com/eomahony/Numberjack). -->
This is Version 2.4 of PyCSP3, a library in Python (version 3.10 or later) for modeling combinatorial constrained problems;
see [www.pycsp.org](https://pycsp.org). With PyCSP3, it is possible to generate instances of:
1. CSP (Constraint Satisfaction Problem)
1. COP (Constraint Optimization Problem)
in format XCSP3; see [www.xcsp.org](https://xcsp.org).
Currently, PyCSP3 is targeted to [XCSP3-core](https://arxiv.org/abs/2009.00514), which allows us to use integer variables (with finite domains) and popular
constraints. Note that:
* a dedicated [website](https://pycsp.org/) with more than 60 Jupyter notebooks is available
* a well-documented [guide](https://arxiv.org/abs/2009.00326) is available
* PyCSP3 is available as a [PyPi package](https://pypi.org/project/pycsp3/)
* a GitHub repository with more than 340 models is available at [pycsp3-models](https://github.com/xcsp3team/pycsp3-models)
At this stage, one can run two embedded solvers:
* the constraint solver [ACE](https://github.com/xcsp3team/ace), with the option -solve or the option -solver=ace
* the constraint solver [Choco](https://choco-solver.org/), with the option -solver=choco
<!-- Information about how piloting these embedded solvers can be found in [this document](https://github.com/xcsp3team/pycsp3/blob/master/docs/optionsSolvers.pdf). -->
Of course, it is possible to launch on generated XCSP3 instances (files) any solver that recognizes the XCSP3 format.
For example, see the solvers involved in the [2022 and 2023 Competitions](https://www.xcsp.org/competitions/).
It is also immediate to run ACE or Choco on XCSP3 instances (files) as the respective executables (jar files) are present in directories `pycsp3/solvers/ace`
and `pycsp3/solvers/choco`.
For example, for running ACE on the XCSP3 instance 'zebra.xml', just execute:
```console
java -jar ACE-YY-MM.jar zebra.xml
```
while replacing YY and MM with the current values that are present in the name of the jar file.
Note that it is also possible to pilot solvers with Python; see [PyCSP3 Solving Process](https://pycsp.org/documentation/solving-process/).
# 0) Zero Installation from Google Colab
This is an immediate solution for using PyCSP3, with no installation required (*you are ready to work in exactly 2 minutes*).
What you have to do is:
1. build a new notebook on [Google colab](https://colab.research.google.com/)
2. insert a first code cell for being able to use PyCSP3 in your notebook:
```
pip install pycsp3
```
3. test a very basic model by inserting in a second code cell something like:
```
from pycsp3 import *
x = VarArray(size=5, dom=range(5))
satisfy(
AllDifferent(x)
)
if solve() is SAT:
print(values(x))
```
Here, we have an array with 5 variables, we enforce them to be all different, and we display the first solution found by the underlying solver.
Just execute these cell codes on Colab. It should return `[0, 1, 2, 3, 4]`.
That's it. However, note that for intensive use, it is better to install PyCSP3 on your computer; see next section.
# 1) Installation from PyPi
This is the second easiest way of installing PyCSP3.
Note that you need first Python (version 3.10, or later) to be installed.
You can do it, for example, from [python.org](https://www.python.org/downloads/)
## Installing PyCSP3 (Linux)
Check if 'pip3' is installed. If it is not the case, execute:
```console
sudo apt install python3-pip
```
Then, install PyCSP3 with the command 'pip3':
```console
sudo pip3 install pycsp3
```
For using the -solve or -solver options, you need to have Java (at least, version 11) installed:
```console
sudo apt-get install openjdk-11-jdk
```
## Installing PyCSP3 (Mac OS)
If Python 3 is installed on your system, the command 'pip3' should already be present.
Install PyCSP3 with the command 'pip3':
```console
sudo pip3 install pycsp3
```
For using the -solve or -solver options, you need to have Java (at least, version 11) installed.
## Installing PyCSP3 (Windows)
You may need to upgrade 'pip'. Open the console and type:
```console
python -m pip install --upgrade pip
```
Then, for installing pycsp3, type:
```console
python -m pip install pycsp3
```
For using the -solve or -solver options, you need to install (at least) Java version 11:
https://www.oracle.com/java/technologies/javase-downloads.html
And add in the PATH the java command, for example, temporally, with the command:
```console
set path=%path%;C:/Program Files/Java/jdk-14.0.1/bin/
```
You can check the java command by typing in your console:
```console
java --version
```
## Updating the Version of PyCSP3 (for PyPi)
For updating your version of PyCSP3, simply execute:
For linux/Mac:
```console
sudo pip3 install --upgrade pycsp3
```
For Windows:
```console
python -m pip install --upgrade pycsp3
```
## Working with a Pool of Models
A GitHub repository is now available with more than 340 models
at [pycsp3-models](https://github.com/xcsp3team/pycsp3-models).
And you can test the compilation of one of these models.
For example, at the root of the directory pycsp3-models:
```console
python single/Zebra/Zebra.py (For Linux/Mac)
python single\Zebra\Zebra.py (For Windows)
```
# 2) Installation (alternative) by Cloning from GitHub
An alternative to PyPi is to clone the code from GitHub.
Here is an illustration for MAC OS.
We assume that Python 3 is installed (otherwise, type `port install python38` for example), and consequently 'pip3' is also installed.
In a console, type:
```console
git clone https://github.com/xcsp3team/pycsp3.git
pip3 install lxml
```
You may need to update the environment variable 'PYTHONPATH', by typing for example:
```console
export PYTHONPATH=$PYTHONPATH:.
```
# 3) Compilation and Examples
We succinctly introduce a few PyCSP3 models, showing how to compile them with different options.
However, note that many illustrations are available on [www.pycsp.org](https://pycsp.org/), notably many [models](https://pycsp.org/documentation/models/) with
Jupyter notebooks.
First, we give some general information about compilation.
## Compiling PyCSP3 Models
For generating an XCSP3 instance from a PyCSP3 model, you have to execute:
```console
python3 <file> [options]
```
with:
* <file>: a Python file to be executed, describing a model in PyCSP3
* [options]: possible options to be used when compiling
Among the options, we find:
* ```-data=<data_value>```: allows us to specify the data to be used by the model. It can be:
+ elementary: -data=5
+ a simple list: -data=[9,0,0,3,9]
+ a JSON file: -data=Bibd-3-4-6.json
Data can then be directly used in the PyCSP3 model by means of a predefined variable `data`.
* ```-dataparser=<file>```: a Python file for reading/parsing data given under any arbitrary form (e.g., by a text file).
See Example Nonogram below, for an illustration.
* ```-dataexport```: exports (saves) the data in JSON format.
See Example Nonogram below, for an illustration.
* ```-variant=<variant_name>```: the name of a variant, to be used with function `variant()`.
See Example AllInterval below, for an illustration.
* ```-solve```: attempts to solve the instance with the embedded solver 'Ace'. It requires that Java version 8 (at least) is installed.
* ```-solver=<solver_name>```: attempts to solve the instance with the solver whose name is given. Currently, it can be 'ace' or 'choco'.
Important: it requires that Java version 8 (at least) is installed.
Information about how piloting these embedded solvers can be found
in [this document](https://github.com/xcsp3team/pycsp3/blob/master/docs/optionsSolvers.pdf).
* ```-output=<file_name>```: sets the filename of the generated XCSP3 instance (think about the extension .xml)
By default, a file containing the XCSP3 instance is generated, unless you use the option:
* ```-display```: displays the XCSP3 instance in the system standard output, instead of generating an XCSP3 file
## Example 1: in console mode
Our first example shows how you can build basic models in console mode.
In this example, we just post two variable and two simple binary constraints.
```console
$ python3
Python 3.5.2
>>> from pycsp3 import *
>>> x = Var(range(10))
>>> y = Var(range(10))
>>> satisfy(
x < y,
x + y > 15
)
>>> compile()
>>> if solve() is SAT:
print(value(x),value(y))
```
Note that to get an XCSP3 file, we call `compile()`.
## Example 2: Send+More=Money
This example shows how you can define a model when no data is required from the user.
This is the classical crypto-arithmetic puzzle 'Send+More=Money'.
#### File **`SendMore.py`**
```python
from pycsp3 import *
# letters[i] is the digit of the ith letter involved in the equation
s, e, n, d, m, o, r, y = letters = VarArray(size=8, dom=range(10))
satisfy(
# letters are given different values
AllDifferent(letters),
# words cannot start with 0
[s > 0, m > 0],
# respecting the mathematical equation
[s, e, n, d] * [1000, 100, 10, 1]
+ [m, o, r, e] * [1000, 100, 10, 1]
== [m, o, n, e, y] * [10000, 1000, 100, 10, 1]
)
```
To generate the XCSP3 instance (file), the command is:
```console
python3 SendMore.py
```
To generate and solve (with ACE) the XCSP3 instance, the command is:
```console
python3 SendMore.py -solve
```
To generate and solve with Choco the XCSP3 instance, the command is:
```console
python3 SendMore.py -solver=choco
```
## Example 3: All-Interval Series
This example shows how you can simply specify an integer (as unique data) for a model.
For our illustration, we consider the problem [All-Interval Series](https://www.csplib.org/Problems/prob007/).
A classical model is:
#### File **`AllInterval.py`** (version 1)
```python
from pycsp3 import *
n = data
# x[i] is the ith note of the series
x = VarArray(size=n, dom=range(n))
satisfy(
# notes must occur once, and so form a permutation
AllDifferent(x),
# intervals between neighbouring notes must form a permutation
AllDifferent(abs(x[i] - x[i + 1]) for i in range(n - 1)),
# tag(symmetry-breaking)
x[0] < x[n - 1]
)
```
Note the presence of a tag `symmetry-breaking` that will be directly integrated into the XCSP3 file generated by the following command:
```console
python3 AllInterval.py -data=5
```
Suppose that you would prefer to declare a second array of variables for representing successive distances.
This would give:
#### File **`AllInterval.py`** (version 2)
```python
from pycsp3 import *
n = data
# x[i] is the ith note of the series
x = VarArray(size=n, dom=range(n))
# y[i] is the distance between x[i] and x[i+1]
y = VarArray(size=n - 1, dom=range(1, n))
satisfy(
# notes must occur once, and so form a permutation
AllDifferent(x),
# intervals between neighbouring notes must form a permutation
AllDifferent(y),
# computing distances
[y[i] == abs(x[i] - x[i + 1]) for i in range(n - 1)],
# tag(symmetry-breaking)
[
x[0] < x[n - 1],
y[0] < y[1]
]
)
```
However, sometimes, it may be relevant to combine different variants of a model in the same file.
In our example, this would give:
#### File **`AllInterval.py`** (version 3)
```python
from pycsp3 import *
n = data
# x[i] is the ith note of the series
x = VarArray(size=n, dom=range(n))
if not variant():
satisfy(
# notes must occur once, and so form a permutation
AllDifferent(x),
# intervals between neighbouring notes must form a permutation
AllDifferent(abs(x[i] - x[i + 1]) for i in range(n - 1)),
# tag(symmetry-breaking)
x[0] < x[n - 1]
)
elif variant("aux"):
# y[i] is the distance between x[i] and x[i+1]
y = VarArray(size=n - 1, dom=range(1, n))
satisfy(
# notes must occur once, and so form a permutation
AllDifferent(x),
# intervals between neighbouring notes must form a permutation
AllDifferent(y),
# computing distances
[y[i] == abs(x[i] - x[i + 1]) for i in range(n - 1)],
# tag(symmetry-breaking)
[
x[0] < x[n - 1],
y[0] < y[1]
]
)
```
For compiling the main model (variant), the command is:
```console
python3 AllInterval.py -data=5
```
For compiling the second model variant, using the option `-variant`, the command is:
```console
python3 AllInterval.py -data=5 -variant=aux
```
To generate and solve (with ACE) the instance of order 10 and variant 'aux', the command is:
```console
python3 AllInterval.py -data=10 -variant=aux -solve
```
## Example 4: BIBD
This example shows how you can specify a list of integers to be used as data for a model.
For our illustration, we consider the problem [BIBD](https://www.csplib.org/Problems/prob028/).
We need five integers `v, b, r, k, l` for specifying a unique instance (possibly, `b` and `r` can be set to 0, so that these values are automatically computed
according to a template for this problem).
The model is:
#### File **`Bibd.py`**
```python
from pycsp3 import *
v, b, r, k, l = data
b = (l * v * (v - 1)) // (k * (k - 1)) if b == 0 else b
r = (l * (v - 1)) // (k - 1) if r == 0 else r
# x[i][j] is the value of the matrix at row i and column j
x = VarArray(size=[v, b], dom={0, 1})
satisfy(
# constraints on rows
[Sum(row) == r for row in x],
# constraints on columns
[Sum(col) == k for col in columns(x)],
# scalar constraints with respect to lambda
[row1 * row2 == l for (row1, row2) in combinations(x, 2)]
)
```
To generate an XCSP3 instance (file), we can for example execute a command like:
```console
python3 Bibd.py -data=[9,0,0,3,9]
```
With some command interpreters (shells), you may have to escape the characters '[' and ']', which gives:
```console
python3 Bibd.py -data=\[9,0,0,3,9\]
```
## Example 5: Rack Configuration
This example shows how you can specify a JSON file to be used as data for a model.
For our illustration, we consider the problem [Rack Configuration](https://www.csplib.org/Problems/prob031/).
The data (for a specific instance) are then initially given in a JSON file, as for example:
#### File **`Rack_r2.json`**
```json
{
"nRacks": 10,
"models": [
[150, 8, 150],
[200, 16, 200]
],
"cardTypes": [
[20, 20],
[40, 8],
[50, 4],
[75, 2]
]
}
```
In the following model, we directly unpack the components of the variable `data` (because it is automatically given under the form of a named tuple) whose
fields are exactly those of the main object in the JSON file.
#### File **`Rack.py`**
```python
from pycsp3 import *
nRacks, models, cardTypes = data
models.append([0, 0, 0]) # we add first a dummy model (0,0,0)
powers, sizes, costs = zip(*models)
cardPowers, cardDemands = zip(*cardTypes)
nModels, nTypes = len(models), len(cardTypes)
T = {(i, powers[i], sizes[i], costs[i]) for i in range(nModels)}
# m[i] is the model used for the ith rack
m = VarArray(size=nRacks, dom=range(nModels))
# p[i] is the power of the model used for the ith rack
p = VarArray(size=nRacks, dom=powers)
# s[i] is the size (number of connectors) of the model used for the ith rack
s = VarArray(size=nRacks, dom=sizes)
# c[i] is the cost (price) of the model used for the ith rack
c = VarArray(size=nRacks, dom=costs)
# nc[i][j] is the number of cards of type j put in the ith rack
nc = VarArray(size=[nRacks, nTypes], dom=lambda i, j: range(min(max(sizes), cardDemands[j]) + 1))
satisfy(
# linking rack models with powers, sizes and costs
[(m[i], p[i], s[i], c[i]) in T for i in range(nRacks)],
# connector-capacity constraints
[Sum(nc[i]) <= s[i] for i in range(nRacks)],
# power-capacity constraints
[nc[i] * cardPowers <= p[i] for i in range(nRacks)],
# demand constraints
[Sum(nc[:, j]) == cardDemands[j] for j in range(nTypes)],
# tag(symmetry-breaking)
[
Decreasing(m),
If(
m[0] == m[1],
Then=nc[0][0] >= nc[1][0]
)
]
)
minimize(
# minimizing the total cost being paid for all racks
Sum(c)
)
```
To generate an XCSP3 instance (file), we execute the command:
```console
python3 Rack.py -data=Rack_r2.json
```
One might want to have the data in the JSON file with another structure, as for example:
#### File **`Rack_r2b.json`**
```json
{
"nRacks": 10,
"rackModels": [
{"power": 150, "nConnectors": 8, "price": 150},
{"power": 200, "nConnectors": 16, "price": 200}
],
"cardTypes": [
{"power": 20, "demand": 20},
{"power": 40, "demand": 8},
{"power": 50, "demand": 4},
{"power": 75, "demand": 2}
]
}
```
We only need to modify one line from the previous model:
#### File **`Rack2.py`**
```python
models.append(models[0].__class__(0, 0, 0)) # we add first a dummy model (0,0,0) ; we get the class of the used named tuples to build a new one
```
To generate an XCSP3 instance (file), we execute the command:
```console
python3 Rack2.py -data=Rack_r2b.json
```
## Example 6: Nonogram
This example shows how you can use an auxiliary Python file for parsing data that are not initially given under JSON format.
For our illustration, we consider the problem [Nonogram](https://www.csplib.org/Problems/prob012/).
The data (for a specific Nonogram puzzle) are initially given in a text file as follows:
1. a line stating the numbers of rows and columns,
1. then, for each row a line stating the number of blocks followed by the sizes of all these blocks (on the same line),
1. then, for each column a line stating the number of blocks followed by the sizes of all these blocks (on the same line).
Below, here is an example of such a text file.
#### File **`Nonogram_example.txt`**
```
24 24
0
1 5
2 3 3
2 1 2
2 2 1
2 1 1
2 3 3
3 1 5 1
3 1 1 1
3 2 1 1
3 1 1 2
3 3 1 3
3 1 3 1
3 1 1 1
3 2 1 2
3 1 1 1
1 5
3 1 1 1
3 1 1 1
3 1 1 1
3 5 1 1
2 1 2
3 2 2 4
2 4 9
0
0
0
1 1
1 2
1 2
2 6 1
3 3 1 3
3 1 1 4
4 2 1 1 7
5 1 1 1 1 1
3 1 12 1
5 1 1 1 1 1
4 2 1 1 7
4 1 1 4 1
4 2 1 2 2
2 8 3
2 1 1
2 1 2
1 4
1 3
1 2
1 1
0
```
First, we need to write a piece of code in Python for building a dictionary `data` that will be then used in our model (after having been automatically
converted to a named tuple).
We have first to import everything (*) from `pycsp3.problems.data.parsing`.
We can then add any new arbitrary item to the dictionary `data` (which is initially empty).
This is what we do below with two items whose keys are called `rowPatterns` and `colPatterns`.
The values associated with these two keys are defined as two-dimensional arrays (lists) of integers, defining the sizes of blocks.
The function `next_int()` can be called for reading the next integer in a text file, which will be specified on the command line (see later).
#### File **`Nonogram_Parser.py`**
```python
from pycsp3.problems.data.parsing import *
nRows, nCols = next_int(), next_int()
data["rowPatterns"] = [[next_int() for _ in range(next_int())] for _ in range(nRows)]
data["colPatterns"] = [[next_int() for _ in range(next_int())] for _ in range(nRows)]
```
Then, we just write the model by getting data from the variable `data`.
The model is totally independent of the way data were initially given (from a text file or a JSON file, for example).
In the code below, note how an object `Automaton` is defined from a specified pattern (list of blocks).
Also, for a `regular` constraint, we just write something like `scope in automaton`.
Finally, `x[:, j]` denotes the jth column of `x`.
#### File **`Nonogram.py`**
```python
from pycsp3 import *
rows, cols = data # patterns for row and columns
nRows, nCols = len(rows), len(cols)
def automaton(pattern):
q = Automaton.q # for building state names
transitions = []
if len(pattern) == 0:
n_states = 1
transitions.append((q(0), 0, q(0)))
else:
n_states = sum(pattern) + len(pattern)
num = 0
for i, size in enumerate(pattern):
transitions.append((q(num), 0, q(num)))
transitions.extend((q(num + j), 1, q(num + j + 1)) for j in range(size))
transitions.append((q(num + size), 0, q(num + size + (1 if i < len(pattern) - 1 else 0))))
num += size + 1
return Automaton(start=q(0), final=q(n_states - 1), transitions=transitions)
# x[i][j] is 1 iff the cell at row i and col j is colored in black
x = VarArray(size=[nRows, nCols], dom={0, 1})
satisfy(
[x[i] in automaton(rows[i]) for i in range(nRows)],
[x[:, j] in automaton(cols[j]) for j in range(nCols)]
)
```
To generate the XCSP3 instance (file), we just need to specify the name of the text file (option `-data`) and the name of the Python parser (
option `-dataparser`).
```console
python3 Nonogram.py -data=Nonogram_example.txt -dataparser=Nonogram_Parser.py
```
Maybe, you think that it would be simpler to have directly the data in JSON file.
You can generate such a file with the option `-dataexport`.
The command is as follows:
```console
python3 Nonogram.py -data=Nonogram_example.txt -dataparser=Nonogram_Parser.py -dataexport
```
A file `Nonogram_example.json` is generated, whose content is:
```json
{
"colPatterns": [
[],
[],
[],
[1],
[2],
[2],
[6, 1],
[3, 1, 3],
[1, 1, 4],
[2, 1, 1, 7],
[1, 1, 1, 1, 1],
[1, 12, 1],
[1, 1, 1, 1, 1],
[2, 1, 1, 7],
[1, 1, 4, 1],
[2, 1, 2, 2],
[8, 3],
[1, 1],
[1, 2],
[4],
[3],
[2],
[1],
[]
],
"rowPatterns": [
[],
[5],
[3, 3],
[1, 2],
[2, 1],
[1, 1],
[3, 3],
[1, 5, 1],
[1, 1, 1],
[2, 1, 1],
[1, 1, 2],
[3, 1, 3],
[1, 3, 1],
[1, 1, 1],
[2, 1, 2],
[1, 1, 1],
[5],
[1, 1, 1],
[1, 1, 1],
[1, 1, 1],
[5, 1, 1],
[1, 2],
[2, 2, 4],
[4, 9]
]
}
```
With this new file, you can directly generate the XCSP3 file with:
```console
python3 Nonogram.py -data=Nonogram_example.json
```
Raw data
{
"_id": null,
"home_page": null,
"name": "pycsp3",
"maintainer": "Szczepanski Nicolas",
"docs_url": null,
"requires_python": ">=3",
"maintainer_email": "szczepanski@cril.fr",
"keywords": "IA CP constraint modeling CSP COP",
"author": "Lecoutre Christophe, Szczepanski Nicolas",
"author_email": "lecoutre@cril.fr, szczepanski@cril.fr",
"download_url": "https://files.pythonhosted.org/packages/82/7f/805922ef8107c132ae269f91c94bd2a032a106befeb2b6c066d0cc55bbaf/pycsp3-2.4.3.tar.gz",
"platform": "LINUX",
"description": "<div id=\"logo\" align=\"center\">\n<img width=\"25%\" src=\"https://www.cril.univ-artois.fr/~lecoutre/logoPyCSP3.gif\" alt=\"logo\"/>\n</div>\n\n<br />\n<h2> PyCSP3 v2.4 <sub><sup>(August 28, 2024)</sup></sub> </h2> \n\n<!-- PyCSP3 is inspired from both [JvCSP3](https://github.com/xcsp3team/XCSP3-Java-Tools/blob/master/doc/JvCSP3v1-1.pdf) (a Java-based API) and [Numberjack](https://github.com/eomahony/Numberjack). -->\n\nThis is Version 2.4 of PyCSP3, a library in Python (version 3.10 or later) for modeling combinatorial constrained problems;\nsee [www.pycsp.org](https://pycsp.org). With PyCSP3, it is possible to generate instances of:\n\n1. CSP (Constraint Satisfaction Problem)\n1. COP (Constraint Optimization Problem)\n\nin format XCSP3; see [www.xcsp.org](https://xcsp.org).\nCurrently, PyCSP3 is targeted to [XCSP3-core](https://arxiv.org/abs/2009.00514), which allows us to use integer variables (with finite domains) and popular\nconstraints. Note that:\n\n* a dedicated [website](https://pycsp.org/) with more than 60 Jupyter notebooks is available\n* a well-documented [guide](https://arxiv.org/abs/2009.00326) is available\n* PyCSP3 is available as a [PyPi package](https://pypi.org/project/pycsp3/)\n* a GitHub repository with more than 340 models is available at [pycsp3-models](https://github.com/xcsp3team/pycsp3-models)\n\nAt this stage, one can run two embedded solvers:\n\n* the constraint solver [ACE](https://github.com/xcsp3team/ace), with the option -solve or the option -solver=ace\n* the constraint solver [Choco](https://choco-solver.org/), with the option -solver=choco\n\n<!-- Information about how piloting these embedded solvers can be found in [this document](https://github.com/xcsp3team/pycsp3/blob/master/docs/optionsSolvers.pdf). -->\n\nOf course, it is possible to launch on generated XCSP3 instances (files) any solver that recognizes the XCSP3 format.\nFor example, see the solvers involved in the [2022 and 2023 Competitions](https://www.xcsp.org/competitions/).\nIt is also immediate to run ACE or Choco on XCSP3 instances (files) as the respective executables (jar files) are present in directories `pycsp3/solvers/ace`\nand `pycsp3/solvers/choco`.\nFor example, for running ACE on the XCSP3 instance 'zebra.xml', just execute:\n\n```console\njava -jar ACE-YY-MM.jar zebra.xml \n\n```\n\nwhile replacing YY and MM with the current values that are present in the name of the jar file.\n\nNote that it is also possible to pilot solvers with Python; see [PyCSP3 Solving Process](https://pycsp.org/documentation/solving-process/).\n\n# 0) Zero Installation from Google Colab\n\nThis is an immediate solution for using PyCSP3, with no installation required (*you are ready to work in exactly 2 minutes*).\nWhat you have to do is:\n\n1. build a new notebook on [Google colab](https://colab.research.google.com/)\n2. insert a first code cell for being able to use PyCSP3 in your notebook:\n ```\n pip install pycsp3\n ```\n3. test a very basic model by inserting in a second code cell something like:\n ```\n from pycsp3 import *\n \n x = VarArray(size=5, dom=range(5))\n \n satisfy(\n AllDifferent(x)\n ) \n \n if solve() is SAT:\n print(values(x))\n ```\n\nHere, we have an array with 5 variables, we enforce them to be all different, and we display the first solution found by the underlying solver.\nJust execute these cell codes on Colab. It should return `[0, 1, 2, 3, 4]`.\n\nThat's it. However, note that for intensive use, it is better to install PyCSP3 on your computer; see next section.\n\n# 1) Installation from PyPi\n\nThis is the second easiest way of installing PyCSP3.\n\nNote that you need first Python (version 3.10, or later) to be installed.\nYou can do it, for example, from [python.org](https://www.python.org/downloads/)\n\n## Installing PyCSP3 (Linux)\n\nCheck if 'pip3' is installed. If it is not the case, execute:\n\n```console\nsudo apt install python3-pip\n\n```\n\nThen, install PyCSP3 with the command 'pip3':\n\n```console\nsudo pip3 install pycsp3\n\n```\n\nFor using the -solve or -solver options, you need to have Java (at least, version 11) installed:\n\n```console\nsudo apt-get install openjdk-11-jdk\n```\n\n## Installing PyCSP3 (Mac OS)\n\nIf Python 3 is installed on your system, the command 'pip3' should already be present.\n\nInstall PyCSP3 with the command 'pip3':\n\n```console\nsudo pip3 install pycsp3\n```\n\nFor using the -solve or -solver options, you need to have Java (at least, version 11) installed.\n\n## Installing PyCSP3 (Windows)\n\nYou may need to upgrade 'pip'. Open the console and type:\n\n```console\npython -m pip install --upgrade pip\n```\n\nThen, for installing pycsp3, type:\n\n```console\npython -m pip install pycsp3\n```\n\nFor using the -solve or -solver options, you need to install (at least) Java version 11:\n\nhttps://www.oracle.com/java/technologies/javase-downloads.html\n\nAnd add in the PATH the java command, for example, temporally, with the command:\n\n```console\nset path=%path%;C:/Program Files/Java/jdk-14.0.1/bin/\n```\n\nYou can check the java command by typing in your console:\n\n```console\njava --version\n```\n\n## Updating the Version of PyCSP3 (for PyPi)\n\nFor updating your version of PyCSP3, simply execute:\n\nFor linux/Mac:\n\n```console\nsudo pip3 install --upgrade pycsp3\n```\n\nFor Windows:\n\n```console\npython -m pip install --upgrade pycsp3\n```\n\n## Working with a Pool of Models\n\nA GitHub repository is now available with more than 340 models\nat [pycsp3-models](https://github.com/xcsp3team/pycsp3-models).\n\nAnd you can test the compilation of one of these models.\nFor example, at the root of the directory pycsp3-models:\n\n```console\npython single/Zebra/Zebra.py (For Linux/Mac)\npython single\\Zebra\\Zebra.py (For Windows)\n```\n\n# 2) Installation (alternative) by Cloning from GitHub\n\nAn alternative to PyPi is to clone the code from GitHub.\nHere is an illustration for MAC OS.\nWe assume that Python 3 is installed (otherwise, type `port install python38` for example), and consequently 'pip3' is also installed.\nIn a console, type:\n\n```console\ngit clone https://github.com/xcsp3team/pycsp3.git\npip3 install lxml\n```\n\nYou may need to update the environment variable 'PYTHONPATH', by typing for example:\n\n```console\nexport PYTHONPATH=$PYTHONPATH:.\n```\n\n# 3) Compilation and Examples\n\nWe succinctly introduce a few PyCSP3 models, showing how to compile them with different options.\nHowever, note that many illustrations are available on [www.pycsp.org](https://pycsp.org/), notably many [models](https://pycsp.org/documentation/models/) with\nJupyter notebooks.\n\nFirst, we give some general information about compilation.\n\n## Compiling PyCSP3 Models\n\nFor generating an XCSP3 instance from a PyCSP3 model, you have to execute:\n\n```console\npython3 <file> [options]\n```\n\nwith:\n\n* <file>: a Python file to be executed, describing a model in PyCSP3\n* [options]: possible options to be used when compiling\n\nAmong the options, we find:\n\n* ```-data=<data_value>```: allows us to specify the data to be used by the model. It can be:\n + elementary: -data=5\n + a simple list: -data=[9,0,0,3,9]\n + a JSON file: -data=Bibd-3-4-6.json\n\n Data can then be directly used in the PyCSP3 model by means of a predefined variable `data`.\n\n* ```-dataparser=<file>```: a Python file for reading/parsing data given under any arbitrary form (e.g., by a text file).\n See Example Nonogram below, for an illustration.\n\n* ```-dataexport```: exports (saves) the data in JSON format.\n See Example Nonogram below, for an illustration.\n\n* ```-variant=<variant_name>```: the name of a variant, to be used with function `variant()`.\n See Example AllInterval below, for an illustration.\n\n* ```-solve```: attempts to solve the instance with the embedded solver 'Ace'. It requires that Java version 8 (at least) is installed.\n\n* ```-solver=<solver_name>```: attempts to solve the instance with the solver whose name is given. Currently, it can be 'ace' or 'choco'.\n Important: it requires that Java version 8 (at least) is installed.\n Information about how piloting these embedded solvers can be found\n in [this document](https://github.com/xcsp3team/pycsp3/blob/master/docs/optionsSolvers.pdf).\n\n* ```-output=<file_name>```: sets the filename of the generated XCSP3 instance (think about the extension .xml)\n\nBy default, a file containing the XCSP3 instance is generated, unless you use the option:\n\n* ```-display```: displays the XCSP3 instance in the system standard output, instead of generating an XCSP3 file\n\n## Example 1: in console mode\n\nOur first example shows how you can build basic models in console mode.\nIn this example, we just post two variable and two simple binary constraints.\n\n```console\n$ python3\nPython 3.5.2\n>>> from pycsp3 import *\n>>> x = Var(range(10))\n>>> y = Var(range(10))\n>>> satisfy(\n x < y,\n x + y > 15\n )\n>>> compile()\n>>> if solve() is SAT:\n print(value(x),value(y)) \n```\n\nNote that to get an XCSP3 file, we call `compile()`.\n\n## Example 2: Send+More=Money\n\nThis example shows how you can define a model when no data is required from the user.\nThis is the classical crypto-arithmetic puzzle 'Send+More=Money'.\n\n#### File **`SendMore.py`**\n\n```python\nfrom pycsp3 import *\n\n# letters[i] is the digit of the ith letter involved in the equation\ns, e, n, d, m, o, r, y = letters = VarArray(size=8, dom=range(10))\n\nsatisfy(\n # letters are given different values\n AllDifferent(letters),\n\n # words cannot start with 0\n [s > 0, m > 0],\n\n # respecting the mathematical equation\n [s, e, n, d] * [1000, 100, 10, 1]\n + [m, o, r, e] * [1000, 100, 10, 1]\n == [m, o, n, e, y] * [10000, 1000, 100, 10, 1]\n)\n```\n\nTo generate the XCSP3 instance (file), the command is:\n\n```console\npython3 SendMore.py\n```\n\nTo generate and solve (with ACE) the XCSP3 instance, the command is:\n\n```console\npython3 SendMore.py -solve\n```\n\nTo generate and solve with Choco the XCSP3 instance, the command is:\n\n```console\npython3 SendMore.py -solver=choco\n```\n\n## Example 3: All-Interval Series\n\nThis example shows how you can simply specify an integer (as unique data) for a model.\nFor our illustration, we consider the problem [All-Interval Series](https://www.csplib.org/Problems/prob007/).\n\nA classical model is:\n\n#### File **`AllInterval.py`** (version 1)\n\n```python\nfrom pycsp3 import *\n\nn = data\n\n# x[i] is the ith note of the series\nx = VarArray(size=n, dom=range(n))\n\nsatisfy(\n # notes must occur once, and so form a permutation\n AllDifferent(x),\n\n # intervals between neighbouring notes must form a permutation\n AllDifferent(abs(x[i] - x[i + 1]) for i in range(n - 1)),\n\n # tag(symmetry-breaking)\n x[0] < x[n - 1]\n)\n```\n\nNote the presence of a tag `symmetry-breaking` that will be directly integrated into the XCSP3 file generated by the following command:\n\n```console\npython3 AllInterval.py -data=5\n```\n\nSuppose that you would prefer to declare a second array of variables for representing successive distances.\nThis would give:\n\n#### File **`AllInterval.py`** (version 2)\n\n```python\nfrom pycsp3 import *\n\nn = data\n\n# x[i] is the ith note of the series\nx = VarArray(size=n, dom=range(n))\n\n# y[i] is the distance between x[i] and x[i+1]\ny = VarArray(size=n - 1, dom=range(1, n))\n\nsatisfy(\n # notes must occur once, and so form a permutation\n AllDifferent(x),\n\n # intervals between neighbouring notes must form a permutation\n AllDifferent(y),\n\n # computing distances\n [y[i] == abs(x[i] - x[i + 1]) for i in range(n - 1)],\n\n # tag(symmetry-breaking)\n [\n x[0] < x[n - 1],\n y[0] < y[1]\n ]\n)\n```\n\nHowever, sometimes, it may be relevant to combine different variants of a model in the same file.\nIn our example, this would give:\n\n#### File **`AllInterval.py`** (version 3)\n\n```python\nfrom pycsp3 import *\n\nn = data\n\n# x[i] is the ith note of the series\nx = VarArray(size=n, dom=range(n))\n\nif not variant():\n\n satisfy(\n # notes must occur once, and so form a permutation\n AllDifferent(x),\n\n # intervals between neighbouring notes must form a permutation\n AllDifferent(abs(x[i] - x[i + 1]) for i in range(n - 1)),\n\n # tag(symmetry-breaking)\n x[0] < x[n - 1]\n )\n\nelif variant(\"aux\"):\n\n # y[i] is the distance between x[i] and x[i+1]\n y = VarArray(size=n - 1, dom=range(1, n))\n\n satisfy(\n # notes must occur once, and so form a permutation\n AllDifferent(x),\n\n # intervals between neighbouring notes must form a permutation\n AllDifferent(y),\n\n # computing distances\n [y[i] == abs(x[i] - x[i + 1]) for i in range(n - 1)],\n\n # tag(symmetry-breaking)\n [\n x[0] < x[n - 1],\n y[0] < y[1]\n ]\n )\n```\n\nFor compiling the main model (variant), the command is:\n\n```console\npython3 AllInterval.py -data=5\n```\n\nFor compiling the second model variant, using the option `-variant`, the command is:\n\n```console\npython3 AllInterval.py -data=5 -variant=aux\n```\n\nTo generate and solve (with ACE) the instance of order 10 and variant 'aux', the command is:\n\n```console\npython3 AllInterval.py -data=10 -variant=aux -solve\n```\n\n## Example 4: BIBD\n\nThis example shows how you can specify a list of integers to be used as data for a model.\nFor our illustration, we consider the problem [BIBD](https://www.csplib.org/Problems/prob028/).\nWe need five integers `v, b, r, k, l` for specifying a unique instance (possibly, `b` and `r` can be set to 0, so that these values are automatically computed\naccording to a template for this problem).\nThe model is:\n\n#### File **`Bibd.py`**\n\n```python\nfrom pycsp3 import *\n\nv, b, r, k, l = data\nb = (l * v * (v - 1)) // (k * (k - 1)) if b == 0 else b\nr = (l * (v - 1)) // (k - 1) if r == 0 else r\n\n# x[i][j] is the value of the matrix at row i and column j\nx = VarArray(size=[v, b], dom={0, 1})\n\nsatisfy(\n # constraints on rows\n [Sum(row) == r for row in x],\n\n # constraints on columns\n [Sum(col) == k for col in columns(x)],\n\n # scalar constraints with respect to lambda\n [row1 * row2 == l for (row1, row2) in combinations(x, 2)]\n)\n```\n\nTo generate an XCSP3 instance (file), we can for example execute a command like:\n\n```console\npython3 Bibd.py -data=[9,0,0,3,9]\n```\n\nWith some command interpreters (shells), you may have to escape the characters '[' and ']', which gives:\n\n```console\npython3 Bibd.py -data=\\[9,0,0,3,9\\]\n```\n\n## Example 5: Rack Configuration\n\nThis example shows how you can specify a JSON file to be used as data for a model.\nFor our illustration, we consider the problem [Rack Configuration](https://www.csplib.org/Problems/prob031/).\nThe data (for a specific instance) are then initially given in a JSON file, as for example:\n\n#### File **`Rack_r2.json`**\n\n```json\n{\n \"nRacks\": 10,\n \"models\": [\n [150, 8, 150],\n [200, 16, 200]\n ],\n \"cardTypes\": [\n [20, 20],\n [40, 8],\n [50, 4],\n [75, 2]\n ]\n}\n```\n\nIn the following model, we directly unpack the components of the variable `data` (because it is automatically given under the form of a named tuple) whose\nfields are exactly those of the main object in the JSON file.\n\n#### File **`Rack.py`**\n\n```python\nfrom pycsp3 import *\n\nnRacks, models, cardTypes = data\nmodels.append([0, 0, 0]) # we add first a dummy model (0,0,0)\npowers, sizes, costs = zip(*models)\ncardPowers, cardDemands = zip(*cardTypes)\nnModels, nTypes = len(models), len(cardTypes)\n\nT = {(i, powers[i], sizes[i], costs[i]) for i in range(nModels)}\n\n# m[i] is the model used for the ith rack\nm = VarArray(size=nRacks, dom=range(nModels))\n\n# p[i] is the power of the model used for the ith rack\np = VarArray(size=nRacks, dom=powers)\n\n# s[i] is the size (number of connectors) of the model used for the ith rack\ns = VarArray(size=nRacks, dom=sizes)\n\n# c[i] is the cost (price) of the model used for the ith rack\nc = VarArray(size=nRacks, dom=costs)\n\n# nc[i][j] is the number of cards of type j put in the ith rack\nnc = VarArray(size=[nRacks, nTypes], dom=lambda i, j: range(min(max(sizes), cardDemands[j]) + 1))\n\nsatisfy(\n # linking rack models with powers, sizes and costs\n [(m[i], p[i], s[i], c[i]) in T for i in range(nRacks)],\n\n # connector-capacity constraints\n [Sum(nc[i]) <= s[i] for i in range(nRacks)],\n\n # power-capacity constraints\n [nc[i] * cardPowers <= p[i] for i in range(nRacks)],\n\n # demand constraints\n [Sum(nc[:, j]) == cardDemands[j] for j in range(nTypes)],\n\n # tag(symmetry-breaking)\n [\n Decreasing(m),\n If(\n m[0] == m[1],\n Then=nc[0][0] >= nc[1][0]\n )\n ]\n)\n\nminimize(\n # minimizing the total cost being paid for all racks\n Sum(c)\n)\n```\n\nTo generate an XCSP3 instance (file), we execute the command:\n\n```console\npython3 Rack.py -data=Rack_r2.json\n```\n\nOne might want to have the data in the JSON file with another structure, as for example:\n\n#### File **`Rack_r2b.json`**\n\n```json\n{\n \"nRacks\": 10,\n \"rackModels\": [\n {\"power\": 150, \"nConnectors\": 8, \"price\": 150},\n {\"power\": 200, \"nConnectors\": 16, \"price\": 200}\n ],\n \"cardTypes\": [\n {\"power\": 20, \"demand\": 20},\n {\"power\": 40, \"demand\": 8},\n {\"power\": 50, \"demand\": 4},\n {\"power\": 75, \"demand\": 2}\n ]\n}\n ```\n\nWe only need to modify one line from the previous model:\n\n#### File **`Rack2.py`**\n\n```python\nmodels.append(models[0].__class__(0, 0, 0)) # we add first a dummy model (0,0,0) ; we get the class of the used named tuples to build a new one\n```\n\nTo generate an XCSP3 instance (file), we execute the command:\n\n```console\npython3 Rack2.py -data=Rack_r2b.json\n```\n\n## Example 6: Nonogram\n\nThis example shows how you can use an auxiliary Python file for parsing data that are not initially given under JSON format.\nFor our illustration, we consider the problem [Nonogram](https://www.csplib.org/Problems/prob012/).\nThe data (for a specific Nonogram puzzle) are initially given in a text file as follows:\n\n1. a line stating the numbers of rows and columns,\n1. then, for each row a line stating the number of blocks followed by the sizes of all these blocks (on the same line),\n1. then, for each column a line stating the number of blocks followed by the sizes of all these blocks (on the same line).\n\nBelow, here is an example of such a text file.\n\n#### File **`Nonogram_example.txt`**\n\n```\n24 24\n0\n1\t5\n2\t3 3\n2\t1 2\n2\t2 1\n2\t1 1\n2\t3 3\n3\t1 5 1\n3\t1 1 1\n3\t2 1 1\n3\t1 1 2\n3\t3 1 3\n3\t1 3 1\n3\t1 1 1\n3\t2 1 2\n3\t1 1 1\n1\t5\n3\t1 1 1\n3\t1 1 1\n3\t1 1 1\n3\t5 1 1\n2\t1 2\n3\t2 2 4\n2\t4 9\n\n0\n0\n0\n1\t1\n1\t2\n1\t2\n2\t6 1\n3\t3 1 3\n3\t1 1 4\n4\t2 1 1 7\n5\t1 1 1 1 1\n3\t1 12 1\n5\t1 1 1 1 1\n4\t2 1 1 7\n4\t1 1 4 1\n4\t2 1 2 2\n2\t8 3\n2\t1 1\n2\t1 2\n1\t4\n1\t3\n1\t2\n1\t1\n0\n```\n\nFirst, we need to write a piece of code in Python for building a dictionary `data` that will be then used in our model (after having been automatically\nconverted to a named tuple).\nWe have first to import everything (*) from `pycsp3.problems.data.parsing`.\nWe can then add any new arbitrary item to the dictionary `data` (which is initially empty).\nThis is what we do below with two items whose keys are called `rowPatterns` and `colPatterns`.\nThe values associated with these two keys are defined as two-dimensional arrays (lists) of integers, defining the sizes of blocks.\nThe function `next_int()` can be called for reading the next integer in a text file, which will be specified on the command line (see later).\n\n#### File **`Nonogram_Parser.py`**\n\n```python\nfrom pycsp3.problems.data.parsing import *\n\nnRows, nCols = next_int(), next_int()\ndata[\"rowPatterns\"] = [[next_int() for _ in range(next_int())] for _ in range(nRows)]\ndata[\"colPatterns\"] = [[next_int() for _ in range(next_int())] for _ in range(nRows)]\n```\n\nThen, we just write the model by getting data from the variable `data`.\nThe model is totally independent of the way data were initially given (from a text file or a JSON file, for example).\nIn the code below, note how an object `Automaton` is defined from a specified pattern (list of blocks).\nAlso, for a `regular` constraint, we just write something like `scope in automaton`.\nFinally, `x[:, j]` denotes the jth column of `x`.\n\n#### File **`Nonogram.py`**\n\n```python\nfrom pycsp3 import *\n\nrows, cols = data # patterns for row and columns \nnRows, nCols = len(rows), len(cols)\n\n\ndef automaton(pattern):\n q = Automaton.q # for building state names\n transitions = []\n if len(pattern) == 0:\n n_states = 1\n transitions.append((q(0), 0, q(0)))\n else:\n n_states = sum(pattern) + len(pattern)\n num = 0\n for i, size in enumerate(pattern):\n transitions.append((q(num), 0, q(num)))\n transitions.extend((q(num + j), 1, q(num + j + 1)) for j in range(size))\n transitions.append((q(num + size), 0, q(num + size + (1 if i < len(pattern) - 1 else 0))))\n num += size + 1\n return Automaton(start=q(0), final=q(n_states - 1), transitions=transitions)\n\n\n# x[i][j] is 1 iff the cell at row i and col j is colored in black\nx = VarArray(size=[nRows, nCols], dom={0, 1})\n\nsatisfy(\n [x[i] in automaton(rows[i]) for i in range(nRows)],\n\n [x[:, j] in automaton(cols[j]) for j in range(nCols)]\n)\n```\n\nTo generate the XCSP3 instance (file), we just need to specify the name of the text file (option `-data`) and the name of the Python parser (\noption `-dataparser`).\n\n```console\npython3 Nonogram.py -data=Nonogram_example.txt -dataparser=Nonogram_Parser.py\n```\n\nMaybe, you think that it would be simpler to have directly the data in JSON file.\nYou can generate such a file with the option `-dataexport`.\nThe command is as follows:\n\n```console\npython3 Nonogram.py -data=Nonogram_example.txt -dataparser=Nonogram_Parser.py -dataexport\n```\n\nA file `Nonogram_example.json` is generated, whose content is:\n\n```json\n{\n \"colPatterns\": [\n [],\n [],\n [],\n [1],\n [2],\n [2],\n [6, 1],\n [3, 1, 3],\n [1, 1, 4],\n [2, 1, 1, 7],\n [1, 1, 1, 1, 1],\n [1, 12, 1],\n [1, 1, 1, 1, 1],\n [2, 1, 1, 7],\n [1, 1, 4, 1],\n [2, 1, 2, 2],\n [8, 3],\n [1, 1],\n [1, 2],\n [4],\n [3],\n [2],\n [1],\n []\n ],\n \"rowPatterns\": [\n [],\n [5],\n [3, 3],\n [1, 2],\n [2, 1],\n [1, 1],\n [3, 3],\n [1, 5, 1],\n [1, 1, 1],\n [2, 1, 1],\n [1, 1, 2],\n [3, 1, 3],\n [1, 3, 1],\n [1, 1, 1],\n [2, 1, 2],\n [1, 1, 1],\n [5],\n [1, 1, 1],\n [1, 1, 1],\n [1, 1, 1],\n [5, 1, 1],\n [1, 2],\n [2, 2, 4],\n [4, 9]\n ]\n}\n```\n\nWith this new file, you can directly generate the XCSP3 file with:\n\n ```console\npython3 Nonogram.py -data=Nonogram_example.json\n```\n\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Modeling Constrained Combinatorial Problems in Python",
"version": "2.4.3",
"project_urls": {
"Documentation": "https://pycsp.org/",
"Git": "https://github.com/xcsp3team/pycsp3",
"Installation": "http://pycsp.org/documentation/installation",
"Models and Data": "http://pycsp.org/models/"
},
"split_keywords": [
"ia",
"cp",
"constraint",
"modeling",
"csp",
"cop"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "204241073dbf28079aa5a14bc74dc2b55dd4be7b858c7063a3e67cf3978a8512",
"md5": "0a67cf776a905cfcc1615b02dbd69ef6",
"sha256": "2f55086d1c36e57237288d90def67b2a6fd8a11628eade90f0f900d84613b2fe"
},
"downloads": -1,
"filename": "pycsp3-2.4.3-py3-none-any.whl",
"has_sig": false,
"md5_digest": "0a67cf776a905cfcc1615b02dbd69ef6",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3",
"size": 10872306,
"upload_time": "2024-09-11T08:03:15",
"upload_time_iso_8601": "2024-09-11T08:03:15.271441Z",
"url": "https://files.pythonhosted.org/packages/20/42/41073dbf28079aa5a14bc74dc2b55dd4be7b858c7063a3e67cf3978a8512/pycsp3-2.4.3-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "827f805922ef8107c132ae269f91c94bd2a032a106befeb2b6c066d0cc55bbaf",
"md5": "9b1b78bdcc82a8a1e6a4f023124809fe",
"sha256": "9649338231174839b1dc20c6194626bc080cbb76f7a1849c20b3f12f49d5ff9c"
},
"downloads": -1,
"filename": "pycsp3-2.4.3.tar.gz",
"has_sig": false,
"md5_digest": "9b1b78bdcc82a8a1e6a4f023124809fe",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3",
"size": 10819709,
"upload_time": "2024-09-11T08:03:19",
"upload_time_iso_8601": "2024-09-11T08:03:19.015574Z",
"url": "https://files.pythonhosted.org/packages/82/7f/805922ef8107c132ae269f91c94bd2a032a106befeb2b6c066d0cc55bbaf/pycsp3-2.4.3.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-09-11 08:03:19",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "xcsp3team",
"github_project": "pycsp3",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "pycsp3"
}