Wedding Seating Optimization

Towards a provably optimal wedding seating chart.

Python PuLP FICO Xpress GLPK.js
Jump to demo

The Problem

Our wedding was largely DIY. We had a hundred things to worry about, and fussing over table assignments was near the bottom of the list. Of all the planning tasks, there wasn't much I wanted to do less than seating.

I had just joined Grubhub's Decision Science team, where I was working on the autodispatch system, a MIP that assigns couriers to deliveries. It occurred to me that the formulation could be generalized: couriers to deliveries, guests to tables, same structure. My colleagues and I set out to develop a formulation that was adequate for the problem and performant enough to solve in reasonable time.

Our first attempt modeled affinity as distance on a social graph between guests. When the solver produced questionable pairings, we layered on explicit weight overrides to force certain guests apart. Two weeks before the wedding, the solver seated the bride's father at the same table as her single friends despite a -10,000 penalty. We scrapped the whole thing and rewrote it in FICO Xpress with explicit pairwise affinity scores: less elegant than graph distance, but it gave us direct control over the inputs that actually mattered.

The Approach

The seating problem is a constrained assignment problem. Each guest party needs to be placed at exactly one table, subject to capacity limits and relationship constraints. The natural formulation is a mixed-integer program (MIP): binary decision variables for assignments, linear constraints for capacity and compatibility, and a linear objective that maximizes total "happiness" across all tables. The original implementation used FICO Xpress; the interactive demo below is a rewrite in GLPK.js (GLPK compiled to WebAssembly), running entirely in your browser.

Formulation

Let PP be the set of guest parties, TT the set of tables, and aija_{ij} the affinity between parties ii and jj (positive means they want to sit together, negative means keep them apart).

Decision Variables

xp,t{0,1}pP,  tTx_{p,t} \in \{0, 1\} \quad \forall\, p \in P,\; t \in T
?

xp,t=1x_{p,t} = 1 if party pp is assigned to table tt.

Linearization

To express "parties ii and jj are at the same table," we introduce continuous variables yi,j,t[0,1]y_{i,j,t} \in [0, 1] with McCormick envelope constraints:

yi,j,txi,t+xj,t1y_{i,j,t} \geq x_{i,t} + x_{j,t} - 1 yi,j,txi,t,yi,j,txj,ty_{i,j,t} \leq x_{i,t}, \quad y_{i,j,t} \leq x_{j,t}
?

Since the xx variables are binary, yi,j,t=xi,txj,ty_{i,j,t} = x_{i,t} \cdot x_{j,t} at any integer feasible solution.

Constraints

Each party is assigned to exactly one table:

tTxp,t=1pP\sum_{t \in T} x_{p,t} = 1 \quad \forall\, p \in P
?

Table capacity is respected:

pPspxp,tCttT\sum_{p \in P} s_p \cdot x_{p,t} \leq C_t \quad \forall\, t \in T
?

where sps_p is the size of party pp and CtC_t is the capacity of table tt.

Objective

Maximize total happiness across all co-seated pairs:

maxtT(i,j)ai,jyi,j,t\max \sum_{t \in T} \sum_{(i,j)} a_{i,j} \cdot y_{i,j,t}
?

The solver also supports alternative objectives: maximizing the minimum per-table happiness (fairness), or minimizing the spread between the happiest and unhappiest tables.

Symmetry Breaking

Because the tables are interchangeable, any solution can be trivially "improved" by relabeling tables. Swapping table 1 and table 2, for instance, changes nothing about who sits with whom. With T!|T|! equivalent permutations of every solution, the branch-and-bound search wastes enormous effort re-exploring symmetric branches that lead to the same objective value.

To break this symmetry, we let the user fix the assignment of some guests to a particular table. These are natural choices: my parents at this table, the best man's family at that one, and so on. For each anchor party pkp_k assigned to table kk:

xpk,k=1k=0,1,x_{p_k,\, k} = 1 \quad k = 0, 1, \ldots
?

Each anchor party gives its table a unique identity, collapsing the T!|T|! equivalent permutations down to one canonical form and dramatically reducing solve time.

Results

The solver found a provably optimal assignment in under 30 seconds. We attempted to solicit feedback on the perceived optimality of the seating arrangements, but the response rate from our guests was too low to draw any meaningful conclusions, a result we attribute to confounding factors including an open bar.

Interactive Demo

This runs a full MIP solver in your browser via GLPK.js (GLPK compiled to WebAssembly). Load a sample dataset or create your own, define affinities between parties, and hit Solve.

17 people / 20 seats
Michael 1 person
Dwight 1 person
Jim & Pam 2 people
Angela 1 person
Kevin 1 person
Oscar 1 person
Stanley 1 person
Phyllis & Bob 2 people
Ryan 1 person
Kelly 1 person
Toby 1 person
Andy 1 person
Darryl 1 person
Meredith 1 person
Creed 1 person