Home | Benchmarks | Categories | Atom Feed

Posted on Tue 09 June 2026 under Solvers

Optimal Seating on the Airbus A380

Earlier this week, I came across a paper titled "Optimizing Aircraft Seating Arrangement[s] for the Airbus A380 Flight SQ322". SQ322 refers to Singapore Airlines' code for their daily Singapore to London Heathrow route.

This paper has a counterpart codebase on GitHub where the paper's authors use a few Python packages to run a solver that aims to maximise revenue with an optimal seating allocation between four classes on Singapore Airlines' SQ322 route.

One of the paper's authors, Inzaghi Moniaga appears to have graduated from UBC last month and now works on the Amazon Alexa team in Vancouver, Canada.

In this post, I'll run the solver and analyse its results.

My Workstation

I'm using a 5.7 GHz AMD Ryzen 9 9950X CPU. It has 16 cores and 32 threads and 1.2 MB of L1, 16 MB of L2 and 64 MB of L3 cache. It has a liquid cooler attached and is housed in a spacious, full-sized Cooler Master HAF 700 computer case.

The system has 96 GB of DDR5 RAM clocked at 4,800 MT/s and a 5th-generation, Crucial T700 4 TB NVMe M.2 SSD which can read at speeds up to 12,400 MB/s. There is a heatsink on the SSD to help keep its temperature down. This is my system's C drive.

The system is powered by a 1,200-watt, fully modular Corsair Power Supply and is sat on an ASRock X870E Nova 90 Motherboard.

I'm running Ubuntu 24 LTS via Microsoft's Ubuntu for Windows on Windows 11 Pro. In case you're wondering why I don't run a Linux-based desktop as my primary work environment, I'm still using an Nvidia GTX 1080 GPU which has better driver support on Windows and ArcGIS Pro only supports Windows natively.

Installing Prerequisites

I'll use Python 3.12 in this post.

$ sudo add-apt-repository ppa:deadsnakes/ppa
$ sudo apt update
$ sudo apt install \
    python3-pip \
    python3.12-venv

I'll set up a Python Virtual Environment and install Google's OR-Tools optimisation suite.

$ python3 -m venv ~/.a380_seating
$ source ~/.a380_seating/bin/activate
$ pip install ortools

I'll clone the GitHub repository containing the solver's assets.

$ git clone \
    https://github.com/InMDev/Optimizing-Seating-Arrangement-for-A380/ \
    ~/a380_seating

I'll use DuckDB, along with its H3, JSON, Lindel, Parquet and Spatial extensions in this post.

$ cd ~
$ wget -c https://github.com/duckdb/duckdb/releases/download/v1.5.1/duckdb_cli-linux-amd64.zip
$ unzip -j duckdb_cli-linux-amd64.zip
$ chmod +x duckdb
$ ~/duckdb
INSTALL h3 FROM community;
INSTALL lindel FROM community;
INSTALL json;
INSTALL parquet;
INSTALL spatial;

I'll set up DuckDB to load every installed extension each time it launches.

$ vi ~/.duckdbrc
.timer on
.width 180
LOAD h3;
LOAD lindel;
LOAD json;
LOAD parquet;
LOAD spatial;

A Double Decker Airliner

The Airbus A380 typically serves upwards of 63 airports with commercial services at any one time. There are 140 airports that are certified for it and there are around 400 airports that technically handle one landing in an emergency.

This is a screenshot from Flightradar24 showing the airborne A380s around the world this evening.

Airbus A380 Seating

Emirates, being the largest operator of the A380, had several about to arrive back home in Dubai when I took this screenshot.

Airbus A380 Seating

The A380 has a two-deck layout with a suggested seat layout for 555 passengers across three classes. The seat layout is configurable and even an 853-passenger, all-economy layout is possible if an airline wanted.

Below is an external 3D rendering of the aircraft that I sourced from Airbus' A380 airport and maintenance planning manual.

Airbus A380 Seating

This is the upper deck layout in their suggested configuration.

Airbus A380 Seating

This is the lower / main deck layout in their suggested configuration.

Airbus A380 Seating

Maximising Revenue

The GitHub repository has a CSV file containing floor area sizes for each section of the A380. Below are its contents.

