Skip to main content

preference-grouping-solver

Assigns students to capacity-limited groups so that the sum of their preference ranks is minimal, solved exactly as a mixed-integer linear program with SciPy/HiGHS.

Installation

uv sync                  # runtime
uv sync --all-extras     # + faker for generating synthetic data

Input

Each entry ranks groups from most to least preferred:

{
    "student_id": 1000000,
    "group_order": [2, 1, 6, 14, 3, 8, 4, 7, 9, 5]
}
  • group_order may be partial; every unranked group receives the worst rank (groups).
  • Groups must be unique and within 1..groups.
  • Input is either a directory of *.json files (one entry or a list of entries per file) or a single JSON file containing a list of entries.

Invalid entries are skipped and reported with their location (file or file[row]) and kind:

kind cause
parse unreadable file or invalid JSON
schema missing/invalid field, repeated or unknown group
duplicate student_id already loaded from an earlier entry

Configuration

field default description
input required directory or JSON file
output group_assignments.csv CSV export (student,group,rank)
groups 16 number of groups, numbered 1..groups
max_group_size 9 maximum students per group
min_group_size 0 minimum students per group
rank_exponent 1.0 cost is rank ** exponent; > 1 avoids very bad placements

Usage

CLI

preference-grouping-solver --input preferences/ --groups 16 --max-group-size 9
preference-grouping-solver --config examples/config.json --rank-exponent 1
python -m preference_grouping_solver --help

Values from --config (JSON, paths relative to the working directory) are overridden by CLI flags. The exit code is 0 on success, 1 if no assignment exists and 2 for invalid arguments.

Python

from preference_grouping_solver import Solver, SolverConfig, load

config = SolverConfig(groups=8, max_group_size=9)
loaded = load("examples/preferences.json", groups=config.groups)

solution = Solver(config).solve(loaded.preferences)
solution.assignments        # student -> group, rank
solution.groups             # size, mean_rank, first_choices per group
solution.rank_distribution  # students per assigned rank
solution.to_csv("group_assignments.csv")

Output example

groups
┏━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ group ┃ size ┃ mean_rank ┃ first_choices ┃
┡━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ 1     │ 9/9  │ 1.00      │ 11            │
│ 2     │ 6/9  │ 1.17      │ 5             │
│ …     │ …    │ …         │ …             │
└───────┴──────┴───────────┴───────────────┘
assigned ranks
┏━━━━━━┳━━━━━━━━━━┓
┃ rank ┃ students ┃
┡━━━━━━╇━━━━━━━━━━┩
│ 1    │ 54       │
│ 2    │ 6        │
└──────┴──────────┘
students: 60, mean rank: 1.10
assignments written to examples/group_assignments.csv

Examples

examples/ contains 60 synthetic students for 8 groups, both as a directory and as a combined file:

uv run python examples/generate.py    # regenerate the data with faker
uv run python examples/run.py         # Python API
uv run preference-grouping-solver --config examples/config.json

Development

uv sync --all-extras
uv run pytest

Tests use faker-generated data and verify optimality against brute force on small instances.

Metadata

Release files for preference-grouping-solver 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for preference-grouping-solver 0.2.0
File Size Uploaded Provenance Actions
preference_grouping_solver-0.2.0.tar.gz 65.0 kB Without provenance Details for preference_grouping_solver-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for preference-grouping-solver 0.2.0
File Interpreter ABI Platform Actions
preference_grouping_solver-0.2.0-py3-none-any.whl Python 3 none any Details for preference_grouping_solver-0.2.0-py3-none-any.whl

Total release size: 74.7 kB

Release files / preference_grouping_solver-0.2.0.tar.gz

Download URL preference_grouping_solver-0.2.0.tar.gz
Size 65.0 kB
Tags Source
SHA-256 checksum
How to use checksums
25a31040b30e86156e6ba38c46384d02ad9284c7e68d4171dc75f8771ebc89db
BLAKE2b-256 checksum
How to use checksums
14af71f29b0183ab1488968824774be0c226be717b4fce1327f80b261b1862d1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.6.16

Release files / preference_grouping_solver-0.2.0-py3-none-any.whl

Download URL preference_grouping_solver-0.2.0-py3-none-any.whl
Size 9.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9ab6ef7044f44604ef6a7f2fb9475a67b20960fe27e1cb62d0afaa85cc266c66
BLAKE2b-256 checksum
How to use checksums
8448376ae3a65db5fe91a419a5e26cd35be39da9f85da95e58772ccfddca373c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.6.16

Security for preference-grouping-solver 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page