Skip to main content

Python wrapper around Clingo/Answer Set Programming

Project description

<p align="center">
<img src="clyngor.png"/><br>
</p>

Handy python wrapper around [Potassco's Clingo](https://potassco.org/) [ASP solver](https://en.wikipedia.org/wiki/Answer%20set%20programming).



## Example
Clyngor offers multiple interfaces. The followings are all equivalent.
(they search for [formal concepts](https://en.wikipedia.org/wiki/Formal_concept_analysis))

```python
from clyngor import ASP, solve

answers = ASP("""
rel(a,(c;d)). rel(b,(d;e)).
obj(X):- rel(X,_) ; rel(X,Y): att(Y).
att(Y):- rel(_,Y) ; rel(X,Y): obj(X).
:- not obj(X):obj(X).
:- not att(Y):att(Y).
""")
for answer in answers:
print(answer)
```

The same, but with the lower level function expecting files:

```python
answers = solve(inline="""
rel(a,(c;d)). rel(b,(d;e)).
obj(X):- rel(X,_) ; rel(X,Y): att(Y).
att(Y):- rel(_,Y) ; rel(X,Y): obj(X).
:- not obj(X):obj(X).
:- not att(Y):att(Y).
""")
```

More traditional interface, using file containing the ASP source code:

```python
answers = solve('concepts.lp'): # also accepts an iterable of file
```

More examples are available in [the unit tests](clyngor/test/).



## Chaining
Once you get your answers, clyngor allows you to specify
the answer sets format using builtin methods:

```python
for answer in answers.by_predicate.first_arg_only:
print('{' + ','.join(answer['obj']) + '} × {' + ','.join(answer['att']) + '}')
```

And if you need a [*pyasp-like*](https://github.com/sthiele/pyasp) interface:

```python
for answer in answers.as_pyasp:
print('{' + ','.join(a.args()[0] for a in answer if a.predicate == 'obj')
+ '} × {' + ','.join(a.args()[0] for a in answer if a.predicate == 'att') + '}')
```

Currently, there is only one way to see all chaining operator available:
[the source code of the Answers object](clyngor/answers.py).
(or `help(clyngor.Answers)`)




## Python embedding
Clyngor provides user with `converted_types` function,
allowing one to avoid boilerplate code based on type annotation when
calling python from inside ASP code.

Example (see [tests](clyngor/test/test_upapi.py) for more):

```python
#script(python)
from clyngor.upapi import converted_types
@converted_types
def f(a:str, b:int):
yield a * b
yield len(a) * b
#end.

p(X):- (X)=@f("hello",2).
p(X):- (X)=@f(1,2). % ignored, because types do not match
```

Without `converted_types`, user have to ensure that `f` is a function returning a list,
and that arguments are of the expected type.


## Debugging
TODO: Clyngor is also able to parse an ASP program to generate debugging help.

## Solver scripting
TODO: Clyngor is able to manage python inside ASP source code, allowing user to fully control the solving.



## Alternatives
[pyasp](https://github.com/sthiele/pyasp) comes into mind,
but do not (yet) supports clingo alone.



## Installation

pip install clyngor

You must have `clingo` in your path. Depending on your OS, it might be done with a system installation,
or through downloading and (compilation and) manual installation.

[See the doc](https://potassco.org/doc/start/).




## Tips
### Careful parsing
By default, clyngor uses a very simple parser (yeah, `str.split`) in order to achieve time efficiency in most time.
However, when asked to compute a particular output format (like `parse_args`) or an explicitely *careful parsing*,
clyngor will use a much more robust parser (made with an [arpeggio](http://www.igordejanovic.net/Arpeggio/) grammar).

### Import/export
See the [`utils` module](clyngor/utils.py) and its [tests](clyngor/test/test_utils.py),
which provides high level routines to save and load answer sets.


### Define the path to clingo binary

```python
import clyngor
clyngor.CLINGO_BIN_PATH = 'path/to/clingo'
```

Note that it will be the very first parameter to [`subprocess.Popen`](https://docs.python.org/3/library/subprocess.html#popen-constructor).


### `clyngor.solve` parameters
The `solve` functions allow to pass explicitely some parameters to clingo
(including number of models to yield, time-limit, and constants).
Using the `options` parameter is just fine, but with the explicit parameters some verifications
are made against data (mostly about type).

Therefore, the two followings are equivalent ; but the first is more readable and will crash earlier with a better error message if `n` is not valid:

```python
solve('file.lp', nb_model=n)
solve('file.lp', options='-n ' + str(n))
```



## FAQ

### Dinopython support ?
No.

### Contributions ?
Yes.

### Why clyngor ?
No, it's pronounced [*clyngor*](https://www.youtube.com/watch?v=RyU99BCNRuU#t=50s).




## Further ideas
- [timeout](https://stackoverflow.com/a/12698328/3077939) in addition to time-limit
- ASP source code debugging generator (started in [clyngor-parser](clyngor-parser))
- use official API for speeding up things when available (currently in-dev in branch [impl/propagators](https://github.com/Aluriak/clyngor/tree/impl/propagator))
- add propagators in-process (currently in-dev in branch [impl/propagators](https://github.com/Aluriak/clyngor/tree/impl/propagator))



## from pyasp to clyngor
If you have a project that makes use of pyasp, but need clingo instead of gringo+clasp, one way to go is to use clyngor instead.

Here was my old code:

```python
from pyasp import asp

def solving(comp, graph):
programs = [comp, graph]
clasp_options = ['--opt-mode=optN', '--parallel-mode=4', '--project']
solver = asp.Gringo4Clasp(clasp_options=clasp_options)
print("solver run as: `clingo {} {}`".format(' '.join(programs), clasp_options))
at_least_one_solution = False
for answerset in solver.run(programs, collapseAtoms=False):
yield answerset

def find_direct_inclusions(model) -> dict:
programs = [ASP_SRC_INCLUSION]
solver = asp.Gringo4Clasp()
add_atoms = ''.join(str(atom) + '.' for atom in model)
answers = tuple(solver.run(programs, collapseAtoms=False,
additionalProgramText=add_atoms))
return answers
```

And here is the version using clyngor, that pass the exact same unit tests:

```python
import clyngor

def solving(comp, graph):
programs = [comp, graph]
clasp_options = '--opt-mode=optN', '--parallel-mode=4', '--project'
answers = clyngor.solve(programs, options=clasp_options)
print("solver run as: `{}`".format(answers.command))
for answerset in answers.as_pyasp.parse_args.int_not_parsed:
yield answerset

def find_direct_inclusions(model) -> dict:
programs = [ASP_SRC_INCLUSION]
add_atoms = ''.join(str(atom) + '.' for atom in model)
answers = tuple(clyngor.solve(programs, inline=add_atoms).as_pyasp.parse_args)
return answers
```

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

clyngor-0.3.2.tar.gz (19.4 kB view details)

Uploaded Source

File details

Details for the file clyngor-0.3.2.tar.gz.

File metadata

  • Download URL: clyngor-0.3.2.tar.gz
  • Upload date:
  • Size: 19.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No

File hashes

Hashes for clyngor-0.3.2.tar.gz
Algorithm Hash digest
SHA256 0301ca1da365c945579d766db49c397755220d51d57668fb2f3f25ac7c65e4c9
MD5 05b75add478f2c78c6c413e6def544bb
BLAKE2b-256 e6306b8445136b0c287eed83895eb339101325ff13d44bd514f1a5b00f69db9a

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page