Lab 3 · Explore: ∀ and ∃ in pandas and in a real SQL database¶

Discrete Mathematics (YMT211) · Fall 2026

The playground half of Lab 3. Nothing here is graded. You will ask the same questions as in quantifiers.py, first with a table library (pandas), then with a real SQL database. Watch how ∀ and ∃ show up everywhere.

1 · The class as a table¶

The same ten fictional students as in data.py, written out here so the notebook also runs on its own.

import pandas as pd

STUDENTS = [
    ("Ada", 2, "Python,C", True, 92), ("Bora", 1, "Python", True, 67), ("Cemre", 2, "Python,Java", True, 74),
    ("Doruk", 3, "Java,C,SQL", False, 58), ("Ece", 1, "C", True, 81), ("Fırat", 2, "Java", True, 49),
    ("Gizem", 4, "Python,SQL", True, 88), ("Hakan", 1, "", False, 55), ("Irmak", 3, "Python,Java,C", True, 95),
    ("Kaan", 2, "Python", False, 62),
]
df = pd.DataFrame(STUDENTS, columns=["name", "year", "langs", "github", "math"])
df["python"] = df.langs.str.contains("Python")
df["java"] = df.langs.str.contains("Java")
df
name year langs github math python java
0 Ada 2 Python,C True 92 True False
1 Bora 1 Python True 67 True False
2 Cemre 2 Python,Java True 74 True True
3 Doruk 3 Java,C,SQL False 58 False True
4 Ece 1 C True 81 False False
5 Fırat 2 Java True 49 False True
6 Gizem 4 Python,SQL True 88 True False
7 Hakan 1 False 55 False False
8 Irmak 3 Python,Java,C True 95 True True
9 Kaan 2 Python False 62 True False

2 · ∀ and ∃ in pandas¶

A column of True/False values has .all() (∀) and .any() (∃).

print("Q1 ∀ github            :", df.github.all())
print("Q2 ∃ java              :", df.java.any())

second = df[df.year == 2]                         # the filter A(x): restrict the domain
print("Q3 ∀ (Y2 → Python)     :", second.python.all())
print("   counterexample      :", list(second[~second.python].name))

first = df[df.year == 1]
print("Q4 ¬∃ (Y1 ∧ fail)      :", not (first.math < 50).any())
Q1 ∀ github            : False
Q2 ∃ java              : True
Q3 ∀ (Y2 → Python)     : False
   counterexample      : ['Fırat']
Q4 ¬∃ (Y1 ∧ fail)      : True

Notice: df[df.year == 2] is the golden rule. Keeping only the second-years and then asking .all() is exactly $\forall x\,(Y_2(x) \to P(x))$: the people outside the filter can never break it.

3 · The same questions in SQL¶

Python ships with a small database engine, SQLite. We load the class into a table and ask SQL.

import sqlite3

db = sqlite3.connect(":memory:")
df.drop(columns=["langs"]).to_sql("students", db, index=False)

def ask(query):
    return db.execute(query).fetchone()[0]

print("Q2 ∃ java:", ask("SELECT EXISTS (SELECT * FROM students WHERE java)"))
Q2 ∃ java: 1

SQL has no FOR ALL. To ask “does every second-year know Python?” a database uses De Morgan: $\forall x\,(Y_2 \to P) \equiv \neg\exists x\,(Y_2 \land \neg P)$, that is, there is no second-year who does not know Python.

q3 = (
    "SELECT NOT EXISTS ("
    "  SELECT * FROM students"
    "  WHERE year = 2 AND NOT python"
    ")"
)
print("Q3 ∀ (Y2 → Python):", ask(q3))
print("who breaks it:", db.execute("SELECT name FROM students WHERE year = 2 AND NOT python").fetchall())
Q3 ∀ (Y2 → Python): 0
who breaks it: [('Fırat',)]

4 · The two classic mistakes, in data¶

The wrong translations from the week 4 notes give silly answers on real data too.

wrong_and = (df.year.eq(2) & df.python).all()        # ∀x (Y2 ∧ P): "everyone is a 2nd-year who knows Python"
wrong_imp = ((~df.year.eq(2)) | df.python).any()     # ∃x (Y2 → P): true thanks to anyone who is not a 2nd-year
print("∀x (Y2 ∧ P) :", wrong_and)
print("∃x (Y2 → P) :", wrong_imp)
∀x (Y2 ∧ P) : False
∃x (Y2 → P) : True

5 · Empty domains¶

What is “every student in year 5 has GitHub”? There are no year-5 students.

fifth = df[df.year == 5]
print("rows       :", len(fifth))
print("∀ (.all()) :", fifth.github.all(), "  ← vacuously true: no counterexample exists")
print("∃ (.any()) :", fifth.github.any(), " ← false: no witness exists")
rows       : 0
∀ (.all()) : True   ← vacuously true: no counterexample exists
∃ (.any()) : False  ← false: no witness exists

6 · Hunting for counterexamples¶

A claim about all integers can never be checked by a loop, but a loop can hunt for a counterexample. The polynomial $n^2 - n + 41$ looks like it always gives a prime. Does it?

def is_prime(n):
    return n > 1 and all(n % d for d in range(2, int(n ** 0.5) + 1))

n = next(n for n in range(1000) if not is_prime(n * n - n + 41))
print(f"counterexample: n = {n},  n² - n + 41 = {n*n - n + 41} = 41 × {(n*n - n + 41)//41}")
counterexample: n = 41,  n² - n + 41 = 1681 = 41 × 41

Every value from $n = 0$ to $n = 40$ gives a prime, then $n = 41$ breaks it. Checking examples can disprove a ∀ statement, never prove it. That is why week 7 is about proofs.

Your turn¶

  1. Ask in pandas and in SQL: “some first-year student knows C.”
  2. Ask “every student with a GitHub account knows at least one language.” (Hint: df.langs != "".)
  3. Change one student in STUDENTS so that Q3 becomes true, and re-run sections 2 and 3.