$ cd ~/a380_seating
$ ~/duckdb -c "FROM 'areaData.csv'"
┌─────────┬────────┬────────┬─────────┐
│ section │ width  │ length │  area   │
│  int64  │ double │ double │ double  │
├─────────┼────────┼────────┼─────────┤
│       1 │   1.57 │  10.18 │ 15.9826 │
│       2 │   2.14 │   7.64 │ 16.3496 │
│       3 │   1.57 │  10.18 │ 15.9826 │
│       4 │   1.57 │  14.56 │ 22.8592 │
│       5 │   2.14 │  12.14 │ 25.9796 │
│       6 │   1.57 │  16.18 │ 25.4026 │
│       7 │   1.57 │  12.06 │ 18.9342 │
│       8 │   2.14 │  10.85 │  23.219 │
│       9 │   1.57 │  12.06 │ 18.9342 │
│      10 │   1.57 │   8.89 │ 13.9573 │
│      11 │   2.14 │   7.64 │ 16.3496 │
│      12 │   1.57 │   8.89 │ 13.9573 │
│      13 │   1.07 │  14.62 │ 15.6434 │
│      14 │   2.14 │  15.62 │ 33.8954 │
│      15 │   1.07 │  14.62 │ 15.6434 │
│      16 │   1.07 │  19.36 │ 20.7152 │
│      17 │   2.14 │  17.42 │ 37.2788 │
│      18 │   1.07 │  19.36 │ 20.7152 │
│      19 │   1.07 │   8.89 │  9.5123 │
│      20 │   2.14 │   8.89 │ 19.0246 │
│      21 │   1.07 │   8.89 │  9.5123 │
│      22 │   1.07 │   4.44 │  4.7508 │
│      23 │   2.14 │   4.44 │  9.5016 │
│      24 │   1.07 │   4.44 │  4.7508 │
└─────────┴────────┴────────┴─────────┘

These are the sections on the upper deck.

Airbus A380 Seating

These are the sections on the lower / main deck.

Airbus A380 Seating

I believe the above diagrams were originally taken as screenshots from SeatGuru, whose owner, Tripadvisor, shut down last year.

The following are the solver's constraints and objectives. It aims to maximise revenue from juggling the number of seats in each fare class across the different sections of the aircraft.

$ python3
import json

import numpy as np
from   ortools.linear_solver import pywraplp
import pandas as pd


solver = pywraplp.Solver.CreateSolver('GLOP')

areaDataframe = pd.read_csv('areaData.csv')
maxArea = areaDataframe['area'].values # in m^2

# Define decision variables
n_classes = 4
n_sections = maxArea.size
seats = {}

# Order:     Economy, Premium, Business, First
cost       = [864,    2054,    5_763,     12_184]     # In USD
demand     = [1200,    250,       50,         26]
sizes      = [0.39,      0.478,    1.06,       1.829] # In m^2
weight     = [64,       64,       64,         64]     # In KGs

max_weight = 395_000 # In KGs

for i in range(n_classes):
    for j in range(n_sections):
        seats[i, j] = solver.IntVar(0,
                                    solver.infinity(),
                                    f'seats_{i}_{j}')

# Define objective function
revenue = solver.Sum(seats[i, j] * cost[i]
                     for i in range(n_classes)
                     for j in range(n_sections))

# Maximize revenue
solver.Maximize(revenue)

# Area constraint
for j in range(n_sections):
    solver.Add(solver.Sum(seats[i, j] * sizes[i]
                          for i in range(n_classes)) <= maxArea[j])

# Weight constraint
weight_constraint = \
    solver.Sum(seats[i, j] * weight[i]
               for i in range(n_classes)
               for j in range(n_sections)) <= max_weight

# Demand constraint
for i in range(n_classes):
    solver.Add((solver.Sum(seats[i, j]
                           for j in range(n_sections)) <= demand[i]))

I'll get the expected revenue from the optimal solution below.

status = solver.Solve()

if status == pywraplp.Solver.OPTIMAL:
    revenue = solver.Objective().Value()
    print(f"Revenue: ${revenue:,.2f}")
else:
    print('The problem does not have an optimal solution.')
Revenue: $1,581,000.99

I'll save a JSON file with the number of seats sold in each section of the aircraft, broken down by fare class.

with open('seating.json', 'w') as f:
    for i, label in enumerate(['Economy', 'Premium', 'Business', 'First']):
        for j in range(n_sections):
            if seats[i, j].solution_value():
                f.write(json.dumps({
                    'fare_class': label,
                    'section':    j,
                    'num_seats':  seats[i, j].solution_value()}) + '\n')

These are the seat counts for each fare class in each section. I've added one to the section number so they're easier to match with the layout diagrams above.

$ ~/duckdb
PIVOT    (SELECT * EXCLUDE(section),
                 section: section + 1
          FROM   'seating.json')
ON       fare_class IN (
            SELECT fare_class: UNNEST(['Economy',
                                       'Premium',
                                       'Business',
                                       'First']))
