An AI reads history

../../_images/AI_reads_history.jpg

I was struck, years ago, by Sixty men from Ur - which points out that human recorded history is short, when measured in human lifetimes. I wanted to extend the point - by using not lifetimes in the abstract, but actual lifetimes of real people. Until recently, however, this would be a pain to do, because it’s difficult to find a set of famous people whose lifetimes match up appropriately. But now we have ChatGPT, and we can just ask it for what we want.

So I chose the first two people: Taylor Swift, possibly the most famous person currently alive, and Queen Elizabeth II, a recent famous person who’s been alive for a long time. And then I asked ChatGPT for the most famous person who had died just after Elizabeth II was born, and it nominated Thomas Hardy. So I added him to the list and proceeded by induction. I checked that everyone nominated was famous enough to have a Wikipedia page, and took birth and death dates from that page. Just for kicks, I also asked ChatGPT to draw a picture of everyone it nominated.

I bent the rules a bit:

  • In the recent period, when there are lots of candidates to choose from, I often rejected the first suggestion if they were a monarch. I didn’t want just a list of ‘names of kings’.

  • In the early period, where it is hard to find candidates, I allowed candidates whose date of birth was not known (to Wikipedia) - deciding that monarchs all ascend the throne at age 20 unless there is information to the contrary.

  • Sometimes ChatGPT offered no good candidates. In this case I left a gap and restarted with someone earlier.

  • I forced Iry-Hor to be the earliest person, because he’s the obvious end point: possibly the first person whose name we know. I made up his dates (choosing plausible values within the Wikipedia range).

  • I forced Ea-nāṣir into the list (no suggestions from ChatGPT for his period, and he’s not a king). I made up his dates (choosing plausible values within the Wikipedia range).

  • A couple of people were suggested by GitHub Copilot, not ChatGPT. I edited the list of selected people (see below) as a CSV file, using a Copilot-enabled editor. And Copilot rapidly worked out the format of that file, so I generally only had to type the first few characters of each new person’s name, and Copilot would suggest the rest (always with the right dates and URL, in the correct format). Copilot would also suggest the next line (next person in the series), but it never quite worked out what algorithm I was using to choose the next person, so the suggestions were generally tempting, but had the wrong death date. On a couple of occasions it did suggest the perfect person, however, and in those cases I just hit ESC and took the suggestion.

List of selected people

