diff --git a/scrapers/departments/__init__.py b/scrapers/departments/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/scrapers/departments/eecs_special_subjects.py b/scrapers/departments/eecs_special_subjects.py new file mode 100644 index 00000000..516b0b3e --- /dev/null +++ b/scrapers/departments/eecs_special_subjects.py @@ -0,0 +1,366 @@ +""" +Temporary helper to parse EECS Subject Updates (Spring 2026). +Intended to help generate override data for Course 6 special subjects. + +Imitates the structure of math_dept.py: scrape a departmental page, parse rows, +and return a dict of overrides. + +Functions: +* get_rows() +* parse_schedule(schedule_line) +* parse_header(text) +* parse_many_timeslots(days, slot, is_pm_int) +* make_raw_sections(days, slot, room, is_pm_int) +* parse_row(row) +* run() +""" + +from __future__ import annotations + +import re +from pprint import pprint +from typing import Any, Dict, List, Literal, Optional, Tuple +from urllib.request import Request, urlopen + +from bs4 import BeautifulSoup, Tag + +from scrapers.fireroad import parse_section, parse_timeslot +from scrapers.utils import EVE_TIMES, TIMES + +# The EECS WordPress page renders its subject list by dynamically loading this HTML +# fragment (see network request `.../plugins/subj_2026SP.html` in a browser). +# `requests.get()` of the WordPress page often returns only navigation chrome, so +# this script scrapes the source-of-truth fragment directly. +URL = "https://eecsis.mit.edu/plugins/subj_2026SP.html" +FRONTEND_URL = ( + "https://www.eecs.mit.edu/academics/subject-updates/subject-updates-spring-2026/" +) +# Match a 6.S### subject header, optionally with an "(also ...)" clause. +# Group 1: the 6.S### number +# Group 2 (optional): comma-separated cross-list numbers (for the "same" field) +# Group 3: the title text (excluding the "(also ...)" clause when present) +COURSE_RE = re.compile( + r"\b(6\.S\d{3})\b" + r"(?:\s*\(\s*also(?: under)?\s+" + r"([A-Za-z]{0,5}\d{0,3}[A-Za-z]{0,3}\.[A-Za-z]{0,3}\d{1,4}[A-Za-z]?" + r"(?:\s*,\s*[A-Za-z]{0,5}\d{0,3}[A-Za-z]{0,3}\.[A-Za-z]{0,3}\d{1,4}[A-Za-z]?)*" + r")\s*\)\s*)?" + r"(.*)$", + re.IGNORECASE, +) +DAY_WORD = { + "monday": "M", + "tuesday": "T", + "wednesday": "W", + "thursday": "R", + "friday": "F", +} + + +Timeslot = Tuple[int, int] +Section = Tuple[List[Timeslot], str] +Units = Dict[ + Literal["lectureUnits", "labUnits", "preparationUnits", "isVariableUnits"], Any +] +RawSectionFields = Dict[str, List[str]] +SectionFields = Dict[str, List[Section]] + + +def normalize_days(days_raw: str) -> str: + """ + Normalize day strings into Fireroad-compatible day letters (MTWRF). + + Examples: + - "TR" -> "TR" + - "Thursdays" -> "R" + """ + assert days_raw, "empty day string" + + if days_raw.isupper(): + assert set(days_raw) <= set("MTWRF"), days_raw + return days_raw + + key = days_raw.lower().rstrip("s") + assert key in DAY_WORD, days_raw + return DAY_WORD[key] + + +def parse_many_timeslots(days: str, slot: str, is_pm_int: int) -> list[Timeslot]: + """ + Parses many timeslots. + + Args: + * days (str): A list of days (e.g. "TR") + * slot (str): The timeslot (e.g. "1-2.30" or "7-10 PM") + * is_pm_int (int): 0 for AM-ish slots, 1 for PM-ish slots + + Returns: + * list[Timeslot]: All parsed timeslots + """ + assert is_pm_int in (0, 1), is_pm_int + return [parse_timeslot(day, slot, bool(is_pm_int)) for day in days] + + +def make_raw_sections(days: str, slot: str, room: str, is_pm_int: int) -> str: + """ + Formats a raw section (same shape as math_dept.py). + """ + assert days + assert slot + assert room + assert is_pm_int in (0, 1), is_pm_int + + return f"{room}/{days}/{is_pm_int}/{slot}" + + +def parse_schedule( + schedule_line: str, +) -> tuple[RawSectionFields, SectionFields, list[str]]: + """ + Parse a schedule value like: + "Lectures: TR2:30-4, room 34-101" + "Lecture: MW1-2:30, room 32-155; Recitations: Tuesdays 2-3p, room 36-112" + "Lectures: Thursdays 7-10pm, room 2-131" + + Args: + * schedule_line (str): The raw schedule line + + Returns: + * (RawSectionFields, SectionFields, list[str]): + - RawSectionFields: mapping like "lectureRawSections" -> list[str] + - SectionFields: mapping like "lectureSections" -> list[Section] + + Both dicts are intended to be merged into the per-course `data` dict in + `parse_row()` via `data.update(...)`. + """ + assert schedule_line and schedule_line != "TBD", schedule_line + + chunks = list(filter(None, schedule_line.split(";"))) + raw_fields: RawSectionFields = {} + section_fields: SectionFields = {} + kinds: list[str] = [] + + for idx, chunk in enumerate(chunks): + m = re.match( + r"^(?:(?PLectures?|Lecture|Recitations?|Recitation|Labs?|Lab|" + r"Designs?|Design):\s*)?" + r"(?P(?:[MTWRF]+)|(?:Monday|Tuesday|Wednesday|Thursday|Friday|" + r"Mondays|Tuesdays|Wednesdays|Thursdays|Fridays))\s*" + r"(?P[0-9]+(?:[.:][0-9]{2})?)" + r"(?:\s*(?Pam|pm|a|p))?\s*-\s*" + r"(?P[0-9]+(?:[.:][0-9]{2})?)" + r"(?:\s*(?Pam|pm|a|p))?\s*,\s*room\s+" + r"(?P[A-Za-z0-9-]+)(?:\s+.*)?$", + chunk.strip(), + re.IGNORECASE, + ) + assert m is not None, chunk + + kind = "lecture" + if m.group("kind") is not None: + kind = m.group("kind").lower().rstrip("s") # drop 's' in e.g. Lectures + else: + assert idx == 0, "Only the first chunk may omit its kind (assumed lecture)" + + start = m.group("start").replace(":", ".") + end = m.group("end").replace(":", ".") + + is_day = start in TIMES and end in TIMES + is_eve = start in EVE_TIMES and end in EVE_TIMES + assert is_day or is_eve, (start, end) + is_pm_int = 0 if is_day else 1 + + raw = make_raw_sections( + normalize_days(m.group("days")), + f"{start}-{end}" + (" PM" if is_pm_int == 1 else ""), + m.group("room"), + is_pm_int, + ) + raw_fields.setdefault(f"{kind}RawSections", []).append(raw) + section_fields.setdefault(f"{kind}Sections", []).append(parse_section(raw)) + kinds.append(kind) + + return raw_fields, section_fields, kinds + + +def get_rows() -> list[Tag]: + """ + Scrapes the EECS subject updates page and returns "rows", each representing + one 6.S### entry as a list of text blocks (header + body). + + Args: none + + Returns: + * list[Tag]: BeautifulSoup tags for each detected 6.S### subject + """ + request = Request(URL) + request.add_unredirected_header( + "User-Agent", "hydrant-scrapers (https://github.com/sipb/hydrant)" + ) + + with urlopen(request, timeout=15) as response: + page_html = response.read().decode("utf-8") + + soup = BeautifulSoup(page_html, features="lxml") + page_text = soup.get_text(" ", strip=True) + assert COURSE_RE.search(page_text) is not None, f"No 6.S### entries found on {URL}" + + # Each subject block begins with an h6 heading containing the subject number. + rows = soup.find_all("h6") + assert rows, "No
course headings found" + return rows + + +def parse_header(text: str) -> tuple[str, str, Optional[str]]: + """ + Parse a header block containing a course number. + + Returns: + * tuple[str, str, str | None]: (course_number, title_fragment, same_csv) + """ + match = COURSE_RE.search(text) + assert match + course = match.group(1) + same_as = match.group(2) + title = match.group(3).lstrip(" :-–—\t").rstrip("§ ") + return course, title, same_as + + +def parse_units(units_str: str) -> Units: + """ + Parse units string like "3-0-9" or "12" into a dict suitable for `data.update(...)`. + + Args: + units_str (str): Units string from the webpage + + Returns: + Units: + Dict with keys: lectureUnits, labUnits, preparationUnits, isVariableUnits. + Raises ValueError if can't parse. + """ + # Parse formats like "3-0-9" + if "-" in units_str: + parts = units_str.split("-") + if len(parts) == 3: + return { + "lectureUnits": int(parts[0]), + "labUnits": int(parts[1]), + "preparationUnits": int(parts[2]), + "isVariableUnits": False, + } + raise ValueError(f"Invalid units string: {units_str}") + + # Single number like "12" - variable units + if units_str.isdigit(): + return { + "lectureUnits": 0, + "labUnits": 0, + "preparationUnits": 0, + "isVariableUnits": True, + } + + # Can't parse + raise ValueError(f"Invalid units string: {units_str}") + + +def parse_level(level_str: str) -> str: + """ + Parse level string to "U" or "G". + + Args: + level_str (str): Level string from the webpage + + Returns: + str: "U" for undergraduate, "G" for graduate + """ + # Note: might be both "undergraduate" and "graduate" + level_str = level_str.lower() + if "graduate" in level_str and "undergrad" not in level_str: + return "G" + return "U" + + +def parse_row(row: Tag) -> dict[str, dict[str, Any]]: + """ + Parses a single row (one subject entry). + + Args: + * row (Tag): header + body blocks + + Returns: + * dict[str, dict[str, Any]]: A single-entry overrides dict + """ + header = row.get_text(" ", strip=True) + course, title, same_as = parse_header(header) + data = {"url": f'{FRONTEND_URL}#{course.replace(".", "_", 1)}'} + + if title: + data["name"] = title + if same_as: + data["same"] = same_as + + # The fragment lays out each subject as: + #
...
+ #
+ # key/value metadata
+ #
+ #
description ...
+ table = row.find_next_sibling("table") + assert table is not None, f"Missing metadata table for {course}" + + meta = {} + for tr in table.find_all("tr"): + tds = tr.find_all("td") + if len(tds) != 2: + continue + key = tds[0].get_text(" ", strip=True).rstrip(":") + val = tds[1].get_text(" ", strip=True) + if key and val: + meta[key] = val + + # Parse Level (if present) + if "Level" in meta: + data["level"] = parse_level(meta["Level"]) + + # Parse Units (if present and parseable) + if "Units" in meta: + data.update(parse_units(meta["Units"])) + + if "Instructors" in meta: + data["inCharge"] = meta["Instructors"].replace("\n", ", ") + + if "Prereqs" in meta: + data["prereqs"] = meta["Prereqs"] + + if "Schedule" in meta and meta["Schedule"] != "TBD": + schedule_data = parse_schedule(meta["Schedule"]) + data.update(schedule_data[0]) + data.update(schedule_data[1]) + if schedule_data[2]: + data["sectionKinds"] = schedule_data[2] + + desc_div = table.find_next_sibling("div") + assert desc_div is not None, f"Missing description block for {course}" + data["description"] = desc_div.get_text(" ", strip=True) + + return {course: data} + + +def run() -> dict[str, dict[str, Any]]: + """ + The main entry point. + + Args: none + + Returns: + * dict[str, dict[str, Any]]: Overrides keyed by subject number. + """ + rows = get_rows() + overrides = {} + for row in rows: + overrides.update(parse_row(row)) + return overrides + + +if __name__ == "__main__": + pprint(run()) diff --git a/scrapers/math_dept.py b/scrapers/departments/math_dept.py similarity index 98% rename from scrapers/math_dept.py rename to scrapers/departments/math_dept.py index 994d573d..0ced80d5 100644 --- a/scrapers/math_dept.py +++ b/scrapers/departments/math_dept.py @@ -21,7 +21,7 @@ from bs4 import BeautifulSoup, Tag -from .fireroad import parse_section, parse_timeslot +from scrapers.fireroad import parse_section, parse_timeslot def parse_when(when: str) -> tuple[str, str]: diff --git a/scrapers/overrides.toml.d/sem/6.toml b/scrapers/overrides.toml.d/sem/6.toml index 9800927b..988d2128 100644 --- a/scrapers/overrides.toml.d/sem/6.toml +++ b/scrapers/overrides.toml.d/sem/6.toml @@ -2,3 +2,249 @@ ["6.3900"] url = "https://introml.mit.edu?from=hydrant" + +["6.S051"] +url = "https://www.eecs.mit.edu/academics/subject-updates/subject-updates-spring-2026/#6_S051" +name = "AI Alignment: Moral, Political, and Computational Foundations" +same = "17.S917" +level = "U" +lectureUnits = 0 +labUnits = 0 +preparationUnits = 0 +isVariableUnits = true +inCharge = "Bailey Flanigan (POLSCI, EECS), Bernardo Zacka (POLSCI)" +prereqs = "This class is open to all. Experience with basic algorithms and a basic grasp of probability theory are helpful but not required." +lectureRawSections = ["2-131/R/0/7-10"] +lectureSections = [[[[104, 6]], "2-131"]] +sectionKinds = ["lecture"] +description = "Explores moral, political, and computational foundations of AI alignment. Draws on political theory and computer science to consider how individual and collective values are elicited and embedded in AI. Topics include bureaucracy-based versus AI-based decision rules; the concept of values; fairness and welfare objectives; pluralism and democratic input; and how well technical elicitation methods can capture considered judgments. Activities include seminar discussion, periodic problem sets, short response papers, and a course research project." + +["6.S056"] +url = "https://www.eecs.mit.edu/academics/subject-updates/subject-updates-spring-2026/#6_S056" +name = "Hack Yourself: Data-Driven Learning and Wellbeing" +level = "U" +lectureUnits = 3 +labUnits = 0 +preparationUnits = 9 +isVariableUnits = false +inCharge = "Ana Bell (EECS), Paola Rebusco (ESG), Carter Jernigan (ESG)" +prereqs = "6.100A" +lectureRawSections = ["32-141/F/0/11-1"] +recitationRawSections = ["36-112/T/0/2-3"] +lectureSections = [[[[146, 4]], "32-141"]] +recitationSections = [[[[50, 2]], "36-112"]] +sectionKinds = ["lecture", "recitation"] +description = """ +Did you know that your mindset can add up to 7 years to your life? Or that only 5% of leaders know the secret to motivating their teams? Or that time pressure decreases your creative problem solving by 45%?\n +\n +Thriving in life, work, and school isn't a mystery—it’s within your grasp. Research shows that nearly half of your well-being is shaped by your daily choices. In Hack Yourself, you’ll learn how to design those choices, building a toolkit of over 60 sustainable positive habits. You’ll apply a data science approach to how you live and lead—using tools from generative AI to vibe coding to statistical analysis to validate and personalize positive psychology practices, while also applying the same data methods across domains from leadership to innovation. \n +\n +Instruction and practice in oral and written communication provided. Students complete a group project in the last ¼ of the course.""" + +["6.S058"] +url = "https://www.eecs.mit.edu/academics/subject-updates/subject-updates-spring-2026/#6_S058" +name = "Introduction to Computer Vision" +level = "U" +lectureUnits = 4 +labUnits = 0 +preparationUnits = 11 +isVariableUnits = false +inCharge = "William Freeman, Kaiming He, Vincent Monardo" +prereqs = "6.3900, (18.06 or 18.C06), and (6.1200, 6.3700, 6.3800, 18.05, or 18.600)" +lectureRawSections = ["34-101/TR/0/2.30-4"] +lectureSections = [[[[51, 3], [119, 3]], "34-101"]] +sectionKinds = ["lecture"] +description = "Provides an introduction to computer vision, covering topics from early vision to mid- and high-level vision, including low-level image analysis, edge detection, image transformations for image synthesis, methods for 3D scene reconstruction, motion analysis and tracking. Additionally, presents basics of machine learning, convolutional neural networks, and transformers in the context of image and video data for object classification, detection, and segmentation." + +["6.S080"] +url = "https://www.eecs.mit.edu/academics/subject-updates/subject-updates-spring-2026/#6_S080" +name = "(also taught under 16.S690) Introduction to Autonomy" +level = "U" +lectureUnits = 2 +labUnits = 0 +preparationUnits = 4 +isVariableUnits = false +inCharge = "Prof. Sertac Karaman" +prereqs = "6.100A or 6.100L" +description = "Provides an introduction to computational principles that underlie autonomous robots and vehicles. Topics include planning on state-space graphs, estimation of probabilistic belief state, formulating constraint programs, and reinforcement learning of optimal decision-making policies." + +["6.S085"] +url = "https://www.eecs.mit.edu/academics/subject-updates/subject-updates-spring-2026/#6_S085" +name = "The History of Computing" +level = "U" +lectureUnits = 3 +labUnits = 0 +preparationUnits = 9 +isVariableUnits = false +inCharge = "Benjamin Lindquist (HISTORY), bclind@mit.edu" +prereqs = "none" +lectureRawSections = ["E51-393/MW/0/11-12.30"] +lectureSections = [[[[10, 3], [78, 3]], "E51-393"]] +sectionKinds = ["lecture"] +description = "Traces the global, material history of computing from the nineteenth century to the present. Use historical artifacts, such as World War II ciphers and thirty-pound laptops, to analyze the changing design, cultural meaning, and purposes of computing. Develops a framework for understanding how computing technologies move across national borders and shaped the modern world." + +["6.S891"] +url = "https://www.eecs.mit.edu/academics/subject-updates/subject-updates-spring-2026/#6_S891" +name = "/6.S893/12.S992 AI for Climate Action" +level = "G" +lectureUnits = 3 +labUnits = 0 +preparationUnits = 9 +isVariableUnits = false +inCharge = "Abigail Bodner (EAPS/EECS), Priya Donti (EECS), Sara Beery (EECS)" +prereqs = "6.3900 OR 6.8300/1 OR 6.7960 OR equivalent" +lectureRawSections = ["4-145/MW/0/2-3.30"] +lectureSections = [[[[16, 3], [84, 3]], "4-145"]] +sectionKinds = ["lecture"] +description = """ +6.S891: Biodiversity and environment - Prof. Sara Beery\n +6.S893: Power and energy systems - Prof. Priya Donti\n +12.S992: Climate models - Prof. Abigail Bodner\n +\n +Examines applications of artificial intelligence and machine learning to climate change mitigation, adaptation, and monitoring. Introduces the physical science of climate change, data-driven modeling and observation, and approaches for decision-making in domains such as climate modeling, biodiversity, and energy systems. Includes common (‘merged’) lectures on climate fundamentals followed by domain-specific sections (‘forks’) focusing on advanced methods such as physics-informed learning, data assimilation, and uncertainty quantification. Within each ‘fork’, students present, critique, and lead discussions of current research papers and develop a written research proposal applying machine learning methods to the track’s focus area. ‘Merged’ sessions later in the term synthesize lessons and foster exchange across domains. Both graduate and undergraduate students are encouraged to register.""" + +["6.S895"] +url = "https://www.eecs.mit.edu/academics/subject-updates/subject-updates-spring-2026/#6_S895" +name = "Computational Textiles (2nd Half-Term)" +same = "4.S52" +level = "G" +lectureUnits = 1 +labUnits = 1 +preparationUnits = 4 +isVariableUnits = false +inCharge = "Professor Mariana Popescu (ARCH), madpope@mit.edu" +prereqs = "experience with 3D modeling and Python coding is preferred; some affinity for physical prototyping would also be useful" +lectureRawSections = ["3-329/T/0/10.30-12.30"] +lectureSections = [[[[43, 4]], "3-329"]] +sectionKinds = ["lecture"] +description = "The goal of the class is to explore the intersection of textile fabrication, computational design, and design thinking. Students will learn how computational methods can transform knitting from a traditional craft into a precise digital fabrication technique for creating complex tensile structures and geometric components. By using the 3D knitting machine, students will gain practical experience with digital knitting tools and develop an understanding of how computation enables new possibilities for textile-based architecture. No prior knitting experience required—just curiosity about the intersection of materials, code, and form." + +["6.S898"] +url = "https://www.eecs.mit.edu/academics/subject-updates/subject-updates-spring-2026/#6_S898" +name = "Parallel Algorithms" +level = "G" +lectureUnits = 3 +labUnits = 0 +preparationUnits = 9 +isVariableUnits = false +inCharge = "Mohsen Ghaffari (EECS)" +prereqs = "Sufficient mathematical maturity to start a graduate-level algorithmic course. 6.1220 Design and Analysis of Algorithms would suffice (this can be placed as the formal prerequisite; students with sufficient algorithmic backgrounds can qualify to skip it, assuming coordination with the instructor). We plan to do this similarly to how it is managed in 6.5250." +lectureRawSections = ["32-155/TR/0/11-12.30"] +lectureSections = [[[[44, 3], [112, 3]], "32-155"]] +sectionKinds = ["lecture"] +description = "As computing systems increasingly rely on parallelism, understanding how to design efficient parallel algorithms has become essential. This graduate-level theory course introduces the fundamental principles of, and the algorithm design techniques for exploiting, parallelism in computation. The focus will be on algorithmic tools and techniques, in the context of rigorous models of parallel computation." + +["6.S899"] +url = "https://www.eecs.mit.edu/academics/subject-updates/subject-updates-spring-2026/#6_S899" +name = "Learning Time Series With Interventions" +level = "U" +lectureUnits = 0 +labUnits = 0 +preparationUnits = 0 +isVariableUnits = true +inCharge = "Munzer Dahleh (EECS); Devavrat Shah (EECS)" +prereqs = "Probability (6.3700/6.3702), Linear Algebra (18.06) and Signals, Systems and Inference (6.3010)" +lectureRawSections = ["32-044/TR/0/10-12"] +recitationRawSections = ["32-044/F/0/12-2"] +lectureSections = [[[[42, 4], [110, 4]], "32-044"]] +recitationSections = [[[[148, 4]], "32-044"]] +sectionKinds = ["lecture", "recitation"] +description = "A time series is a set of time-stamped observations that capture an evolving process, often affected by noise. These observations may be interdependent in a specific yet unknown manner. Examples of time series include stock prices, currency exchange rates relative to the dollar, average housing prices, the number of Covid-19 infections, or the pitch angle of an airplane during flight. Modeling these processes for prediction or intervention is a fundamental challenge in statistical learning. This course provides a foundation for understanding, predicting, and intervening in such processes." + +["6.S955"] +url = "https://www.eecs.mit.edu/academics/subject-updates/subject-updates-spring-2026/#6_S955" +name = "Machine Learning for Signal Processing" +level = "U" +lectureUnits = 3 +labUnits = 0 +preparationUnits = 9 +isVariableUnits = false +inCharge = "Paris Smaragdis (MTA/EECS), paris@mit.edu" +prereqs = "6.3000, 18.06 or 18.C06, 6.3700 or 6.3800, or 18.05" +lectureRawSections = ["32-155/MW/0/1-2.30"] +lectureSections = [[[[14, 3], [82, 3]], "32-155"]] +sectionKinds = ["lecture"] +description = "This class covers of machine learning and signal processing as they pertain to the development of systems that can understand complex real-world signals, such as speech, images, movies, music, biological and mechanical readings, etc. It focuses on developing a strong unifying perspective between machine learning and signal processing, covering both classical techniques, but also many of the latest developments. Subjects include hands-on examples of how to decompose, analyze, classify, detect and consolidate signals, and we examine various commonplace operations such as finding faces from camera feeds, organizing personal music collections, designing speech dialog systems, focusing radio arrays, etc. Graduate credit includes extra homework assignments and a final project." + +["6.S976"] +url = "https://www.eecs.mit.edu/academics/subject-updates/subject-updates-spring-2026/#6_S976" +name = "Cryptography and Machine Learning: Foundations and Frontiers" +same = "18.S996" +level = "G" +lectureUnits = 3 +labUnits = 0 +preparationUnits = 9 +isVariableUnits = false +inCharge = "Vinod Vaikuntanathan (EECS), Shafi Goldwasser (Math)" +prereqs = "6.1220 (Algorithms) AND 6.390 (Intro to Machine Learning); or equivalent." +lectureRawSections = ["24-115/TR/0/11-12.30"] +lectureSections = [[[[44, 3], [112, 3]], "24-115"]] +sectionKinds = ["lecture"] +description = "Cryptography offers a playbook for building trust on untrusted platforms. This course applies that playbook to modern machine learning. We will study how cryptographic modeling and tools—ranging from privacy-preserving algorithms to interactive proofs and debate protocols—can endow ML systems with privacy, verifiability, and reliability. Topics include mechanisms for data and model privacy; methods to verify average-case quality and certify worst-case correctness; and strategies for robustness and alignment across discriminative and generative models. By the end, students will see the contours of a new field at the Crypto × ML interface and identify concrete problems in trustworthy ML that benefit from cryptographic thinking and techniques." + +["6.S977"] +url = "https://www.eecs.mit.edu/academics/subject-updates/subject-updates-spring-2026/#6_S977" +name = "Ethical Machine Learning in Human Systems" +level = "G" +lectureUnits = 3 +labUnits = 0 +preparationUnits = 9 +isVariableUnits = false +inCharge = "Prof. Marzyeh Ghassemi, EECS/IMES" +prereqs = "2 introductory courses in machine learning (e.g. 6.7900, 6.3900, 6.3720, 6.3730)" +lectureRawSections = ["56-154/F/0/10-1"] +lectureSections = [[[[144, 6]], "56-154"]] +sectionKinds = ["lecture"] +description = """ +Focuses on the human-facing considerations in the pipeline of machine learning (ML) development in human-facing systems like healthcare, employment, and education. Topics covered include algorithmic fairness, sources of model error/bias, model robustness, human/model evaluations, and policy. Pertinent issues co-occurring in society will be discussed, and course projects will emphasize the technical and ethical challenges of using machine learning in human systems. \n +\n +For more information: https://canvas.mit.edu/courses/37283""" + +["6.S984"] +url = "https://www.eecs.mit.edu/academics/subject-updates/subject-updates-spring-2026/#6_S984" +name = "Datacenter Computing" +level = "G" +lectureUnits = 0 +labUnits = 0 +preparationUnits = 0 +isVariableUnits = true +inCharge = "Christina Delimitrou (EECS), delimitrou@csail.mit.edu" +prereqs = "6.1910 AND 6.1800" +lectureRawSections = ["36-144/MW/0/1-2.30"] +lectureSections = [[[[14, 3], [82, 3]], "36-144"]] +sectionKinds = ["lecture"] +description = """ +Warehouse-scale datacenters host a wide range of online services, including social networks, web search, video streaming, machine learning, and serverless workloads. In this course, we will study the end-to-end stack of modern datacenters, from hardware and OS all the way to resource managers and programming frameworks. We will also explore cross-cutting issues, such as total cost of ownership, service level objectives, availability, and reliability. The course is a combination of lectures and paper readings. Students will read up to two papers per topic and submit brief summaries. During class meetings, we will start with a student presentation of the papers followed by an in-class discussion. The main deliverable for the course is a semester-long group project which should address an open research problem in modern cloud environments (project suggestions will be provided by the instructor, but students are also welcome to propose their own). \n + \n +The class is appropriate for graduate and advanced undergraduate students who want to learn more about cloud computing and datacenter systems.""" + +["6.S985"] +url = "https://www.eecs.mit.edu/academics/subject-updates/subject-updates-spring-2026/#6_S985" +name = "Modeling: Multimodal Approaches" +same = "MAS.S60" +level = "G" +lectureUnits = 3 +labUnits = 0 +preparationUnits = 9 +isVariableUnits = false +inCharge = "Paul Liang (MAS+EECS), Dimitris Bertsimas (Sloan), Sang-Gook Kim (MechE), Jinhua Zhao (DUSP)" +prereqs = "6.390 or equivalent" +lectureRawSections = ["E14-633/TR/0/2.30-4"] +lectureSections = [[[[51, 3], [119, 3]], "E14-633"]] +sectionKinds = ["lecture"] +description = "Artificial Intelligence (AI) holds great promise to enhance digital productivity, physical interactions, overall well-being, and the human experience. To enable the true impact of AI, these systems will need to be grounded in many real-world data modalities, from language-only systems to holistically integrating vision, audio, sensors, medical data, music, art, smell, taste, and more. This course introduces the principles of multimodal AI that can process many modalities at once, such as connecting language and images, music and art, sensing and actuation, and more. We will cover AI methods to (1) represent and fuse heterogeneous and interconnected data sources, (2) align data across different views, (3) reason over multiple steps with many modalities, (4) generate new multimodal content, (5) transfer knowledge from high-resource to low-resource data, and (6) quantify the principles of multimodal AI for safe, ethical, and human-aligned deployment." + +["6.S986"] +url = "https://www.eecs.mit.edu/academics/subject-updates/subject-updates-spring-2026/#6_S986" +name = "Uncertainty Quantification with AI" +level = "G" +lectureUnits = 3 +labUnits = 0 +preparationUnits = 9 +isVariableUnits = false +inCharge = "Stephen Bates (EECS)" +prereqs = "undergraduate probability (6.3700 or 18.600 or equivalent), undergraduate statistics (18.650 or equivalent), at least one graduate-level course in machine learning (e.g., 6.7900 or 6.7960 or similar)" +lectureRawSections = ["32-144/TR/0/2.30-4"] +lectureSections = [[[[51, 3], [119, 3]], "32-144"]] +sectionKinds = ["lecture"] +description = "Investigates modern algorithms and theory for in uncertainty quantification at an advanced graduate level. Covers widely-used methods such as (Bayesian) deep ensembles, calibration, and conformal prediction, as well as their underlying motivation and theoretical support. Subsequently, the course surveys research literature from the last few years. Intended for PhD students with research interests in statistics, machine learning, or similar." diff --git a/scrapers/pe.py b/scrapers/pe.py index 0bc2812b..4f4a7470 100644 --- a/scrapers/pe.py +++ b/scrapers/pe.py @@ -5,11 +5,11 @@ from __future__ import annotations import csv -from functools import lru_cache import json import os import time as time_c from datetime import date, time +from functools import lru_cache from typing import Literal, TypedDict from urllib.request import Request, urlopen