USING    ROUND(SUM(num_seats))::INT
GROUP BY section
ORDER BY section;
┌─────────┬─────────┬─────────┬──────────┬───────┐
│ section │ Economy │ Premium │ Business │ First │
│  int64  │  int32  │  int32  │  int32   │ int32 │
├─────────┼─────────┼─────────┼──────────┼───────┤
│       1 │    NULL │    NULL │       15 │  NULL │
│       2 │      42 │    NULL │     NULL │  NULL │
│       3 │    NULL │    NULL │       15 │  NULL │
│       4 │      17 │    NULL │       15 │  NULL │
│       5 │    NULL │      54 │     NULL │  NULL │
│       6 │    NULL │      53 │     NULL │  NULL │
│       7 │      49 │    NULL │     NULL │  NULL │
│       8 │    NULL │      49 │     NULL │  NULL │
│       9 │    NULL │      40 │     NULL │  NULL │
│      10 │      36 │    NULL │     NULL │  NULL │
│      11 │      42 │    NULL │     NULL │  NULL │
│      12 │    NULL │    NULL │     NULL │     8 │
│      13 │      10 │      25 │     NULL │  NULL │
│      14 │      54 │    NULL │     NULL │     7 │
│      15 │      40 │    NULL │     NULL │  NULL │
│      16 │    NULL │    NULL │     NULL │    11 │
│      17 │      96 │    NULL │     NULL │  NULL │
│      18 │      53 │    NULL │     NULL │  NULL │
│      19 │      24 │    NULL │     NULL │  NULL │
│      20 │      49 │    NULL │     NULL │  NULL │
│      21 │      24 │    NULL │     NULL │  NULL │
│      22 │    NULL │      10 │     NULL │  NULL │
│      23 │    NULL │      20 │     NULL │  NULL │
│      24 │    NULL │    NULL │        4 │  NULL │
└─────────┴─────────┴─────────┴──────────┴───────┘

Revenue Simulations

The code base also includes a revenue simulator. It takes a number of seats for each fare class, runs a number of simulations and returns a total revenue amount.

$ python3
from enum import Enum

import numpy as np


class TravelClass(Enum):
    ECONOMY         = "1"
    PREMIUM_ECONOMY = "2"
    BUSINESS        = "3"
    FIRST           = "4"


# Order: Economy, Premium, Business, First
prices = [864, 2054, 5_763, 12_184] # In USD


def simulate_buying_tickets():
    """
    Sample from the Zipf distribution num_samples times. Returns an array of tickets bought for each flight class

    Returns:
    An array of tickets bought for each flight class
    """
    # Total number of people buying tickets
    demand = np.random.lognormal(mean=np.log(1000), sigma=0.2)

    # Sample from Zipf distribution the number of tickets per class
    samples = np.random.zipf(3, size=int(demand))

    # Clip samples to be within range [1, 4]
    samples = np.clip(samples, 1, 4)

    # Convert samples to integers
    samples = samples.astype(int)


    return np.histogram(samples, bins=np.arange(1, 6))[0]


def simulate_revenue(num_econ,
                     num_prem_econ,
                     num_business,
                     num_first,
                     num_iters=1000):
    """
    Simulate the ticket buying process for a given set of seats. Returns the average revenue.

    Params:
    - num_econ:      total allowed number of economy class tickets in layout
    - num_prem_econ: total allowed number of economy class tickets in layout
    - num_business:  total allowed number of business class tickets in layout
    - num_first:     total allowed number of first class tickets in layout
    - num_iters:     number of trials to run simulating ticket buying

    Returns:
    Average revenue
    """
    total_revenues = []

    for _ in range(num_iters):
        ticket_counts = np.array(simulate_buying_tickets())

        ticket_counts[0] = max(ticket_counts[0], num_econ)
        ticket_counts[1] = max(ticket_counts[1], num_prem_econ)
        ticket_counts[2] = max(ticket_counts[2], num_business)
        ticket_counts[3] = max(ticket_counts[3], num_first)

        ticket_prices = np.array(prices)

        revenue = np.dot(ticket_counts, ticket_prices)
        total_revenues.append(revenue)

    return np.average(total_revenues)

I'll run the above with the following seat counts.

revenue = simulate_revenue(
            num_econ=473,
            num_prem_econ=250,
            num_business=50,
            num_first=24)
print(f"Revenue: ${revenue:,.2f}")
Revenue: $1,956,060.36

Every run of the above simulation will produce a slightly different value. These were the next three results.

Revenue: $1,950,810.23
Revenue: $1,962,026.60
Revenue: $1,954,938.53

An A380-900 Stretch

There has been an A380 variant proposed called the "A380-900 Stretch". It would have a suggested passenger count of 656 but could seat up to 960 passengers in an all-economy configuration.

I'll run the above simulator to see how much revenue 960 economy passengers could produce.

revenue = simulate_revenue(
            num_econ=960,
            num_prem_econ=0,
            num_business=0,
            num_first=0)
print(f"Revenue: ${revenue:,.2f}")
Revenue: $1,677,590.00
Thank you for taking the time to read this post. I offer both consulting and hands-on development services to clients in North America and Europe. If you'd like to discuss how my offerings can help your business please contact me via LinkedIn.

Copyright © 2014 - 2026 Mark Litwintschik. This site's template is based off a template by Giulio Fidente.