Name                  ,birth year ,death year ,sex ,category    ,wikipedia URL
Taylor Swift          ,1989       ,2023       ,f   ,musician    ,https://en.wikipedia.org/wiki/Taylor_Swift
Queen Elizabeth II    ,1926       ,2022       ,f   ,monarch     ,https://en.wikipedia.org/wiki/Elizabeth_II
Thomas Hardy          ,1840       ,1928       ,m   ,writer      ,https://en.wikipedia.org/wiki/Thomas_Hardy
Robert Southey        ,1774       ,1843       ,m   ,poet        ,https://en.wikipedia.org/wiki/Robert_Southey
David Hume            ,1711       ,1776       ,m   ,philosopher ,https://en.wikipedia.org/wiki/David_Hume
King Louis XIV        ,1638       ,1715       ,m   ,monarch     ,https://en.wikipedia.org/wiki/Louis_XIV
Anthony van Dyck      ,1599       ,1641       ,m   ,artist      ,https://en.wikipedia.org/wiki/Anthony_van_Dyck
Queen Elizabeth I     ,1533       ,1603       ,f   ,monarch     ,https://en.wikipedia.org/wiki/Elizabeth_I
Sir Thomas More       ,1478       ,1535       ,m   ,politician  ,https://en.wikipedia.org/wiki/Thomas_More
Mehmed the Conqueror  ,1432       ,1481       ,m   ,monarch     ,https://en.wikipedia.org/wiki/Mehmed_II
Emperor Zhu Di        ,1360       ,1424       ,m   ,monarch     ,https://en.wikipedia.org/wiki/Yongle_Emperor
Giovanni Boccaccio    ,1313       ,1375       ,m   ,writer      ,https://en.wikipedia.org/wiki/Giovanni_Boccaccio
Dante Alighieri       ,1265       ,1321       ,m   ,writer      ,https://en.wikipedia.org/wiki/Dante_Alighieri
Thomas Aquinas        ,1225       ,1274       ,m   ,saint       ,https://en.wikipedia.org/wiki/Thomas_Aquinas
Francis of Assisi     ,1181       ,1226       ,m   ,saint       ,https://en.wikipedia.org/wiki/Francis_of_Assisi
Minamoto no Yoritomo  ,1147       ,1199       ,m   ,monarch     ,https://en.wikipedia.org/wiki/Minamoto_no_Yoritomo
Bernard of Clairvaux  ,1090       ,1153       ,m   ,priest      ,https://en.wikipedia.org/wiki/Bernard_of_Clairvaux
El Cid                ,1043       ,1099       ,m   ,general     ,https://en.wikipedia.org/wiki/El_Cid
Edward the Confessor  ,1003       ,1066       ,m   ,monarch     ,https://en.wikipedia.org/wiki/Edward_the_Confessor
Sweyn Forkbeard       ,963        ,1014       ,m   ,monarch     ,https://en.wikipedia.org/wiki/Sweyn_Forkbeard
Pope John XII         ,930        ,964        ,m   ,priest      ,https://en.wikipedia.org/wiki/Pope_John_XII
King Henry the Fowler ,876        ,936        ,m   ,monarch     ,https://en.wikipedia.org/wiki/Henry_the_Fowler
Methodius             ,815        ,885        ,m   ,priest      ,https://en.wikipedia.org/wiki/Cyril_and_Methodius
Louis the Pious       ,778        ,840        ,m   ,monarch     ,https://en.wikipedia.org/wiki/Louis_the_Pious
Offa of Mercia        ,735        ,796        ,m   ,monarch     ,https://en.wikipedia.org/wiki/Offa_of_Mercia
Charles Martel        ,688        ,741        ,m   ,monarch     ,https://en.wikipedia.org/wiki/Charles_Martel
Cædwalla of Wessex    ,659        ,689        ,m   ,monarch     ,https://en.wikipedia.org/wiki/C%C3%A6dwalla_of_Wessex
Eligius               ,588        ,660        ,m   ,priest      ,https://en.wikipedia.org/wiki/Saint_Eligius
Leander of Seville    ,534        ,600        ,m   ,priest      ,https://en.wikipedia.org/wiki/Leander_of_Seville
Count Belisarius      ,500        ,565        ,m   ,general     ,https://en.wikipedia.org/wiki/Belisarius
Clovis I              ,466        ,511        ,m   ,monarch     ,https://en.wikipedia.org/wiki/Clovis_I
Procopius Anthemius   ,420        ,472        ,m   ,monarch     ,https://en.wikipedia.org/wiki/Anthemius
St. Jerome            ,342        ,420        ,m   ,priest      ,https://en.wikipedia.org/wiki/Jerome
St. Nicholas          ,270        ,343        ,m   ,priest      ,https://en.wikipedia.org/wiki/Saint_Nicholas
Emperor Aurelian      ,214        ,275        ,m   ,monarch     ,https://en.wikipedia.org/wiki/Aurelian
Hippolytus of Rome    ,170        ,235        ,m   ,priest      ,https://en.wikipedia.org/wiki/Hippolytus_of_Rome
Ptolemy               ,100        ,170        ,m   ,scientist   ,https://en.wikipedia.org/wiki/Ptolemy
Plutarch              ,46         ,120        ,m   ,writer      ,https://en.wikipedia.org/wiki/Plutarch
Boudica               ,30         ,61         ,f   ,general     ,https://en.wikipedia.org/wiki/Boudica
Jesus                 ,-6         ,33         ,m   ,religious   ,https://en.wikipedia.org/wiki/Jesus
Augustus Caesar       ,-63        ,14         ,m   ,monarch     ,https://en.wikipedia.org/wiki/Augustus
Cicero                ,-106       ,-43        ,m   ,writer      ,https://en.wikipedia.org/wiki/Cicero
Mithridates VI        ,-134       ,-63        ,m   ,monarch     ,https://en.wikipedia.org/wiki/Mithridates_VI
Attalus III           ,-170       ,-133       ,m   ,monarch     ,https://en.wikipedia.org/wiki/Attalus_III
Cato the Elder        ,-234       ,-149       ,m   ,writer      ,https://en.wikipedia.org/wiki/Cato_the_Elder
Archimedes            ,-287       ,-212       ,m   ,scientist   ,https://en.wikipedia.org/wiki/Archimedes
Lysimachus            ,-361       ,-281       ,m   ,monarch     ,https://en.wikipedia.org/wiki/Lysimachus
Plato                 ,-428       ,-348       ,m   ,philosopher ,https://en.wikipedia.org/wiki/Plato
Pericles              ,-495       ,-429       ,m   ,monarch     ,https://en.wikipedia.org/wiki/Pericles
Pythagoras            ,-570       ,-495       ,m   ,philosopher ,https://en.wikipedia.org/wiki/Pythagoras
Nebuchadnezzar II     ,-642       ,-562       ,m   ,monarch     ,https://en.wikipedia.org/wiki/Nebuchadnezzar_II
Ashurbanipal          ,-685       ,-631       ,m   ,monarch     ,https://en.wikipedia.org/wiki/Ashurbanipal
Sennacherib           ,-745       ,-681       ,m   ,monarch     ,https://en.wikipedia.org/wiki/Sennacherib
Tiglath-Pileser III   ,-795       ,-727       ,m   ,monarch     ,https://en.wikipedia.org/wiki/Tiglath-Pileser_III
Adad-nirari III       ,-825       ,-783       ,m   ,monarch     ,https://en.wikipedia.org/wiki/Adad-nirari_III
Shalmaneser III       ,-893       ,-824       ,m   ,monarch     ,https://en.wikipedia.org/wiki/Shalmaneser_III
Adad-nirari II        ,-931       ,-891       ,m   ,monarch     ,https://en.wikipedia.org/wiki/Adad-nirari_II
Solomon               ,-990       ,-931       ,m   ,monarch     ,https://en.wikipedia.org/wiki/Solomon
King David            ,-1040      ,-970       ,m   ,monarch     ,https://en.wikipedia.org/wiki/David
Samuel                ,-1070      ,-1015      ,m   ,religious   ,https://en.wikipedia.org/wiki/Samuel
King Wen of Zhou      ,-1112      ,-1050      ,m   ,monarch     ,https://en.wikipedia.org/wiki/King_Wen_of_Zhou
Ramesses XI           ,-1127      ,-1077      ,m   ,monarch     ,https://en.wikipedia.org/wiki/Ramesses_XI
Ramesses III          ,-1186      ,-1155      ,m   ,monarch     ,https://en.wikipedia.org/wiki/Ramesses_III

