From 6d9d8978cfb54ce180a9c4b6506f327469cb5e26 Mon Sep 17 00:00:00 2001 From: "Thomas (Tom) C. Gorordo" Date: Fri, 25 Apr 2025 17:31:03 -0700 Subject: [PATCH] tweak cgi html form with TODOs --- src/carousel/__init__.py | 52 ++-------------------------------------- src/carousel/brute.py | 3 +++ src/carousel/def_acc.py | 50 ++++++++++++++++++++++++++++++++++++++ src/cgi/form.html | 10 ++++++++ 4 files changed, 65 insertions(+), 50 deletions(-) create mode 100644 src/carousel/brute.py create mode 100644 src/carousel/def_acc.py diff --git a/src/carousel/__init__.py b/src/carousel/__init__.py index 32d8c28..c9f37dc 100644 --- a/src/carousel/__init__.py +++ b/src/carousel/__init__.py @@ -144,56 +144,8 @@ def check_stable(*args, **kwargs): return not check_unstable(*args, **kwargs) -def deferred_acceptance(applicant_rankings, reviewer_rankings): - """Find the Gale-Shapley deferred-acceptance stable matching for preferences A, R.""" - reviewer_rankings = reviewer_rankings.rename( - {reviewer_rankings.columns[0]: "applicant"} - ) - - app_prefs = rank_to_pref(applicant_rankings) - offers = app_prefs.transpose( - include_header=True, - header_name="applicant", - column_names=["pref" + str(i + 1) for i in range(app_prefs.width)], - ).with_columns(pl.coalesce(pl.all().exclude("applicant")).alias("offer")) - - # offers = pl.concat(pl.align_frames(offers, reviewer_rankings, on="applicant"), how="horizontal") - offers = pl.concat([offers, reviewer_rankings], how="align_left") - - match = pl.DataFrame( - { - r: offers.select(pl.col("applicant", "offer").sort_by(r)) - .select( - pl.when(pl.col("offer").eq(r)).then(pl.col("applicant")).otherwise(None) - ) - .select(pl.all().fill_null(strategy="backward").first()) - .to_series() - for r in reviewer_rankings.columns[1:] - } - ) # .select(pl.all().fill_null(strategy="backward").first()) - - # while check_unstable(match, applicant_rankings, reviewer_rankings): - while match.select(pl.any_horizontal(pl.all().has_nulls())).item(): - # TODO null applicant preferences that rejected - - rejected_applicants = offers.select( - pl.col("applicant").is_in(match.row(0)).alias("matched") - ) - - return match - - offers = offers.with_columns(pl.col("pref")) - - offers = offers.with_columns( - pl.coalesce( - # TODO: select prefn columns using a regex - ).alias("offer") - ) - - # TODO update match - - # else if stable - return match +from .brute import * +from .def_acc import * def main() -> None: diff --git a/src/carousel/brute.py b/src/carousel/brute.py new file mode 100644 index 0000000..fcb7d83 --- /dev/null +++ b/src/carousel/brute.py @@ -0,0 +1,3 @@ +def brute(applicant_rankings, reviewer_rankings): + """Brute force search for stable matches.""" + pass diff --git a/src/carousel/def_acc.py b/src/carousel/def_acc.py new file mode 100644 index 0000000..63808e1 --- /dev/null +++ b/src/carousel/def_acc.py @@ -0,0 +1,50 @@ +def deferred_acceptance(applicant_rankings, reviewer_rankings): + """Find Gale-Shapley deferred-acceptance stable matchings for preferences A, R.""" + reviewer_rankings = reviewer_rankings.rename( + {reviewer_rankings.columns[0]: "applicant"} + ) + + app_prefs = rank_to_pref(applicant_rankings) + offers = app_prefs.transpose( + include_header=True, + header_name="applicant", + column_names=["pref" + str(i + 1) for i in range(app_prefs.width)], + ).with_columns(pl.coalesce(pl.all().exclude("applicant")).alias("offer")) + + # offers = pl.concat(pl.align_frames(offers, reviewer_rankings, on="applicant"), how="horizontal") + offers = pl.concat([offers, reviewer_rankings], how="align_left") + + match = pl.DataFrame( + { + r: offers.select(pl.col("applicant", "offer").sort_by(r)) + .select( + pl.when(pl.col("offer").eq(r)).then(pl.col("applicant")).otherwise(None) + ) + .select(pl.all().fill_null(strategy="backward").first()) + .to_series() + for r in reviewer_rankings.columns[1:] + } + ) # .select(pl.all().fill_null(strategy="backward").first()) + + # while check_unstable(match, applicant_rankings, reviewer_rankings): + while match.select(pl.any_horizontal(pl.all().has_nulls())).item(): + # TODO null applicant preferences that rejected + + rejected_applicants = offers.select( + pl.col("applicant").is_in(match.row(0)).alias("matched") + ) + + return match + + offers = offers.with_columns(pl.col("pref")) + + offers = offers.with_columns( + pl.coalesce( + # TODO: select prefn columns using a regex + ).alias("offer") + ) + + # TODO update match + + # else if stable + return match diff --git a/src/cgi/form.html b/src/cgi/form.html index fbb5954..fdb7a5f 100644 --- a/src/cgi/form.html +++ b/src/cgi/form.html @@ -399,8 +399,14 @@

Tabular Input

You can enter tables of preferences directly in the first tab of the form above. + + TODO

Example:

+ Suppose we want to figure out a Grad TA-to-Class assignment. Graduate students will be our "Applicants" + - each one will be assigned a class to TA -, and classes will be our "Reviewers" - taking a quota of needed TAs. + + TODO

File Upload

@@ -410,8 +416,12 @@ Two file formats are supported, Excel and CSV:

Excel

+ + TODO

CSV

+ + TODO