GitHub - maxlit/powerindex: Calculation of power indices (e.g. Banzhaf power index, Shapley-Shubik power index etc)

6 min read Original article ↗

powerindex

A python library to compute power indices

Installation:

For development:

Youtube: Short overview
Google colab: colab

TL;DR

Calculation of power indices. Example of CLI ("px" alias) run (EEC in 1957 ):

# EEC (1958-1982): Germany, France, Italy, Netherlands, Belgium, Luxemburg
px -i b -q 12 -w 4 4 4 2 2 1
# expected result:
# 0.238,0.238,0.238,0.143,0.143,0.0

# With party labels and CSV output
px -i b -q 12 -w Germany:4 France:4 Italy:4 Netherlands:2 Belgium:2 Luxembourg:1 --csv
# expected result:
# Germany,0.238
# France,0.238
# Italy,0.238
# Netherlands,0.143
# Belgium,0.143
# Luxembourg,0.0

CLI demo

What is all about

The aim of the package is to compute different power indices of the so-called weighted voting systems (games).

Players have weights and can form coalitions. A coalition that achieves the required threshold wins.

To start with a simple example, consider a system with two parties A and B having 51 and 49 seats respectively with a simple majority rule (i.e. the threshold is 51 seats). How much power do they have? It may appear that according to the number of the seats they have 51% and 49% respectively.

However, party A can impose any decision without cooperating with party B.

It leads to a conclusion that any reasonable rule would assign to party A 100% of the power (since it wins without cooperation) and to the party B 0% of the power and not 51% to 49%.

The most popular approaches to measure power are Banzhaf and Shapley-Shubik power indices.

