Metadata-Version: 2.4
Name: interregnum
Version: 0.2.0
Summary: multi-district electoral systems
Author-email: wmj <wmj.py@gmx.com>
License: LGPL-3.0-or-later
Project-URL: BugReports, https://gitlab.com/wmj/interregnum/-/issues
Project-URL: Source, https://gitlab.com/wmj/interregnum
Project-URL: Documentation, https://interregnum.readthedocs.io
Keywords: electoral systems,proportional vote,preferential vote,condorcet,dhondt,hare,webster,adams,jefferson,sainte laguë,compensatory systems,huntington hill,bi-proportional system,electoral thresholds,gregory transfers,imperiali,droop,hagenbach bischoff,dean,highest averages,largest remainder,iterative divisor,ranked pairs,borda,copeland,minimax,first past the post,mixed member,levelling seats,additional member,instant run-off
Classifier: Environment :: Console
Classifier: Topic :: Utilities
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: PyYAML
Requires-Dist: typing_extensions
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Provides-Extra: docs
Requires-Dist: sphinx; extra == "docs"
Requires-Dist: myst-parser; extra == "docs"
Provides-Extra: dev
Requires-Dist: build; extra == "dev"
Requires-Dist: sphinx; extra == "dev"
Requires-Dist: reuse; extra == "dev"
Requires-Dist: interregnum[docs,test]; extra == "dev"
Dynamic: license-file

<!--
SPDX-FileCopyrightText: 2025 wmj <wmj.py@gmx.com>

SPDX-License-Identifier: LGPL-3.0-or-later
-->

![L. Cassius Longinus. 60 BC. AR Denarius"](cassius-longinus-denarius.png){ width="330px" margin="0 auto" align="center" }

# interregnum

This package provides electoral apportionment methods and it allows to build electoral systems based on multiple constituencies/districts.

Features:

- proportional, preferential and bi-proportional methods
- compensatory systems
- district based systems
- constraints definition system (for example: vote threshold)


## Installation

`interregnum` is available on [PyPI](https://pypi.org/project/interregnum/). It can be installed using `pip`:

```console
pip install interregnum
```

Python $\ge 3.10$ is required.


## Quickstart

### Command line interface

:::{seealso}
For details, read the {doc}`CLI documentation <cli>`.
:::

The CLI app allows to calculate some data using an electoral system.

Let's create a sample electoral system: use the Highest Averages method using the D'Hondt divisor.

```yaml
# freedonia.yaml
name: Freedonia - Local elections 1933
method: highest_averages
method_params:
    divisor_f: dhondt
seats: 3
candidates:
- name: Freedonia for Rufus T. Firefly
  votes: 8950
- name: Sylvania for Louis Clahern
  votes: 5203
```

Now, let's calculate:

```console
interregnum-cli calc freedonia.yaml results.json --results seats.tsv
```

The original YAML file has been saved to a JSON file named `results.json`, with the same information, plus a field results containing the allocated seats and additional information about what the method internally did (event log, random state, etc.):

```json
{
    "resume_allocation": false,
    "initial_seats": "min_seats",
    "method": "highest_averages",
    "method_params": {
        "divisor_f": "dhondt"
    },
    "type": "district",
    "name": "Freedonia - Local elections 1933",
    "result": {
        "allocation": [
            {
                "name": {
                    "name": "Freedonia for Rufus T. Firefly",
                    "alliance": "Freedonia for Rufus T. Firefly"
                },
                "votes": 8950,
                "seats": 2
            },
            {
                "name": {
                    "name": "Sylvania for Louis Clahern",
                    "alliance": "Sylvania for Louis Clahern"
                },
                "votes": 5203,
                "seats": 1
            }
        ],
        "data": {
            "log": [
                {
                    "EVENT": "winner",
                    "target": {
                        "name": "Freedonia for Rufus T. Firefly",
                        "alliance": "Freedonia for Rufus T. Firefly"
                    },
                    "criterion": "best_quotient",
                    "quota": 8950
                },
                {
                    "EVENT": "winner",
                    "target": {
                        "name": "Sylvania for Louis Clahern",
                        "alliance": "Sylvania for Louis Clahern"
                    },
                    "criterion": "best_quotient",
                    "quota": 5203
                },
                {
                    "EVENT": "winner",
                    "target": {
                        "name": "Freedonia for Rufus T. Firefly",
                        "alliance": "Freedonia for Rufus T. Firefly"
                    },
                    "criterion": "best_quotient",
                    "quota": 4475
                }
            ],
            "max_quota": 8950,
            "min_quota": 4475,
            "remaining_seats": 0
        },
        "deterministic": true,
        "random_state": {}
    },
    "candidates": [
        {
            "name": "Freedonia for Rufus T. Firefly",
            "votes": 8950,
            "groups": []
        },
        {
            "name": "Sylvania for Louis Clahern",
            "votes": 5203,
            "groups": []
        }
    ],
    "seats": 3
}
```

Additionally, we saved a tabular file `seats.tsv` including only the allocation result:

```text
node    district        name    alliance        votes   seats   min_seats       max_seats       groups  meta
Freedonia - Local elections 1933                Freedonia for Rufus T. Firefly  Freedonia for Rufus T. Firefly  8950    2       0
Freedonia - Local elections 1933                Sylvania for Louis Clahern      Sylvania for Louis Clahern      5203    1       0
```

### Python library

:::{seealso}
For details, read the {doc}`API documentation <modules/modules>`.
:::

We will calculate the same data using the `HighestAveragesAllocator` class:

```python
from interregnum.methods.singlevote import HighestAveragesAllocator

# create a d'Hondt allocator
dhondt = HighestAveragesAllocator("dhondt")
# allocate 3 seats for given set of votes using the created allocator
result = dhondt(
    candidates=[("Freedonia for Rufus T. Firefly", 8950),
                ("Sylvania for Louis Clahern", 5203)],
    seats=3
)

# content: result.allocation
[Candidate(name='Freedonia for Rufus T. Firefly', votes=8950, seats=2),
 Candidate(name='Sylvania for Louis Clahern', votes=5203, seats=1)]

# content: result.data.log
[QuotaWinnerEvent(target='Freedonia for Rufus T. Firefly', criterion='best_quotient', quota=Fraction(8950, 1)),
 QuotaWinnerEvent(target='Sylvania for Louis Clahern', criterion='best_quotient', quota=Fraction(5203, 1)),
 QuotaWinnerEvent(target='Freedonia for Rufus T. Firefly', criterion='best_quotient', quota=Fraction(4475, 1))]
```

### Electoral systems based on districts

It is possible to create complex electoral systems whose districts can be expressed as a [directed acyclic graph](https://en.wikipedia.org/wiki/Directed_acyclic_graph). A district can depend on another district's elements: tallied votes or results.

<!--
SPDX-FileCopyrightText: 2025 wmj <wmj.py@gmx.com>

SPDX-License-Identifier: LGPL-3.0-or-later
-->

## Changelog


### 0.2.0

- Command line: generate a dependency graph in Graphviz DOT format from an electoral system


### 0.1.0

- Initial public release
