# Tutorial¶

```>>> from whalrus import *
```

## Quick start¶

Some simple elections:

```>>> RulePlurality(['a', 'a', 'b', 'c']).winner_
'a'
>>> RuleBorda(['a > b > c', 'b > c > a']).gross_scores_
{'a': 2, 'b': 3, 'c': 1}
```

Elections can optionally have weights and voter names:

```>>> RulePlurality(
...     ['a', 'a', 'b', 'c'], weights=[1, 1, 3, 2],
...     voters=['Alice', 'Bob', 'Cate', 'Dave']
... ).winner_
'b'
```

The tie-breaking rule can be specified:

```>>> RulePlurality(['a', 'a', 'b', 'b', 'c'], tie_break=Priority.ASCENDING).winner_
'a'
```

## Computed attributes of an election¶

```>>> plurality = RulePlurality(['a', 'a', 'b', 'b', 'c'], tie_break=Priority.ASCENDING)
```

Once the election is defined, you can access its computed attributes, whose names end with an underscore:

```>>> plurality.candidates_
{'a', 'b', 'c'}
>>> plurality.gross_scores_
{'a': 2, 'b': 2, 'c': 1}
>>> plurality.scores_
{'a': Fraction(2, 5), 'b': Fraction(2, 5), 'c': Fraction(1, 5)}
>>> plurality.best_score_
Fraction(2, 5)
>>> plurality.worst_score_
Fraction(1, 5)
>>> plurality.order_
[{'a', 'b'}, {'c'}]
>>> plurality.strict_order_
['a', 'b', 'c']
>>> plurality.cowinners_
{'a', 'b'}
>>> plurality.winner_
'a'
>>> plurality.cotrailers_
{'c'}
>>> plurality.trailer_
'c'
```

## General syntax¶

In the most general syntax, firstly, you define the rule and enter its options:

```>>> plurality = RulePlurality(tie_break=Priority.ASCENDING)
```

Secondly, you use it as a callable to load a particular election (profile, set of candidates):

```>>> plurality(ballots=['a', 'b', 'c'], weights=[2, 2, 1], voters=['Alice', 'Bob', 'Cate'],
...           candidates={'a', 'b', 'c', 'd'})  # doctest:+ELLIPSIS
<... object at ...>
```

Finally, you can access the computed variables:

```>>> plurality.gross_scores_
{'a': 2, 'b': 2, 'c': 1, 'd': 0}
```

Later, if you wish, you can load another profile with the same voting rule, and so on.

## Under the hood¶

A `whalrus.Ballot` contains the message emitted by the voter, but also some contextual information such as the set of candidates that were available at the moment when she cast her ballot:

```>>> ballot = BallotOrder('a > b ~ c')
>>> ballot
BallotOrder(['a', {'b', 'c'}], candidates={'a', 'b', 'c'})
```

This architecture allows Whalrus to deal with asynchronous elections where the set of candidates may vary during the election itself (such as some asynchronous online polls).

A `whalrus.Profile` contains a list of `whalrus.Ballot` objects, a list of weights and a list of voters:

```>>> profile = Profile(['a > b ~ c', 'a ~ b > c'])
>>> profile.ballots[0]
BallotOrder(['a', {'b', 'c'}], candidates={'a', 'b', 'c'})
>>> profile.weights
[1, 1]
>>> profile.voters
[None, None]
```

Internally, a voting rule is always applied to a `whalrus.Profile`. Hence, if the inputs are given in a “loose” format, they are converted to a `whalrus.Profile`:

```>>> borda = RuleBorda(['a > b ~ c', 'a ~ b > c'])
>>> borda.profile_converted_  # doctest:+ELLIPSIS
Profile(ballots=[BallotOrder(['a', {'b', 'c'}], candidates={'a', 'b', 'c'}), ...)
```

Under the hood, some conversions are performed so that a variety of inputs are understood by Whalrus. In the example above, the first ballot was manually entered as `a > b ~ c`. In the absence of other information, Whalrus then considered that only candidates a, b and c were available when this voter cast her ballot. If you want to give more detailed information, the most general syntax consists in using the constructors of classes `whalrus.Profile`, `whalrus.Ballot` and their subclasses:

```>>> a_more_complex_ballot = BallotOrder('a > b ~ c', candidates={'a', 'b', 'c', 'd', 'e'})
```

The ballot above means that the voter emitted the message ‘a > b ~ c’ in a context where the candidates d and e where also available, i.e. she deliberately abstained about these two candidates.

## Change the candidates¶

It is possible to change the set of candidates, compared to when the voters cast their ballots.

```>>> profile = Profile(['a > b > c', 'a ~ b > c'])
>>> RulePlurality(profile, candidates={'b', 'c'}).gross_scores_
{'b': 2, 'c': 0}
```