Search by

mieuxvoter / majority-judgment

domi41

Rank the candidates of your polls using Majority Judgment with a fast, robust, interface-oriented and scalable algorithm.

Package info

github.com/MieuxVoter/majority-judgment-library-php

pkg:composer/mieuxvoter/majority-judgment

Statistics

Installs: 110

Dependents: 0

Suggesters: 0

Stars: 6

Open Issues: 0

2.0.0 2026-10-03 08:47 UTC

This package is auto-updated.

Last update: 2026-10-30 01:16:55 UTC


README

MIT Release Build Status Code Quality Join the Discord chat at https://discord.gg/k9YRuZPSZs

Rank candidates of Majority Judgment polls.

Features

  • Majority judgment deliberation from merit profiles
  • Fast & extensible
  • Supports billions of voters
  • Supports thousands of candidates
  • Test-Driven Development
  • No floating-point arithmetic
  • Using composer and namespaces
  • Also available as a single file

Installation

Require it in your own project, using composer:

composer require mieuxvoter/majority-judgment

If you do not have composer, for example when writing a Wordpress plugin, you can also download the whole, bundled library from a single file.

Usage example

Let's say you have a poll with three candidates and merit profiles like so:

Three merit profiles showing the amount of judgments received per grade, per candidate

You can get the rank of each candidate like so:

use MieuxVoter\MajorityJudgment\MajorityJudgment;
use MieuxVoter\MajorityJudgment\Model\Tally\ArrayPollTally;

$grades = [
    "to reject",
    "insufficient",
    "passable",
    "somewhat good",
    "good",
    "very good",
    "excellent",
];
$proposals = [
    "Arancini",
    "Burger",
    "Chips",
];
$meritProfiles = [
    [1, 1, 4, 3, 7, 4, 1], // for each proposal, tally the
    [2, 2, 5, 5, 5, 0, 2], // amount of judgments for each grade
    [0, 1, 2, 1, 6, 7, 4], // from "worst" grade to "best" grade
];

$pollTally = new ArrayPollTally($meritProfiles);
$mj = new MajorityJudgment();
$result = $mj->deliberate($pollTally);

foreach($result->getProposalResultsRanked() as $proposalResult) {
    // … Do something, for example:
    print(sprintf(
        "#%d %s (%s)\n",
        $proposalResult->getRank(),
        $proposals[$proposalResult->getIndex()],
        $grades[$proposalResult->getMedian()],
    ));
}

// #1 Chips (very good)
// #2 Arancini (good)
// #3 Burger (somewhat good)

Three ranked merit profiles showing the amount of judgments received per grade, per candidate

Tip

These images were generated with our online merit profile tool.

Unbalanced Tallies

If your tally is unbalanced, because some proposals received more judgments than others, you will need to balance the tally using one of the provided balancing methods (or your own).

Using ad hoc normalization

This is akin to using percentages to resolve Majority Judgment, instead of tallies.

This kind of balancing is relevant when:

  • there are many proposals in your poll
  • users cannot be expected to judge them all
  • you ensured that participation is somewhat balanced
  • you excluded proposals that received too little participation
$meritProfiles = [
    [3, 3, 3, 3, 3],
    [0, 1, 2, 3, 4],
    [0, 2, 4, 6, 8],
    [7, 7, 7, 7, 7],
];

$balanceUsingNormalization = true;
$pollTally = new ArrayPollTally($meritProfiles);
$mj = new MajorityJudgment();
$result = $mj->deliberate($pollTally, $balanceUsingNormalization);

// Ranks:
// 3
// 1
// 1
// 3

Warning

Due to a design choice (not using a library for big integers), this ad hoc normalization is limited to a little over 3 billion voters. If you have more voters than that, its result is unpredictable — it might work, or it might glitch out by buffer overflow.

Using a Static Default Grade

The most common balancing strategy is to consider missing judgments as of the "worst" grade. This incentivizes candidates to be clear and to promote themselves.

Here's how one can use the Balancer to balance a poll tally:

use MieuxVoter\MajorityJudgment\Balancer;

$initialPollTally = new ArrayPollTally(
    [
        "Arancini" => [1, 2, 3, 4],
        "Burger" => [4, 0, 0, 0],
        "Chips" => [0, 1, 1, 2],
    ],
);

$balancedPollTally = Balancer::balanceUsingStaticDefaultGrade($initialPollTally);

print_r($balancedPollTally->getProposalsTallies()[0]->getGradesTallies());
print_r($balancedPollTally->getProposalsTallies()[1]->getGradesTallies());
print_r($balancedPollTally->getProposalsTallies()[2]->getGradesTallies());

// [  1, 2, 3, 4 ]
// [ 10, 0, 0, 0 ]
// [  6, 1, 1, 2 ]

Using the Median Grade

I can't fathom why you'd want this, but here it is anyway:

$initialPollTally = new ArrayPollTally(
    [
        "Arancini" => [1, 2, 3, 4],
        "Burger" => [2, 0, 0, 0],
        "Chips" => [0, 1, 1, 0],
    ],
);

$balancedPollTally = Balancer::balanceUsingMedianDefaultGrade($initialPollTally);

print_r($balancedPollTally->getProposalsTallies()[0]->getGradesTallies());
print_r($balancedPollTally->getProposalsTallies()[1]->getGradesTallies());
print_r($balancedPollTally->getProposalsTallies()[2]->getGradesTallies());

// [1, 2, 3, 4]
// [10, 0, 0, 0]
// [0, 9, 1, 0]

Warning

This balancing strategy is not very interesting, nor fair. You probably should prefer normalization, or using the lowest grade.

Testing

See the tests in test/.

composer install
vendor/bin/phpunit test