Ramesses the Great    ,-1303      ,-1213      ,m   ,monarch     ,https://en.wikipedia.org/wiki/Ramesses_II

Tutankhamun           ,-1341      ,-1323      ,m   ,monarch     ,https://en.wikipedia.org/wiki/Tutankhamun
Tushratta             ,-1378      ,-1335      ,m   ,monarch     ,https://en.wikipedia.org/wiki/Tushratta
Amenhotep III         ,-1410      ,-1352      ,m   ,monarch     ,https://en.wikipedia.org/wiki/Amenhotep_III
Amenhotep II          ,-1447      ,-1401      ,m   ,monarch     ,https://en.wikipedia.org/wiki/Amenhotep_II
Thutmose III          ,-1481      ,-1425      ,m   ,monarch     ,https://en.wikipedia.org/wiki/Thutmose_III
Hatshepsut            ,-1507      ,-1458      ,f   ,monarch     ,https://en.wikipedia.org/wiki/Hatshepsut





Ea-nāṣir              ,-1775      ,-1725      ,m   ,merchant    ,https://en.wikipedia.org/wiki/Complaint_tablet_to_Ea-n%C4%81%E1%B9%A3ir
Hammurabi             ,-1810      ,-1750      ,m   ,monarch     ,https://en.wikipedia.org/wiki/Hammurabi






Ur-Nammu              ,-2068      ,-2030      ,m   ,monarch     ,https://en.wikipedia.org/wiki/Ur-Nammu
Shulgi                ,-2104      ,-2047      ,m   ,monarch     ,https://en.wikipedia.org/wiki/Shulgi



Naram-Sin             ,-2274      ,-2218      ,m   ,monarch     ,https://en.wikipedia.org/wiki/Naram-Sin_of_Akkad

Sargon of Akkad       ,-2354      ,-2279      ,m   ,monarch     ,https://en.wikipedia.org/wiki/Sargon_of_Akkad





Cheops                ,-2609      ,-2566      ,m   ,monarch     ,https://en.wikipedia.org/wiki/Khufu









Iry-Hor               ,-3175      ,-3125      ,m   ,monarch     ,https://en.wikipedia.org/wiki/Iry-Hor

