3. Analysis of the voting rules

To explore the rules in more details, we created a class MovingVoterProfile, which enables to see the evolution of the candidates’ scores depending on the embeddings of one particular voter, which are changing.

[1]:
import embedded_voting as ev
import numpy as np
[2]:
moving_profile = ev.MovingVoter()

Description of the profile

The basic version of the profile contains 4 candidates and 4 voters :

  • Voter 0 is the moving voter. His initial position is the same than the Voter 1, and he gives a score of \(0.8\) to every candidate, except for Candidate 4 which receive a score of \(0.5\).
  • Voter 1, 2, and 3 respectively supports Candidate 1, 2 and 3 with a score of \(1\) and gives a score of \(0.1\) to every other candidate, except Candidate 4 which receive a score of \(0.5\) from every voter.

The following figure shows the initial configuration of the profile.

[3]:
moving_profile.embeddings.plot()
../_images/notebooks_moving_6_0.png
[3]:
<matplotlib.axes._subplots.Axes3DSubplot at 0x2531c6f3b38>

The evolution of the scores

Then we want to track the evolution of the scores of the different candidates depending on the embeddings of the Voter 0. These embeddings are changing as detailed on the following figure:

moving voter

As you can see, the voter starts in the red area and ends in the green area but always remains orthogonal to the blue voter.

Using this, we can see what happens to the different scores depending on the voting rule used.

Without any surprise, it does not change anything for rules which do not depend on the embeddings :

  • When we do the sum of the scores, every candidate has the same final score.
  • When we do the product of the scores, only the Candidate 4 (consensus) has a good score.
[4]:
rule = ev.RuleSumRatings()
moving_profile(rule).plot_scores_evolution()
../_images/notebooks_moving_9_0.png
[5]:
rule = ev.RuleShiftProduct()
moving_profile(rule).plot_scores_evolution()
../_images/notebooks_moving_10_0.png

It becomes interesting when we look at geometrical rules. What happens for the Zonotope and MaxCube rules?

  • The Consensus candidate gets the best score.
  • The second best candidate is the one supported by the Orthogonal vector. Indeed, he is supported by the moving voter and a another one which is orthogonal to the first one, and orthogonality maximizes the volume.
  • For the same reason, the candidate supported by the voter of the start gets a better score at the end, and the candidate supported by the voter of the end get the better score at the beginning.

However, you can notice that the score of some candidate is greater when the moving voter is between the two positions, and there is no intuitive interpretation of this observation.

[6]:
rule = ev.RuleZonotope()
moving_profile(rule).plot_scores_evolution()
../_images/notebooks_moving_12_0.png
[7]:
rule = ev.RuleMaxParallelepiped()
moving_profile(rule).plot_scores_evolution()
../_images/notebooks_moving_13_0.png

What happens with SVD Rules?

  • SVDNash, SVDLog and SVDSum work a bit like the Zonotope and MaxCube rules, but the scores of the candidates are always between their scores at the beginning and at the end.
  • SVDMin is not very interesting: nothing really change between the beginning and the end.
  • SVDMax is the opposite of the other rules : the Consensus candidate and the Orthogonal candidate receive the worst scores, but the candidate supported by the voter from the start get the best score at the beginning and the candidate supported by the voter from the end get the best score at the end.
[8]:
rule = ev.RuleSVDNash()
moving_profile(rule).plot_scores_evolution()
../_images/notebooks_moving_15_0.png
[9]:
rule = ev.RuleSVDMin()
moving_profile(rule).plot_scores_evolution()
../_images/notebooks_moving_16_0.png
[10]:
rule = ev.RuleSVDSum()
moving_profile(rule).plot_scores_evolution()
../_images/notebooks_moving_17_0.png
[11]:
rule = ev.RuleSVDMax()
moving_profile(rule).plot_scores_evolution()
../_images/notebooks_moving_18_0.png

Finally, we obtain a beautiful figure with the Features rule, even if it is a bit strange.

[12]:
rule = ev.RuleFeatures()
moving_profile(rule).plot_scores_evolution()
../_images/notebooks_moving_20_0.png

The evolutions of the features

Some rules associate features vectors to every candidate. That is the case of the SVDMax and the Features rules. We can show the evolution of these vectors using the same class.

You can see that there are major differences between the features of the two rules. For instance, the features of the Consensus candidate follow the moving voter for the SVDMax rule, and they are on the center of the simplex for the Features rule.

[13]:
rule = ev.RuleSVDMax()
moving_profile(rule).plot_features_evolution()
../_images/notebooks_moving_23_0.png
[14]:
rule = ev.RuleFeatures()
moving_profile(rule).plot_features_evolution()
../_images/notebooks_moving_24_0.png

More complex profiles

Of course, you can play with more complex profiles, and even change the index of the moving voter.

[15]:
scores = np.array([[1, .1, .1, .3], [.1, 1, .1, .3], [.1, .1, 1, .3]])
embs = ev.EmbeddingsGeneratorPolarized(50, 3)(polarisation=.8)
profile = ev.RatingsFromEmbeddingsCorrelated(.8, scores, 3, 4)(embs)
[16]:
embs.plot_candidates(profile)
../_images/notebooks_moving_28_0.png
[17]:
moving_profile = ev.MovingVoter(embs)

We now obtain very funny plots for the SVD Rules:

[18]:
rule = ev.RuleSVDNash()
moving_profile(rule, profile).plot_scores_evolution()
../_images/notebooks_moving_31_0.png
[19]:
rule = ev.RuleSVDSum()
moving_profile(rule, profile).plot_scores_evolution()
../_images/notebooks_moving_32_0.png
[20]:
rule = ev.RuleSVDMax()
moving_profile(rule, profile).plot_scores_evolution()
../_images/notebooks_moving_33_0.png
[21]:
rule = ev.RuleSVDMin()
moving_profile(rule, profile).plot_scores_evolution()
../_images/notebooks_moving_34_0.png
[22]:
rule = ev.RuleFeatures()
moving_profile(rule, profile).plot_scores_evolution()
../_images/notebooks_moving_35_0.png

The features between SVDMax and Features rules are now far more similar:

[23]:
rule = ev.RuleSVDMax()
moving_profile(rule, profile).plot_features_evolution()
../_images/notebooks_moving_37_0.png
[24]:
rule = ev.RuleFeatures()
moving_profile(rule, profile).plot_features_evolution()
../_images/notebooks_moving_38_0.png