Metadata-Version: 2.3
Name: afsolver
Version: 0.1.0
Summary: Lightweight solver for Dung-style argumentation framework semantics (conflict-free, admissible, complete, preferred, grounded, stable extensions)
Author: Meng Li, Timothy McPhillips, Bertram Ludäscher
Requires-Python: >=3.13
Project-URL: Repository, https://github.com/meng6/afsolver
Description-Content-Type: text/markdown

# afsolver

A lightweight Python solver for [Dung-style abstract argumentation
framework](https://en.wikipedia.org/wiki/Argumentation_framework) (AF)
semantics: conflict-free, admissible, complete, preferred, grounded, and
stable.

## Install

```sh
uv add afsolver
```

or with pip:

```sh
pip install afsolver
```

## Quick start

```python
from afsolver import ArgumentationFramework

af = ArgumentationFramework(
    arguments=["a", "b", "c"],
    attacks=[("a", "b"), ("b", "a"), ("c", "b")],  # a<->b mutual attack, c attacks b
)

af.preferred()   # [frozenset({'a', 'c'})]
af.grounded()    # frozenset({'a', 'c'})
af.extensions()  # dict with all six semantics at once
```

`arguments` is optional — if omitted, arguments are inferred from the
`attacks` pairs (deduplicated, in order of first appearance):

```python
af = ArgumentationFramework(attacks=[("b", "a"), ("c", "b"), ("d", "c")])
af.arguments  # ('b', 'a', 'c', 'd')
```

Attack pairs can be tuples or lists — both are accepted and normalized to
tuples internally.

## API

`ArgumentationFramework(arguments=(), attacks=())`

| Method | Returns |
|---|---|
| `.conflict_free()` | `list[frozenset]` — conflict-free sets |
| `.admissible()` | `list[frozenset]` — admissible sets |
| `.complete()` | `list[frozenset]` — complete extensions |
| `.preferred(contains=None)` | `list[frozenset]` — preferred (maximal admissible) extensions, optionally restricted to those containing all arguments in `contains` |
| `.grounded()` | `frozenset` — the (unique) grounded extension |
| `.stable()` | `list[frozenset]` — stable extensions |
| `.minimal_admissible(contains=None)` | `list[frozenset]` — non-empty admissible sets minimal by set inclusion, optionally restricted to those containing all arguments in `contains` |
| `.extension(name)` | dispatch to any of the six semantics above by string name |
| `.extensions()` | `dict[str, ...]` — all six semantics computed at once |

### `preferred` with a `contains` filter

Restrict the preferred extensions to those that include a given argument
(or set of arguments) — useful for credulous-acceptance-style queries:

```python
af = ArgumentationFramework(
    ["a", "b", "c", "d", "e"],
    [("b", "a"), ("c", "b"), ("d", "b"), ("c", "e"), ("e", "c")],
)

af.preferred()                     # [{'a', 'c', 'd'}, {'a', 'd', 'e'}]
af.preferred(contains=["a", "c"])  # [{'a', 'c', 'd'}]
```

### `minimal_admissible`

Useful for finding the smallest admissible sets that justify
including a given argument (or set of arguments):

```python
af = ArgumentationFramework(
    ["a", "b", "c", "d", "e"],
    [("b", "a"), ("c", "b"), ("d", "b"), ("c", "e"), ("e", "c")],
)

af.minimal_admissible()                # [{'c'}, {'d'}, {'e'}]
af.minimal_admissible(contains=["a"])  # [{'a', 'c'}, {'a', 'd'}]
```

The empty set is always excluded — it's trivially admissible and a subset
of everything else, so including it would hide the smallest non-trivial
results.

## Development

```sh
uv sync            # install project + dev dependencies
uv run pytest -q   # run tests
uv build           # build sdist + wheel
```