All the pictures:

Code to make the poster

Written with much assistance from the awesome GitHub Copilot. (This whole project was a test of the value of AI assistance, and I’m completely sold on it.)

#!/usr/bin/env python

# Plain rectangular format

import sys

import matplotlib
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.patches import Rectangle

from PIL import Image
import numpy as np
import random

# Load the data from a csv file
import csv

# Open the CSV file
people = []
with open("people.csv", "r") as file:
    # Create a CSV reader
    reader = csv.reader(file)

    # Get the header row
    header = next(reader)

    # Loop through each row in the CSV
    countBack = 0
    for row in reader:
        if len(row) == 0:
            people.append(row)
        else:
            people.append(
                [
                    row[0].strip(),
                    int(row[1]),
                    int(row[2]),
                    row[3].strip(),
                    row[4].strip(),
                    row[5].strip,
                    countBack,
                ]
            )
        countBack += 1

# Create a figure
fig = Figure(
    figsize=(30, 42.43),
    dpi=300,
    facecolor=(0.5, 0.5, 0.5, 1),
    edgecolor=None,
    linewidth=0.0,
    frameon=False,
    subplotpars=None,
    tight_layout=None,
)
canvas = FigureCanvas(fig)
font = {
    "family": "sans-serif",
    "sans-serif": "Arial",
    "weight": "normal",
    "size": 32,
}
matplotlib.rc("font", **font)

# Plain background
bgcolour = (0.95, 0.95, 0.95, 1)
axb = fig.add_axes([0, 0, 1, 1])
axb.set_axis_off()
axb.add_patch(
    Rectangle(
        (0, 0),
        1,
        1,
        facecolor=bgcolour,
        fill=True,
        zorder=1,
    )
)

fig_width, fig_height = fig.get_size_inches()
fig_aspect = fig_width / fig_height


# Set the margins
borderFractions = [0.05, 0.025, 0.01, 0.01]

# Axes for plot
ax = fig.add_axes(
    [
        borderFractions[0],
        borderFractions[1],
        1 - borderFractions[0] - borderFractions[2],
        1 - borderFractions[1] - borderFractions[3],
    ],
    facecolor=(0.95, 0.95, 0.95, 1),
)
ax_aspect = (
    fig_width
    * (1 - borderFractions[0] - borderFractions[2])
    / fig_height
    * (1 - borderFractions[1] - borderFractions[3])
)


# No black border
for edge in ["top", "right"]:
    ax.spines[edge].set_visible(False)

# X axis from -3200 to present+space
ax.set_xlim(-4200, 3250)
ax.set_xlabel("Year")
ax.set_xticks(range(-3500, 2100, 500))

# Y axis from 0 to length of csv
ax.set_ylabel("Lifetimes ago")
# Y axis integers in csv length
ax.set_ylim(len(people) + 0, -10)
ax.set_yticks(range(0, len(people) + 5, 5))

ax.set_aspect("auto")

# Add a grid
ax.grid(color=(0.5, 0.5, 0.5, 0.25), linestyle="-", linewidth=0.25, zorder=-10)

Austria = dict(
    colors=("#a40000", "#16317d", "#007e2f", "#ffcd12", "#b86092", "#721b3e", "#00b7a7")
)


# Different professions in different colours
# Uses 'Austria' palette from MetBrewer
def set_color(person):
    if person[4] == "monarch":
        return "#a40000"
    elif person[4] == "writer" or person[4] == "poet":
        return "#16317d"
    elif person[4] == "philosopher" or person[4] == "scientist":
        return "#007e2f"
    elif person[4] == "priest" or person[4] == "saint" or person[4] == "religious":
        return "#ffcd12"
    elif person[4] == "artist" or person[4] == "musician":
        return "#b86092"
    elif person[4] == "general":
        return "#721b3e"
    elif person[4] == "politician":
        return "#00b7a7"
    elif person[4] == "merchant":
        return (0.722, 0.451, 0.2, 1)
    else:
        return (0, 0, 0, 1)