This package also implements the Contested Garment Rule (strictly speaking it's not a power index, but similar to some extent).

How to use it

It can be run either as a command line (px) or in python interpreter. CLI run:

Command Line Flags

  • -i (index): Power index type

    • b or bz - Banzhaf index
    • ss - Shapley-Shubik index
    • cg - Contested Garment rule
  • -q (quota): Number of votes required for a coalition to win

    • If not specified, defaults to simple majority (⌈sum(weights)/2⌉)
  • -w (weights): Weights/votes of players

    • Simple format: 3 2 1 (unlabeled)
    • Labeled format: PartyA:3 PartyB:2 PartyC:1
  • --csv: Output in CSV format with label,value columns

    • Useful for importing results into spreadsheets or other tools
  • -r (round): Number of decimal places to round to

    • Default: 3
    • Example: -r 5 for 5 decimal places
  • -a (absolute): Calculate in absolute values

    • Mainly useful for Contested Garment rule

Examples

# Basic usage with simple weights
px -i b -q 51 -w 30 25 15 10

# With party labels
px -i ss -w Union:221 SPD:127 Green:90 Left:68 BSW:39 -q 355

# CSV output with labels and custom rounding
px -i b -w PartyA:40 PartyB:30 PartyC:20 PartyD:10 --csv -r 2
# Output:
# PartyA,0.42
# PartyB,0.25
# PartyC,0.25
# PartyD,0.08

# Contested Garment with absolute values
px -i cg -q 210 -a -w 100 200 300

There's a jupyter notebook /powerindex/README.ipynb that shows some python examples.

A trivial example from the introduction:

#bash
px -i b -q 51 -w 51 49
px -i ss -q 51 -w 51 49 
#python
#%matplotlib inline
import powerindex as px
game=px.Game(quota=51,weights=[51,49])

Calculation of Banzhaf and Shapley-Shubik power indices:

#python
game.calc_banzhaf()
print(game.banzhaf)
#python
game.calc_shapley_shubik()
print(game.shapley_shubik)

Function calc() computes all available indices.

Thus, in this simple example both indices give 100% to 0% distribution.

Now let's changes the seats distribution to the parity and see what happens:

#bash
px -i b -q 51 -w 50 50
px -i ss -q 51 -w 50 50 
#python
game=px.Game(51,weights=[50,50])
game.calc()

print(game.banzhaf)
print(game.shapley_shubik)

As the result, the distribution of power is also at parity.

Now, consider a non-trivial, but still a simple examples from Wikipedia:

#bash
px -i b -q 6 -w 4 3 2 1 
#python
game=px.Game(6,[4, 3, 2, 1])
game.calc_banzhaf()
print(game.banzhaf)
[0.4166666666666667, 0.25, 0.25, 0.08333333333333333]

Interpretation is simple. A committee where 4 parties hold 40%, 30%, 20% and 10% of seats with required qualified majority of 60%, have 41.7%, 25%, 25%, 8.3% of power respectively.

In this example, having 2 or 3 seats leads to the same level of power.

Another example:

#bash
px -i b -q 6 -w 3 2 1 1 
#python
game=px.Game(6,[3, 2, 1, 1])
game.calc_banzhaf()
print(game.banzhaf)
[0.375, 0.375, 0.125, 0.125]

Notice that in the previous two examples Banzhaf and Shapley-Shubik indices coincides. It doesn't hold in general even in the games of 3 voters:

#bash
px -i ss -q 6 -w 3 2 1 1 
#python
game=px.Game(4,[3, 2, 1])
game.calc() # again it calculates all available indices
print("Banzhaf index:")
print(game.banzhaf)
print("Shapley-Shubik index:")
print(game.shapley_shubik)
Banzhaf index:
[0.6, 0.2, 0.2]
Shapley-Shubik index:
[0.6666666666666667, 0.16666666666666669, 0.16666666666666669]

Contested Garment Rule

Not exactly a power index, but similar to some extent. Contested Garment Rule offers a way to split the value of the contested asset when its value is smaller than the some of claims. It's different from the proportional rule.
e.g. if the claims are 100, 200 and 300 (i.e. 600 in sum) among three participants, and there's only 210 to split, the rule suggests the split 50, 80, 80 (and not 35, 70, 105).
Note the flag -a (absolute) to generate the split in the absolute numbers.

!px -i cg -q 210 -a -w 100 200 300
game=px.Game(210,[100, 200, 300], absolute=True)
game.calc_contested_garment()
print(game.contested_garment)

Plot results

There's a possibility to plot the power distribution as a pie chart:

#python
game=px.Game(4,[3, 2, 1])
game.calc()
game.pie_chart()

png

As you can see on the plot, the parties have numbers. In order, to put their names on the chart you need to work with Party class.

Let's take Europen Economic Community (EEC) in the years 1958-1972, its members were Germany (4 votes), France (4 votes), Italy (4 votes), Belgium (2 votes), Netherlands (2 votes) and Luxembourg (1 vote) with qualified majority of 12 votes:

#python
countries={"Germany":4,"France":4,"Italy":4,"Belgium":2,"Netherlands":2,"Luxembourg":1}
parties=[px.Party(countries[country],country) for country in countries]
game=px.Game(12,parties=parties)
game.calc()
game.pie_chart()

png

On computation

Usually the exact and fast computation of indices is based on enumeration methods implemented by dynamic programming given that the weights and thresholds are integers.

For instance, the computation of Banzhaf is O(qn) hard and computation of Shapley-Shubik is O(qn^2) hard. If the input has non-integers, then an approximation scheme is usually involved. Consult the list of literature if you want to start exploring the topic by yourself.

Literature

B.Keijzer - A Survey on the Computation of Power Indices (2008)

T.Uno - Efficient Computation of Power Indices for Weighted Majority Games (2003)

Matsui, Y. Matsui - A Survey of Algorithms for Calculating Power Indices of Weighted Majority Games (2000)

B.Meglicki - Generating functions partitioning algorithm for com­puting power indices in weighted voting games (2010)

Zyczkowski, W. Slomczynski - Voting in the European Union: the square root system of Penrose and a critical point (2004)

Citation

@misc{powerindex2024,
  author = {Maxim Litvak},
  title = {powerindex: A Python Library for Calculation of Some Common Power Indices},
  year = {2024},  % Year of the version or first release, as appropriate
  version = {0.2.4},
  howpublished = {\url{https://github.com/maxlit/powerindex}},
}