# Find the spot to add an image
def getImageExtent(box_bb, name_bb, pIdx):
    ximscale = 0.05
    ximsize = (ax.get_xlim()[1] - ax.get_xlim()[0]) * ximscale
    aspect = abs(
        (ax.get_ylim()[1] - ax.get_ylim()[0]) / (ax.get_xlim()[1] - ax.get_xlim()[0])
    )
    yimsize = ximsize * ax.get_data_ratio() * ax_aspect
    xoffset = ximsize
    yoffset = yimsize
    if pIdx // 2 % 3 != 1:
        xoffset += ximsize
        yoffset += yimsize
    if pIdx // 2 % 3 == 2:
        xoffset += ximsize
        yoffset += yimsize
    if pIdx % 2 == 1:  # Bottom right
        xoffset -= 100
        yoffset -= 0
        return [
            box_bb.x1 + xoffset,
            box_bb.x1 + xoffset + ximsize,
            box_bb.y0 + yoffset + yimsize,
            box_bb.y0 + yoffset,
        ]
    else:
        xoffset += 1000  # Need space for names
        if pIdx == 108:  # Don't waste space on Iry-Hor
            xoffset -= 1000
        return [
            box_bb.x0 - xoffset + ximsize,
            box_bb.x0 - xoffset,
            box_bb.y0 - yoffset + yimsize,
            box_bb.y0 - yoffset,
        ]


# For each person, plot a bar showing their lifespan
for pIdx in range(len(people)):
    person = people[pIdx]
    if len(person) != 0:
        col = set_color(person)
        box = ax.add_patch(
            Rectangle(
                (person[1], pIdx - 0.48),
                person[2] - person[1],
                0.96,
                facecolor=col,
                fill=True,
                zorder=20,
            )
        )
        name = ax.text(
            person[1] - 25,
            pIdx,
            person[0],
            horizontalalignment="right",
            verticalalignment="center",
            color=(0, 0, 0, 1),
            zorder=30,
            fontsize=24,
        )
        name_bg = ax.text(  # Background for name text
            person[1] - 25,
            pIdx,
            person[0],
            horizontalalignment="right",
            verticalalignment="center",
            color=bgcolour,
            zorder=10,
            fontsize=24,
            backgroundcolor=bgcolour,
        )

        # Get plot positions of box and name to use placing picture
        box_bb = box.get_bbox()  # In ax data coords
        name_bb = name.get_window_extent()  # In display coords
        name_bb = name_bb.transformed(ax.transData.inverted())  # In ax data coords
        img_extent = getImageExtent(box_bb, name_bb, pIdx)

        img = Image.open("pictures/%s.webp" % person[0])
        img_array = np.array(img)
        imgs = ax.imshow(img_array, extent=img_extent, aspect="auto", zorder=40)
        # Draw a line linking box and name
        if pIdx % 2 == 1:  # Below right
            ax.plot(
                [box_bb.x1 + 10, img_extent[0]],
                [(box_bb.y0 + box_bb.y1) / 2, img_extent[3]],
                color=col,
                linewidth=0.35,
                zorder=5,
            )
        else:
            ax.plot(
                [name_bb.x0 - 10, img_extent[0]],
                [(name_bb.y0 + name_bb.y1) / 2, img_extent[2]],
                color=col,
                linewidth=0.35,
                zorder=5,
            )

# Add some overall descriptive text
ax.text(
    -4000,
    -9,
    "An AI Reads History",
    horizontalalignment="left",
    verticalalignment="top",
    color=(0, 0, 0, 1),
    fontsize=56,
    zorder=100,
    backgroundcolor=bgcolour,
)
ax.text(
    -4000,
    -6.5,
    "78 people from Wikipedia",
    horizontalalignment="left",
    verticalalignment="top",
    color=(0, 0, 0, 1),
    fontsize=40,
    zorder=100,
    backgroundcolor=bgcolour,
)
ax.text(
    -4000,
    -0,
    "How many overlapping human lifetimes does it take \nto cover recorded history.\n\n"
    + "People selected, and pictured, by ChatGPT.",
    horizontalalignment="left",
    verticalalignment="top",
    color=(0, 0, 0, 1),
    fontsize=32,
    zorder=100,
    backgroundcolor=bgcolour,
)
axb.text(
    0.99,
    0.005,
    "Philip Brohan, 2023-12-28\n"
    + "https://brohan.org/Posters/posters/An_AI_reads_history",
    horizontalalignment="right",
    verticalalignment="bottom",
    color=(0, 0, 0, 1),
    fontsize=16,
    zorder=100,
    backgroundcolor=bgcolour,
)
# Output as png
fig.savefig("AI_reads_history.png")