<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.3">Jekyll</generator><link href="https://kevinchen.co/feed.xml" rel="self" type="application/atom+xml" /><link href="https://kevinchen.co/" rel="alternate" type="text/html" /><updated>2025-07-19T11:36:10-07:00</updated><id>https://kevinchen.co/feed.xml</id><title type="html">Kevin Chen</title><subtitle>Personal website of Kevin Chen</subtitle><author><name>Kevin Chen</name></author><entry><title type="html">Real estate is one of the hardest open problems in scaled self driving</title><link href="https://kevinchen.co/blog/real-estate-hard-open-problem-in-autonomous-driving/" rel="alternate" type="text/html" title="Real estate is one of the hardest open problems in scaled self driving" /><published>2024-09-03T16:24:33-07:00</published><updated>2024-09-03T16:24:33-07:00</updated><id>https://kevinchen.co/blog/real-estate-hard-open-problem-in-autonomous-driving</id><content type="html" xml:base="https://kevinchen.co/blog/real-estate-hard-open-problem-in-autonomous-driving/"><![CDATA[<p>I’ve had a minor obsession with Waymo’s autonomous vehicle depots recently.</p>

<p>Over the past few months, I’ve <a href="https://www.youtube.com/watch?v=3QZ3e7mWD9E">flown a drone</a> as part of a stakeout to understand how they work. And I’ve taken a deep dive into an apparent Waymo outage to find the company <a href="https://www.youtube.com/watch?v=kgKTVtj1xQU">charging its electric vehicles from temporary diesel generators</a>.</p>

<p>The reason for my obsession? <strong>I believe depot buildouts will be one of the last hard problems in scaled autonomous driving.</strong> Long after the hardware, software, and AI have been perfected, real estate acquisition will remain a limiting factor in large-scale AV deployment.</p>

<figure>
    <a href="/assets/blog/av-depot-problem/waymo-main-depot-hero-3840w.jpg">
        <img class="extrawide" srcset="
                /assets/blog/av-depot-problem/waymo-main-depot-hero-3840w.jpg 3840w,
                /assets/blog/av-depot-problem/waymo-main-depot-hero-2400w.jpg 2400w,
                /assets/blog/av-depot-problem/waymo-main-depot-hero-1200w.jpg 1200w" src="/assets/blog/av-depot-problem/waymo-main-depot-hero-2400w.jpg" sizes="(max-width: 1200px) 100vw, 1200px" width="1200" height="675" alt="A photograph of a parking lot with white autonomous vehicles and charging equipment in front of a hill on a partly cloudy day" />
    </a>
    <figcaption><p>Waymo’s main depot at 201 Toland Street, San Francisco.</p></figcaption>
</figure>

<h2 id="will-self-driving-follow-software-scaling-laws">Will self driving follow software scaling laws?</h2>

<p>In 2021, Elon Musk claimed that Tesla FSD’s release will be <a href="https://x.com/elonmusk/status/1450868052040507392">“one of the biggest asset value increases in history.”</a></p>

<blockquote class="twitter-tweet" data-conversation="none">
    <p lang="en" dir="ltr">The day FSD goes to wide release will be one of the biggest asset value increases in history</p>&mdash; Elon Musk (@elonmusk) <a href="https://twitter.com/elonmusk/status/1450868052040507392?ref_src=twsrc%5Etfw">October 20, 2021</a>
</blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<p>Musk is arguing that, once autonomous driving has been solved, it can be instantly rolled out at the push of a button. Nearly all of Tesla’s fleet could be put to productive use without humans behind the wheel.</p>

<p>While Musk’s viewpoint is on the extreme end, it’s a sentiment shared by many who have worked on or invested in autonomous driving over the years. Once you have hardware capable of supporting safe driverless operation, it’s just a matter of developing the right software. Software can be replicated infinitely at zero marginal cost. Could autonomous driving therefore scale as quickly as software platforms like Uber or DoorDash?</p>

<p>The answer is not so simple. Self-driving cars are still cars — cars that exist in the physical world and need to be parked, fueled, cleaned, and repaired. Uber and other multi-sided marketplace platforms have been able to grow exponentially because they distribute these responsibilities to the individual drivers — many Uber drivers park at their own homes — allowing the platform provider to focus on developing the software pieces.</p>

<p>So far, AV companies like Waymo and Cruise have taken a different approach. They’ve preferred to centralize these operational tasks in large depots staffed with their own personnel. This is because AV technology is still maturing and cannot be easily productized in the short term. Additionally, <a href="https://www.understandingai.org/p/waymos-investments-in-san-francisco">Timothy B. Lee notes in <em>Understanding AI</em></a> that “having hardware, software, and support services all under one roof makes it easier for Waymo to experiment with different technologies and business models.” When the kinks are still being worked out, it is more straightforward to vertically integrate everything in a single organization.</p>

<h2 id="the-many-jobs-to-be-done-of-a-robotaxi-depot">The many jobs to be done of a robotaxi depot</h2>

<p>Depots for human-driven fleets, such as rental cars or delivery vans, only require a parking area with minimal additional infrastructure. This enables a fairly straightforward trade-off between location and cost: the fleet operator seeks a location close to customer demand while minimizing rent. For example, a logistics company participating in <a href="https://logistics.amazon.com/">Amazon’s Delivery Service Partner program</a> can run its depot from any sufficiently cheap parking lot near the local Amazon warehouse.</p>

<p>The same constraints affect depot selection for autonomous vehicles. However, the depot also needs to be more than just a convenient parking lot to store off-duty cars.</p>

<ul>
  <li>Because AVs are often also EVs, the ideal site also has electric vehicle charging.</li>
  <li>Because AVs need to upload driving logs to the cloud, it should have a high-speed Internet connection too.</li>
</ul>

<p>Let’s explore these constraints in detail.</p>

<h3 id="location">Location</h3>

<p>Depots should be placed close to customer demand to minimize <a href="https://en.wikipedia.org/wiki/Dead_mileage">deadheading</a> (non-revenue driving), which would raise costs while degrading the customer experience with longer pickup times. Ideal depots are therefore located in desirable residential or commercial areas, where there is more competition among potential tenants.</p>

<p>Placing depots in high-demand neighborhoods instead of industrial areas can also increase the probability of local opposition. Waymo has already encountered opposition during a proposed expansion of their main depot, even though it is located in an industrial neighborhood with many similar facilities. Again <a href="https://www.understandingai.org/p/waymos-investments-in-san-francisco">from Timothy B. Lee</a>:</p>

<blockquote>
  <p>Waymo sought a permit to convert the warehouse next door into some office space and a parking lot for Waymo employees. San Francisco’s Board of Supervisors <a href="https://www.cbsnews.com/sanfrancisco/news/sf-supervisors-reject-proposed-waymo-private-lot/">unanimously rejected</a> Waymo’s application.</p>

  <p>The rejection was partly based on fears that Waymo would eventually use the space to launch a delivery service in the city (Waymo hasn’t announced any plans to do this so far). But it also reflected city leaders’ frustration with their general lack of power over Waymo.</p>
</blockquote>

<p>Now consider the recent incident in which <a href="https://www.youtube.com/watch?v=4gONBQb80n0">driverless Waymo vehicles honked at each other</a> while entering a depot near residential buildings in San Francisco, often well into the early hours of the morning. While residents and the company resolved the situation amicably, it will surely be raised in future discussions of new Waymo depots in residential areas should they come before the Board of Supervisors or Planning Commission.</p>

<h3 id="electric-vehicle-charging">Electric vehicle charging</h3>

<p>AV developers have preferred to run their services with electric vehicles. Although AV and EV technologies are not inherently coupled, running a fully electric fleet adds an environmental angle to the AV sales pitch, allowing the companies to claim that AV rides reduce emissions by displacing gas-powered driving. An EV fleet also lowers vehicle maintenance costs.</p>

<p>Waymo and Cruise each have locations with DC fast charging capability. This approach avoids relying on public chargers. Taking Waymo’s primary San Francisco depot as an example, the company installed <a href="https://www.understandingai.org/p/waymos-investments-in-san-francisco">38 chargers</a> of <a href="https://youtube.com/clip/UgkxuYyMy-TAcFXz3QBG2cyk4SwsLQoCisbM?si=iNXc8iSE10LJvm_A">approximately 60 kW each</a>, implying a total site power of around 2.4 MW.</p>

<figure>
    <a href="/assets/blog/av-depot-problem/waymo-main-depot-top-down-3840w.jpg">
        <img class="extrawide" srcset="
                /assets/blog/av-depot-problem/waymo-main-depot-top-down-3840w.jpg 3840w,
                /assets/blog/av-depot-problem/waymo-main-depot-top-down-2400w.jpg 2400w,
                /assets/blog/av-depot-problem/waymo-main-depot-top-down-1200w.jpg 1200w" src="/assets/blog/av-depot-problem/waymo-main-depot-top-down-2400w.jpg" sizes="(max-width: 1200px) 100vw, 1200px" width="1200" height="675" alt="A photograph of a parking lot with white autonomous vehicles and charging equipment" />
    </a>
    <figcaption><p>Waymo vehicles charging in San Francisco. Approximately one-third of parking spots in the main depot have charging.</p></figcaption>
</figure>

<p>Bringing in so many high-power chargers likely added significant complexity to Waymo’s depot construction. While we don’t know Waymo’s process, we have a fairly good benchmark from the Tesla community, which tracks Tesla Supercharger installations closely. From <a href="https://teslamotorsclub.com/tmc/threads/california-supercharger-locations-faq.217021/">Bruce Mah</a>, a seasoned EV charging observer, the construction process is:</p>

<blockquote>
  <p>As with any construction project, things usually start with selecting a site and permitting. There will often be some demolition / excavation of part of a parking lot (Superchargers are often built in existing parking lots). Tesla equipment such as charging cabinets, posts, etc. will usually be installed next (see T1 below). Eventually there will be some inspections from the local Authority Having Jurisdiction (AHJ). A utility transformer (from PG&amp;E, SCE, etc.) is usually the last piece of equipment to be installed. Repaving, painting, and installation of parking stops will also usually happen late in the process, as well as landscaping and lighting enhancements.</p>
</blockquote>

<p>Of these steps, permitting and utility work are not within the charging operator’s control. California municipalities, especially San Francisco, have a notoriously slow and political permitting process. With PG&amp;E, the utility serving much of the state, electrical service upgrades involving a new distribution transformer <a href="https://teslamotorsclub.com/tmc/threads/supercharger-mariposa-ca-live-12-may-2023-12-v3-stalls.149673/page-4">can take months</a>.</p>

<p>Timelines aside, building out a charging site is also expensive. For example, an <a href="https://weho.granicus.com/MetaViewer.php?view_id=22&amp;clip_id=3580&amp;meta_id=196819">agreement between Tesla and the City of West Hollywood</a> values an eight-plug location at $482,942 for both equipment and construction.</p>

<h3 id="data-offload">Data offload</h3>

<p>The final piece of the puzzle is data offload. Autonomous vehicles log vast amounts of data as they drive, measured in hundreds of GBs to TBs per hour.</p>

<ul>
  <li>Some of the data is subject to mandatory retention and must be uploaded for later review. At a minimum, all AV collisions in California must be reported to the DMV. Regulators at all levels of government expect the AV developer to present analyses of serious incidents, including recordings from the vehicle and explanations of the AV’s decisions.</li>
  <li>In addition to regulatory requirements, the AV developer often wants to return much more data for engineering purposes: near misses, stuck events, novel or interesting scenarios, and more.</li>
</ul>

<p>The upshot is that the AV operator needs to upload a substantial portion of the hundreds of GBs to TBs logged per hour of driving. Uploading over cellular networks would not be cost effective. These transfers must occur at a depot.</p>

<p>Today, it’s likely that Waymo and Cruise use disk swapping for data offload. When a car fills up its internal logging disk, it notifies an operator to plug in a fresh one. The full disks may be uploaded directly from the depot or shipped to a datacenter. This whole process is labor intensive and, over time, may pose a reliability concern due to dust or water ingress.</p>

<figure>
    <a href="/assets/blog/av-depot-problem/waymo-disk-swap-2300w.jpg">
        <img srcset="/assets/blog/av-depot-problem/waymo-disk-swap-2300w.jpg 2300w,
                /assets/blog/av-depot-problem/waymo-disk-swap-1280w.jpg 1280w,
                /assets/blog/av-depot-problem/waymo-disk-swap-640w.jpg 640w" src="/assets/blog/av-depot-problem/waymo-disk-swap-1280w.jpg" sizes="(max-width: 680px) calc(100vw - 40px), 640px" width="640" height="427" alt="A person installing a gray metallic box in the trunk of a white autonomous vehicle" />
    </a>
    <figcaption><p>A Waymo operator performs a possible disk swap.</p></figcaption>
</figure>

<p>Many AV developers are moving toward direct data transfer from the vehicle using Ethernet, Wi-Fi, or a private 5G network, which reduces the number of manual touch points and moving parts. Charging is a great time to perform these transfers. However, this imposes an additional requirement on the depot: a fast upload speed, probably a fiber connection of at least 10 Gbps.</p>

<h2 id="where-do-we-go-from-here">Where do we go from here?</h2>

<p>When we put all three requirements together (great location, high-power EV charging, and high-speed Internet), there may be few to none sites that fit the bill. This would require the AV operator to take on site-specific construction projects to add amenities like charging and Internet — a strategy that sits in direct opposition to rapid and cost-effective scaling.</p>

<p>Another possibility is to engineer ways to relax the constraints.</p>

<h3 id="decoupling-the-requirements">Decoupling the requirements</h3>

<p>Waymo and Cruise do not require all of their locations to have charging and data offload. For example, Waymo operates satellite lots in downtown San Francisco only for storing their off-hail vehicles. Every night, fleet management software instructs the cars to travel back to the <a href="https://maps.app.goo.gl/pK67WeFPQiBQBDeP7">main depot</a> for charging and data offload.</p>

<figure>
    <a href="/assets/blog/av-depot-problem/waymo-satellite-depot-2400w.jpg">
        <img class="extrawide" srcset="/assets/blog/av-depot-problem/waymo-satellite-depot-2400w.jpg 2400w,
                /assets/blog/av-depot-problem/waymo-satellite-depot-1200w.jpg 1200w" src="/assets/blog/av-depot-problem/waymo-satellite-depot-2400w.jpg" sizes="(max-width: 1200px) 100vw, 1200px" width="1200" height="800" alt="A parking lot in an urban area with many white autonomous vehicles" />
    </a>
    <figcaption><p>A Waymo satellite location in San Francisco with minimal staffing, no charging, and apparently no data offloading.</p></figcaption>
</figure>

<p>This solution works as long as the total charging and data transfer capacity across all locations exceeds the average throughput required to keep the fleet in working order. However, the lack of redundancy can lead to cascading failures, such as the <a href="https://www.youtube.com/watch?v=kgKTVtj1xQU">apparent power outage at Waymo’s main depot</a> that led the company to shut down many vehicles during a Friday evening rush hour.</p>

<h3 id="reducing-charging-power">Reducing charging power</h3>

<p>Waymo and Cruise currently use DC fast charging (DCFC) for their fleets. Level 2 (L2, also known as AC) charging could reduce the cost of buildouts because the equipment is cheaper and can often be installed without bringing in a new utility transformer. This could enable overnight charging in satellite locations that do not currently have any charging capacity. Imagine an operator arriving at night to plug in all the cars when there is little demand, then returning in the morning to unplug them before customers wake up.</p>

<p>There is an order of magnitude speed difference between L2 and DCFC.</p>

<ul>
  <li>This is important for consumer charging, where the consumer cares about the time to get a single car back on the road.</li>
  <li>However, charging power for any individual car becomes less important when charging a large fleet. Fleet operators care about the total throughput of turning around cars, which is proportional to total power delivered across all chargers.</li>
</ul>

<p>In other words, assuming an autonomous ride hailing service will always have overnight lulls in demand and enough parking spots during those times, the most scalable strategy is to procure your desired total charging power at the lowest price.</p>

<p>DCFC equipment costs disproportionately more per kW due to the additional complexity of the charging equipment — and that doesn’t include the additional maintenance complexity. The table below compares ChargePoint’s cheapest L2 and DCFC units:</p>

<div class="table-scroll">

  <table>
    <thead>
      <tr>
        <th style="text-align: left">Charger</th>
        <th style="text-align: right">Power (kW)</th>
        <th style="text-align: right">Price ($)</th>
        <th style="text-align: right">Unit Price ($/kW)</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td style="text-align: left"><a href="https://smartchargeamerica.com/electric-car-chargers/commercial/chargepoint-cpf50/">ChargePoint CPF50</a></td>
        <td style="text-align: right">9.6 kW</td>
        <td style="text-align: right">$1,299</td>
        <td style="text-align: right">$135/kW</td>
      </tr>
      <tr>
        <td style="text-align: left"><a href="https://smartchargeamerica.com/electric-car-chargers/commercial/chargepoint-express-250/">ChargePoint CPE250</a></td>
        <td style="text-align: right">62.5 kW</td>
        <td style="text-align: right">$52,000</td>
        <td style="text-align: right">$832/kW</td>
      </tr>
    </tbody>
  </table>

</div>

<p>In addition to more scalable depot buildouts, reduced charging power can also improve the longevity of the vehicle’s traction battery, which is an important factor in managing vehicle depreciation.</p>

<h3 id="reducing-data-logging-rate">Reducing data logging rate</h3>

<p>Most AV developers start out by logging and uploading all data generated on their vehicles. This makes development easy because the data is always there when you need it. These assumptions need to be broken when a growing fleet generates proportionally more log data, most of which contain routine driving and are not very interesting.</p>

<p>We can split the data logged by AVs into two categories:</p>

<ul>
  <li>Raw sensor data, such as lidar point clouds, camera images, radar returns, and audio.</li>
  <li>Derived data, such as detections from the perception system or motion plans from the behavior system.</li>
</ul>

<p>One approach is to keep only one category of data. Retaining only the derived data can still enable debugging of serious incidents, as long as the perception system can be trusted to provide a faithful representation of the raw sensor data. On the other hand, retaining only the raw sensor data makes the logs more useful for developing the mapping and perception system. An approximation of the derived data can be generated by running a <a href="/blog/autonomous-vehicle-simulation-taxonomy/">replay simulator</a> as needed, but it is challenging to reproduce the exact same outputs as those on the vehicle unless the AV software is fully deterministic.</p>

<p>Data retention decisions can also be made temporally. The key challenge here is high-recall classification of which time ranges in the log must be retained. For example, if a DMV-reportable collision occurs, the associated log data must never be discarded. These decisions can happen either on-device or in the cloud, but they must be made without uploading the full log to the cloud, since our bottleneck is the connection from the vehicle to the Internet.</p>

<h2 id="conclusion">Conclusion</h2>

<p>The current trajectory for scaled autonomous driving would require desirable depot locations to include charging and Internet, making real estate acquisition challenging. There exist opportunities to reduce the additional requirements over time with the goal of making the problem closer to “rent a bunch of conveniently located parking lots.” While these are not traditionally considered autonomous driving problems, solving them will be key to unlocking the next phase of scaling.</p>]]></content><author><name>Kevin Chen</name></author><summary type="html"><![CDATA[Why parking & charging will become the limiting factor to bringing autonomous driving to the masses]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kevinchen.co/blog/av-depot-problem/waymo-main-depot-hero-1200w.jpg" /><media:content medium="image" url="https://kevinchen.co/blog/av-depot-problem/waymo-main-depot-hero-1200w.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Large language models are a sustaining innovation for Siri</title><link href="https://kevinchen.co/blog/large-language-models-sustaining-innovation-for-siri/" rel="alternate" type="text/html" title="Large language models are a sustaining innovation for Siri" /><published>2024-06-11T11:37:00-07:00</published><updated>2024-06-11T11:37:00-07:00</updated><id>https://kevinchen.co/blog/large-language-models-sustaining-innovation-for-siri</id><content type="html" xml:base="https://kevinchen.co/blog/large-language-models-sustaining-innovation-for-siri/"><![CDATA[<p>Many people assume that large language models (LLMs) will disrupt existing consumer voice assistants. Compared to Siri, while today’s ChatGPT is largely unable to complete real-world tasks like hailing an Uber, it’s far better than Siri at understanding and generating language, especially in response to novel requests.</p>

<p>From <a href="https://www.tomshardware.com/tech-industry/artificial-intelligence/openais-new-assistant-makes-apples-siri-look-primitive-also-announces-gpt-4o-and-new-desktop-pc-client">Tom’s Hardware</a>, this captures the sentiment I see among tech commentators:</p>

<blockquote>
  <p>GPT-4o will enable ChatGPT to become a legitimate Siri competitor, with real-time conversations via voice that are responded to instantly without lag time. […] ChatGPT’s new real-time responses make tools like Siri and Echo seem lethargic. And although ChatGPT likely won’t be able to schedule your haircuts like Google Assistant can, it did put up admirable real-time translating chops to challenge Google.</p>
</blockquote>

<p>Last year, there were <a href="https://www.theinformation.com/articles/designer-jony-ive-and-open-ais-sam-altman-discuss-ai-hardware-project">rumors</a> that OpenAI was working on its own hardware, which would open the possibility of integrating ChatGPT at the system level along the lines of the Humane Ai Pin. Would such a product be able to mount a successful challenge against Siri, Alexa, and Google Assistant?</p>

<p>After <a href="https://www.youtube.com/live/RXeOiIDNNek?t=4565s">Apple’s WWDC keynote yesterday</a> and seeing the <a href="https://developer.apple.com/videos/play/wwdc2024/10134/">updated Siri APIs</a>, I think it’s more likely that LLMs are a <em>sustaining</em> innovation for Siri — a technological innovation that strengthens the position of incumbent voice assistants.</p>

<figure>
    <a href="/assets/blog/llm-siri/wwdc-2024-siri-in-app-actions-3840w.jpg">
        <img class="extrawide" srcset="/assets/blog/llm-siri/wwdc-2024-siri-in-app-actions-2400w.jpg 2400w,
                /assets/blog/llm-siri/wwdc-2024-siri-in-app-actions-1200w.jpg 1200w" sizes="(max-width: 1200px) 100vw, 1200px" src="/assets/blog/llm-siri/wwdc-2024-siri-in-app-actions-2400w.jpg" width="1200" height="675" alt="A woman stands in front of a screen displaying various app icons and text descriptions of actions associated with each app" />
    </a>
    <figcaption><p>Apple promised to increase the number of ways in which Siri can take action in apps. Source: Apple.</p></figcaption>
</figure>

<p>Traditional voice assistants work by matching the user’s queries to a fixed set of intents. <a href="https://freeman.vc/notes/lets-talk-about-siri">Pierce Freeman explains the general approach</a>:</p>

<blockquote>
  <p>The previous generation of personal assistants had control logic that was largely hard-coded. They revolved around the idea of an intent — a known task that a user wanted to do like sending a message, searching for weather, etc. Detecting this intent might be keyword based or trained into a model that converts a sequence to a one-hot class space. But generally speaking there were discrete tasks and the job of the NLU pipeline was to delegate it to sub-modules.</p>
</blockquote>

<p>Once the query has been matched to an intent, the next step is to “fill in the blanks” for any inputs needed by the intent:</p>

<blockquote>
  <p>If it believes you’re looking for weather, a sub-module would attempt to detect what city you’re asking about. This motivated a lot of the research into NER (named entity recognition) to detect the more specific objects of interest and map them to real world quantities. <code class="language-plaintext highlighter-rouge">city:San Francisco</code> and <code class="language-plaintext highlighter-rouge">city:SF</code> to <code class="language-plaintext highlighter-rouge">id:4467</code> for instance.</p>

  <p>Conversational history was implemented by keeping track of what the user had wanted in previous steps. If a new message is missing some intent, it would assume that a previous message in the flow had a relevant intent. This process of back-detecting the relevant intent was mostly hard-coded or involved a shallow model.</p>
</blockquote>

<p>A natural outcome is that Apple is forced to develop an expansive and complicated system of intents because it is the only way to expand the assistant’s capabilities. In 2016, Apple also allowed third-party developers to integrate their apps’ functionality by providing intents through <a href="https://developer.apple.com/videos/play/tech-talks/10168?time=23">SiriKit</a>. Once the developer defines the inputs and outputs, the intents could appear in Siri, the Shortcuts app, proactive notifications, etc. alongside first-party intents by Apple. Similar frameworks exist on other platforms: <a href="https://codelabs.developers.google.com/codelabs/appactions">Android App Actions</a> and <a href="https://developer.amazon.com/en-US/docs/alexa/custom-skills/implement-the-built-in-intents.html">Alexa Skills</a>.</p>

<p>However, no matter how rich the intent library becomes, the overall user experience can still suffer if access is gated by a brittle intent-matching process: either (1) matching to the incorrect intent, or (2) after matching the correct intent, parsing the request parameters incorrectly. In my opinion, the intent matching stage is the primary source of users’ frustration with Siri.</p>

<figure>
    <a href="/assets/blog/llm-siri/siri-incorrect-named-entity.png">
        <img src="/assets/blog/llm-siri/siri-incorrect-named-entity.png" width="176" height="215" alt="A screenshot of a Siri request for the time in Taipei answered by providing the time in San Francisco" />
    </a>
    <figcaption><p>Incorrect named entity recognition by Siri.</p></figcaption>
</figure>

<p>Contrast this with <a href="https://openai.com/index/chatgpt-plugins/">ChatGPT plugins</a>, a similar system that allows the model to interact with external APIs by determining which plugin might be relevant to the user’s request, then reading the plugin’s API specification to determine the input and output parameters. In other words, the intent matching is performed by an LLM. The generalist nature of LLMs seems to reduce brittleness. For example, when using the code interpreter plugin, the model can write arbitrary Python code and fix resulting runtime errors.</p>

<p>The main issue for challengers (OpenAI, Humane, and Rabbit) is the lack of third-party integrations to make their assistants helpful in consumers’ digital lives, extending beyond general knowledge tasks. For example:</p>

<ul>
  <li>The Humane Ai Pin only streams music from Tidal, not Spotify nor Apple Music.</li>
  <li>The Rabbit R1 “large action model” is, in reality, just a few <a href="https://x.com/xyz3va/status/1787964478878777760">handwritten UI automation scripts</a> for the applications shown in their demo. The system does not appear to generalize to unseen applications.
    <ul>
      <li>In general, while companies working on UI agents have shown some limited demos, I’m not aware of any that run with high reliability and scale. Even if they achieve scale and generalization, their agents could be made less reliable by app developers using anti-scraping techniques because the developers prefer to own the customer relationship, or as leverage for future partnership negotiations. This type of system is probably a few years out at a minimum.</li>
    </ul>
  </li>
</ul>

<p>Without a large user base, developers have no incentive to port their apps, leaving the integration work to the platform owner, as in the cases of Humane and Rabbit. Meanwhile, Apple, Amazon, and Google each have a pre-existing app ecosystem. Their position as <a href="https://stratechery.com/aggregation-theory/">aggregators</a> means developers are highly motivated to access the enormous installed base of iOS, Alexa, and Android devices.</p>

<p>Assuming <a href="https://www.semianalysis.com/p/google-we-have-no-moat-and-neither">LLM technology will become a commodity</a>, the incumbents’ in-house LLMs ought to be able to provide decent intent matching and language skills. Combined with an expansive library of intents, it seems very possible that integrating LLMs will cement the incumbent voice assistants as the dominant platforms. Challengers might be better off building products in areas that don’t depend on third-party integrations for key functionality.</p>]]></content><author><name>Kevin Chen</name></author><summary type="html"><![CDATA[Why it will be challenging to replace consumer voice assistants with ChatGPT]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kevinchen.co/blog/llm-siri/wwdc-2024-siri-in-app-actions-1200w.jpg" /><media:content medium="image" url="https://kevinchen.co/blog/llm-siri/wwdc-2024-siri-in-app-actions-1200w.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How autonomous vehicle simulation works</title><link href="https://kevinchen.co/blog/autonomous-vehicle-simulation-taxonomy/" rel="alternate" type="text/html" title="How autonomous vehicle simulation works" /><published>2024-06-06T18:57:40-07:00</published><updated>2024-06-06T18:57:40-07:00</updated><id>https://kevinchen.co/blog/autonomous-vehicle-simulation-taxonomy</id><content type="html" xml:base="https://kevinchen.co/blog/autonomous-vehicle-simulation-taxonomy/"><![CDATA[<p>When autonomous vehicle developers justify the safety of their driverless vehicle deployments, they lean heavily on their testing in simulation. Common talking points take the form of “we made our car drive X billion miles in simulation.” From these vague statements, it’s challenging to determine what a simulator is, or how it works.</p>

<figure>
    <a class="invert" href="/assets/blog/av-simulation-taxonomy/car-mesh-vaporwave-hero-1792w.jpg">
        <img style="background: #000020;" srcset="/assets/blog/av-simulation-taxonomy/car-mesh-vaporwave-hero-1792w.jpg 1792w,
                /assets/blog/av-simulation-taxonomy/car-mesh-vaporwave-hero-1280w.jpg 1280w,
                /assets/blog/av-simulation-taxonomy/car-mesh-vaporwave-hero-640w.jpg 640w" src="/assets/blog/av-simulation-taxonomy/car-mesh-vaporwave-hero-1280w.jpg" sizes="(max-width: 680px) calc(100vw - 40px), 640px" width="640" height="360" alt="Sedan driving into the sunset on a vaporwave-style wireframe road flanked by hills" title="DALL-E 3 prompt: &ldquo;Please generate a landscape orientation image depicting a sedan driving down a road with hills on either side. The image style is reminiscent of 1980s retro-futuristic aesthetics, commonly known as synthwave or vaporwave. The road and hills are rendered as a grid or wireframe, reminiscent of early computer graphics and suggesting a digital landscape or virtual environment. The grid extends towards the horizon in a perspective view. The grid emits a soft, blue neon-style glow. Above the grid, there is a large, partially setting sun, with horizontal lines breaking its continuity. The setting sun emits a soft orange and pink glow that contrasts with the dark blue sky above and the neon blue glow of the grid. The overall image is a blend of nostalgia and futurism, with a striking color palette of blues, oranges, and purples, and pinks.&rdquo;" />
    </a>
    <figcaption><p>There&rsquo;s more to simulation than endless driving in a virtual environment.</p></figcaption>
</figure>

<p>For example, <a href="https://waymo.com/waymo-driver/">Waymo’s technology overview page</a> says (emphasis mine):</p>

<blockquote>
  <p><strong>We’ve driven more than 20 billion miles in simulation</strong> to help identify the most challenging situations our vehicles will encounter on public roads. We can either replay and tweak real-world miles or build completely new virtual scenarios, for our autonomous driving software to practice again and again.</p>
</blockquote>

<p><a href="https://www.getcruise.com/road-to-driverless/">Cruise’s safety page</a> contains similar language:<sup id="fnref:cruise-simulator-posts" role="doc-noteref"><a href="#fn:cruise-simulator-posts" class="footnote" rel="footnote">1</a></sup></p>

<blockquote>
  <p>Before setting out on public roads, <strong>Cruise vehicles complete more than 250,000 simulations</strong> and closed course testing during everyday and extreme conditions.</p>
</blockquote>

<p>The main impression one gets from these overviews is that (1) simulation can test many driving scenarios, and (2) everyone will be super impressed if you use it a lot.</p>

<p>Going one layer deeper to the few <a href="https://www.getcruise.com/news/blog/2020/rethinking-cruises-av-development-loop-during-covid-19/">blog</a> <a href="https://waymo.com/blog/2021/07/simulation-city/">posts</a> and <a href="https://www.youtube.com/watch?v=7BF34b4EMz8">talks</a> full of slick GIFs, you might reach the conclusion that simulation is like a video game for the autonomous vehicle in the vein of <a href="https://www.technologyreview.com/2016/09/12/157605/self-driving-cars-can-learn-a-lot-by-playing-grand-theft-auto/">Grand Theft Auto</a> (GTA): a fully generated 3D environment complete with textures, lighting, and non-player characters (NPCs). Much like human players of GTA, the autonomous vehicle would be able to drive however it likes, freed from real-world consequences.</p>

<figure>
    <a class="invert" href="/assets/blog/av-simulation-taxonomy/cruise-synthetic-simulation-3rd-person-3821w.jpg">
        <img style="background: #2a303a;" srcset="
                /assets/blog/av-simulation-taxonomy/cruise-synthetic-simulation-3rd-person-1280w.jpg 1280w,
                /assets/blog/av-simulation-taxonomy/cruise-synthetic-simulation-3rd-person-640w.jpg 640w" src="/assets/blog/av-simulation-taxonomy/cruise-synthetic-simulation-3rd-person-1280w.jpg" sizes="(max-width: 680px) calc(100vw - 40px), 640px" width="640" height="360" alt="A video game screenshot of a white and orange Cruise autonomous vehicle driving on a street with a bus and a construction sign" />
    </a>
    <figcaption><p>Source: <a href="https://www.youtube.com/watch?v=9G2zUUTeG1I">Cruise</a>.</p></figcaption>
</figure>

<p>While this type of fully synthetic simulation exists in the world of autonomous driving, it’s actually the least commonly used type of simulation.<sup id="fnref:fn-synthetic-quip" role="doc-noteref"><a href="#fn:fn-synthetic-quip" class="footnote" rel="footnote">2</a></sup></p>

<p>Instead, just as a software developer leans on many kinds of testing before releasing an application, an AV developer runs many types of simulation before deploying an autonomous vehicle. Each type of simulation is best suited for a particular use case, with trade-offs between realism, coverage, technical complexity, and cost to operate.</p>

<p>In this post, we’ll walk through the system design of a simulator at a hypothetical AV company, starting from first principles.</p>

<p>We may never know the details of the actual simulator architecture used by any particular AV developer. However, by exploring the design trade-offs from first principles, I hope to shed some light on how this key system works.</p>

<h2 class="no_toc" id="contents">Contents</h2>

<ul id="markdown-toc">
  <li><a href="#our-imaginary-self-driving-car" id="markdown-toc-our-imaginary-self-driving-car">Our imaginary self-driving car</a></li>
  <li><a href="#replay-simulation" id="markdown-toc-replay-simulation">Replay simulation</a>    <ul>
      <li><a href="#interactivity-and-the-pose-divergence-problem" id="markdown-toc-interactivity-and-the-pose-divergence-problem">Interactivity and the pose divergence problem</a></li>
    </ul>
  </li>
  <li><a href="#synthetic-simulation" id="markdown-toc-synthetic-simulation">Synthetic simulation</a>    <ul>
      <li><a href="#the-high-cost-of-realistic-imagery" id="markdown-toc-the-high-cost-of-realistic-imagery">The high cost of realistic imagery</a></li>
      <li><a href="#round-trip-conversions-to-pixels-and-back" id="markdown-toc-round-trip-conversions-to-pixels-and-back">Round-trip conversions to pixels and back</a></li>
      <li><a href="#skipping-the-sensor-data" id="markdown-toc-skipping-the-sensor-data">Skipping the sensor data</a></li>
      <li><a href="#making-smart-agents" id="markdown-toc-making-smart-agents">Making smart agents</a></li>
      <li><a href="#generating-scene-descriptions" id="markdown-toc-generating-scene-descriptions">Generating scene descriptions</a></li>
      <li><a href="#limitations-of-pure-synthetic-simulation" id="markdown-toc-limitations-of-pure-synthetic-simulation">Limitations of pure synthetic simulation</a></li>
    </ul>
  </li>
  <li><a href="#hybrid-simulation" id="markdown-toc-hybrid-simulation">Hybrid simulation</a></li>
  <li><a href="#conclusion" id="markdown-toc-conclusion">Conclusion</a></li>
</ul>

<h2 id="our-imaginary-self-driving-car">Our imaginary self-driving car</h2>

<p>Let’s begin by defining our hypothetical autonomous driving software, which will help us illustrate how simulation fits into the development process.</p>

<p>Imagine it’s 2015, the peak of self-driving hype, and our team has raised a vast sum of money to develop an autonomous vehicle. Like a human driver, our software drives by continuously performing a few basic tasks:</p>

<ul>
  <li>It makes observations about the road and other road users.</li>
  <li>It reasons about what others might do and plans how it should drive.</li>
  <li>Finally, it executes those planned motions by steering, accelerating, and braking.</li>
  <li>Rinse and repeat.</li>
</ul>

<p>This mental model helps us group related code into modules, enabling them to be developed and tested independently. There will be four modules in our system:<sup id="fnref:e2e-onboard" role="doc-noteref"><a href="#fn:e2e-onboard" class="footnote" rel="footnote">3</a></sup></p>

<ol>
  <li><strong>Sensor Interface:</strong> Take in raw sensor data such as camera images and lidar point clouds.</li>
  <li><strong>Sensing:</strong> Detect objects such as vehicles, pedestrians, lane lines, and curbs.</li>
  <li><strong>Behavior:</strong> Determine the best trajectory (path) for the vehicle to drive.</li>
  <li><strong>Vehicle Interface:</strong> Convert the trajectory into steering, accelerator, and brake commands to control the vehicle’s drive-by-wire (DBW) system.</li>
</ol>

<p>We connect our modules to each other using an inter-process communication framework (“middleware”) such as <a href="https://www.ros.org/">ROS</a>, which provides a <a href="https://en.wikipedia.org/wiki/Publish–subscribe_pattern">publish–subscribe system</a> (pubsub) for our modules to talk to each other.</p>

<p>Here’s a concrete example of our module-based encapsulation system in action:</p>

<ul>
  <li>The sensing module publishes a message containing the positions of other road users.</li>
  <li>The behavior module subscribes to this message when it wants to know whether there are pedestrians nearby.</li>
</ul>

<p>The behavior module doesn’t know and doesn’t care how the perception module detected those pedestrians; it just needs to see a message that conforms to the agreed-upon API schema.</p>

<p>Defining a schema for each message also allows us to store a copy of everything sent through the pubsub system. These driving logs will come in handy for debugging because it allows us to inspect the system with module-level granularity.</p>

<p>Our full system looks like this:</p>

<figure>
    <a href="/assets/blog/av-simulation-taxonomy/architecture-onboard-software.pdf">
        <picture>
            <source srcset="/assets/blog/av-simulation-taxonomy/architecture-onboard-software-dark-2400w.png" media="(prefers-color-scheme: dark)" />
            <img class="extrawide" src="/assets/blog/av-simulation-taxonomy/architecture-onboard-software-2400w.png" width="1200" height="260" alt="A block diagram with four main components: Sensor Interface, Sensing, Behavior, and Vehicle Interface. Arrows indicate data flow from left to right. A Logging component runs along the bottom, receiving data from the connections between all other components." />
        </picture>
    </a>
    <figcaption><p>Simplified architecture diagram for an autonomous vehicle.</p></figcaption>
</figure>

<p>Now it’s time to take our autonomous vehicle for a spin. We drive around our neighborhood, encountering some scenarios in which our vehicle drives incorrectly, which cause our in-car safety driver to take over driving from the autonomous vehicle. Each disengagement gets reviewed by our engineering team. They analyze the vehicle’s logs and propose some software changes.</p>

<p>Now we need a way to prove our changes have actually improved performance. We need the ability to compare the effectiveness of multiple proposed fixes. We need to do this quickly so our engineers can receive timely feedback. We need a simulator!</p>

<h2 id="replay-simulation">Replay simulation</h2>

<p>Motivated by the desire to make progress quickly, we try the simplest solution first. The key insight: our software modules don’t care where the incoming messages come from. Could we simulate a past scenario by simply replaying messages from our log as if they were being sent in real time?</p>

<p>As the name suggests, this is exactly how <strong>replay simulation</strong> works.</p>

<ul>
  <li>Under normal operation, the input to our software is sensor data captured from real sensors. The simulator replaces this by <em>replaying</em> sensor data from an existing log.</li>
  <li>Under normal operation, the output of our software is a trajectory (or a set of accelerator and steering commands) that the real car executes. The simulator intercepts the output to control the simulated vehicle’s position instead.</li>
</ul>

<figure>
    <a href="/assets/blog/av-simulation-taxonomy/architecture-replay-simulation.pdf">
        <picture>
            <source srcset="/assets/blog/av-simulation-taxonomy/architecture-replay-simulation-dark-2400w.png" media="(prefers-color-scheme: dark)" />
            <img class="extrawide" src="/assets/blog/av-simulation-taxonomy/architecture-replay-simulation-2400w.png" width="1200" height="260" alt="A modified block diagram with an arrow labeled lidar and camera pointing from Log Storage into Sensing. The diagram contains two main components: Sensing and Behavior. Sensing feeds map and detections into Behavior. The additional components Sensor Interface and Vehicle Interface are drawn in gray." />
        </picture>
    </a>
    <figcaption><p>Modified architecture diagram for running replay simulation.</p></figcaption>
</figure>

<p>There are two primary ways we can use this type of simulator, depending on whether we use a different software version as the onroad drive:</p>

<ol>
  <li><strong>Different software:</strong> By running modified versions of our modules in the simulator, we can get a rough idea of how the changes will affect the vehicle’s behavior. This can provide early feedback on whether a change improves the vehicle’s behavior or successfully fixes a bug.</li>
  <li><strong>Same software:</strong> After a disengagement, we may want to know what would have happened if the autonomous vehicle were allowed to continue driving without human input. Simulation can provide this counterfactual by continuing to play back messages as if the disengagement never happened.</li>
</ol>

<p>We’ve gained these important testing capabilities with relatively little effort. Rather than take on the complexity of a fully generated 3D environment, we got away with a few modifications to our pubsub framework.</p>

<h3 id="interactivity-and-the-pose-divergence-problem">Interactivity and the pose divergence problem</h3>

<p>The simplicity of a pure replay simulator also leads to its key weakness: a complete lack of interactivity. Everything in the simulated environment was loaded verbatim from a log. Therefore, the environment does not respond to the simulated vehicle’s behavior, which can lead to unrealistic interactions with other road users.</p>

<p>This classic example demonstrates what can happen when the simulated vehicle’s behavior changes too much:</p>

<figure>
    <video class="extrawide" width="1200" height="513" poster="/assets/blog/av-simulation-taxonomy/drago-pose-divergence-poster.jpg" src="https://files.kevinchen.co/website/assets/blog/av-simulation-taxonomy/drago-pose-divergence-1080p.mp4" preload="metadata" controls="" playsinline="">
        <p><a href="https://www.youtube.com/embed/Q0nGo2-y0xY?clip=UgkxOOl-jWYx9K9-OE70RoizjMnGu7a9RAF5&amp;clipt=ENiNehiYrXw">Watch on YouTube</a>.</p>
    </video>
    <figcaption><p>Dragomir Anguelov&rsquo;s guest lecture at MIT. Source: <a href="https://www.youtube.com/embed/Q0nGo2-y0xY?clip=UgkxOOl-jWYx9K9-OE70RoizjMnGu7a9RAF5&amp;clipt=ENiNehiYrXw">Lex Fridman</a>.</p></figcaption>
</figure>

<blockquote>
  <p>Our vehicle, when it drove in the real world, was where the green vehicle is. Now, in simulation, we drove differently and we have the blue vehicle.</p>

  <p>So we’re driving…bam. What happened?</p>

  <p>Well, there is a purple agent over there — a pesky purple agent — who, in the real world, saw that we passed them safely. And so it was safe for them to go, but it’s no longer safe, because we changed what we did.</p>

  <p>So the insight is: <strong>in simulation, our actions affect the environment and needed to be accounted for.</strong></p>
</blockquote>

<p>Anguelov’s video shows the simulated vehicle driving slower than the real vehicle. This kind of problem is called <strong>pose divergence,</strong> a term that covers any simulation where differences in the simulated vehicle’s driving decisions cause its position to differ from the real-world vehicle’s position.</p>

<p>In the video, the pose divergence leads to an unrealistic collision in simulation. A reasonable driver in the purple vehicle’s position would have observed the autonomous vehicle and waited for it to pass before entering the intersection.<sup id="fnref:fn-pose-divergence-occlusion" role="doc-noteref"><a href="#fn:fn-pose-divergence-occlusion" class="footnote" rel="footnote">4</a></sup> However, in replay simulation, all we can do is play back the other driver’s actions verbatim.</p>

<p>In general, problems arising from the lack of interactivity mean the simulated scenario no longer provides useful feedback to the AV developer. This is a pretty serious limitation! The whole point of the simulator is to allow the simulated vehicle to make different driving decisions. If we cannot trust the realism of our simulations anytime there is an interaction with another road user, it rules out a lot of valuable use cases.</p>

<h2 id="synthetic-simulation">Synthetic simulation</h2>

<p>We can solve these interactivity problems by using a simulated environment to generate synthetic inputs that respond to our vehicle’s actions. Creating a <strong>synthetic simulation</strong> usually starts with a high-level scene description containing:</p>

<ul>
  <li><strong>Agents:</strong> fully interactive <a href="https://en.wikipedia.org/wiki/Non-player_character">NPCs</a> that react to our vehicle’s behavior.</li>
  <li><strong>Environments:</strong> 3D models of roads, signs, buildings, weather, etc. that can be rendered from any viewpoint.</li>
</ul>

<p>From the scene description, we can generate different types of synthetic inputs for our vehicle to be injected at different layers of its software stack, depending on which modules we want to test.</p>

<p>In <strong>synthetic sensor</strong> simulation, the simulator uses a game engine to render the scene description into fake sensor data, such as camera images, lidar point clouds, and radar returns. The simulator sets up our software modules to receive the generated imagery instead of sensor data logged from real-world driving.</p>

<figure>
    <a href="/assets/blog/av-simulation-taxonomy/architecture-synthetic-sensor-simulation.pdf">
        <picture>
            <source srcset="/assets/blog/av-simulation-taxonomy/architecture-synthetic-sensor-simulation-dark-2400w.png" media="(prefers-color-scheme: dark)" />
            <img class="extrawide" src="/assets/blog/av-simulation-taxonomy/architecture-synthetic-sensor-simulation-2400w.png" width="1200" height="535" alt="A modified block diagram with an arrow labeled lidar and camera pointing from Scene Generator and Imagery Generator to Sensing. Sensing feeds map and detections into Behavior. The additional components Sensor Interface and Vehicle Interface are drawn in gray." />
        </picture>
    </a>
    <figcaption><p>Modified architecture diagram for running synthetic simulation with generated sensors.</p></figcaption>
</figure>

<p>The same game engine can render the scene from any arbitrary perspective, including third-person views. This is how they make all those slick highlight reels.</p>

<h3 id="the-high-cost-of-realistic-imagery">The high cost of realistic imagery</h3>

<p>Simulations that generate fake sensor data can be quite expensive, both to develop and to run. The developer needs to create a high-quality 3D environment with realistic object models and lighting rivaling AAA games.</p>

<figure>
    <a href="/assets/blog/av-simulation-taxonomy/cruise-synthetic-simulation-1st-person-3840w.jpg">
        <img class="extrawide" srcset="/assets/blog/av-simulation-taxonomy/cruise-synthetic-simulation-1st-person-3840w.jpg 3840w,
                /assets/blog/av-simulation-taxonomy/cruise-synthetic-simulation-1st-person-2400w.jpg 2400w,
                /assets/blog/av-simulation-taxonomy/cruise-synthetic-simulation-1st-person-1200w.jpg 1200w" sizes="(max-width: 1200px) 100vw, 1200px" src="/assets/blog/av-simulation-taxonomy/cruise-synthetic-simulation-1st-person-2400w.jpg" width="1200" height="675" alt="A screenshot from Cruise simulation. The top row contains a panoramic image of the road ahead, including a woman and a dog crossing the street. The bottom left contains radar and lidar data visualized as colorful dots with a Cruise vehicle in the center of each. The bottom right contains camera images of the surroundings, including parked cars and a white van entering the road." />
    </a>
    <figcaption><p>Example of Cruise&rsquo;s synthetic simulation showing the same scene rendered into synthetic camera, lidar, and radar data. Source: <a href="https://www.youtube.com/watch?v=qOuROC4dSbw&amp;t=16s">Cruise</a>.</p></figcaption>
</figure>

<p>For example, a <a href="https://www.getcruise.com/news/blog/2020/rethinking-cruises-av-development-loop-during-covid-19/">Cruise blog post</a> mentions some elements of their synthetic simulation roadmap (emphasis mine):</p>

<blockquote>
  <p>With limited time and resources, we have to make choices. For example, we ask how accurately we should model tires, and whether or not it is more important than other factors we have in our queue, like <strong>modeling LiDAR reflections off of car windshields and rearview mirrors or correctly modeling radar multipath returns.</strong></p>
</blockquote>

<p>Even if rendering reflections and translucent surfaces is already well understood in computer graphics, Cruise may still need to make sure <em>their</em> renderer generates realistic reflections that resemble <em>their</em> lidar. This challenge gives a sense of the attention to detail required. It’s only one of many that needs to be solved when building a synthetic sensor simulator.</p>

<p>So far, we have only covered the high development costs. Synthetic sensor simulation also incurs high variable costs every time simulation is run.</p>

<h3 id="round-trip-conversions-to-pixels-and-back">Round-trip conversions to pixels and back</h3>

<p>By its nature, synthetic sensor simulation performs a round-trip conversion to and from synthetic imagery to test the perception system. The game engine first renders its scene description to synthetic imagery for each sensor on the simulated vehicle, burning many precious GPU-hours in the process, only to have the perception system perform the <em>inverse</em> operation when it detects the objects in the scene to produce the autonomous vehicle’s internal scene representation.<sup id="fnref:inverse-graphics" role="doc-noteref"><a href="#fn:inverse-graphics" class="footnote" rel="footnote">5</a></sup> Every time you launch a synthetic sensor simulation, NVIDIA, Intel, and/or AWS are laughing all the way to the bank.</p>

<p>Despite the expense of testing the perception system with synthetic simulation, it is also arguably less effective than testing with real-world imagery paired with ground truth labels. With real imagery, there can be no question about its realism. Synthetic imagery never looks quite right.</p>

<p>These practical limitations mean that synthetic sensor simulation ends up as the least used simulator type in AV companies. Usually, it’s also the last type of simulator to be built at a new company. Developers don’t need synthetic imagery most of the time, especially when they have at their disposal a fleet of vehicles that can record the real thing.</p>

<p>On the other hand, we cannot easily test risky driving behavior in the real world. For example, it is better to synthesize a bunch of red light runners than try to find them in the real world. This means we are primarily using synthetic simulation to test the behavior system.</p>

<h3 id="skipping-the-sensor-data">Skipping the sensor data</h3>

<p>In <strong>synthetic agent</strong> simulation, the simulator uses a high-level scene description to generate synthetic outputs from the perception/sensing system. In software development terms, it’s like replacing the perception system with a mock to focus on testing downstream components.</p>

<p>This type of simulation requires fewer computational resources to run because the scene description doesn’t need to make a round-trip conversion to sensor data.</p>

<figure>
    <a href="/assets/blog/av-simulation-taxonomy/architecture-synthetic-agent-simulation.pdf">
        <picture>
            <source srcset="/assets/blog/av-simulation-taxonomy/architecture-synthetic-agent-simulation-dark-2400w.png" media="(prefers-color-scheme: dark)" />
            <img class="extrawide" src="/assets/blog/av-simulation-taxonomy/architecture-synthetic-agent-simulation-2400w.png" width="1200" height="400" alt="A modified block diagram with an arrow labeled map and detections pointing from Scene Generator to Behavior. The additional components Sensor Interface, Sensing, and Vehicle Interface are drawn in gray." />
        </picture>
    </a>
    <figcaption><p>Modified architecture diagram for running synthetic simulation with generated agents.</p></figcaption>
</figure>

<p>With image quality out of the picture, the value of synthetic simulation rests solely on the quality of the scenarios it can create. We can split this into two main challenges:</p>

<ol>
  <li>designing agents with realistic behaviors</li>
  <li>generating the scene descriptions containing various agents, street layouts, and environmental conditions</li>
</ol>

<h3 id="making-smart-agents">Making smart agents</h3>

<p>You could start developing the control policy for a smart agent similar to NPC design in early video games.</p>

<ul>
  <li>A basic smart agent could simply follow a line or a path without reacting to anyone else, which could be used to test the autonomous vehicle’s reaction to a right of way violation.</li>
  <li>A fancier smart agent could follow a path while also maintaining a safe following distance from the vehicle in front. This type of agent could be placed behind our simulated vehicle, resolving the rear-ending problem mentioned above.</li>
</ul>

<p>Like an audience of demanding gamers, the users of our simulator quickly expect increasingly complex and intelligent behaviors from the smart agents. An ideal smart agent system would capture the full spectrum of every action that other road users could possibly take. This system would also generate realistic behaviors, including realistic-looking trajectories and reaction times, so that we can trust the outcomes of simulations involving smart agents. Finally, our smart agents need to be controllable: they can be given destinations or intents, enabling developers to design simulations that test specific scenarios.</p>

<figure>
    <video width="640" height="720" poster="/assets/blog/av-simulation-taxonomy/cruise-smart-agents-poster.jpg" src="https://files.kevinchen.co/website/assets/blog/av-simulation-taxonomy/cruise-smart-agents-1080p.mp4" preload="metadata" autoplay="" loop="" muted="" controls="" playsinline="">
        <p><a href="https://www.youtube.com/watch?v=uJWN0K26NxQ&amp;t=3489s">Watch on YouTube</a>.</p>
    </video>
    <figcaption><p>Two Cruise simulations in which smart agents (orange boxes) interact with the autonomous vehicle. In the second simulation, two parked cars have been inserted into the bottom of the visualization. Notice how the smart agents and the autonomous vehicle drive differently in the two simulations as they interact with each other and the additional parked cars. Source: <a href="https://www.youtube.com/watch?v=uJWN0K26NxQ&amp;t=3489s">Cruise</a>.</p></figcaption>
</figure>

<p>Developing a great smart agent policy ends up falling in the same difficulty ballpark as developing a great autonomous driving policy. The two systems may even share technical foundations. For example, they may have a shared component that is trained to predict the behaviors of other road users, which can be used for both planning our vehicle’s actions and for generating realistic agents in simulation.</p>

<h3 id="generating-scene-descriptions">Generating scene descriptions</h3>

<p>Even with the ability to generate realistic synthetic imagery and realistic smart agent behaviors, our synthetic simulation is not complete. We still need a broad and diverse dataset of scene descriptions that can thoroughly test our vehicle.</p>

<p>These scene descriptions usually come from a mix of sources:</p>

<ul>
  <li><strong>Automatic conversion from onroad scenarios:</strong> We can write a program that takes a logged real-world drive, guesses the intent of other road users, and stores those intents as a synthetic simulation scenario.</li>
  <li><strong>Manual design:</strong> Analogous to a level editor in a video game. A human either builds the whole scenario from scratch or makes manual edits to an automatic conversion. For example, a human can <a href="https://waymo.com/blog/2021/03/replaying-real-life/">design a scenario based on a police report</a> of a human-on-human-driver collision to simulate what the vehicle might have done in that scenario.</li>
  <li><strong>Generative AI:</strong> <a href="https://www.amazon.science/blog/scenario-diffusion-helps-zoox-vehicles-navigate-safety-critical-situations">Recent work from Zoox uses diffusion models</a> trained on a large dataset of onroad scenarios.</li>
</ul>

<figure>
    <a href="/assets/blog/av-simulation-taxonomy/cruise-synthetic-scene-conversion-1920w.jpg">
        <img class="extrawide" srcset="/assets/blog/av-simulation-taxonomy/cruise-synthetic-scene-conversion-1920w.jpg 1920w,
                /assets/blog/av-simulation-taxonomy/cruise-synthetic-scene-conversion-1200w.jpg 1200w" sizes="(max-width: 1200px) 100vw, 1200px" src="/assets/blog/av-simulation-taxonomy/cruise-synthetic-scene-conversion-1920w.jpg" width="1200" height="675" alt="A collage of several images. The top row contains realistic camera images of a city street full of people holding signs. The bottom row contains the same street and people rendered in the style of a video game." />
    </a>
    <figcaption><p>Example of a real-world log (top) converted to a synthetic simulation scenario, then rendered into synthetic camera images (bottom). Notice how some elements, such as the protest signs, are not carried over, perhaps because they are not supported by the perception system or the scene converter. Source: <a href="https://www.youtube.com/watch?v=uJWN0K26NxQ&amp;t=3453s">Cruise</a>.</p></figcaption>
</figure>

<p>Scenarios can also be fuzzed, where the simulator adds random noise to the scene parameters, such as the speed limit of the road or the goals of simulated agents. This can upsample a small number of converted or manually designed scenes to a larger set that can be used to check for robustness and prevent overfitting.</p>

<p>Fuzzing can also help developers understand the space of possible outcomes, as shown in the example below, which fuzzes the reaction time of a synthetic tailgater:</p>

<figure>
    <a href="/assets/blog/av-simulation-taxonomy/waymo-synthetic-simulation-fuzzing-graph-1280w.jpg">
        <img srcset="/assets/blog/av-simulation-taxonomy/waymo-synthetic-simulation-fuzzing-graph-1280w.jpg 1280w,
                /assets/blog/av-simulation-taxonomy/waymo-synthetic-simulation-fuzzing-graph-640w.jpg 640w" src="/assets/blog/av-simulation-taxonomy/waymo-synthetic-simulation-fuzzing-graph-1280w.jpg" sizes="(max-width: 680px) calc(100vw - 40px), 640px" width="640" height="360" alt="A diagram showing a vehicle closely following another vehicle. A histogram with red and green dots." />
    </a>
    <figcaption><p>An example of fuzzing tailgater reaction time. Source: <a href="https://waymo.com/blog/2021/07/simulation-city/">Waymo</a>.</p></figcaption>
</figure>

<p>The distribution on the right shows a dot for each variant of the scenario, colored green or red depending on whether a simulated collision occurred. In this experiment, the collision becomes unavoidable once the simulated tailgater’s reaction time exceeds about 1 second.</p>

<h3 id="limitations-of-pure-synthetic-simulation">Limitations of pure synthetic simulation</h3>

<p>With these sources plus fuzzing, we’ve ensured the <em>quantity</em> of scenarios in our library, but we still don’t have any guarantees on the <em>quality.</em></p>

<p>Perhaps the scenarios we (and maybe our generative AI tools) invent are too hard or too easy compared to the distribution of onroad driving our vehicle encounters.</p>

<ul>
  <li>If our vehicle drives poorly in a synthetic scenario, does the autonomous driving system need improvement? Or is the scenario unrealistically hard, perhaps because the behavior of its smart agents is too unreasonable?</li>
  <li>If our vehicle passes with flying colors, is it doing a good job? Or is the scenario library missing some challenging scenarios simply because we did not imagine that they could happen?</li>
</ul>

<p>This is a fundamental problem of pure synthetic simulation. Once we start modifying and fuzzing our simulated scenarios, there isn’t a straightforward way to know whether they remain representative of the real world. And we still need to collect a large quantity of real-world mileage to ensure that we have not missed any rare scenarios.</p>

<h2 id="hybrid-simulation">Hybrid simulation</h2>

<p>We can combine our two types of simulator into a <strong>hybrid simulator</strong> that takes advantages of the strengths of each, providing an environment that is both realistic and interactive without breaking the bank.</p>

<ul>
  <li>From <strong>replay simulation,</strong> use log replay to ensure every simulated scenario is rooted in a real-world scenario and has perfectly realistic sensor data.</li>
  <li>From <strong>synthetic simulation,</strong> make the simulation interactive by selectively replacing other road users with smart agents if they could interact with our vehicle.<sup id="fnref:hybrid-compute-cost" role="doc-noteref"><a href="#fn:hybrid-compute-cost" class="footnote" rel="footnote">6</a></sup></li>
</ul>

<figure>
    <a href="/assets/blog/av-simulation-taxonomy/architecture-synthetic-agent-simulation.pdf">
        <picture>
            <source srcset="/assets/blog/av-simulation-taxonomy/architecture-hybrid-simulation-dark-2400w.png" media="(prefers-color-scheme: dark)" />
            <img class="extrawide" src="/assets/blog/av-simulation-taxonomy/architecture-hybrid-simulation-2400w.png" width="1200" height="400" alt="A modified block diagram with an arrow labeled lidar and camera pointing from Log Storage into Sensing. An arrow labeled map and detections pointing from Scene Generator to Behavior. The additional components Sensor Interface and Vehicle Interface are drawn in gray." />
        </picture>
    </a>
    <figcaption><p>Modified architecture diagram merging parts of replay and synthetic simulation.</p></figcaption>
</figure>

<p>Hybrid simulation usually serves as the default type of simulation that works well for most use cases. One convenient interpretation is that hybrid simulation is a worry-free replacement for replay simulation: anytime the developer would have used replay, they can absentmindedly switch to hybrid simulation to take care of the most common simulation artifacts while retaining most of the benefits of replay simulation.</p>

<h2 id="conclusion">Conclusion</h2>

<p>We’ve seen that there are many types of simulation used in autonomous driving. They exist on a spectrum from purely replaying onroad scenarios to fully synthesized environments. The ideal simulation platform allows developers to pick an operating point on that spectrum that fits their use case. Hybrid simulation based on a large volume of real-world miles satisfies most testing needs at a reasonable cost, while fully synthetic modes serve niche use cases that can justify the higher development and operating costs.</p>

<hr />
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:cruise-simulator-posts" role="doc-endnote">
      <p>Cruise has written several deep dives about the <a href="https://medium.com/cruise/tagged/simulation">usage</a> and <a href="https://cloud.google.com/blog/products/containers-kubernetes/how-cruise-tests-its-avs-on-a-google-cloud-platform">scaling</a> of their simulation platform. However, neither Cruise nor Waymo provide many details on the <em>construction</em> of their simulator. <a href="#fnref:cruise-simulator-posts" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:fn-synthetic-quip" role="doc-endnote">
      <p>I’ve even heard arguments that it’s <em>only</em> good for making videos. <a href="#fnref:fn-synthetic-quip" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:e2e-onboard" role="doc-endnote">
      <p>There exist architectures that are more end-to-end. However, to the best of my knowledge, those systems do not have driverless deployments with nontrivial mileage, making simulation testing less relevant. <a href="#fnref:e2e-onboard" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:fn-pose-divergence-occlusion" role="doc-endnote">
      <p>Another interactivity problem arises from the replay simulator’s inability to simulate different points of view as the simulated vehicle moves. A large pose divergence often causes the simulated vehicle to drive into an area not observed by the vehicle that produced the onroad log. For example, a simulated vehicle could decide to drive around a corner much earlier. But it wouldn’t be able to see anything until the log data also rounds the corner. No matter where the simulated vehicle drives, it will always be limited to what the logged vehicle saw. <a href="#fnref:fn-pose-divergence-occlusion" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:inverse-graphics" role="doc-endnote">
      <p>“Computer vision is inverse computer graphics.” <a href="#fnref:inverse-graphics" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:hybrid-compute-cost" role="doc-endnote">
      <p>As a nice bonus, because the irrelevant road users are replayed exactly as they drove in real life, this may reduce the compute cost of simulation. <a href="#fnref:hybrid-compute-cost" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Kevin Chen</name></author><category term="starred" /><summary type="html"><![CDATA[Demystifying the virtual test drives that accelerate AV development]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kevinchen.co/blog/av-simulation-taxonomy/car-mesh-vaporwave-hero-1280w.jpg" /><media:content medium="image" url="https://kevinchen.co/blog/av-simulation-taxonomy/car-mesh-vaporwave-hero-1280w.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Why autonomous trucking is harder than autonomous rideshare</title><link href="https://kevinchen.co/blog/autonomous-trucking-harder-than-rideshare/" rel="alternate" type="text/html" title="Why autonomous trucking is harder than autonomous rideshare" /><published>2024-01-10T15:22:43-08:00</published><updated>2024-01-10T15:22:43-08:00</updated><id>https://kevinchen.co/blog/autonomous-trucking-harder-than-rideshare</id><content type="html" xml:base="https://kevinchen.co/blog/autonomous-trucking-harder-than-rideshare/"><![CDATA[<p>Recently, The Verge asked, <a href="https://www.theverge.com/23981006/autonomous-truck-semi-driverless-aurora-kodiak-infrastructure">“where are all the robot trucks?”</a></p>

<p>It’s a good question.</p>

<p>Trucking was supposed to be the ideal first application of autonomous driving. Freeways contain predictable, highly structured driving scenarios. An autonomous truck would not have to deal with the complexities of intersections and two-way traffic. It could easily drive hundreds of miles without encountering a single pedestrian.</p>

<figure>
    <a href="/assets/blog/av-truck-difficulty/truck-watercolor-hero-1792w.jpg">
        <img class="extrawide" srcset="/assets/blog/av-truck-difficulty/truck-watercolor-hero-1792w.jpg 1792w,
                /assets/blog/av-truck-difficulty/truck-watercolor-hero-896w.jpg 896w" sizes="(max-width: 1200px) 100vw, 1200px" src="/assets/blog/av-truck-difficulty/truck-watercolor-hero-1792w.jpg" width="1200" height="686" alt="A watercolor painting of a red truck driving on a snowy highway" />
    </a>
    <figcaption><p>DALL-E 3 prompt: “Generate an artistic, landscape aspect ratio watercolor painting of a truck with a bright red cab, pulling a white trailer. The truck drives uphill on an empty, rural highway during wintertime, lined with evergreen trees and a snow bank on a foggy, cloudy day.”</p></figcaption>
</figure>

<p>The trucks could also be commercially viable with only freeway driving capability, or freeways plus a short segment of surface streets needed to reach a <a href="https://web.archive.org/web/20180201192005/https://medium.com/@UberATG/the-future-of-trucking-b3d2ea0d2db9">transfer hub</a>. The AV company would only need to deal with a limited set of businesses as customers, bypassing the messiness of supporting a large pool of consumers inherent to the B2C model.</p>

<p>Autonomous trucks would not be subject to rest requirements. As <a href="https://www.theverge.com/23981006/autonomous-truck-semi-driverless-aurora-kodiak-infrastructure"><em>The Verge</em></a> notes, “truck operators are allowed to drive a maximum of 11 hours a day and have to take a 30-minute rest after eight consecutive hours behind the wheel. Autonomous trucks would face no such restrictions,” enabling them to provide a service that would be literally unbeatable by a human driver.</p>

<hr />

<p>If you had asked me in 2018, when I first started working in the AV industry, I would’ve bet that driverless trucks would be the first vehicle type to achieve a million-mile driverless deployment. Aurora even <a href="https://techcrunch.com/2020/07/20/aurora-expands-to-texas-in-bid-to-ramp-up-self-driving-truck-efforts/">pivoted</a> their entire company to trucking in 2020, believing it to be easier than city driving.</p>

<p>Yet sitting here in 2024, we know that both Waymo and Cruise have driven millions of miles on city streets — a large portion in the dense urban environment of San Francisco — and there are no driverless truck deployments. What happened?</p>

<p>I think the problem is that <em>driverless</em> autonomous trucking is simply harder than driverless rideshare.</p>

<p>The trucking problem appears easier at the outset, and indeed many AV developers quickly reach their initial milestones, giving them false confidence. But the difficulty ramps up sharply when the developer starts working on the last bit of polish. They encounter thorny problems related to the high speeds on freeways and trucks’ size, which must be solved before taking the human out of the driver’s seat.</p>

<h2 id="what-is-the-driverless-bar">What is the driverless bar?</h2>

<p>Here’s a simplistic framework:</p>

<ul>
  <li>No driver in the vehicle.</li>
  <li>No guarantee of a timely response from remote operators or backend services.</li>
  <li>Therefore, all safety-critical decisions must be made by the onboard computer alone.</li>
  <li>Under these constraints, the system still meets or exceeds human safety level.</li>
</ul>

<p>This is a really, really high bar. For example, on surface streets, this means the system <em>on its own</em> is capable of driving at least 100k miles without property damage and 40M miles without fatality.<sup id="fnref:fn-waymo-baseline" role="doc-noteref"><a href="#fn:fn-waymo-baseline" class="footnote" rel="footnote">1</a></sup></p>

<p>The system can still have flaws, but virtually all of those problems must result in a lack of progress, rather than collision or injury. In short, while the system may not know the <em>right</em> thing to do in every scenario, it should never do the <em>wrong</em> thing.</p>

<p>(There are several high quality safety frameworks for those interested in a rigorous definition.<sup id="fnref:fn-kalra-safety" role="doc-noteref"><a href="#fn:fn-kalra-safety" class="footnote" rel="footnote">2</a></sup><sup id="fnref:fn-waymo-safety-2023" role="doc-noteref"><a href="#fn:fn-waymo-safety-2023" class="footnote" rel="footnote">3</a></sup> It’s beyond the scope of this post.)</p>

<p>Now, let’s look at each aspect of trucking to see how it exacerbates these challenges.</p>

<h2 id="truck-specific-challenges">Truck-specific challenges</h2>

<h3 id="stopping-distance-vs-sensing-range">Stopping distance vs. sensing range</h3>

<p>The required sensor capability for an autonomous vehicle is determined by the most challenging scenario that the vehicle needs to handle. A major challenge in trucking is stopping behind a stalled vehicle or large debris in a travel lane. To avoid collision, the autonomous vehicle would need a sensing range greater than or equal to its stopping distance.</p>

<p>We’ll make a simplifying assumption that stopping distance defines the minimum detection range requirements. A driverless-quality perception system needs perfect <a href="https://en.wikipedia.org/wiki/Precision_and_recall">recall</a> on other vehicles within the vehicle’s worst-case stopping distance.</p>

<p>Passenger vehicles can decelerate up to –8 m/s². Trucks can only achieve around –4 m/s², which increases the stopping distance and puts the sensing range requirement right at the edge of what today’s sensors can deliver.</p>

<p>Here are the sight stopping distances for an empty truck in dry conditions on roads of varying grade:<sup id="fnref:fn-truck-braking" role="doc-noteref"><a href="#fn:fn-truck-braking" class="footnote" rel="footnote">4</a></sup></p>

<figure>
  <div class="table-scroll"><table>
    <thead>
      <tr>
        <th style="text-align: right">Speed (mph)</th>
        <th style="text-align: right">0% Grade (m)</th>
        <th style="text-align: right">–3% Grade (m)</th>
        <th style="text-align: right">–6% Grade (m)</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td style="text-align: right">50</td>
        <td style="text-align: right">115–141</td>
        <td style="text-align: right">124–150</td>
        <td style="text-align: right">136–162</td>
      </tr>
      <tr>
        <td style="text-align: right">70</td>
        <td style="text-align: right">122–178</td>
        <td style="text-align: right">136–162</td>
        <td style="text-align: right">236–305</td>
      </tr>
    </tbody>
  </table></div>
  <figcaption><p>Sight stopping distances defined as the distance needed to stop assuming a 2.5-second reaction time with no braking, followed by maximum braking. The distance is computed for an empty truck in dry conditions on roads of varying grade. Stopping distance increases in wet weather or when driving downhill with a load (not shown).</p></figcaption>
</figure>

<p>Now let’s compare these distances with the capabilities of various sensors:</p>

<p><strong>Lidar</strong> sensors provide trustworthy 3D data because they take direct measurements based on physical principles. They have a usable range of around 200–250 meters, plenty for city driving but not enough for every truck use case. Lidar detection models may also need to accumulate multiple scans/frames over time to detect faraway objects reliably, especially for smaller items like debris, further decreasing the usable detection range.</p>

<p>Note that some solid-state lidars claim significantly more range than 250 meters. These numbers are collected under ideal conditions; for computing minimum sensing capability, we are interested in the range that can provide perfect recall and really great precision. For example, the lidar may be unable to reach its maximum range over the entire field of view, or may require undesirable trade-offs like a scan pattern that reduces point density and field of view to achieve more range.</p>

<p><strong>Radar</strong> can see farther than lidar. For example, <a href="https://web.archive.org/web/20231023083200/https://www.zf.com/products/en/cars/products_64255.html">this high-end ZF radar</a> claims vehicle detections up to 350 meters away. Radar is great for tracking moving vehicles, but has trouble distinguishing between stationary vehicles and other background objects. Tesla Autopilot has infamously <a href="https://slate.com/technology/2021/08/teslas-allegedly-hitting-emergency-vehicles-why-it-could-be-happening.html">shown</a> this problem by braking for overpasses and running into stalled vehicles. “Imaging” radars like the ZF device will do better than the radars on production vehicles. They still do not have the azimuth resolution to separate objects beyond 200 meters, where radar input is most needed.</p>

<p><strong>Cameras</strong> can detect faraway objects as long as there are enough pixels on the object, which leads to the selection of cameras with high resolution and a narrow field of view (telephoto lens). A vehicle will carry multiple narrow cameras for full coverage during turns. However, cameras cannot measure distance or speed directly.</p>

<p>A combined camera + radar system using machine learning probably has the best chance here, especially with recent advances in ML-based early fusion, but would need to perform well enough to serve as the primary detection source beyond 200 meters. Training such a model is closer to an open problem than simply receiving that data from a lidar.</p>

<p>In summary, we don’t appear to have any sensing solutions with the performance needed for trucks to meet the driverless bar.</p>

<h3 id="controls">Controls</h3>

<p>Controlling a passenger vehicle — determining the amount of steering and throttle input to make the vehicle follow a trajectory — is a simpler problem than controlling a truck. For example, passenger vehicles are generally modeled as a single rigid body, while a truck and its trailer can move separately. The planner and controller need to account for this when making sharp turns and, in extreme low-friction conditions, to avoid <a href="https://en.wikipedia.org/wiki/Jackknifing">jackknifing</a>.</p>

<p>These features come in addition to all the usual controls challenges that also apply to passenger vehicles. They can be built but require additional development and validation time.</p>

<h2 id="freeway-specific-challenges">Freeway-specific challenges</h2>

<p>OK, so trucks are hard, but what about the freeway part? It may now sound appealing to build L4 freeway autonomy for passenger vehicles. However, driving on freeways also brings additional challenges on top of what is needed for city streets.</p>

<h3 id="achieving-the-minimal-risk-condition-on-freeways">Achieving the minimal risk condition on freeways</h3>

<p>Autonomous vehicles are supposed to stop when they detect an internal fault or driving situation that they can’t handle. This is called the <a href="https://cyberlaw.stanford.edu/blog/2022/01/deep-weeds-levels-driving-automation-lurks-ambiguous-minimal-risk-condition">minimal risk condition (MRC)</a>. For example, an autonomous passenger vehicle that detects an error in the HD map or a sensor failure might be programmed to execute a pullover or stop in lane depending on the problem severity.</p>

<p>While MRC behaviors are <a href="https://x.com/friscolive415/status/1690281516935589888">annoying</a> for other road users and embarrassing for the AV developer, they do not add undue risk on surface streets given the low speeds and already chaotic nature of city driving. This gives the AV developer more breathing room (within reason) to deploy a system that does not know how to handle every driving scenario perfectly, but knows enough to stay out of trouble.</p>

<p>It’s a different story on the freeway. Stopping in lane becomes much more dangerous with the possibility of a rear-end collision at high speed. All stopping should be planned well in advance, ideally exiting at the next ramp, or at least driving to the closest shoulder with enough room to park.</p>

<p>This greatly increases the scope of edge cases that need to be handled autonomously and at freeway speeds. For example:</p>

<p><strong>Scene understanding:</strong> If the vehicle encounters an unexpected construction zone, crash site, or other non-nominal driving scenario, it’s not enough to detect and stop. Rerouting, while a viable option on surface streets, usually isn’t an option on freeways because it may be difficult or illegal to make a u-turn by the time the vehicle can see the construction. A freeway under construction is also more likely to be the only path to the destination, especially if the autonomous vehicle in question is not designed to drive on city streets.</p>

<p>Operational solutions are also not enough for a scaled deployment. AV developers often disallow their vehicles from routing through known problem areas gathered from manually driven scouting vehicles or announcements made by authorities. For a scaled deployment, however, it’s not reasonable to know the status of every mile of road at all times.</p>

<p>Therefore, the system needs to find the right path through unstructured scenarios, possibly following instructions from police directing traffic, even if it involves traffic violations such as driving on the wrong side of the road. We know that current state-of-the-art autonomous vehicles still occasionally drive into <a href="https://www.sfgate.com/tech/article/cruise-stuck-wet-concrete-sf-18297946.php">wet concrete</a> and <a href="https://x.com/duckduckgeese/status/1715334636103168200">trenches</a>, which shows it is nontrivial to make a correct decision.</p>

<p><strong>Mapping:</strong> If the lane lines have been repainted, and the system normally uses an HD map, it needs to ignore the map and build a new one on-the-fly from the perception system’s output. It needs to distinguish between mapping and perception errors.</p>

<p><strong>Uptime:</strong> Sensor, computer, and software failures need to be virtually eliminated through redundancy and/or engineering elbow grease. The system needs almost perfect uptime. For example, it’s fine to enter a max-braking MRC when losing a sensor or restarting a software module on surface streets, provided those failures are rare. The same maneuver would be dangerous on the freeway, so the failure must be eliminated, or a fallback/redundancy developed.</p>

<p>These problems are not impossible to overcome. Every autonomous passenger vehicle has solved them to some extent, with the remaining edge cases punted to some combination of MRC and remote operators. The difference is that, on freeways, they need to be solved with a very high level of reliability to meet the driverless bar.</p>

<h3 id="freeways-are-boring">Freeways are boring</h3>

<p>The features that make freeways simpler — controlled access, no intersections, one-way traffic — also make <a href="https://medium.com/cruise/why-testing-self-driving-cars-in-sf-is-challenging-but-necessary-77dbe8345927">“interesting” events</a> more rare. This is a double-edged sword. While the simpler environment reduces the number of software features to be developed, it also increases the iteration time and cost.</p>

<p>During development, “interesting” events are needed to train data-hungry ML models. For validation, each new software version to be qualified for driverless operation needs to encounter a minimum number of “interesting” events before comparisons to a human safety level can have statistical significance. Overall, iteration becomes more expensive when it takes more vehicle-hours to collect each event.</p>

<p>AV developers can only respond by increasing the size of their operations teams or accepting more time between software releases. (Note that simulation is not a perfect solution either. The rarity of events increases vehicle-hours run in simulation, and so far, nobody has shown a substitute for real-world miles in the context of driverless software validation.)</p>

<h2 id="is-it-ever-going-to-happen">Is it ever going to happen?</h2>

<p>Trucking requires longer range sensing and more complex controls, increasing system complexity and pushing the problem to the bleeding edge of current sensing capabilities. At the same time, driving on freeways brings additional reliability requirements, raising the quality bar on every software component from mapping to scene understanding.</p>

<p>If both the truck form factor and the freeway domain increase the level of difficulty, then driverless trucking might be the hardest application of autonomous driving:</p>

<div class="table-scroll"><table>
  <thead>
    <tr>
      <th style="border-top-color: transparent; border-left-color: transparent;"></th>
      <th>City</th>
      <th>Freeway</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>Cars</th>
      <td>Baseline</td>
      <td style="background-color: rgba(255, 0, 0, 0.37);">Harder</td>
    </tr>
    <tr>
      <th>Trucks</th>
      <td style="background-color: rgba(255, 0, 0, 0.37);">Harder</td>
      <td style="background-color: rgba(255, 0, 0, 0.60);">Hardest</td>
    </tr>
  </tbody>
</table></div>

<p>Now that scaled rideshare is mostly working in cities, I expect to see scaled <a href="https://waymo.com/blog/2024/01/from-surface-streets-to-freeways-safely.html">freeway rideshare</a> next.</p>

<p>Does this mean driverless trucking will never happen? No, I still believe AV developers will overcome these challenges eventually. <a href="https://blog.aurora.tech/products/the-aurora-driver-is-feature-complete">Aurora</a>, <a href="http://archive.today/2024.07.09-211356/https://www.prnewswire.com/news-releases/kodiak-unveils-industry-first-semi-truck-designed-for-scaled-driverless-deployment-302029917.html">Kodiak</a>, and <a href="http://archive.today/2023.12.15-162144/https://www.forbes.com/sites/richardbishop1/2023/12/14/krogergatik-partnership-provides--counterpoint-to-gm-cruise-debacle/?sh=3b8f0243ca3e">Gatik</a> have all promised some form of driverless deployment by the end of the year. We probably won’t see anything close to a million-mile deployment in 2024 though. Getting there will require advances in sensing, machine learning, and a lot of hard work.</p>

<hr />

<p><em>Thanks to Steven W. and others for the discussions and feedback.</em></p>

<hr />
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:fn-waymo-baseline" role="doc-endnote">
      <p>This should be considered a bare minimum because humans perform much better on freeways, raising the bar for AVs. Rough numbers taken from Table 3, passenger vehicle national average on surface streets: Scanlon, J. M., Kusano, K. D., Fraade-Blanar, L. A., McMurry, T. L., Chen, Y. H., &amp; Victor, T. (2023). <a href="https://arxiv.org/abs/2312.13228">Benchmarks for Retrospective Automated Driving System Crash Rate Analysis Using Police-Reported Crash Data.</a> <em>arXiv preprint arXiv:2312.13228.</em> (<a href="https://waymo.com/blog/2023/12/waymo-significantly-outperforms.html">blog</a>) <a href="#fnref:fn-waymo-baseline" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:fn-kalra-safety" role="doc-endnote">
      <p>Kalra, N., &amp; Paddock, S. M. (2016). <a href="https://scholar.google.com/scholar?cluster=7456875758174909592">Driving to safety: How many miles of driving would it take to demonstrate autonomous vehicle reliability?</a> <em>Transportation Research Part A: Policy and Practice, 94,</em> 182-193. <a href="#fnref:fn-kalra-safety" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:fn-waymo-safety-2023" role="doc-endnote">
      <p>Favaro, F., Fraade-Blanar, L., Schnelle, S., Victor, T., Peña, M., Engstrom, J., … &amp; Smith, D. (2023). <a href="https://arxiv.org/abs/2306.01917">Building a Credible Case for Safety: Waymo’s Approach for the Determination of Absence of Unreasonable Risk.</a> <em>arXiv preprint arXiv:2306.01917.</em> (<a href="https://waymo.com/blog/2023/03/a-blueprint-for-av-safety-waymos.html">blog</a>) <a href="#fnref:fn-waymo-safety-2023" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:fn-truck-braking" role="doc-endnote">
      <p><a href="https://docs.google.com/spreadsheets/d/1Xt_Ogyp85xlGEVPS46Kdg8GcQdSEUEnszBmPfax_sMk/edit">Computed</a> from tables 1 and 2: Harwood, D. W., Glauz, W. D., &amp; Mason, J. M. (1989). <a href="https://scholar.google.com/scholar?cluster=15139719330673244962">Stopping sight distance design for large trucks.</a> <em>Transportation Research Record, 1208,</em> 36-46. <a href="#fnref:fn-truck-braking" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Kevin Chen</name></author><category term="starred" /><summary type="html"><![CDATA[Freeways were supposed to be easy. What happened?]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kevinchen.co/blog/av-truck-difficulty/truck-watercolor-hero-896w.jpg" /><media:content medium="image" url="https://kevinchen.co/blog/av-truck-difficulty/truck-watercolor-hero-896w.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How Cruise vehicles return to the garage autonomously in heavy rain</title><link href="https://kevinchen.co/blog/cruise-heavy-rain-driving-and-operations/" rel="alternate" type="text/html" title="How Cruise vehicles return to the garage autonomously in heavy rain" /><published>2023-01-16T20:54:09-08:00</published><updated>2023-01-16T20:54:09-08:00</updated><id>https://kevinchen.co/blog/cruise-heavy-rain-driving-and-operations</id><content type="html" xml:base="https://kevinchen.co/blog/cruise-heavy-rain-driving-and-operations/"><![CDATA[<p>Cruise doesn’t carry passengers in heavy rain. The operational design domain (ODD) in their CPUC permit (<a href="https://www.cpuc.ca.gov/-/media/cpuc-website/divisions/consumer-protection-and-enforcement-division/documents/tlab/av-programs/cruise-driverless-deployment-odd-20220606.pdf">PDF</a>) only allows services in light rain.</p>

<p>I’ve always wondered how they implement this operationally. For example, <a href="https://youtu.be/oWuLbng4uHE">Waymo preemptively launches all cars with operators in the driver’s seat</a> anytime there’s rain in the forecast. Cruise has no such policy: I have never seen them assign operators to customer-facing vehicles.</p>

<p>Yet Cruise claims to run <a href="/blog/cruise-occupancy-kyle-vogt-fleet-monitoring-dashboard/">up to 100 driverless vehicles concurrently</a>. It would be impractical to dispatch a human driver to each vehicle whenever it starts raining. When the latest atmostpheric river hit San Francisco, I knew it was my chance to find out how it worked.</p>

<iframe class="extrawide" width="1200" height="675" src="https://www.youtube.com/embed/XTdbZXf_S7I" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<h2 id="monitoring-the-cruise-app">Monitoring the Cruise app</h2>

<p>As the rain intensified, as expected, all cars disappeared from Cruise’s app and the weather pause icon appeared.</p>

<figure>
    <a href="/assets/blog/cruise-rain-ops/cruise-app-cars-unavailable.jpg">
        <img class="left" src="/assets/blog/cruise-rain-ops/cruise-app-cars-unavailable.jpg" style="max-width: 50%;" width="320" height="693" alt="A screenshot with a map and the message: We'll be back when a car becomes available." />
    </a>
    <a href="/assets/blog/cruise-rain-ops/cruise-app-weather-pause.jpg">
        <img class="left" src="/assets/blog/cruise-rain-ops/cruise-app-weather-pause.jpg" style="max-width: 50%;" width="320" height="692" alt="A screenshot with a map and the message: We'll be back when the weather clears." />
    </a>
    <div class="clear"></div>
</figure>

<p>But then something unusual happened. The app returned to its normal state. A few cars showed up near a hole in the geofence — and they were actually hailable.</p>

<figure>
    <a href="/assets/blog/cruise-rain-ops/cruise-app-cars-lined-up.png">
        <img class="left" src="/assets/blog/cruise-rain-ops/cruise-app-cars-lined-up.png" style="max-width: 50%;" width="320" height="693" alt="A screenshot of a map with three cars in close proximity." />
    </a>
    <a href="/assets/blog/cruise-rain-ops/cruise-app-hailable-in-rain.jpg">
        <img class="left" src="/assets/blog/cruise-rain-ops/cruise-app-hailable-in-rain.jpg" style="max-width: 50%;" width="320" height="693" alt="A screenshot of the trip plan. Car arriving in 39 minutes." />
    </a>
    <div class="clear"></div>
</figure>

<h2 id="visiting-the-garage">Visiting the garage</h2>

<p>I drove over to find that this street is the entrance to one of Cruise’s garages. The same location has been featured in Cruise executives’ <a href="https://twitter.com/kvogt/status/1593738608594206721">past tweets promoting the service</a>.<sup id="fnref:missing-tweet" role="doc-noteref"><a href="#fn:missing-tweet" class="footnote" rel="footnote">1</a></sup></p>

<p>Despite the heavy rain and gusts strong enough to blow my hat/jacket off, a steady stream of Cruise vehicles were returning themselves to the garage in driverless mode.</p>

<figure>
    <a href="/assets/blog/cruise-rain-ops/cruise-entering-driveway-in-rain.jpg">
        <img class="extrawide" srcset="/assets/blog/cruise-rain-ops/cruise-entering-driveway-in-rain-2400w.jpg 2400w,
                /assets/blog/cruise-rain-ops/cruise-entering-driveway-in-rain-1280w.jpg 1280w" sizes="(max-width: 1200px) 100vw, 1200px" src="/assets/blog/cruise-rain-ops/cruise-entering-driveway-in-rain-2400w.jpg" width="1200" height="675" alt="Two vehicles turn into a driveway" />
    </a>
    <figcaption><p>Driverless Cruise vehicles enter the garage during heavy rain.</p></figcaption>
</figure>

<figure>
    <a href="/assets/blog/cruise-rain-ops/cruise-garage-operator-entering-vehicle.jpg">
        <img class="extrawide" srcset="/assets/blog/cruise-rain-ops/cruise-garage-operator-entering-vehicle-2400w.jpg 2400w,
                /assets/blog/cruise-rain-ops/cruise-garage-operator-entering-vehicle-1280w.jpg 1280w" sizes="(max-width: 1200px) 100vw, 1200px" src="/assets/blog/cruise-rain-ops/cruise-garage-operator-entering-vehicle-2400w.jpg" width="1200" height="675" alt="A person enters the driver's door of a vehicle" />
    </a>
    <figcaption><p>A member of Cruise’s operations team enters the vehicle to drive it into the garage.</p></figcaption>
</figure>

<p>In total, I observed:</p>

<ul>
  <li>8 driverless vehicles</li>
  <li>1 manually driven vehicle</li>
  <li>1 support vehicle (unmodified Chevy Bolt not capable of autonomous driving)</li>
</ul>

<h2 id="two-vehicles-skip-the-garage">Two vehicles skip the garage</h2>

<p>After the first six driverless vehicles returned, the next two kept driving past the garage. I followed them in my own car. They drove for about 16 minutes, handling large puddles and road spray without noticeable comfort issues. Eventually they looped back to the garage and successfully entered.</p>

<figure>
    <a href="/assets/blog/cruise-rain-ops/cruise-drives-through-puddle.jpg">
        <img class="extrawide" srcset="/assets/blog/cruise-rain-ops/cruise-drives-through-puddle-2400w.jpg 2400w,
                /assets/blog/cruise-rain-ops/cruise-drives-through-puddle-1280w.jpg 1280w" sizes="(max-width: 1200px) 100vw, 1200px" src="/assets/blog/cruise-rain-ops/cruise-drives-through-puddle-2400w.jpg" width="1200" height="675" alt="An autonomous vehicle turns right at an intersection, kicking up water from a puddle" />
    </a>
    <figcaption><p>A Cruise vehicle drives through a puddle during its detour.</p></figcaption>
</figure>

<p>I’m not totally sure what happened here. I can think of two reasonable explanations:</p>

<ol>
  <li><strong>Boring:</strong> The cars missed the turn for some unknown reason.</li>
  <li><strong>Exciting:</strong> Cruise has implemented logic to avoid overwhelming the operations team’s ability to put cars back in the garage. If there are too many vehicles waiting to return, subsequent cars take a detour to kill time instead of blocking the driveway.</li>
</ol>

<h2 id="key-take-aways">Key take-aways</h2>

<ul>
  <li>Cruise is capable of handling heavy rain in driverless mode.</li>
  <li>The majority of Cruise vehicles returned to the garage autonomously. This enables them to handle correlated events, such as rain, without deploying a large operations team.</li>
  <li>Cruise may have implemented “take a lap around the block” logic to avoid congestion at the garage entrance.</li>
</ul>

<hr />
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:missing-tweet" role="doc-endnote">
      <p><del>I can’t find the timelapse of Cruise launching their driverless cars anymore. I’m pretty sure it was posted to Twitter. Please let me know if you have the link!</del> <strong>Update:</strong> <a href="https://twitter.com/kvogt/status/1593738608594206721">Link to tweet by @kvogt</a>. <a href="#fnref:missing-tweet" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Kevin Chen</name></author><category term="starred" /><summary type="html"><![CDATA[Their autonomous vehicles and operations team work together to handle heavy rain.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kevinchen.co/blog/cruise-rain-ops/cruise-entering-driveway-in-rain-1280w.jpg" /><media:content medium="image" url="https://kevinchen.co/blog/cruise-rain-ops/cruise-entering-driveway-in-rain-1280w.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Inferring Cruise occupancy from Kyle Vogt’s fleet dashboard screenshot</title><link href="https://kevinchen.co/blog/cruise-occupancy-kyle-vogt-fleet-monitoring-dashboard/" rel="alternate" type="text/html" title="Inferring Cruise occupancy from Kyle Vogt’s fleet dashboard screenshot" /><published>2023-01-04T19:56:15-08:00</published><updated>2023-01-04T19:56:15-08:00</updated><id>https://kevinchen.co/blog/cruise-occupancy-kyle-vogt-fleet-monitoring-dashboard</id><content type="html" xml:base="https://kevinchen.co/blog/cruise-occupancy-kyle-vogt-fleet-monitoring-dashboard/"><![CDATA[<h2 id="background">Background</h2>

<p>Last month, Kyle Vogt (CEO of Cruise) tweeted a screenshot of Cruise’s fleet management dashboard:</p>

<blockquote class="twitter-tweet" data-theme="light">
    <p lang="en" dir="ltr">The press: AVs are over-hyped and are still 5-10 years away.<br /><br />Us: At this moment there are 100 <a href="https://twitter.com/Cruise?ref_src=twsrc%5Etfw">@Cruise</a> AVs in driverless mode in SF, and many are currently carrying passengers.<br /><br />(from two nights ago) <a href="https://t.co/rV6gWRgK0q">pic.twitter.com/rV6gWRgK0q</a></p>&mdash; Kyle Vogt (@kvogt) <a href="https://twitter.com/kvogt/status/1598351946112323584?ref_src=twsrc%5Etfw">December 1, 2022</a>
</blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<p>Eagle-eyed commenters noticed there were several types of vehicle icons and wondered whether they had any meaning.</p>

<figure>
    <a href="/assets/blog/cruise-fleet-dashboard/cruise-fleet-dashboard-from-kvogt.jpg">
        <img class="extrawide" src="/assets/blog/cruise-fleet-dashboard/cruise-fleet-dashboard-from-kvogt.jpg" width="1200" height="607" alt="A screenshot of a map of San Francisco with vehicle and headphone icons overlaid" />
    </a>
    <figcaption><p>Full-resolution image from Kyle’s tweet.</p></figcaption>
</figure>

<h2 id="cruises-letter-to-the-cpuc">Cruise’s letter to the CPUC</h2>

<p>The answer has been hiding in plain sight. Two weeks later, on December 16, 2022, Cruise submitted an Advice Letter (<a href="https://www.cpuc.ca.gov/-/media/cpuc-website/divisions/consumer-protection-and-enforcement-division/documents/tlab/av-programs/2022-12-16-final-tier-2-advice-letter-for-cruise-driverless-deployment-expansion.pdf">PDF, 23 MB</a>) to the <a href="https://www.cpuc.ca.gov/regulatory-services/licensing/transportation-licensing-and-analysis-branch/autonomous-vehicle-programs/phase-i-driverless-autonomous-vehicle-deployment-program-advice-letter-status">California Public Utilities Commission</a>. They were requesting an expansion of Cruise’s driverless deployment permit.</p>

<p>Page 48 contains a screenshot of Cruise’s fleet monitoring dashboard:</p>

<blockquote>
  <p><strong>6.3. Fleet Monitoring and Learning</strong></p>

  <p>Cruise continuously monitors its driverless fleet while it is in operation. Cruise uses a suite of internal tools to oversee its fleet of Cruise AVs, including information about each Cruise AV on the road, such as their current location, operating condition and passenger states.</p>
</blockquote>

<figure>
    <a href="/assets/blog/cruise-fleet-dashboard/cruise-fleet-dashboard-with-key.png">
        <img class="extrawide" src="/assets/blog/cruise-fleet-dashboard/cruise-fleet-dashboard-with-key.jpg" width="1200" height="771" alt="A screenshot of a map of San Francisco with vehicle and headphone icons overlaid" />
    </a>
    <figcaption><blockquote><p>Figure 24: Cruise internal fleet monitoring tool (provided as example; actual may vary)</p></blockquote></figcaption>
</figure>

<p>The key shows:</p>

<ul>
  <li>Failed: red</li>
  <li>Healthy: green</li>
  <li>Recovery: blue</li>
  <li>Occupied: filled</li>
  <li>Vacant: outline</li>
</ul>

<p>I’m assuming occupied vs. vacant indicates whether the vehicle currently carries a passenger.</p>

<p>Note that the numbers in this image don’t add up, suggesting it might be a mockup rather than the live app. For example:</p>

<ul>
  <li>12 failed + 160 driverless + 4 recovery + 24 manual = <strong>200 vehicles total</strong></li>
  <li>56 awaiting ride assignment + 13 assigned a ride + 131 assignment in progress + 20 returning to facility + 30 unavailable = <strong>250 vehicles total</strong></li>
</ul>

<h2 id="interpreting-kyles-screenshot">Interpreting Kyle’s screenshot</h2>

<p>We now have a snapshot of Cruise’s ridership at some unknown time on the night of November 29, 2022.</p>

<ul>
  <li><strong>Healthy, occupied:</strong> 2 vehicles</li>
  <li><strong>Healthy, vacant:</strong> 85 vehicles</li>
  <li><strong>Total:</strong> 87 vehicles</li>
</ul>

<p>Note that the number of vehicles may be undercounted because:</p>

<ul>
  <li>The viewport doesn’t show Cruise’s entire service area.</li>
  <li>Some map icons overlap.</li>
  <li>We don’t know whether the filter by AV mode has been set to show only healthy (green) vehicles.</li>
</ul>]]></content><author><name>Kevin Chen</name></author><summary type="html"><![CDATA[I figured out what the car icons mean.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kevinchen.co/blog/cruise-fleet-dashboard/cruise-fleet-dashboard-from-kvogt.jpg" /><media:content medium="image" url="https://kevinchen.co/blog/cruise-fleet-dashboard/cruise-fleet-dashboard-from-kvogt.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Tearing down the Rewind app</title><link href="https://kevinchen.co/blog/rewind-ai-app-teardown/" rel="alternate" type="text/html" title="Tearing down the Rewind app" /><published>2022-12-24T23:59:00-08:00</published><updated>2022-12-24T23:59:00-08:00</updated><id>https://kevinchen.co/blog/rewind-ai-app-teardown</id><content type="html" xml:base="https://kevinchen.co/blog/rewind-ai-app-teardown/"><![CDATA[<p><a href="https://www.rewind.ai/">Rewind</a> is a Mac app that records your computer’s screen and audio, allowing the user to scroll through a timeline of past screen recordings. Rewind also recognizes text, including text in videos and Zoom calls, allowing the user to perform full-text search on anything that has been displayed. Rewind is developed by the same team as <a href="https://www.scribe.ai/">Scribe</a>.</p>

<iframe class="extrawide" width="1200" height="675" src="https://www.youtube.com/embed/dIV0ZiZluQo?t=11" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<p>Rewind also records Zoom meetings as a first-class feature. Whenever the user enters a meeting, Rewind asks to record and transcribe audio from all participants.</p>

<figure>
    <a href="/assets/blog/rewind-app/rewind-app-zoom-meeting-transcript.png">
        <img class="extrawide" src="/assets/blog/rewind-app/rewind-app-zoom-meeting-transcript.jpg" width="1200" height="779" alt="A screenshot of a Zoom meeting and a timeline." />
    </a>
    <figcaption><p>A Zoom call shown in the Rewind app’s history browser.</p></figcaption>
</figure>

<p>All indexing, including OCR and speech-to-text, happens locally. The developers claim that Rewind “doesn’t tax system resources, like CPU and memory, while recording” by taking advantage of accelerators built into the Apple M1 and M2. Given these claims, I was eager to sign up for the beta ($20/month, first month free) to find out how they pulled this off.</p>

<h2 class="no_toc" id="contents">Contents</h2>

<ul id="markdown-toc">
  <li><a href="#how-it-works-overview" id="markdown-toc-how-it-works-overview">How it works: Overview</a></li>
  <li><a href="#analyzing-the-rewind-app" id="markdown-toc-analyzing-the-rewind-app">Analyzing the Rewind app</a>    <ul>
      <li><a href="#application-bundle" id="markdown-toc-application-bundle">Application Bundle</a></li>
      <li><a href="#frameworks" id="markdown-toc-frameworks">Frameworks</a></li>
      <li><a href="#permissions" id="markdown-toc-permissions">Permissions</a></li>
      <li><a href="#excluded-applications--private-browsing" id="markdown-toc-excluded-applications--private-browsing">Excluded Applications &amp; Private Browsing</a></li>
      <li><a href="#storage-format" id="markdown-toc-storage-format">Storage Format</a>        <ul>
          <li><a href="#1-chunks-h264-videos" id="markdown-toc-1-chunks-h264-videos">(1) <code class="language-plaintext highlighter-rouge">chunks</code>: H.264 videos</a></li>
          <li><a href="#2-temp-png-screenshots" id="markdown-toc-2-temp-png-screenshots">(2) <code class="language-plaintext highlighter-rouge">temp</code>: PNG screenshots</a></li>
          <li><a href="#3-dbsqlite3-metadata" id="markdown-toc-3-dbsqlite3-metadata">(3) <code class="language-plaintext highlighter-rouge">db.sqlite3</code>: Metadata</a></li>
        </ul>
      </li>
      <li><a href="#resource-usage--battery-life" id="markdown-toc-resource-usage--battery-life">Resource Usage &amp; Battery Life</a></li>
    </ul>
  </li>
  <li><a href="#ideas-for-improvement" id="markdown-toc-ideas-for-improvement">Ideas for Improvement</a>    <ul>
      <li><a href="#battery-life" id="markdown-toc-battery-life">Battery Life</a></li>
      <li><a href="#storage-format-1" id="markdown-toc-storage-format-1">Storage Format</a></li>
      <li><a href="#security" id="markdown-toc-security">Security</a></li>
      <li><a href="#privacy" id="markdown-toc-privacy">Privacy</a></li>
    </ul>
  </li>
  <li><a href="#conclusion" id="markdown-toc-conclusion">Conclusion</a></li>
  <li><a href="#appendix" id="markdown-toc-appendix">Appendix</a>    <ul>
      <li><a href="#rewind-app" id="markdown-toc-rewind-app">Rewind App</a></li>
      <li><a href="#database-schema" id="markdown-toc-database-schema">Database Schema</a></li>
    </ul>
  </li>
</ul>

<h2 id="how-it-works-overview">How it works: Overview</h2>

<ul>
  <li>Use accessibility APIs to identify the frontmost window.
    <ul>
      <li>Store the timestamps to a SQLite database in the user’s <a href="https://support.apple.com/guide/mac-help/folders-that-come-with-your-mac-mchlp1143/mac">Library</a> folder.</li>
    </ul>
  </li>
  <li>Take a screenshot of the screen that contains the frontmost window.
    <ul>
      <li>If there are multiple screens, only the currently focused screen will be captured.</li>
      <li>Use <a href="https://developer.apple.com/documentation/screencapturekit/scshareablecontent">ScreenCaptureKit</a> to hide disallowed windows, including private browser windows and a user-defined exclusion list.</li>
    </ul>
  </li>
  <li>OCR the screenshot on-device using <a href="https://developer.apple.com/documentation/vision/recognizing_text_in_images">Apple’s Vision framework</a>, the same pipeline that powers Live Text.
    <ul>
      <li>Store the inference results to a SQLite database.</li>
    </ul>
  </li>
  <li>Compress the screenshot sequence to an H.264 video with FFmpeg.
    <ul>
      <li>Store videos in the user’s Library folder.</li>
    </ul>
  </li>
</ul>

<p>Additionally, if the user joins a Zoom call and enables transcription through Rewind:</p>

<ul>
  <li>Transcribe the audio on-device using the <a href="https://github.com/openai/whisper">OpenAI Whisper</a> model.
    <ul>
      <li>Store the transcripts and speaker information in a SQLite database.</li>
    </ul>
  </li>
</ul>

<p>In the following sections, I’ll provide details on how I came to these conclusions.</p>

<h2 id="analyzing-the-rewind-app">Analyzing the Rewind app</h2>

<h3 id="application-bundle">Application Bundle</h3>

<p>After installation, I poked through the <code class="language-plaintext highlighter-rouge">Rewind.app</code> bundle.</p>

<p>Executables:</p>

<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">Rewind</code>:</strong> the Cocoa application</li>
  <li><strong><code class="language-plaintext highlighter-rouge">Rewind Helper</code>:</strong> a non-UI binary</li>
</ul>

<p>Other notable files include:</p>

<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">Resources/ggml-base.en.bin</code>:</strong> The model weights for the <a href="https://github.com/openai/whisper">OpenAI Whisper</a> transcription model in the <a href="https://github.com/ggerganov/whisper.cpp">whisper.cpp</a>, likely for Zoom call transcription. The SHA-1 taken on my local machine (<code class="language-plaintext highlighter-rouge">137c40403d78fd54d454da0f9bd998f78703390c</code>) matches the file from the <a href="https://huggingface.co/datasets/ggerganov/whisper.cpp">weights repo</a>.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">Resources/favicons</code>:</strong> A directory of 912 favicons for popular websites, stored as PNG images. Examples include <code class="language-plaintext highlighter-rouge">amazon_com.png</code>, <code class="language-plaintext highlighter-rouge">dropbox_com.png</code>, <code class="language-plaintext highlighter-rouge">youtu_be.png</code>, etc. These are used in the timeline view when the frontmost app is a web browser, instead of showing the browser’s app icon.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">Frameworks/Sparkle.framework</code>:</strong> Despite installing through a <code class="language-plaintext highlighter-rouge">.pkg</code>, Rewind uses <a href="https://sparkle-project.org/">Sparkle</a> for updates.</li>
</ul>

<h3 id="frameworks">Frameworks</h3>

<p>After loading into a disassembler, the <code class="language-plaintext highlighter-rouge">Rewind</code> binary contains references to:</p>

<ul>
  <li><a href="https://github.com/ggerganov/whisper.cpp">whisper.cpp</a></li>
  <li><a href="https://developer.apple.com/documentation/visionkit">VisionKit</a>
    <ul>
      <li><a href="https://developer.apple.com/documentation/visionkit/imageanalyzer">ImageAnalyzer</a> — API for running Live Text inference and accessing results</li>
      <li><a href="https://developer.apple.com/documentation/visionkit/imageanalysisoverlayview">ImageAnalyzerOverlayView</a> — displays Live Text results and allows the user to copy text</li>
    </ul>
  </li>
  <li><a href="https://docs.sentry.io/platforms/native/">Sentry</a> — telemetry</li>
</ul>

<p>The <code class="language-plaintext highlighter-rouge">Rewind Helper</code> contains a statically linked <a href="https://ffmpeg.org/">FFmpeg</a>.</p>

<h3 id="permissions">Permissions</h3>

<p>Upon launch, Rewind requests permissions to:</p>

<ul>
  <li>record the screen</li>
  <li>record the microphone</li>
  <li>control other applications (accessibility).</li>
</ul>

<p>Recording the microphone is optional if the user doesn’t want to transcribe Zoom meeting audio.</p>

<h3 id="excluded-applications--private-browsing">Excluded Applications &amp; Private Browsing</h3>

<p>Rewind allows the user to exclude apps from its recording. By default, Rewind also excludes private windows from several popular browsers.</p>

<p>In this example, I’ve opened three windows (listed from back to front):</p>

<ul>
  <li>TextEdit</li>
  <li>Safari (regular)</li>
  <li>Safari (Private Browsing)</li>
</ul>

<figure>
    <a href="/assets/blog/rewind-app/rewind-app-private-browsing-screenshot.png">
        <img class="extrawide" src="/assets/blog/rewind-app/rewind-app-private-browsing-screenshot.jpg" width="1200" height="779" alt="A screenshot of a three windows that read: This is TextEdit, this is a normal window, this is a private browsing window." />
    </a>
    <a href="/assets/blog/rewind-app/rewind-app-private-browsing-search.png">
        <img class="extrawide" src="/assets/blog/rewind-app/rewind-app-private-browsing-search.jpg" width="1200" height="779" alt="A screenshot of a two windows that read: this is TextEdit, this is a normal window. A search bar appears in front of the windows." />
    </a>
    <figcaption><p><strong>Top:</strong> My actual desktop. <strong>Bottom:</strong> What Rewind sees.</p></figcaption>
</figure>

<p>Rewind’s screenshot excludes the private browsing window and the menu bar — and <em>reveals additional content</em> previously occluded by the private window.</p>

<p>This suggests that Rewind uses Apple’s ScreenCaptureKit, which allows <a href="https://developer.apple.com/documentation/screencapturekit/scshareablecontent">filtering by window</a> and can recomposite the desktop based on the input parameters. (The legacy <a href="https://developer.apple.com/documentation/coregraphics/1456259-cgdisplaycapture">CGDisplayCapture</a> API can only capture the entire screen or individual windows. The app developer would have to perform their own compositing.)</p>

<h3 id="storage-format">Storage Format</h3>

<p>Rewind stores all screen recordings and transcripts to:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~/Library/Application Support/com.memoryvault.MemoryVault
</code></pre></div></div>

<p>There are three items in this directory:</p>

<h4 id="1-chunks-h264-videos">(1) <code class="language-plaintext highlighter-rouge">chunks</code>: H.264 videos</h4>

<p>A directory of timestamped movie files. Surprisingly, the date format contains colons.</p>

<figure>
    <img src="/assets/blog/rewind-app/rewind-app-chunks.png" width="765" height="374" alt="A screenshot of a Zoom meeting and a timeline." />
    <figcaption><p>The chunks directory.</p></figcaption>
</figure>

<p>Each <code class="language-plaintext highlighter-rouge">chunk</code> file is an MP4 container:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cd ~/Library/Application\ Support/com.memoryvault.MemoryVault/chunks/2022-12-19T00:57:32
$ file chunk
chunk: ISO Media, MP4 Base Media v1 [ISO 14496-12:2003]
</code></pre></div></div>

<p>Inspecting the file with VLC, we find that it contains a single H.264 stream, about 5 minutes long at 0.5 fps.</p>

<figure>
    <img src="/assets/blog/rewind-app/rewind-app-chunks-video-info.png" width="552" height="529" alt="Screenshot of a window titled Media Information. Stream 0 is shown with Frame rate 0.5." />
    <figcaption><p>VLC inspector on a chunk file.</p></figcaption>
</figure>

<h4 id="2-temp-png-screenshots">(2) <code class="language-plaintext highlighter-rouge">temp</code>: PNG screenshots</h4>

<p>This directory contains a series of screenshots. A new screenshot is created every two seconds.</p>

<p>With a screen resolution of 3024 × 1964 (14-inch MacBook Pro), the app writes about 1–2 MB/s of PNGs when displaying text. The exact throughput depends on the total screen size and content being shown. In the worst case, such as when playing back a full-screen movie, each screenshot might exceed 10 MB.</p>

<figure>
    <video width="756" height="347" poster="/assets/blog/rewind-app/rewind-app-temp-folder-thumb.jpg" preload="" autoplay="" loop="" muted="" playsinline="">
        <source src="https://files.kevinchen.co/website/assets/blog/rewind-app/rewind-app-temp-folder-video.mp4" type="video/mp4" />
        <source src="https://files.kevinchen.co/website/assets/blog/rewind-app/rewind-app-temp-folder-video.mov" type="video/quicktime" />
        <p><a href="https://files.kevinchen.co/website/assets/blog/rewind-app/rewind-app-temp-folder-video.mp4">Download</a></p>
    </video>
    <figcaption><p>A PNG file gets created every two seconds.</p></figcaption>
</figure>

<h4 id="3-dbsqlite3-metadata">(3) <code class="language-plaintext highlighter-rouge">db.sqlite3</code>: Metadata</h4>

<p>Rewind uses this SQLite database to store video file metadata, focused app metadata, OCR results, and call transcripts. Here are the key tables:</p>

<p><strong><code class="language-plaintext highlighter-rouge">frame</code></strong> contains a row for each video frame.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">frame</span> <span class="k">LIMIT</span> <span class="mi">10</span>
</code></pre></div></div>

<details>
    <summary>Query Results</summary>
    <div>
        <table>
            <thead>
                <tr>
                    <th>id</th>
                    <th>createdAt</th>
                    <th>imageFileName</th>
                    <th>segmentId</th>
                    <th>videoId</th>
                    <th>videoFrameIndex</th>
                    <th>isStarred</th>
                    <th>encodingStatus</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>1</td>
                    <td>2022-12-19T00:57:32.890</td>
                    <td>2022-12-19T00:57:32.788</td>
                    <td>1</td>
                    <td>1</td>
                    <td>0</td>
                    <td>0</td>
                    <td>success</td>
                </tr>
                <tr>
                    <td>2</td>
                    <td>2022-12-19T00:57:36.845</td>
                    <td>2022-12-19T00:57:36.742</td>
                    <td>1</td>
                    <td>1</td>
                    <td>1</td>
                    <td>0</td>
                    <td>success</td>
                </tr>
                <tr>
                    <td>3</td>
                    <td>2022-12-19T00:57:38.827</td>
                    <td>2022-12-19T00:57:38.738</td>
                    <td>2</td>
                    <td>1</td>
                    <td>2</td>
                    <td>0</td>
                    <td>success</td>
                </tr>
                <tr>
                    <td>4</td>
                    <td>2022-12-19T00:57:40.833</td>
                    <td>2022-12-19T00:57:40.741</td>
                    <td>2</td>
                    <td>1</td>
                    <td>3</td>
                    <td>0</td>
                    <td>success</td>
                </tr>
                <tr>
                    <td>5</td>
                    <td>2022-12-19T00:57:43.573</td>
                    <td>2022-12-19T00:57:43.494</td>
                    <td>3</td>
                    <td>1</td>
                    <td>4</td>
                    <td>0</td>
                    <td>success</td>
                </tr>
                <tr>
                    <td>6</td>
                    <td>2022-12-19T00:57:44.807</td>
                    <td>2022-12-19T00:57:44.723</td>
                    <td>4</td>
                    <td>1</td>
                    <td>5</td>
                    <td>0</td>
                    <td>success</td>
                </tr>
                <tr>
                    <td>7</td>
                    <td>2022-12-19T00:57:46.794</td>
                    <td>2022-12-19T00:57:46.722</td>
                    <td>4</td>
                    <td>1</td>
                    <td>6</td>
                    <td>0</td>
                    <td>success</td>
                </tr>
                <tr>
                    <td>8</td>
                    <td>2022-12-19T00:57:48.854</td>
                    <td>2022-12-19T00:57:48.763</td>
                    <td>4</td>
                    <td>1</td>
                    <td>7</td>
                    <td>0</td>
                    <td>success</td>
                </tr>
                <tr>
                    <td>9</td>
                    <td>2022-12-19T00:57:50.848</td>
                    <td>2022-12-19T00:57:50.759</td>
                    <td>4</td>
                    <td>1</td>
                    <td>8</td>
                    <td>0</td>
                    <td>success</td>
                </tr>
                <tr>
                    <td>10</td>
                    <td>2022-12-19T00:57:52.836</td>
                    <td>2022-12-19T00:57:52.744</td>
                    <td>4</td>
                    <td>1</td>
                    <td>9</td>
                    <td>0</td>
                    <td>success</td>
                </tr>
            </tbody>
        </table>
    </div>
</details>

<p><strong><code class="language-plaintext highlighter-rouge">search_content</code></strong> contains raw OCR results, and <strong><code class="language-plaintext highlighter-rouge">node</code></strong> maps them to positions on <code class="language-plaintext highlighter-rouge">frame</code>. There can be multiple <code class="language-plaintext highlighter-rouge">node</code> per <code class="language-plaintext highlighter-rouge">frame</code>.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">search_content</span> <span class="k">LIMIT</span> <span class="mi">1</span>
</code></pre></div></div>

<details>
    <summary>Query Results</summary>
    <div>
        <table>
            <thead>
                <tr>
                    <th>docid</th>
                    <th>c0text</th>
                    <th>c1otherText</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>1</td>
                    <td>? Advanced… 000 ###: Security &amp; Privacy Q Search Click the lock to make changes…</td>
                    <td>4:54 PM 4:54 PM y Access! Q ock Auctions 12/15/22 )23 12/15/22 Date Received out…</td>
                </tr>
            </tbody>
        </table>
    </div>
</details>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">node</span> <span class="k">LIMIT</span> <span class="mi">10</span>
</code></pre></div></div>

<details>
    <summary>Query Results</summary>
    <div>
        <table>
            <thead>
                <tr>
                    <th>id</th>
                    <th>frameId</th>
                    <th>nodeOrder</th>
                    <th>textOffset</th>
                    <th>textLength</th>
                    <th>leftX</th>
                    <th>topY</th>
                    <th>width</th>
                    <th>height</th>
                    <th>windowIndex</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>1</td>
                    <td>1</td>
                    <td>0</td>
                    <td>0</td>
                    <td>1</td>
                    <td>0.467398302591349</td>
                    <td>0.597970651366107</td>
                    <td>0.00672236248460967</td>
                    <td>0.013157853170445</td>
                    <td>0</td>
                </tr>
                <tr>
                    <td>2</td>
                    <td>1</td>
                    <td>1</td>
                    <td>2</td>
                    <td>9</td>
                    <td>0.399312973022461</td>
                    <td>0.59765262753272</td>
                    <td>0.0522676955821902</td>
                    <td>0.0116193030208916</td>
                    <td>0</td>
                </tr>
                <tr>
                    <td>3</td>
                    <td>1</td>
                    <td>2</td>
                    <td>12</td>
                    <td>3</td>
                    <td>0.0610130221344704</td>
                    <td>0.0570757653384834</td>
                    <td>0.0364272095436274</td>
                    <td>0.0161139305601729</td>
                    <td>0</td>
                </tr>
                <tr>
                    <td>4</td>
                    <td>1</td>
                    <td>3</td>
                    <td>16</td>
                    <td>23</td>
                    <td>0.163880813953488</td>
                    <td>0.0565509518477043</td>
                    <td>0.113372092912819</td>
                    <td>0.0179171332422108</td>
                    <td>0</td>
                </tr>
                <tr>
                    <td>5</td>
                    <td>1</td>
                    <td>4</td>
                    <td>40</td>
                    <td>8</td>
                    <td>0.381177325581395</td>
                    <td>0.0565509518477043</td>
                    <td>0.0414244182655039</td>
                    <td>0.0156774914800299</td>
                    <td>0</td>
                </tr>
                <tr>
                    <td>6</td>
                    <td>1</td>
                    <td>5</td>
                    <td>49</td>
                    <td>31</td>
                    <td>0.0875726744186047</td>
                    <td>0.598544232922732</td>
                    <td>0.127906976671512</td>
                    <td>0.0145576707649976</td>
                    <td>0</td>
                </tr>
                <tr>
                    <td>7</td>
                    <td>1</td>
                    <td>6</td>
                    <td>81</td>
                    <td>13</td>
                    <td>0.107422983923624</td>
                    <td>0.499536426710789</td>
                    <td>0.0514842521312625</td>
                    <td>0.014643243018617</td>
                    <td>0</td>
                </tr>
                <tr>
                    <td>8</td>
                    <td>1</td>
                    <td>7</td>
                    <td>95</td>
                    <td>18</td>
                    <td>0.10719476744186</td>
                    <td>0.458566629339306</td>
                    <td>0.0821220929505814</td>
                    <td>0.0145576707606783</td>
                    <td>0</td>
                </tr>
                <tr>
                    <td>9</td>
                    <td>1</td>
                    <td>8</td>
                    <td>114</td>
                    <td>10</td>
                    <td>0.106081352677456</td>
                    <td>0.417901666006876</td>
                    <td>0.0499914524167083</td>
                    <td>0.0125384870061416</td>
                    <td>0</td>
                </tr>
                <tr>
                    <td>10</td>
                    <td>1</td>
                    <td>9</td>
                    <td>125</td>
                    <td>7</td>
                    <td>0.287038004675577</td>
                    <td>0.376678364274216</td>
                    <td>0.0357664684916651</td>
                    <td>0.0107925261255073</td>
                    <td>0</td>
                </tr>
            </tbody>
        </table>
    </div>
</details>

<p><strong><code class="language-plaintext highlighter-rouge">search</code></strong> is a virtual table constructed from <code class="language-plaintext highlighter-rouge">search_content</code> using the <a href="https://www.sqlite.org/fts3.html">SQLite FTS</a> (full-text search) extension.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sqlite&gt; .schema search
CREATE VIRTUAL TABLE "search" USING fts4("text", "otherText", tokenize=porter)
/* search(text,otherText) */;
</code></pre></div></div>

<p><strong><code class="language-plaintext highlighter-rouge">segment</code></strong> contains a row for each instance of the focused application changing. This data appears to be gathered from the accessibility API, because the timestamps and window titles are exact. Additionally, there’s an optional column for the browser URL when the focused application is Safari, Chrome, or Arc. I’m not sure how this information is captured.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">segment</span> <span class="k">WHERE</span> <span class="n">id</span> <span class="o">&gt;</span> <span class="mi">85</span> <span class="k">LIMIT</span> <span class="mi">10</span>
</code></pre></div></div>

<details>
    <summary>Query Results</summary>
    <div>
        <table>
            <thead>
                <tr>
                    <th>id</th>
                    <th>appId</th>
                    <th>startTime</th>
                    <th>endTime</th>
                    <th>windowName</th>
                    <th>browserUrl</th>
                    <th>browserProfile</th>
                    <th>type</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>86</td>
                    <td>com.apple.Safari</td>
                    <td>2022-12-19T01:07:16.816</td>
                    <td>2022-12-19T01:07:18.815</td>
                    <td>Storage | Rewind Help Center</td>
                    <td>https://help.rewind.ai/en/collections/3698681-storage</td>
                    <td>&nbsp;</td>
                    <td>screenshot</td>
                </tr>
                <tr>
                    <td>87</td>
                    <td>com.apple.Safari</td>
                    <td>2022-12-19T01:07:18.815</td>
                    <td>2022-12-19T01:07:42.808</td>
                    <td>How does Rewind compression work? | Rewind Help Center</td>
                    <td>https://help.rewind.ai/en/articles/6706118-how-does-rewind-compression-work</td>
                    <td>&nbsp;</td>
                    <td>screenshot</td>
                </tr>
                <tr>
                    <td>88</td>
                    <td>com.apple.finder</td>
                    <td>2022-12-19T01:07:42.808</td>
                    <td>2022-12-19T01:07:44.793</td>
                    <td>Desktop — Local</td>
                    <td>&nbsp;</td>
                    <td>&nbsp;</td>
                    <td>screenshot</td>
                </tr>
                <tr>
                    <td>89</td>
                    <td>com.apple.ActivityMonitor</td>
                    <td>2022-12-19T01:07:44.793</td>
                    <td>2022-12-19T01:08:06.847</td>
                    <td>Activity Monitor</td>
                    <td>&nbsp;</td>
                    <td>&nbsp;</td>
                    <td>screenshot</td>
                </tr>
                <tr>
                    <td>90</td>
                    <td>com.facebook.archon</td>
                    <td>2022-12-19T01:08:06.847</td>
                    <td>2022-12-19T01:08:32.845</td>
                    <td>Messenger</td>
                    <td>&nbsp;</td>
                    <td>&nbsp;</td>
                    <td>screenshot</td>
                </tr>
                <tr>
                    <td>91</td>
                    <td>com.apple.ActivityMonitor</td>
                    <td>2022-12-19T01:08:32.845</td>
                    <td>2022-12-19T01:08:36.840</td>
                    <td>Activity Monitor</td>
                    <td>&nbsp;</td>
                    <td>&nbsp;</td>
                    <td>screenshot</td>
                </tr>
                <tr>
                    <td>92</td>
                    <td>com.apple.finder</td>
                    <td>2022-12-19T01:08:36.840</td>
                    <td>2022-12-19T01:08:40.844</td>
                    <td>Desktop — Local</td>
                    <td>&nbsp;</td>
                    <td>&nbsp;</td>
                    <td>screenshot</td>
                </tr>
                <tr>
                    <td>93</td>
                    <td>com.apple.finder</td>
                    <td>2022-12-19T01:08:40.844</td>
                    <td>2022-12-19T01:08:44.844</td>
                    <td>Library</td>
                    <td>&nbsp;</td>
                    <td>&nbsp;</td>
                    <td>screenshot</td>
                </tr>
                <tr>
                    <td>94</td>
                    <td>com.apple.finder</td>
                    <td>2022-12-19T01:08:44.844</td>
                    <td>2022-12-19T01:08:50.839</td>
                    <td>Application Support</td>
                    <td>&nbsp;</td>
                    <td>&nbsp;</td>
                    <td>screenshot</td>
                </tr>
                <tr>
                    <td>95</td>
                    <td>com.apple.finder</td>
                    <td>2022-12-19T01:08:50.839</td>
                    <td>2022-12-19T01:08:54.833</td>
                    <td>&nbsp;</td>
                    <td>&nbsp;</td>
                    <td>&nbsp;</td>
                    <td>screenshot</td>
                </tr>
            </tbody>
        </table>
    </div>
</details>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">segment</span> <span class="k">WHERE</span> <span class="k">type</span> <span class="o">!=</span> <span class="nv">"screenshot"</span>
</code></pre></div></div>

<details>
    <summary>Query Results</summary>
    <div>
        <table>
            <thead>
                <tr>
                    <th>id</th>
                    <th>appId</th>
                    <th>startTime</th>
                    <th>endTime</th>
                    <th>windowName</th>
                    <th>browserUrl</th>
                    <th>browserProfile</th>
                    <th>type</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>349</td>
                    <td>ai.rewind.audiorecorder</td>
                    <td>2022-12-19T01:47:37.511</td>
                    <td>2022-12-19T02:03:54.695</td>
                    <td>&nbsp;</td>
                    <td>&nbsp;</td>
                    <td>&nbsp;</td>
                    <td>audio</td>
                </tr>
            </tbody>
        </table>
    </div>
</details>

<p><strong><code class="language-plaintext highlighter-rouge">transcript_word</code></strong> contains a row for each word of Zoom call transcripts. This storage is rather inefficient given the app’s current functionality, but appears to be setting up for a future UI that can match the transcript to the call audio.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">transcript_word</span> <span class="k">LIMIT</span> <span class="mi">20</span> <span class="k">OFFSET</span> <span class="mi">24</span>
</code></pre></div></div>

<details>
    <summary>Query Results</summary>
    <div>
        <table>
            <thead>
                <tr>
                    <th>id</th>
                    <th>segmentId</th>
                    <th>speechSource</th>
                    <th>word</th>
                    <th>timeOffset</th>
                    <th>fullTextOffset</th>
                    <th>duration</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>25</td>
                    <td>349</td>
                    <td>me</td>
                    <td>how’s</td>
                    <td>51000</td>
                    <td>93</td>
                    <td>1000</td>
                </tr>
                <tr>
                    <td>26</td>
                    <td>349</td>
                    <td>me</td>
                    <td>it</td>
                    <td>52000</td>
                    <td>99</td>
                    <td>1000</td>
                </tr>
                <tr>
                    <td>27</td>
                    <td>349</td>
                    <td>me</td>
                    <td>going?</td>
                    <td>53000</td>
                    <td>102</td>
                    <td>1000</td>
                </tr>
                <tr>
                    <td>28</td>
                    <td>349</td>
                    <td>me</td>
                    <td>Yo,</td>
                    <td>54000</td>
                    <td>109</td>
                    <td>250</td>
                </tr>
                <tr>
                    <td>29</td>
                    <td>349</td>
                    <td>me</td>
                    <td>so</td>
                    <td>54250</td>
                    <td>113</td>
                    <td>250</td>
                </tr>
                <tr>
                    <td>30</td>
                    <td>349</td>
                    <td>me</td>
                    <td>this</td>
                    <td>54500</td>
                    <td>116</td>
                    <td>250</td>
                </tr>
                <tr>
                    <td>31</td>
                    <td>349</td>
                    <td>me</td>
                    <td>is</td>
                    <td>54750</td>
                    <td>121</td>
                    <td>250</td>
                </tr>
                <tr>
                    <td>32</td>
                    <td>349</td>
                    <td>me</td>
                    <td>going</td>
                    <td>55000</td>
                    <td>124</td>
                    <td>250</td>
                </tr>
                <tr>
                    <td>33</td>
                    <td>349</td>
                    <td>me</td>
                    <td>to</td>
                    <td>55250</td>
                    <td>130</td>
                    <td>250</td>
                </tr>
                <tr>
                    <td>34</td>
                    <td>349</td>
                    <td>me</td>
                    <td>transcribe</td>
                    <td>55500</td>
                    <td>133</td>
                    <td>250</td>
                </tr>
                <tr>
                    <td>35</td>
                    <td>349</td>
                    <td>me</td>
                    <td>us?</td>
                    <td>55750</td>
                    <td>144</td>
                    <td>250</td>
                </tr>
                <tr>
                    <td>36</td>
                    <td>349</td>
                    <td>me</td>
                    <td>Is</td>
                    <td>56000</td>
                    <td>148</td>
                    <td>400</td>
                </tr>
                <tr>
                    <td>37</td>
                    <td>349</td>
                    <td>me</td>
                    <td>that</td>
                    <td>56400</td>
                    <td>151</td>
                    <td>400</td>
                </tr>
                <tr>
                    <td>38</td>
                    <td>349</td>
                    <td>me</td>
                    <td>what</td>
                    <td>56800</td>
                    <td>156</td>
                    <td>400</td>
                </tr>
                <tr>
                    <td>39</td>
                    <td>349</td>
                    <td>me</td>
                    <td>I’m</td>
                    <td>57200</td>
                    <td>161</td>
                    <td>400</td>
                </tr>
                <tr>
                    <td>40</td>
                    <td>349</td>
                    <td>me</td>
                    <td>asking?</td>
                    <td>57600</td>
                    <td>165</td>
                    <td>400</td>
                </tr>
                <tr>
                    <td>41</td>
                    <td>349</td>
                    <td>me</td>
                    <td>Yeah,</td>
                    <td>58000</td>
                    <td>173</td>
                    <td>249</td>
                </tr>
                <tr>
                    <td>42</td>
                    <td>349</td>
                    <td>me</td>
                    <td>this</td>
                    <td>58249</td>
                    <td>179</td>
                    <td>249</td>
                </tr>
                <tr>
                    <td>43</td>
                    <td>349</td>
                    <td>me</td>
                    <td>meeting</td>
                    <td>58499</td>
                    <td>184</td>
                    <td>249</td>
                </tr>
                <tr>
                    <td>44</td>
                    <td>349</td>
                    <td>me</td>
                    <td>is</td>
                    <td>58749</td>
                    <td>192</td>
                    <td>249</td>
                </tr>
            </tbody>
        </table>
    </div>
</details>

<p>Finally, clip deletion is temporarly enqueued in the <strong><code class="language-plaintext highlighter-rouge">purge</code></strong> table. It appears to contain a row per deleted frame or <code class="language-plaintext highlighter-rouge">chunk</code>. The referenced rows in <code class="language-plaintext highlighter-rouge">frame</code>, rows in <code class="language-plaintext highlighter-rouge">video</code>, and the <code class="language-plaintext highlighter-rouge">chunk</code> files get deleted soon after user request. But the <code class="language-plaintext highlighter-rouge">purge</code> table only gets cleared the next time the app starts up.</p>

<p>If the user deletes all clips contained in a <code class="language-plaintext highlighter-rouge">chunk</code> file, the chunk file is also deleted. In other cases, the app implements soft-deletion by removing the SQLite metadata only.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">purge</span>
<span class="k">WHERE</span> <span class="n">path</span> <span class="o">&gt;=</span> <span class="s1">'2022-12-25T00:19:26'</span>
<span class="k">ORDER</span> <span class="k">BY</span> <span class="n">path</span>
<span class="k">LIMIT</span> <span class="mi">5</span>
</code></pre></div></div>

<details>
    <summary>Query Results</summary>
    <div>
        <table>
            <thead>
                <tr>
                    <th>path</th>
                    <th>fileType</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>2022-12-25T00:19:26.962</td>
                    <td>image</td>
                </tr>
                <tr>
                    <td>2022-12-25T00:19:26/chunk</td>
                    <td>video</td>
                </tr>
                <tr>
                    <td>2022-12-25T00:19:28.943</td>
                    <td>image</td>
                </tr>
                <tr>
                    <td>2022-12-25T00:19:30.951</td>
                    <td>image</td>
                </tr>
                <tr>
                    <td>2022-12-25T00:19:32.931</td>
                    <td>image</td>
                </tr>
            </tbody>
        </table>
    </div>
</details>

<h3 id="resource-usage--battery-life">Resource Usage &amp; Battery Life</h3>

<p>CPU usage while recording on my 14-inch MacBook Pro with M1 Pro:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Rewind</code> uses about 20% CPU continuously</li>
  <li><code class="language-plaintext highlighter-rouge">Rewind Helper</code> spikes over 200% CPU every time the temporary PNG images are compressed to a H.264 video</li>
</ul>

<p>I suspect that Rewind also indirectly consumes resources through WindowServer because Rewind requests filtered windows. This may require WindowServer to perform additional compositing just for Rewind. However, I haven’t been able to measure this conclusively.</p>

<p>Storage usage:</p>

<ul>
  <li>Screen recordings (<code class="language-plaintext highlighter-rouge">chunks</code>): 180 MB / hour</li>
  <li>Metadata, OCR results, and call transcripts (<code class="language-plaintext highlighter-rouge">db.sqlite3</code>): 26 MB / hour</li>
  <li>Console logs: 4 MB / hour</li>
</ul>

<p>These numbers were calculated after the first 11 hours of using Rewind. My workload is a mix of text-heavy (reading, writing) and image-heavy (video editing) tasks. The storage used will vary depending on workload: for example, a text- and Zoom-heavy workload will generate a larger SQLite database.</p>

<p>Overall, running Rewind reduces my battery life by about 20–40 percent.</p>

<h2 id="ideas-for-improvement">Ideas for Improvement</h2>

<p>Below are some areas the Rewind app’s architecture could be improved. It’s understandable that the developers prioritized shipping an MVP over polishing these details. However, if the pricing remains $20 per month, users will expect a lot of polish in the released app.</p>

<h3 id="battery-life">Battery Life</h3>

<p>Reduced battery life is my primary pain point when using Rewind.</p>

<p><strong>PNG encoding.</strong> Rewind could encode the screenshots directly to H.264, instead of temporarily writing PNG images. This would eliminate throwaway work to encode and decode PNGs.</p>

<p>It would also eliminate 0.6–3.0 GB / hour of writes, or 4–19 TB / year assuming continuous operation. From a NAND wear perspective, this is unlikely to be a problem: modern SSDs can handle well over 500 TB written per TB of capacity. However, generating a large amount of I/O remains a battery life issue.</p>

<p><strong>Video encoding.</strong> Rewind encodes with FFmpeg. <del>I wasn’t able to determine whether FFmpeg was using the M1’s Media Engine (video encode/decode accelerator).</del> <strong>Update (February 2, 2023):</strong> Rewind appears to encode video on the CPU (libx264 via FFmpeg). Matteo Contrini and Andy Xu pointed out that it’s possible to read encoder settings from an ffmpeg-encoded movie. Matteo writes:</p>

<blockquote>
  <p>Regarding the ffmpeg part, if they’re not overriding metadata you can find which encoder was used with ffmpeg itself, or ffprobe.</p>

  <p>For example, if you ffprobe a video file encoded by <a href="https://developer.apple.com/documentation/videotoolbox">VideoToolbox</a> (hw encoding) you would find <code class="language-plaintext highlighter-rouge">h264_videotoolbox</code> in the <code class="language-plaintext highlighter-rouge">encoder</code> field of the metadata. If it’s x264, you’d find <code class="language-plaintext highlighter-rouge">libx264</code>.</p>
</blockquote>

<p>Rewind appears to use software encoding — see <code class="language-plaintext highlighter-rouge">libx264</code> below. This appears consistently on files generated by Rewind version 0.6309 (the version originally tested) through 0.7312 (the current version as of February 2, 2023).</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ffprobe ~/Library/Application\ Support/com.memoryvault.MemoryVault/chunks/2023-01-31T22\:33\:11/chunk
ffprobe version 5.1.2 Copyright (c) 2007-2022 the FFmpeg developers
[...]
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/Users/Kevin/Library/Application Support/com.memoryvault.MemoryVault/chunks/2023-01-31T22:33:11/chunk':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf59.30.100
  Duration: 00:05:00.00, start: 0.000000, bitrate: 497 kb/s
  Stream #0:0[0x1](und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(progressive), 3024x1964, 496 kb/s, 0.50 fps, 0.50 tbr, 16384 tbn (default)
    Metadata:
      handler_name    : VideoHandler
      vendor_id       : [0][0][0][0]
      encoder         : Lavc59.42.103 libx264
</code></pre></div></div>

<p>As of Rewind 0.7168 (released on January 27, 2023), the app now <a href="https://help.rewind.ai/en/articles/6942804-how-does-rewind-s-battery-mode-work">defers H.264 encoding</a> when the user’s device is running on battery power. This resolves the battery impact of video encoding.</p>

<p>However, this approach still strikes me as wasteful. Rewind might compete with the user for CPU time in some cases, and at a minimum, it heats up the user’s machine unnecessarily.</p>

<p>Rewind already requires Apple Silicon. This allows them to assume that hardware-accelerated encoding is always available. Perhaps they have other reasons not to use it, such as needing to support a wider range of encoder settings.</p>

<p><strong>On-device inference frequency.</strong> OCR currently runs on every image (0.5 Hz). It’s possible that OCR can be subsampled or deferred entirely when running on battery without compromising the search experience. Any deferred images could be scanned the next time the user plugs in their computer or opens the Rewind UI.</p>

<h3 id="storage-format-1">Storage Format</h3>

<p>The current directory structure and schemas are performant enough for a user base that only has a few months of recordings at most. Although Rewind can set a retention period, the default setting retains recordings forever, and it’s clear from the marketing materials that indefinite retention is the intended use case. The storage formats could be changed slightly to account for an ever-growing archive:</p>

<p><strong>Database.</strong> SQLite is quite performant; however, the text search table also grows quickly (26 MB / hour on my machine). Putting all text into a single table may cause problems in the future. For example, backup software may have trouble with a large and constantly changing file, especially <a href="https://eclecticlight.co/2021/03/04/time-machine-to-apfs-evolution/">Time Machine on HFS+</a> and others that don’t support block-level deduplication. For users with retention enabled, performing a SQLite VACUUM after deleting a large number of records may take a long time and require lots of scratch space.</p>

<p><strong>Directory structure.</strong> Currently, each video clip receives its own subdirectory in the <code class="language-plaintext highlighter-rouge">chunks</code> directory. Filesystems become less performant when listing and traversing directories as the number of children grows, even modern filesystems that use hash tables. A common solution is to create <a href="https://serverfault.com/questions/46384/how-to-solve-linux-subdirectories-number-limit">multiple levels of subdirectories using the prefix</a> of the desired directory name. For Rewind’s timestamp directories (such as <code class="language-plaintext highlighter-rouge">2022-12-19T00:57:32</code>), the prefixes could be some concatenation of the year, month, and day.</p>

<h3 id="security">Security</h3>

<p>Rewind currently doesn’t encrypt data at rest. Any app with full disk access and any attacker who encounters an unlocked computer has the ability to read recordings from all time, including soft-deleted clips.</p>

<p>Rewind could encrypt files on disk using a shared secret stored in the macOS Keychain. Access to Keychain secrets can be configured on a per-app, per-secret basis.</p>

<p>For increased security, Rewind could implement public-key cryptography, where the public key is used to append recordings and the private key is required to search them. Users would need to authenticate to unlock the search UI.</p>

<h3 id="privacy">Privacy</h3>

<p>Rewind currently allows users to exclude apps from recording and to delete specific past recordings. It would be great to combine these features by offering bulk deletion by app, time range, URL, etc.</p>

<p>Ideally, deletions would always remove the underlying imagery, even if it requires an expensive re-encode of the <code class="language-plaintext highlighter-rouge">chunk</code> files to support partial deletion.</p>

<h2 id="conclusion">Conclusion</h2>

<p>The Rewind app is a clever and helpful user interface built on top of components such as Apple’s VisionKit and OpenAI’s Whisper. Imagery is compressed with H.264 and text search uses SQLite FTS.</p>

<p>As a developer, it’s cool to see that such advanced features can be built without training any ML models. The ever-growing collection of powerful, pretrained models continues to lower the barrier to entry for building ML-powered apps. On the other hand, I would also feel concerned that “fast follower” type competitors can easily clone apps like Rewind.</p>

<p>As a user, I’m excited to be served by increasingly intelligent software that, in addition to competing on the quality of machine learning, must also compete on user experience.</p>

<h2 id="appendix">Appendix</h2>

<h3 id="rewind-app">Rewind App</h3>

<p>Version: 0.6309</p>

<p>Bundle ID: <code class="language-plaintext highlighter-rouge">com.memoryvault.MemoryVault</code></p>

<h3 id="database-schema">Database Schema</h3>

<p>Table names:</p>

<ul>
  <li>doc_segment</li>
  <li>frame</li>
  <li>node</li>
  <li>purge</li>
  <li>search</li>
  <li>search_content</li>
  <li>search_docsize</li>
  <li>search_segdir</li>
  <li>search_segments</li>
  <li>search_stat</li>
  <li>segment</li>
  <li>segment_video</li>
  <li>tokenizer</li>
  <li>transcript_word</li>
  <li>video</li>
</ul>

<p>Full schema:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">IF</span> <span class="k">NOT</span> <span class="k">EXISTS</span> <span class="nv">"frame"</span> <span class="p">(</span><span class="nv">"id"</span> <span class="nb">INTEGER</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="n">AUTOINCREMENT</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"createdAt"</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"imageFileName"</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"segmentId"</span> <span class="nb">INTEGER</span> <span class="k">REFERENCES</span> <span class="nv">"segment"</span> <span class="p">(</span><span class="nv">"id"</span><span class="p">),</span> <span class="nv">"videoId"</span> <span class="nb">INTEGER</span> <span class="k">REFERENCES</span> <span class="nv">"video"</span> <span class="p">(</span><span class="nv">"id"</span><span class="p">),</span> <span class="nv">"videoFrameIndex"</span> <span class="nb">INTEGER</span><span class="p">,</span> <span class="nv">"isStarred"</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span> <span class="k">DEFAULT</span> <span class="p">(</span><span class="mi">0</span><span class="p">),</span> <span class="nv">"encodingStatus"</span> <span class="nb">TEXT</span><span class="p">);</span>

<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">sqlite_sequence</span><span class="p">(</span><span class="n">name</span><span class="p">,</span><span class="n">seq</span><span class="p">);</span>

<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">IF</span> <span class="k">NOT</span> <span class="k">EXISTS</span> <span class="nv">"node"</span> <span class="p">(</span><span class="nv">"id"</span> <span class="nb">INTEGER</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="n">AUTOINCREMENT</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"frameId"</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span> <span class="k">REFERENCES</span> <span class="nv">"frame"</span> <span class="p">(</span><span class="nv">"id"</span><span class="p">),</span> <span class="nv">"nodeOrder"</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"textOffset"</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"textLength"</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"leftX"</span> <span class="nb">REAL</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"topY"</span> <span class="nb">REAL</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"width"</span> <span class="nb">REAL</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"height"</span> <span class="nb">REAL</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"windowIndex"</span> <span class="nb">INTEGER</span><span class="p">);</span>

<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">IF</span> <span class="k">NOT</span> <span class="k">EXISTS</span> <span class="nv">"segment"</span> <span class="p">(</span><span class="nv">"id"</span> <span class="nb">INTEGER</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="n">AUTOINCREMENT</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"appId"</span> <span class="nb">TEXT</span><span class="p">,</span> <span class="nv">"startTime"</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"endTime"</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"windowName"</span> <span class="nb">TEXT</span><span class="p">,</span> <span class="nv">"browserUrl"</span> <span class="nb">TEXT</span><span class="p">,</span> <span class="nv">"browserProfile"</span> <span class="nb">TEXT</span><span class="p">,</span> <span class="nv">"type"</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span> <span class="k">DEFAULT</span> <span class="p">(</span><span class="s1">'screenshot'</span><span class="p">));</span>

<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">IF</span> <span class="k">NOT</span> <span class="k">EXISTS</span> <span class="nv">"video"</span> <span class="p">(</span><span class="nv">"id"</span> <span class="nb">INTEGER</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="n">AUTOINCREMENT</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"frameDuration"</span> <span class="nb">REAL</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"height"</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"width"</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"path"</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span> <span class="k">DEFAULT</span> <span class="p">(</span><span class="s1">''</span><span class="p">),</span> <span class="nv">"captureType"</span> <span class="nb">TEXT</span><span class="p">,</span> <span class="nv">"fileSize"</span> <span class="nb">INTEGER</span><span class="p">);</span>

<span class="k">CREATE</span> <span class="k">INDEX</span> <span class="nv">"index_frame_on_segmentid_createdat"</span> <span class="k">ON</span> <span class="nv">"frame"</span> <span class="p">(</span><span class="nv">"segmentId"</span><span class="p">,</span> <span class="nv">"createdAt"</span><span class="p">);</span>

<span class="k">CREATE</span> <span class="k">INDEX</span> <span class="nv">"index_node_on_frameid"</span> <span class="k">ON</span> <span class="nv">"node"</span> <span class="p">(</span><span class="nv">"frameId"</span><span class="p">);</span>

<span class="k">CREATE</span> <span class="k">INDEX</span> <span class="nv">"index_frame_on_createdat"</span> <span class="k">ON</span> <span class="nv">"frame"</span> <span class="p">(</span><span class="nv">"createdAt"</span><span class="p">);</span>

<span class="k">CREATE</span> <span class="n">VIRTUAL</span> <span class="k">TABLE</span> <span class="nv">"search"</span> <span class="k">USING</span> <span class="n">fts4</span><span class="p">(</span><span class="nv">"text"</span><span class="p">,</span> <span class="nv">"otherText"</span><span class="p">,</span> <span class="n">tokenize</span><span class="o">=</span><span class="n">porter</span><span class="p">)</span>

<span class="cm">/* search(text,otherText) */</span><span class="p">;</span>

<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">IF</span> <span class="k">NOT</span> <span class="k">EXISTS</span> <span class="s1">'search_content'</span><span class="p">(</span><span class="n">docid</span> <span class="nb">INTEGER</span> <span class="k">PRIMARY</span> <span class="k">KEY</span><span class="p">,</span> <span class="s1">'c0text'</span><span class="p">,</span> <span class="s1">'c1otherText'</span><span class="p">);</span>

<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">IF</span> <span class="k">NOT</span> <span class="k">EXISTS</span> <span class="s1">'search_segments'</span><span class="p">(</span><span class="n">blockid</span> <span class="nb">INTEGER</span> <span class="k">PRIMARY</span> <span class="k">KEY</span><span class="p">,</span> <span class="n">block</span> <span class="nb">BLOB</span><span class="p">);</span>

<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">IF</span> <span class="k">NOT</span> <span class="k">EXISTS</span> <span class="s1">'search_segdir'</span><span class="p">(</span><span class="k">level</span> <span class="nb">INTEGER</span><span class="p">,</span><span class="n">idx</span> <span class="nb">INTEGER</span><span class="p">,</span><span class="n">start_block</span> <span class="nb">INTEGER</span><span class="p">,</span><span class="n">leaves_end_block</span> <span class="nb">INTEGER</span><span class="p">,</span><span class="n">end_block</span> <span class="nb">INTEGER</span><span class="p">,</span><span class="n">root</span> <span class="nb">BLOB</span><span class="p">,</span><span class="k">PRIMARY</span> <span class="k">KEY</span><span class="p">(</span><span class="k">level</span><span class="p">,</span> <span class="n">idx</span><span class="p">));</span>

<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">IF</span> <span class="k">NOT</span> <span class="k">EXISTS</span> <span class="s1">'search_docsize'</span><span class="p">(</span><span class="n">docid</span> <span class="nb">INTEGER</span> <span class="k">PRIMARY</span> <span class="k">KEY</span><span class="p">,</span> <span class="k">size</span> <span class="nb">BLOB</span><span class="p">);</span>

<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">IF</span> <span class="k">NOT</span> <span class="k">EXISTS</span> <span class="s1">'search_stat'</span><span class="p">(</span><span class="n">id</span> <span class="nb">INTEGER</span> <span class="k">PRIMARY</span> <span class="k">KEY</span><span class="p">,</span> <span class="n">value</span> <span class="nb">BLOB</span><span class="p">);</span>

<span class="k">CREATE</span> <span class="n">VIRTUAL</span> <span class="k">TABLE</span> <span class="n">tokenizer</span> <span class="k">USING</span> <span class="n">fts3tokenize</span><span class="p">(</span><span class="n">porter</span><span class="p">)</span>

<span class="cm">/* tokenizer(input,token,start,"end",position) */</span><span class="p">;</span>

<span class="k">CREATE</span> <span class="k">INDEX</span> <span class="nv">"index_frame_on_isstarred_createdat"</span> <span class="k">ON</span> <span class="nv">"frame"</span> <span class="p">(</span><span class="nv">"isStarred"</span><span class="p">,</span> <span class="nv">"createdAt"</span><span class="p">);</span>

<span class="k">CREATE</span> <span class="k">INDEX</span> <span class="nv">"index_frame_on_videoid"</span> <span class="k">ON</span> <span class="nv">"frame"</span> <span class="p">(</span><span class="nv">"videoId"</span><span class="p">);</span>

<span class="k">CREATE</span> <span class="k">INDEX</span> <span class="nv">"index_segment_on_starttime"</span> <span class="k">ON</span> <span class="nv">"segment"</span> <span class="p">(</span><span class="nv">"startTime"</span><span class="p">);</span>

<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">IF</span> <span class="k">NOT</span> <span class="k">EXISTS</span> <span class="nv">"doc_segment"</span> <span class="p">(</span><span class="nv">"docid"</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span> <span class="k">UNIQUE</span> <span class="k">REFERENCES</span> <span class="nv">"search"</span> <span class="p">(</span><span class="nv">"docid"</span><span class="p">),</span> <span class="nv">"segmentId"</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span> <span class="k">REFERENCES</span> <span class="nv">"segment"</span> <span class="p">(</span><span class="nv">"id"</span><span class="p">),</span> <span class="nv">"frameId"</span> <span class="nb">INTEGER</span> <span class="k">REFERENCES</span> <span class="nv">"frame"</span> <span class="p">(</span><span class="nv">"id"</span><span class="p">));</span>

<span class="k">CREATE</span> <span class="k">INDEX</span> <span class="nv">"index_doc_segment_on_segmentid_docid"</span> <span class="k">ON</span> <span class="nv">"doc_segment"</span> <span class="p">(</span><span class="nv">"segmentId"</span><span class="p">,</span> <span class="nv">"docid"</span><span class="p">);</span>

<span class="k">CREATE</span> <span class="k">INDEX</span> <span class="nv">"index_doc_segment_on_frameid_docid"</span> <span class="k">ON</span> <span class="nv">"doc_segment"</span> <span class="p">(</span><span class="nv">"frameId"</span><span class="p">,</span> <span class="nv">"docid"</span><span class="p">);</span>

<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">IF</span> <span class="k">NOT</span> <span class="k">EXISTS</span> <span class="nv">"segment_video"</span> <span class="p">(</span><span class="nv">"segmentId"</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span> <span class="k">REFERENCES</span> <span class="nv">"segment"</span> <span class="p">(</span><span class="nv">"id"</span><span class="p">),</span> <span class="nv">"videoId"</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span> <span class="k">REFERENCES</span> <span class="nv">"video"</span> <span class="p">(</span><span class="nv">"id"</span><span class="p">),</span> <span class="nv">"startTime"</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"endTime"</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">);</span>

<span class="k">CREATE</span> <span class="k">INDEX</span> <span class="nv">"index_segment_video_on_segmentid_starttime_endtime"</span> <span class="k">ON</span> <span class="nv">"segment_video"</span> <span class="p">(</span><span class="nv">"segmentId"</span><span class="p">,</span> <span class="nv">"startTime"</span><span class="p">,</span> <span class="nv">"endTime"</span><span class="p">);</span>

<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">IF</span> <span class="k">NOT</span> <span class="k">EXISTS</span> <span class="nv">"transcript_word"</span> <span class="p">(</span><span class="nv">"id"</span> <span class="nb">INTEGER</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="n">AUTOINCREMENT</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"segmentId"</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span> <span class="k">REFERENCES</span> <span class="nv">"segment"</span> <span class="p">(</span><span class="nv">"id"</span><span class="p">),</span> <span class="nv">"speechSource"</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"word"</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"timeOffset"</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"fullTextOffset"</span> <span class="nb">INTEGER</span><span class="p">,</span> <span class="nv">"duration"</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">);</span>

<span class="k">CREATE</span> <span class="k">INDEX</span> <span class="nv">"index_transcript_word_on_segmentid_fulltextoffset"</span> <span class="k">ON</span> <span class="nv">"transcript_word"</span> <span class="p">(</span><span class="nv">"segmentId"</span><span class="p">,</span> <span class="nv">"fullTextOffset"</span><span class="p">);</span>

<span class="k">CREATE</span> <span class="k">INDEX</span> <span class="nv">"index_segment_on_appid"</span> <span class="k">ON</span> <span class="nv">"segment"</span> <span class="p">(</span><span class="nv">"appId"</span><span class="p">);</span>

<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">IF</span> <span class="k">NOT</span> <span class="k">EXISTS</span> <span class="nv">"purge"</span> <span class="p">(</span><span class="nv">"path"</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="nv">"fileType"</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="k">UNIQUE</span> <span class="p">(</span><span class="nv">"path"</span><span class="p">,</span> <span class="nv">"fileType"</span><span class="p">));</span>

</code></pre></div></div>]]></content><author><name>Kevin Chen</name></author><category term="starred" /><summary type="html"><![CDATA[This ML-powered app records your entire screen, yet “doesn’t tax system resources.” How did they do it?]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kevinchen.co/blog/rewind-app/rewind-app-blog-thumbnail.jpg" /><media:content medium="image" url="https://kevinchen.co/blog/rewind-app/rewind-app-blog-thumbnail.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">What’s on my ballot: November 2022 California general election</title><link href="https://kevinchen.co/blog/my-ballot-november-2022-california-general-election/" rel="alternate" type="text/html" title="What’s on my ballot: November 2022 California general election" /><published>2022-10-22T01:07:39-07:00</published><updated>2022-10-22T01:07:39-07:00</updated><id>https://kevinchen.co/blog/my-ballot-november-2022-california-general-election</id><content type="html" xml:base="https://kevinchen.co/blog/my-ballot-november-2022-california-general-election/"><![CDATA[<p>Here’s how I’m voting in the November 2022 general election. While
preparing for this election, I consulted:</p>

<ul>
  <li><a href="https://www.sfchronicle.com/projects/2022/california-voter-guide-november/">San Francisco <em>Chronicle</em> voter guide</a></li>
  <li><a href="https://www.spur.org/voter-guide/2022-11">SPUR endorsements</a></li>
  <li><a href="https://growsf.org/voter-guide/">GrowSF voter guide</a>, which links to questionnaires from many local candidates that are far more detailed than their campaign websites</li>
</ul>

<p>I also referred to <a href="/blog/my-ballot-june-2022-california-primary-election/">my research for the June 2022 primary</a>.</p>

<h2 class="no_toc" id="contents">Contents</h2>

<ul id="markdown-toc">
  <li><a href="#california-voter-nominated-offices" id="markdown-toc-california-voter-nominated-offices">California: Voter-nominated Offices</a>    <ul>
      <li><a href="#governor" id="markdown-toc-governor">Governor</a></li>
      <li><a href="#lieutenant-governor" id="markdown-toc-lieutenant-governor">Lieutenant Governor</a></li>
      <li><a href="#secretary-of-state" id="markdown-toc-secretary-of-state">Secretary of State</a></li>
      <li><a href="#controller" id="markdown-toc-controller">Controller</a></li>
      <li><a href="#treasurer" id="markdown-toc-treasurer">Treasurer</a></li>
      <li><a href="#attorney-general" id="markdown-toc-attorney-general">Attorney General</a></li>
      <li><a href="#insurance-commissioner" id="markdown-toc-insurance-commissioner">Insurance Commissioner</a></li>
      <li><a href="#board-of-equalization-district-2" id="markdown-toc-board-of-equalization-district-2">Board of Equalization, District 2</a></li>
      <li><a href="#state-assembly-district-17" id="markdown-toc-state-assembly-district-17">State Assembly, District 17</a></li>
    </ul>
  </li>
  <li><a href="#federal" id="markdown-toc-federal">Federal</a>    <ul>
      <li><a href="#united-states-senator" id="markdown-toc-united-states-senator">United States Senator</a></li>
      <li><a href="#united-states-representative-district-11" id="markdown-toc-united-states-representative-district-11">United States Representative, District 11</a></li>
    </ul>
  </li>
  <li><a href="#california-non-partisan-offices" id="markdown-toc-california-non-partisan-offices">California: Non-partisan Offices</a>    <ul>
      <li><a href="#judges" id="markdown-toc-judges">Judges</a></li>
      <li><a href="#superintendent-of-public-education" id="markdown-toc-superintendent-of-public-education">Superintendent of Public Education</a></li>
    </ul>
  </li>
  <li><a href="#san-francisco-education" id="markdown-toc-san-francisco-education">San Francisco: Education</a>    <ul>
      <li><a href="#member-board-of-education" id="markdown-toc-member-board-of-education">Member, Board of Education</a></li>
      <li><a href="#member-community-college-board-term-ending-2027" id="markdown-toc-member-community-college-board-term-ending-2027">Member, Community College Board (term ending 2027)</a></li>
      <li><a href="#member-community-college-board-term-ending-2025" id="markdown-toc-member-community-college-board-term-ending-2025">Member, Community College Board (term ending 2025)</a></li>
    </ul>
  </li>
  <li><a href="#san-francisco" id="markdown-toc-san-francisco">San Francisco</a>    <ul>
      <li><a href="#assessorrecorder" id="markdown-toc-assessorrecorder">Assessor–Recorder</a></li>
      <li><a href="#district-attorney" id="markdown-toc-district-attorney">District Attorney</a></li>
      <li><a href="#public-defender" id="markdown-toc-public-defender">Public Defender</a></li>
      <li><a href="#board-of-supervisors-district-10" id="markdown-toc-board-of-supervisors-district-10">Board of Supervisors, District 10</a></li>
    </ul>
  </li>
  <li><a href="#california-ballot-measures" id="markdown-toc-california-ballot-measures">California Ballot Measures</a>    <ul>
      <li><a href="#a-note-on-navigating-ballot-propositions" id="markdown-toc-a-note-on-navigating-ballot-propositions">A note on navigating ballot propositions</a></li>
      <li><a href="#1-constitutional-right-to-reproductive-freedom" id="markdown-toc-1-constitutional-right-to-reproductive-freedom">1: Constitutional Right to Reproductive Freedom</a></li>
      <li><a href="#26-in-person-roulette-dice-games-sports-wagering-on-tribal-lands" id="markdown-toc-26-in-person-roulette-dice-games-sports-wagering-on-tribal-lands">26: In-person Roulette, Dice Games, Sports Wagering on Tribal Lands</a></li>
      <li><a href="#27-online-sports-gambling-outside-tribal-lands" id="markdown-toc-27-online-sports-gambling-outside-tribal-lands">27: Online Sports Gambling Outside Tribal Lands</a></li>
      <li><a href="#28-additional-funding-for-arts-and-music-in-public-schools" id="markdown-toc-28-additional-funding-for-arts-and-music-in-public-schools">28: Additional Funding for Arts and Music in Public Schools</a></li>
      <li><a href="#29-on-site-licensed-medical-professional-at-kidney-dialysis-clinics" id="markdown-toc-29-on-site-licensed-medical-professional-at-kidney-dialysis-clinics">29: On-site Licensed Medical Professional at Kidney Dialysis Clinics</a></li>
      <li><a href="#30-electric-vehicle-subsidies-income-tax-above-2m" id="markdown-toc-30-electric-vehicle-subsidies-income-tax-above-2m">30: Electric Vehicle Subsidies, Income Tax above $2M</a>        <ul>
          <li><a href="#common-misconceptions" id="markdown-toc-common-misconceptions">Common Misconceptions</a></li>
        </ul>
      </li>
      <li><a href="#31-flavored-tobacco-referendum" id="markdown-toc-31-flavored-tobacco-referendum">31: Flavored Tobacco Referendum</a></li>
    </ul>
  </li>
  <li><a href="#san-francisco-ballot-measures" id="markdown-toc-san-francisco-ballot-measures">San Francisco Ballot Measures</a>    <ul>
      <li><a href="#a-city-retiree-cost-of-living-adjustment" id="markdown-toc-a-city-retiree-cost-of-living-adjustment">A: City Retiree Cost of Living Adjustment</a></li>
      <li><a href="#b-department-of-sanitation-and-streets" id="markdown-toc-b-department-of-sanitation-and-streets">B: Department of Sanitation and Streets</a></li>
      <li><a href="#c-establish-homelessness-oversight-commission" id="markdown-toc-c-establish-homelessness-oversight-commission">C: Establish Homelessness Oversight Commission</a></li>
      <li><a href="#d-streamline-affordable-housing-approval-voter-initiative" id="markdown-toc-d-streamline-affordable-housing-approval-voter-initiative">D: Streamline Affordable Housing Approval (Voter Initiative)</a></li>
      <li><a href="#e-streamline-affordable-housing-approval-board-of-supervisors" id="markdown-toc-e-streamline-affordable-housing-approval-board-of-supervisors">E: Streamline Affordable Housing Approval (Board of Supervisors)</a></li>
      <li><a href="#f-renew-library-preservation-fund" id="markdown-toc-f-renew-library-preservation-fund">F: Renew Library Preservation Fund</a></li>
      <li><a href="#g-establish-student-success-fund" id="markdown-toc-g-establish-student-success-fund">G: Establish Student Success Fund</a></li>
      <li><a href="#h-city-elections-in-even-numbered-years" id="markdown-toc-h-city-elections-in-even-numbered-years">H: City Elections in Even-numbered Years</a></li>
      <li><a href="#i-allow-motor-vehicles-on-jfk-drive-great-highway" id="markdown-toc-i-allow-motor-vehicles-on-jfk-drive-great-highway">I: Allow Motor Vehicles on JFK Drive, Great Highway</a></li>
      <li><a href="#j-recreational-use-of-jfk-drive" id="markdown-toc-j-recreational-use-of-jfk-drive">J: Recreational Use of JFK Drive</a></li>
      <li><a href="#l-reauthorize-sales-tax-for-public-transportation" id="markdown-toc-l-reauthorize-sales-tax-for-public-transportation">L: Reauthorize Sales Tax for Public Transportation</a></li>
      <li><a href="#m-vacancy-tax" id="markdown-toc-m-vacancy-tax">M: Vacancy Tax</a></li>
      <li><a href="#n-golden-gate-park-underground-parking" id="markdown-toc-n-golden-gate-park-underground-parking">N: Golden Gate Park Underground Parking</a></li>
      <li><a href="#o-city-college-parcel-tax" id="markdown-toc-o-city-college-parcel-tax">O: City College Parcel Tax</a></li>
    </ul>
  </li>
</ul>

<h2 id="california-voter-nominated-offices">California: Voter-nominated Offices</h2>

<h3 id="governor">Governor</h3>

<p>➡️ <strong>Gavin Newsom</strong></p>

<p>Newsom supports housing, although his effectiveness has been questionable. His challenger, Brian Dahle, has some questionable environmental positions on allowing oil drilling and desalination.</p>

<h3 id="lieutenant-governor">Lieutenant Governor</h3>

<p>➡️ <strong>Eleni Kounalakis</strong></p>

<p>I’m voting for Kounalakis with reservations. Kounalakis is less pro-housing than I’d prefer. For example, she <a href="https://www.sfchronicle.com/opinion/editorials/article/Endorsement-lieutenant-governor-17185630.php">prefers to reform the CEQA without legislative changes</a>.</p>

<h3 id="secretary-of-state">Secretary of State</h3>

<p>➡️ <strong>Shirley Weber</strong></p>

<p>The other candidate, Rob Bernosky, has a platform with several euphemisms, including “cleaning California’s voter rolls.”</p>

<h3 id="controller">Controller</h3>

<p>➡️ <strong>Lanhee Chen</strong></p>

<p>Unfortunately, Ron Galperin (my preferred primary candidate) didn’t make it to the general election. The candidates are now:</p>

<ul>
  <li>Malia Cohen, who leans more left compared to most Californians</li>
  <li>Lanhee Chen, who leans more right</li>
</ul>

<p>Cohen’s platform contains several social policies around healthcare, minimum wages, and gun violence that are better addressed by the legislature.</p>

<p>I’ll vote for Chen because he seems more focused on the State Controller’s watchdog role. This is a weak endorsement because Chen’s top policy priority is reducing fraud in the application processes for public benefits, such as Medi-Cal. Depending on implementation, this may also lead to fewer qualified people receiving public benefits as well.</p>

<h3 id="treasurer">Treasurer</h3>

<p>➡️ <strong>Fiona Ma</strong></p>

<h3 id="attorney-general">Attorney General</h3>

<p>➡️ <strong>Rob Bonta</strong></p>

<p>Bonta has been actively enforcing statewide housing production laws to ensure municipalities’ compliance.</p>

<h3 id="insurance-commissioner">Insurance Commissioner</h3>

<p>➡️ <em>none</em></p>

<p>Unfortunately, Marc Levine didn’t make it to the general election, leaving us with two less-then-ideal candidates:</p>

<ul>
  <li>The <a href="https://www.sfchronicle.com/opinion/editorials/article/endorsement-california-ricardo-lara-insurance-17506950.php"><em>Chronicle</em> notes</a> incument Ricardo Lara’s ethical lapses while in office, including swaying decisions to favor campaign donors.</li>
  <li>Like many other primary candidates, Robert Howell touts his background in cybersecurity rather than insurance. This should not be a selling point. It means he’s unlikely to have the domain knowledge needed to do the job effectively.</li>
</ul>

<h3 id="board-of-equalization-district-2">Board of Equalization, District 2</h3>

<p>➡️ <em>none</em></p>

<p>The <a href="https://www.sfchronicle.com/opinion/editorials/article/Endorsement-eliminate-the-board-of-equalization-17494261.php"><em>Chronicle</em> points out</a> that the Board of Equalization doesn’t do much anymore. The vast majority of responsibilities have been moved to other bodies.</p>

<h3 id="state-assembly-district-17">State Assembly, District 17</h3>

<p>➡️ <strong>Matt Haney</strong></p>

<p>Haney supports housing development. Many local governments in California
obstruct development, so it’s important to have housing advocates in the state
legislature.</p>

<h2 id="federal">Federal</h2>

<h3 id="united-states-senator">United States Senator</h3>

<p>➡️ <strong>Alex Padilla</strong> (both terms)</p>

<p>Padilla performed well as California’s Secretary of State. His no-nonsense
approach to election trust included prosecuting those setting up fake ballot
boxes.</p>

<h3 id="united-states-representative-district-11">United States Representative, District 11</h3>

<p>➡️ <strong>Nancy Pelosi</strong></p>

<p>It would be great to bring in some younger politicians so they can start
building their influence, etc. Unfortunately, Pelosi decided to run one more time.</p>

<h2 id="california-non-partisan-offices">California: Non-partisan Offices</h2>

<h3 id="judges">Judges</h3>

<p>➡️ <strong>Yes to all</strong></p>

<p>Philosophically, I don’t think it makes sense to elect judges as they are supposed to be appointed by the executive. But given that this is the system we have in California, I treat these retention elections like recalls: keep the candidate in office except in cases of serious ethical lapses.</p>

<h3 id="superintendent-of-public-education">Superintendent of Public Education</h3>

<p>➡️ <strong>Tony Thurmond</strong></p>

<p>Thurmond’s track record isn’t great and he received endorsements from problematic Californian teachers’ unions. However, Lance Christensen supported school vouchers in a <a href="https://www.sfchronicle.com/opinion/editorials/article/endorsement-tony-thurmond-california-17494025.php"><em>Chronicle</em> endorsement</a> interview. This position is far to the right of most Californians.</p>

<h2 id="san-francisco-education">San Francisco: Education</h2>

<h3 id="member-board-of-education">Member, Board of Education</h3>

<p>➡️ <strong>Ann Hsu</strong><br />
➡️ <strong>Lainie Motamedi</strong><br />
➡️ <strong>Lisa Weissman-Ward</strong></p>

<p>In Feburary, San Francisco voted to recall several members from the school board in a <a href="https://ballotpedia.org/San_Francisco_Unified_School_District_recall,_California_(2021-2022)">landslide election</a>. The interest group <a href="https://www.sfguardians.org/">SF Guardians</a> led the recall campaign. I consider them experts on the workings of the school board. They are now endorsing the interim, mayor-appointed candidates to be elected to the school board.</p>

<p>The candidates I didn’t select:</p>

<ul>
  <li>Gabriela López is running again in this election. Voters recalled her earlier this year with 72 percent of votes.</li>
  <li>Karen Fleshman opposed the school board recall in an <a href="https://drive.google.com/file/d/1r4phCEF3uDalhBHjt6b4g8KkAJATf7tO/view">interview with GrowSF</a>. On the bright side, she supports accelerated learning programs, such as reintroducing middle-school algebra.</li>
  <li>Alida Fisher opposed the school board recall in an <a href="https://drive.google.com/file/d/1s_JmmTk2BeeDaIeFigj9WssQ_jotnM1V/view">interview with GrowSF</a>.</li>
</ul>

<h3 id="member-community-college-board-term-ending-2027">Member, Community College Board (term ending 2027)</h3>

<p>➡️ <strong>Jill Yee</strong><br />
➡️ <strong>Marie Hurabiell</strong><br />
➡️ <strong>John Rizzo</strong></p>

<p>The City College of San Francisco continues to face a budget crisis, aging facilities, and low enrollment. It is also on verge of losing accreditation, which threatens to take away state funding and further decrease enrollment. My ideal board candidate would solve these problems by taking an analytical, detail-oriented approach and not being afraid to make big changes.</p>

<p>Making this determination isn’t easy because it’s hard to find details about the candidates’ platforms. The <em>Chronicle</em> hasn’t completed its endorsement interviews yet. Even the candidates’ own websites only have vague descriptions at best.</p>

<p>To choose these candidates, I read the entire GrowSF interview for each candidate. I didn’t consider anyone who declined to answer the questionnaire.</p>

<p>I decided to vote for these candidates:</p>

<ul>
  <li><a href="https://drive.google.com/file/d/1FWqF38IBLerVWXkmpN2Bm8MHxpw2dWDn/view">Jill Yee</a> is a former student, current professor, and dean at CCSF. The depth of her experience at CCSF, her analytical approach, and no-nonsense mindset shine in the interview. For example, she proposes addressing the college’s budget crisis by cutting low-enrollment programs to focus resources on in-demand vocational programs. Her methodology involved reading the course catalog to identify overlapping curriculum. She also understands the details of CCSF’s funding sources, which will help with the upcoming accreditation.</li>
  <li><a href="https://drive.google.com/file/d/1hd3pVCVg-DdMdRcutMXSOVosHeqtFqHU/view">Marie Hurabiell</a> has personal experience with CCSF’s problems from the students’ perspective. She also brings experience from nearly a decade on the Board of Regents for Georgetown.</li>
  <li><a href="https://drive.google.com/file/d/17FDZdn6yTOHdmXy3aFalEMyNaNdK5lX8/view">John Rizzo</a> is an incumbent board member with 15 years of experience. I believe this context will be useful to keep on the board. His top priority is accreditation.</li>
</ul>

<p>These incumbents seem fine and could be substituted for John Rizzo, although they have less experience on the board:</p>

<ul>
  <li><a href="https://drive.google.com/file/d/1Q4GIWBGU1uIX1lNXmbT-tWrS1So1uTG9/view">Brigitte Davila</a> is an incumbent board member with seven years of experience. Her top priority is accreditation.</li>
  <li><a href="https://drive.google.com/file/d/1wpTGrRGcuK_w0NvGL6rNkOEZJtkfe1Xs/view">Thea Selby</a> is an incumbent board member with seven years of experience. Her top priority is accreditation.</li>
</ul>

<p>I don’t believe these these candidates are qualified:</p>

<ul>
  <li><a href="https://drive.google.com/file/d/1KNpwCZPmkqbygMISYi2E8MiDYCdotTqj/view">Jason Zeng</a>’s responses sound good, but they’re too vague to predict his concrete actions if elected.</li>
  <li><a href="https://drive.google.com/file/d/11BaCot9aCd7-thlUA0UwjfDLjWUbZZxL/view">William Walker</a> only has experience as a CCSF student. His policy positions are vague (“Identify areas where the college might be willing to shrink”). Other positions are impractical. His top priority is enrollment growth through community partnership, which can’t be achieved without accreditation.</li>
</ul>

<h3 id="member-community-college-board-term-ending-2025">Member, Community College Board (term ending 2025)</h3>

<p>➡️ <strong>Murrell Green</strong></p>

<p>No candidate responded to GrowSF’s survey. Green was recently appointed by London Breed.</p>

<h2 id="san-francisco">San Francisco</h2>

<h3 id="assessorrecorder">Assessor–Recorder</h3>

<p>➡️ <strong>Joaquín Torres</strong></p>

<p>There are no other candidates.</p>

<h3 id="district-attorney">District Attorney</h3>

<p>➡️ <strong>Brooke Jenkins</strong></p>

<p>I am generally okay with incumbent Brooke Jenkins’ policies.</p>

<p>In GrowSF interviews, challengers <a href="https://drive.google.com/file/d/1UtxzGq2z9rwug0J924nMD-xHlHB-spC0/view">Maurice Chenier</a> and <a href="https://drive.google.com/file/d/1QU4MqLtbK90NsiTvKI90GiFlPttkwGGU/view">Joe Alioto Veronese</a> could have provided more thoughtful opinions instead of buzzwords. For example, Chenier repeats “victim-based administration” to nearly every question.</p>

<p>John Hamasaki supports concealed carry, which is a non-starter. He also conducts himself poorly on social media.</p>

<h3 id="public-defender">Public Defender</h3>

<p>➡️ <strong>Mano Raju</strong></p>

<p>Both candidates come with great qualifications and seem to have reasonable viewpoints. Raju has focused on activism outside the courtroom, while challenger Rebecca Susan Feng Young wants the office to focus on trials.</p>

<h3 id="board-of-supervisors-district-10">Board of Supervisors, District 10</h3>

<p>➡️ <strong>Brian Sam Adam</strong></p>

<p>Incumbent Shamann Walton is one of the worst supervisors in SF. He opposes housing development, which is a non-starter.</p>

<p>On other issues, he holds nonsensical positions. For example, he opposed closing JFK Drive to cars on the grounds that it is <a href="https://www.sfchronicle.com/local/heatherknight/article/Golden-Gate-Park-s-main-drag-has-been-closed-to-16081726.php">“segregationist”</a> toward District 10 residents who typically drive to Golden Gate Park. He responded to criticism by doubling down, <a href="https://twitter.com/yourprotagonist/status/1392188418331840514">directly comparing the street to the Jim Crow–era South</a> during a city hearing.</p>

<p>From his <a href="https://drive.google.com/file/d/1hO-GjnVmqKQ3gW7jbx4uUpUIE2O2TpTf/view">GrowSF interview</a>, I found that I agree with challenger Brian Sam Adam on several issues:</p>

<ul>
  <li>Supports car-free JFK and car-free Great Highway on weekends</li>
  <li>Supports repealing <a href="https://en.wikipedia.org/wiki/1978_California_Proposition_13">Proposition 13</a></li>
</ul>

<p>I don’t agree with these positions on education and housing:</p>

<ul>
  <li>Opposed the school board recall</li>
  <li>Opposes accelerated learning programs</li>
  <li>Doesn’t believe upzoning will significantly improve the housing crisis</li>
  <li>Wants to limit upzoning to commercial and industrial areas</li>
</ul>

<p>It is understandable that both <a href="https://www.sfyimby.org/endorsements#h.pumfd4q70jig">SF YIMBY</a> and <a href="https://growsf.org/voter-guide/#supervisor-district-10">GrowSF</a> declined to provide an endorsement in this race. However, I’ll still vote for Adam as the candidate with more reasonable policies and public conduct.</p>

<h2 id="california-ballot-measures">California Ballot Measures</h2>

<h3 id="a-note-on-navigating-ballot-propositions">A note on navigating ballot propositions</h3>

<p><a href="https://en.wikipedia.org/wiki/California_ballot_proposition">California’s ballot proposition system</a> requires voter approval in certain situations, such as issuing bonds or amending the state constitution. Voters can also introduce new laws and veto laws already passed by the legislature. Ballot measures can only be repealed by another ballot measure, unless otherwise specified.</p>

<p>The downside of direct democracy is that most voters are less informed than their representatives. Voters don’t spend time talking to constituents and can’t request analysis from staffers. As a result, the proposition section of the ballot has become a prime target of astroturfing campaigns and populist policies like <a href="https://en.wikipedia.org/wiki/1978_California_Proposition_13">Proposition 13</a>.</p>

<p>Because of its tendency to produce bad ideas and make them hard to undo, my heuristic is to vote “no” by default. I’ll also watch out for propositions that could be passed as normal legislation. They tend to be put on the ballot by special interests or astroturf campaigns looking for hard-to-repeal regulatory capture.</p>

<h3 id="1-constitutional-right-to-reproductive-freedom">1: Constitutional Right to Reproductive Freedom</h3>

<p>✅ <strong>Yes</strong></p>

<p>This proposition would amend the state constitution to make the existing reproductive rights protection unambiguous. The existing language could be susceptible to future reinterpretation by the courts, similar to what happened in <em>Dobbs v. Jackson Women’s Health Organization.</em></p>

<h3 id="26-in-person-roulette-dice-games-sports-wagering-on-tribal-lands">26: In-person Roulette, Dice Games, Sports Wagering on Tribal Lands</h3>

<p>❎ <strong>No</strong></p>

<p>Sports gambling is fine, but this could have been submitted by the legislature, as <a href="https://www.spur.org/voter-guide/2022-11/ca-prop-26-person-sports-betting">SPUR</a> and the <a href="https://www.sfchronicle.com/opinion/editorials/article/endorsement-proposition-26-27-sports-gambling-17476979.php"><em>Chronicle</em></a> note. This proposition is written by industry players with large amounts of revenue at stake. It’s likely to lead to regulatory capture.</p>

<p>For example, the tax rate is fixed at 10 percent. A good-faith attempt at policymaking would have allowed the legislature to adjust the tax rates and other parameters.</p>

<p>I’d like the industry to try again in two years. Given the size of the gambling market in California, they’re all but guaranteed to submit another ballot proposition if this one does not pass.</p>

<h3 id="27-online-sports-gambling-outside-tribal-lands">27: Online Sports Gambling Outside Tribal Lands</h3>

<p>❎ <strong>No</strong></p>

<p>Generally the same reason as 26.</p>

<p>This proposition is even more clearly trying to accelerate regulatory capture. Its primary sponsors are DraftKings and FanDuel. Gaming companies must be qualified to offer online sports betting in at least 10 states, or five states and operating 12 brick-and-mortar casinos.</p>

<h3 id="28-additional-funding-for-arts-and-music-in-public-schools">28: Additional Funding for Arts and Music in Public Schools</h3>

<p>❎ <strong>Weak No</strong></p>

<p>This measure would reallocate existing public education funding so that at least 1 percent goes toward arts and music. While increasing funding and removing local school district control are generally good ideas, I think the legislature should have more flexibility to adjust the funding amount.</p>

<h3 id="29-on-site-licensed-medical-professional-at-kidney-dialysis-clinics">29: On-site Licensed Medical Professional at Kidney Dialysis Clinics</h3>

<p>❎ <strong>No</strong></p>

<p>This measure is a rerun of 2020 Proposition 23, which was a rerun of 2018 Proposition 8. Every other year in California, we need to vote down another nonsense dialysis proposition. Sorry, I don’t make the rules…this is just how voting works now.</p>

<p>SEIU-UHW West, the union representing dialysis clinic workers, wants to increase minimum staffing levels. They have been unable to achieve this in negotiations.</p>

<p>It’s not appropriate to make these decisions through ballot propositions. The public shouldn’t be tiebreaking union negotiations. We’re also not qualified to decide whether there’s a medical reason to require a licensed professional at dialysis clinics.</p>

<h3 id="30-electric-vehicle-subsidies-income-tax-above-2m">30: Electric Vehicle Subsidies, Income Tax above $2M</h3>

<p>✅ <strong>Weak Yes</strong></p>

<p>I strongly support increasing electric vehicle incentives. The measure generally seems well designed too.</p>

<ul>
  <li>It explicitly allocates funds to low-income car owners.</li>
  <li>It balances funding between at-home charging (30 percent) and public fast-chargers (10 percent), which avoids the misconception that fast chargers are a drop-in replacement for gas stations.</li>
  <li>It provides general guidelines for programs and budget allocations, while delegating the specific implementation to public agencies.</li>
</ul>

<p>However, I don’t think all provisions need to be done through ballot propositions. The EV market is changing rapidly, including key factors like battery mineral availability and consumer interest. We don’t know how our needs will change between now and the proposition’s expiration date in 2043. It’s also unclear how much value this mesure adds beyond the federal EV rebates that take effect next year and also target low-income drivers through used car subsidies.</p>

<h4 id="common-misconceptions">Common Misconceptions</h4>

<p>I also want to correct two common misconceptions about this measure:</p>

<p><strong>It’s fine that this measure is primarily funded by Lyft.</strong> Lyft is interested because they are required to serve 90 percent of its California mileage using electric vehicles by 2030. That doesn’t necessarily mean there is something nefarious going on. Any vehicle subsidy also indirectly subsidizes Lyft’s service. Plus, all internal combustion vehicles pollute the same air regardless of whether they are on Lyft’s platform.</p>

<p><strong>This measure may not include e-bikes.</strong> Co-sponsor SPUR says it includes e-bike purchase subsidies, while the <em>Chronicle</em> says it doesn’t.</p>

<p>The <a href="https://oag.ca.gov/system/files/initiatives/pdfs/21-0037A1%20%28Electric%20Vehicle%20Funding%20%29.pdf">text of the measure</a>, section 80220, is ambiguous:</p>

<blockquote>
  <p><strong>80220. Eligible Programs.</strong></p>

  <p>Programs eligible for funding pursuant to this chapter may include, but are not limited to, those that:</p>

  <p>[…]</p>

  <p>(b) Provide block grants, grants, loans, or other incentives for zero-emission transit buses so people get to where they need to go in ZEVs.</p>

  <p>(c) Provide incentives, grants, and block grants for governments and businesses to buy medium-, heavy-duty, and off-road agricultural and construction ZEVs.</p>

  <p>(d) Provide financing assistance to help those without access to capital or high credit acquire new and used ZEVs.</p>

  <p>(e) Help people retire old polluting vehicles and replace them with new and used ZEVs or other zero-emission mobility options.</p>

  <p>[…]</p>

  <p>(h) Increase access to clean mobility options, including but not limited to:</p>

  <p>(1) Electric bikes.</p>

  <p>(2) Bike-sharing.</p>

  <p>(3) Protected bike lanes.</p>

  <p>(4) Transit passes.</p>
</blockquote>

<p>Note the difference between (b) through (d), which explicitly call out financial incentives and assistance for ZEVs, which are defined as motor vehicles that also meet the zero-emissions requirements. On the other hand, e-bikes would fall into sections (d) and (e), which use weaker language. It’s not clear to me how this will be interpreted.</p>

<h3 id="31-flavored-tobacco-referendum">31: Flavored Tobacco Referendum</h3>

<p>✅ <strong>Yes</strong></p>

<p>This proposition is a referendum of the legislature’s 2020 ban on flavored tobacco products, such as vape liquids. A yes vote will uphold the existing law and allow it to take effect.</p>

<p>Several tobacco companies <a href="https://ballotpedia.org/California_Proposition_31,_Flavored_Tobacco_Products_Ban_Referendum_(2022)#Campaign_finance">spent a total of $23 million</a> to support the repeal campaign.</p>

<h2 id="san-francisco-ballot-measures">San Francisco Ballot Measures</h2>

<h3 id="a-city-retiree-cost-of-living-adjustment">A: City Retiree Cost of Living Adjustment</h3>

<p>✅ <strong>Yes</strong></p>

<p>This provides city retirees the same COLA treatment regardless of retirement date.</p>

<p>Also, it doesn’t make sense to have a hard requirement on whether the retirement system is fully funded. This changes depending on market conditions and isn’t the best indicator of system health.</p>

<h3 id="b-department-of-sanitation-and-streets">B: Department of Sanitation and Streets</h3>

<p>✅ <strong>Yes</strong></p>

<p>This measure would move the responsibilities of the Department of Sanitation and Streets back to the Department of Public Works, partially repealing 2020 Measure B. At the time, <a href="/blog/my-ballot-november-2020-general-election/">I opposed the measure</a> because:</p>

<blockquote>
  <p>It’s not clear to me that this reorganization would fix San Francisco’s sanitation problems. For example, the argument in favor says that this will allow “data-driven cleaning.” But they haven’t shown why that practice is impossible to implement under the current organizational structure.</p>
</blockquote>

<p>Since then, we’ve learned that duplicating administrative roles comes with significant cost. The <a href="https://sfelections.sfgov.org/sites/default/files/Prop%20B%20-%20DPW%20Commission%20SAS%20Commission%20-%20VIP.pdf">Controller reports</a> that recombining the departments will save $3.5 million in FY23 and $2.5 million in FY24.</p>

<h3 id="c-establish-homelessness-oversight-commission">C: Establish Homelessness Oversight Commission</h3>

<p>❎ <strong>Weak No</strong></p>

<p>The city spends over $700 million per year to reduce homelessness. I agree that this spending needs oversight to ensure effectiveness. However, the board needs the right background to oversee the budget.</p>

<p>Based on the <a href="https://sfelections.sfgov.org/sites/default/files/HomelessnessOversightCommission_LegalText.pdf">legal text</a>, the commission members are:</p>

<ul>
  <li>Four seats appointed by the Mayor and confirmed by the Board of Supervisors, with these profiles:
    <ul>
      <li>Person who has personally experienced homelessness</li>
      <li>Service provider or advocate</li>
      <li>Mental health or substance abuse expert</li>
      <li>Neighborhood or small business association member</li>
    </ul>
  </li>
  <li>Three seats appointed by the Board of Supervisors:
    <ul>
      <li>Person who has personally experienced homelessness</li>
      <li>Service provider or advocate</li>
      <li>Social worker for homeless families with children or homeless youth</li>
    </ul>
  </li>
</ul>

<p>The commission isn’t required to have any members with experience in government or managing a $700 million budget. The current composition represents the stakeholders well, so would be more suitable for an advisory board rather than approving budgets.</p>

<h3 id="d-streamline-affordable-housing-approval-voter-initiative">D: Streamline Affordable Housing Approval (Voter Initiative)</h3>

<p>✅ <strong>Yes</strong></p>

<p>San Francisco’s long lead times for building permit approval are well known. It makes sense to streamline the approval process and protect the projects from objections through abuse of the California Environmental Quality Act review process.</p>

<h3 id="e-streamline-affordable-housing-approval-board-of-supervisors">E: Streamline Affordable Housing Approval (Board of Supervisors)</h3>

<p>❎ <strong>No</strong></p>

<p>This is a weaker version of Proposition D placed on the ballot by the Board of Supervisors so they can retain the ability to deny new housing. If both D and E pass, the proposition with the most votes will take effect.</p>

<h3 id="f-renew-library-preservation-fund">F: Renew Library Preservation Fund</h3>

<p>✅ <strong>Yes</strong></p>

<p>The fund provides nearly all of the library’s budget. Renewing the fund maintains the status quo of public library funding.</p>

<h3 id="g-establish-student-success-fund">G: Establish Student Success Fund</h3>

<p>❎ <strong>Weak No</strong></p>

<p>I generally support using excess property taxes to fund public schools. I’m not sure this measure is the best way to increase funding.</p>

<p><a href="https://www.spur.org/voter-guide/2022-11/sf-prop-g-student-success-measure">SPUR’s analysis</a> summarizes the requirements to receive funds:</p>

<blockquote>
  <p>The three minimum criteria are: that the school has a school council composed of administration, students, parents and other key stakeholders to support grant implementation, that the school has or hires a full-time community school coordinator and that the school agrees to coordinate its services with the city and school district.</p>
</blockquote>

<p>Schools that meet the requirements can apply for grants up to $1 million per school. It’s not clear that this will make a difference, given the high overhead for implementation. The alternative is keeping the money in the General Fund.</p>

<h3 id="h-city-elections-in-even-numbered-years">H: City Elections in Even-numbered Years</h3>

<p>✅ <strong>Yes</strong></p>

<h3 id="i-allow-motor-vehicles-on-jfk-drive-great-highway">I: Allow Motor Vehicles on JFK Drive, Great Highway</h3>

<p>❎ <strong>No</strong></p>

<p>JFK Drive and the Great Highway have been SF’s most successful slow streets. I bike on these streets multiple times per week. The Board of Supervisors even voted to make them permanent. This measure would return these streets to the pre-pandemic status quo.</p>

<p>Additionally, the <em>Chronicle</em> points out that Proposition I would require the city to maintain indefinitely the Great Highway between Sloat and Skyline Boulevards. This segment was already scheduled to be closed next year because of natural erosion and sea level rise.</p>

<p>For more details, I found these endorsements helpful and I completely agree with them:</p>

<ul>
  <li><a href="https://www.spur.org/voter-guide/2022-11/sf-prop-i-jfk-drive-and-great-highway-car-use">SPUR</a></li>
  <li><a href="https://www.sfchronicle.com/opinion/editorials/article/Endorsement-jfk-great-highway-proposition-I-17459451.php">San Francisco <em>Chronicle</em></a></li>
</ul>

<h3 id="j-recreational-use-of-jfk-drive">J: Recreational Use of JFK Drive</h3>

<p>✅ <strong>Yes</strong></p>

<p>This measure will reaffirm the Board of Supervisors’ decisions on JFK Drive, keeping the status quo. If both Propositions I and J pass, we need more votes on J to keep JFK Drive closed to motor vehicles.</p>

<h3 id="l-reauthorize-sales-tax-for-public-transportation">L: Reauthorize Sales Tax for Public Transportation</h3>

<p>✅ <strong>Yes</strong></p>

<p>This measure reauthorizes the existing sales tax until 2053. I’m always supportive of funding transit because it’s important for a healthy city.</p>

<h3 id="m-vacancy-tax">M: Vacancy Tax</h3>

<p>❎ <strong>No</strong></p>

<p>This measure would impose a tax on unoccupied apartments, but exempts single-family homes and duplexes for some reason. I generally support vacancy taxes to discourage homeowners withholding investment properties from the market. This implementation seems ineffective.</p>

<h3 id="n-golden-gate-park-underground-parking">N: Golden Gate Park Underground Parking</h3>

<p>✅ <strong>Yes</strong></p>

<p>The parking garage is currently operated by a private entity, which has decided to repay the construction costs by setting high prices. It currently costs <a href="http://goldengateparking.com/">$6.25 per hour on weekends</a>, which is similar to other private lots in SF but may be prohibitively high for low-income visitors.</p>

<p>The proposition would allow the city to acquire the garage, set or subsidize parking rates, while not obligating any action.</p>

<h3 id="o-city-college-parcel-tax">O: City College Parcel Tax</h3>

<p>❎ <strong>No</strong></p>

<p>While CCSF needs more funding, this parcel tax is unlikely to help in the long term. The tax is expected to raise $37 million per year. For comparison, CCSF’s anticipates <a href="https://www.ccsf.edu/sites/default/files/2022/document/2022-23-adoption-budget.pdf">$316 million in revenue</a> for 2022–2023.</p>

<p>Another budget risk a transition in the state’s apportionment formula, which will require big changes at CCSF regardless of whether Proposition O passes.</p>

<p>In FY19, California adopted a new “Student Centered Funding Formula” for apportionment. The nonpartisan <a href="https://lao.ca.gov/Publications/Report/4531#Student_Centered_Funding_Formula">Legislative Analyst’s Office describes</a> its components as:</p>

<blockquote>
  <p>(1) a base allocation linked to enrollment, (2) a supplemental allocation linked to low‑income student counts, and (3) a student success allocation linked to specified student outcomes.</p>

  <p>[…]</p>

  <p>The new funding formula included a temporary “hold harmless” provision for those districts that would have received more funding under the former apportionment formula. The intent of the hold harmless protection was to provide time for those districts to ramp down their budgets…</p>
</blockquote>

<p>CCSF enrollment has been declining since 2018–2019 which should decrease funding from the SCFF’s enrollment component. The “hold harmless” grace period delays this decline until 2024–2025.</p>]]></content><author><name>Kevin Chen</name></author><category term="election" /><summary type="html"><![CDATA[Here’s how I’m voting in the November 2022 general election. While preparing for this election, I consulted:]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kevinchen.co/blog/election-2022-11/election-2022-11-preview.png" /><media:content medium="image" url="https://kevinchen.co/blog/election-2022-11/election-2022-11-preview.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Analyzing Tesla AI Day 2022</title><link href="https://kevinchen.co/blog/tesla-ai-day-2022/" rel="alternate" type="text/html" title="Analyzing Tesla AI Day 2022" /><published>2022-10-01T21:49:21-07:00</published><updated>2022-10-01T21:49:21-07:00</updated><id>https://kevinchen.co/blog/tesla-ai-day-2022</id><content type="html" xml:base="https://kevinchen.co/blog/tesla-ai-day-2022/"><![CDATA[<p>I was fortunate enough to attend this year’s Tesla AI Day. Tesla’s architecture has always been interesting to me because their hardware limitations drive advances in their software. Relative to other players in the industry:</p>

<ul>
  <li><strong>Low-spec sensors</strong> require ML- and data-first approaches, instead of adding more sensor hardware.</li>
  <li><strong>Low-spec compute</strong> requires efficient model architectures. For example, preferring to share backbones among several tasks rather than adding more and larger task-specific models.</li>
</ul>

<p>These are good directions that will help reduce the cost of machine learning and robotics, making them more relevant for everyday applications.</p>

<figure>
    <img class="extrawide" src="/assets/blog/tesla-ai-day-2022/tesla-ai-day-2022-elon-musk-onstage.jpg" width="1200" height="675" alt="Elon Musk stands on a stage in front of a screen. He addresses a seated audience." />
    <figcaption><p>Elon Musk opens the presentation.</p></figcaption>
</figure>

<p>Below, I’ll discuss three topics that stood out to me:</p>

<ol>
  <li>An LLM-inspired lane graph regression model</li>
  <li>Running the lane model efficiently on the Autopilot computer</li>
  <li>Increasing Dojo training tile utilization</li>
</ol>

<h2 id="online-lane-graph-predictions-with-a-language-model">Online lane graph predictions with a language model</h2>

<p>Tesla described their approach for producing sparse lane graph predictions from a dense feature map produced by their 2D to 3D vision model. This is a key component in the autonomous vehicle software stack that enables the behavior system to reason about the possible actions of the vehicle and other agents.</p>

<iframe class="extrawide" width="1200" height="675" src="https://www.youtube.com/embed/ODSJsviD_SU?clip=UgkxGaN_d_LPkw9-DMQIGBz-of_7C25JSAE0&amp;clipt=ENeKwgIYh8bDAg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<blockquote>
  <p>We approached this problem like an image captioning task, where the input is a dense tensor, and the output text is predicted into a special language that we developed at Tesla for encoding lanes and their connectivities. In this language of lanes, the words and tokens are the lane positions in 3D space. The ordering of the tokens and predicted modifiers in the tokens encode the connective relationships between these lanes.</p>
</blockquote>

<p>The architecture is inspired by transformer-based language models:</p>

<figure>
    <a href="/assets/blog/tesla-ai-day-2022/tesla-ai-day-fsd-lane-model-architecture.png">
        <img class="extrawide" src="/assets/blog/tesla-ai-day-2022/tesla-ai-day-fsd-lane-model-architecture.jpg" width="1200" height="675" alt="A diagram of a language-model-like transformer model predicting tokens representing the positions of each lane in an intersection." />
    </a>
    <figcaption><p>Tesla’s lane prediction model architecture.</p></figcaption>
</figure>

<p>Producing a lane graph from the model output sentence requires less postprocessing than parsing a segmentation mask or a heatmap. The DSL directly encodes the data structure needed by downstream consumers.</p>

<p>Recent advances in large language models and stable diffusion have produced impressive results approaching the quality and creativity of human-generated text and images. While they have clear applications in creative tools, they are limited by the need to have a human interpret the generated output.</p>

<p>I think the biggest impact of generative models will be in ingesting and producing structured data, such as Tesla’s lane graph DSL. This allows the learned component to be integrated into a larger software system. The models’ impact can grow with compute capacity rather than with the number of person-hours available to prepare inputs or view outputs.</p>

<h2 id="deploying-the-lane-model-array-indexing-with-dot-products">Deploying the lane model: array indexing with dot products</h2>

<p>Deploying the language model to the vehicle posed a challenge: <a href="https://www.reddit.com/r/teslamotors/comments/acjdrt/tesla_autopilot_hw3_details/">Tesla’s neural network accelerator (“TRIP”)</a> only supports multiply–accumulate operations.</p>

<iframe class="extrawide" width="1200" height="675" src="https://www.youtube.com/embed/ODSJsviD_SU?clip=Ugkx32ZsfbVNI_lwlgcrEngoUsp8VuUhlzYG&amp;clipt=EJWU3wIY7_PgAg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<blockquote>
  <p>When we built this hardware, we kept it simple and made sure it can do one thing ridiculously fast: dense dot products. But this architecture is autoregressive and iterative. […] The here challenge was: how can we do this sparse point prediction and sparse computation on a dense dot product engine?</p>
</blockquote>

<figure>
    <a href="/assets/blog/tesla-ai-day-2022/tesla-ai-day-fsd-lane-model-lookup-table.png">
        <img class="extrawide" src="/assets/blog/tesla-ai-day-2022/tesla-ai-day-fsd-lane-model-lookup-table.jpg" width="1200" height="675" alt="A diagram of showing a matrix multiplication between an embedding table and a one-hot vector." />
    </a>
    <figcaption><p>A matrix multiplication operation selects an entry from the embedding table.</p></figcaption>
</figure>

<p>They are rewriting indexing operations as a dot product with a one-hot vector. As an illustration, here’s how they might select the second item from a 1D lookup table with four entries:</p>

\[\begin{bmatrix}
0 \\
1 \\
0 \\
0 \\
\end{bmatrix}^\intercal
\begin{bmatrix}
X_0 \\
X_1 \\
X_2 \\
X_3 \\
\end{bmatrix}\]

<p>This feels like a workaround to get the model running on existing acclerator hardware developed with dense convolutions in mind. The current Autopilot computer, HW3, shipped in 2019. It was not clear that sparse operations would become so important to vision tasks. Unlike other companies, Tesla doesn’t operate its own fleet and unlike smartphone manufacturers, Tesla does not currently have the volume to spin new chips each year. They’ve made it work with the hardware they have.</p>

<p>The downside is that this implementation wastes FLOPs linearly with the number of entries in the lookup table. They definitely contain more than four items and the illustration suggests they might have two dimensions.</p>

<p>Transformers are becoming more prevalent in Tesla’s model architecture. It will be interesting to see whether future hardware runs these models more natively.</p>

<h2 id="dojo-dealing-with-density">Dojo: Dealing with density</h2>

<p>In <a href="https://youtu.be/j0z4FweCy4M?t=6340">last year’s presentation</a>, Tesla introduced Dojo, the most unconventional system in Tesla’s tech stack.</p>

<ul>
  <li>Its D1 accelerator chips contain a custom CPU with vector instructions, SRAM, and a chip-to-chip interconnect.</li>
  <li>The chips are arranged in a 2D grid and connected with their neighbors, forming a tile.</li>
  <li>Tiles are then packaged into a rack mount to connect to <em>their</em> neighbors.</li>
  <li>Several cabinets combine to form a cluster.</li>
</ul>

<p>The overall strategy is to train arbitrarily large models by greatly increasing bandwidth and reducing latency between processors. This requires increasing density. All other design decisions are downstream of this strategy:</p>

<ul>
  <li><strong>Enclosure:</strong> 15 kW per tile, six tiles per tray. I don’t think any other accelerator comes close to this W/m³.</li>
  <li><strong>Memory:</strong> SRAM instead of DRAM for storing weights and activations (nearly 700 MB per chip). On a more traditional computer architecture, this would be like storing a program’s working set in the CPU cache.</li>
  <li><strong>Microarchitecture:</strong> The D1 accelerator chip doesn’t have much logic compared to other processors. They push the complexity to software. For example, Tesla performs static scheduling at compile time because the D1 CPU only supports in-order dispatch.</li>
</ul>

<h3 id="a-blast-from-the-past-vliw">A blast from the past: VLIW</h3>

<p>D1’s architecture reminds me of very long instruction word (VLIW) designs from the 1990s and early 2000s, such as Intel Itanium. Those processors <a href="https://en.wikipedia.org/wiki/Very_long_instruction_word#Backward_compatibility">failed in the market</a> because instruction set compatibility dominated. When it came to improving performance, software developers would rather wait for next year’s microarchitecture than recompile their binaries for a new instruction set.</p>

<p>Deep learning workloads are different. Developers are highly motivated to increase performance. So far, they have been willing to adapt to architecture changes.</p>

<h3 id="feeding-examples-to-the-d1-accelerator">Feeding examples to the D1 accelerator</h3>

<p>This year, Tesla provided additional details on how the training tiles are integrated into the rest of the datacenter.</p>

<p>There are several interface processors mounted below each tile. These standard x86 machines contain network interfaces and video decoding hardware to load training examples.</p>

<figure>
    <a href="/assets/blog/tesla-ai-day-2022/tesla-ai-day-dojo-host-interface-computer.png">
        <img class="extrawide" src="/assets/blog/tesla-ai-day-2022/tesla-ai-day-dojo-host-interface-computer.jpg" width="1200" height="675" alt="Six Dojo tiles arranged in a two by three grid above a large number of PCIe cards." />
    </a>
    <figcaption><p>Interface processor details.</p></figcaption>
</figure>

<p>Tesla faced a problem when training a video model on Dojo. The utilization of the accelerator chips was only 4 percent.</p>

<iframe class="extrawide" width="1200" height="675" src="https://www.youtube.com/embed/ODSJsviD_SU?clip=Ugkx71KaSDI-g_NoDwQWixIkGvnVIA5wYFmm&amp;clipt=EOCbigQYzPWKBA" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<blockquote>
  <p>With our dense ML compute, Dojo hosts effectively have 10x more ML compute than the GPU hosts. The data loaders running on this one host simply couldn’t keep up with all that ML hardware.</p>
</blockquote>

<p>That’s right: the Dojo tiles achieved <em>too much density</em> relative to the x86 machines keeping them fed with training examples. The Dojo tiles spent most of their time waiting for data. For the system to run efficiently, the two machine types need to have a similar throughput.</p>

<p>They developed a custom DMA over Ethernet protocol that allows adding additional, external data loading hosts to communicate with the Dojo tiles. This improved utilization to an impressive 97 percent.</p>

<figure>
    <a href="/assets/blog/tesla-ai-day-2022/tesla-ai-day-dojo-external-data-loaders.png">
        <img class="extrawide" src="/assets/blog/tesla-ai-day-2022/tesla-ai-day-dojo-external-data-loaders.jpg" width="1200" height="675" alt="A diagram of the Dojo Mesh and Data Loading Tier connected through an Ethernet switch at 10 TB/s." />
    </a>
    <figcaption><p>External data loading hosts connected over Ethernet. The diagram suggests the additional machines might be located in another rack.</p></figcaption>
</figure>

<p>However, the root cause of an unbalanced system remains unaddressed. While it is convenient to change the ratio between data loading machines and training accelerators by adding more machines to the network, I doubt this is the long-term architecture for Dojo. I would expect a future design to make the system more balanced.</p>

<hr />

<figure>
    <img class="extrawide" src="/assets/blog/tesla-ai-day-2022/tesla-ai-day-2022-cybertruck-bar.jpg" width="1200" height="675" alt="A Tesla Cybertruck suspended over a bar and bartender." />
    <figcaption><p>The Cybertruck was the only show car we weren’t allowed to approach.</p></figcaption>
</figure>]]></content><author><name>Kevin Chen</name></author><category term="starred" /><summary type="html"><![CDATA[Language models and computer architecture for self-driving]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kevinchen.co/blog/tesla-ai-day-2022/tesla-ai-day-2022-elon-musk-onstage.jpg" /><media:content medium="image" url="https://kevinchen.co/blog/tesla-ai-day-2022/tesla-ai-day-2022-elon-musk-onstage.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">What’s on my ballot: June 2022 California primary election</title><link href="https://kevinchen.co/blog/my-ballot-june-2022-california-primary-election/" rel="alternate" type="text/html" title="What’s on my ballot: June 2022 California primary election" /><published>2022-06-05T20:37:36-07:00</published><updated>2022-06-05T20:37:36-07:00</updated><id>https://kevinchen.co/blog/my-ballot-june-2022-california-primary-election</id><content type="html" xml:base="https://kevinchen.co/blog/my-ballot-june-2022-california-primary-election/"><![CDATA[<p>Here’s how I’m voting in the <a href="https://www.sos.ca.gov/elections/upcoming-elections/primary-election-june-7-2022">June 2022 primary election</a>. While
preparing for this election, I consulted the <a href="https://www.sfchronicle.com/projects/2022/california-voter-guide-june/endorsements/">San Francisco
<em>Chronicle</em></a> and <a href="https://www.spur.org/voter-guide/2022-06">SPUR</a> endorsements.</p>

<h2 class="no_toc" id="contents">Contents</h2>

<ul id="markdown-toc">
  <li><a href="#california" id="markdown-toc-california">California</a>    <ul>
      <li><a href="#governor" id="markdown-toc-governor">Governor</a></li>
      <li><a href="#lieutenant-governor" id="markdown-toc-lieutenant-governor">Lieutenant Governor</a></li>
      <li><a href="#secretary-of-state" id="markdown-toc-secretary-of-state">Secretary of State</a></li>
      <li><a href="#controller" id="markdown-toc-controller">Controller</a></li>
      <li><a href="#treasurer" id="markdown-toc-treasurer">Treasurer</a></li>
      <li><a href="#insurance-commissioner" id="markdown-toc-insurance-commissioner">Insurance Commissioner</a></li>
      <li><a href="#united-states-senator" id="markdown-toc-united-states-senator">United States Senator</a></li>
      <li><a href="#united-states-representative-district-11" id="markdown-toc-united-states-representative-district-11">United States Representative, District 11</a></li>
      <li><a href="#state-assembly-district-17" id="markdown-toc-state-assembly-district-17">State Assembly, District 17</a></li>
    </ul>
  </li>
  <li><a href="#san-francisco" id="markdown-toc-san-francisco">San Francisco</a>    <ul>
      <li><a href="#city-attorney" id="markdown-toc-city-attorney">City Attorney</a></li>
    </ul>
  </li>
  <li><a href="#san-francisco-ballot-measures" id="markdown-toc-san-francisco-ballot-measures">San Francisco Ballot Measures</a>    <ul>
      <li><a href="#navigating-ballot-propositions" id="markdown-toc-navigating-ballot-propositions">Navigating ballot propositions</a></li>
      <li><a href="#a-muni-bond" id="markdown-toc-a-muni-bond">A: MUNI Bond</a></li>
      <li><a href="#b-department-of-building-inspection-appointment-process" id="markdown-toc-b-department-of-building-inspection-appointment-process">B: Department of Building Inspection appointment process</a></li>
      <li><a href="#c-limit-recall-period" id="markdown-toc-c-limit-recall-period">C: Limit recall period</a></li>
      <li><a href="#d-office-of-victim-and-witness-rights" id="markdown-toc-d-office-of-victim-and-witness-rights">D: Office of Victim and Witness Rights</a></li>
      <li><a href="#e-behested-payments" id="markdown-toc-e-behested-payments">E: Behested payments</a></li>
      <li><a href="#f-refuse-rate-board" id="markdown-toc-f-refuse-rate-board">F: Refuse Rate Board</a></li>
      <li><a href="#g-public-health-emergency-leave" id="markdown-toc-g-public-health-emergency-leave">G: Public health emergency leave</a></li>
      <li><a href="#h-chesa-boudin-recall" id="markdown-toc-h-chesa-boudin-recall">H: Chesa Boudin recall</a></li>
    </ul>
  </li>
</ul>

<h2 id="california">California</h2>

<h3 id="governor">Governor</h3>

<p>➡️ <strong>Gavin Newsom</strong></p>

<p>Newsom supports housing, although his effectiveness has been questionable.</p>

<h3 id="lieutenant-governor">Lieutenant Governor</h3>

<p>➡️ <strong>Eleni Kounalakis</strong></p>

<p>Kounalakis is less pro-housing than I’d prefer. For example, she <a href="https://www.sfchronicle.com/opinion/editorials/article/Endorsement-lieutenant-governor-17185630.php">prefers to
reform the CEQA without legislative changes</a>. The other
candidates are not qualified.</p>

<h3 id="secretary-of-state">Secretary of State</h3>

<p>➡️ <strong>Shirley Weber</strong></p>

<p>There are no other reasonable candidates in this race. For example, many other
candidates believe in widespread voter fraud.</p>

<h3 id="controller">Controller</h3>

<p>➡️ <strong>Ron Galperin</strong></p>

<p>Galperin promises to improve the efficiency of housing and homeless programs, an
important area for California.</p>

<p>Lanhee Chen’s top policy priority is reducing fraud in the application processes
for public benefits, such as Medi-Cal. It will likely lead to more complicated
applications. This is a situation where the optimal amount of fraud is non-zero:
I prefer to accept some fraud in exchange for e.g. a lower-friction application
process that allows more people to access public benefits.</p>

<p>Steve Glazer also seems fine but has a less clear platform. Some items (gun
control) could be better handled through legislation.</p>

<h3 id="treasurer">Treasurer</h3>

<p>➡️ <strong>Fiona Ma</strong></p>

<p>Although the <em>Chronicle</em> notes Ma’s “series of scandals,” there are no other
qualified candidates.</p>

<h3 id="insurance-commissioner">Insurance Commissioner</h3>

<p>➡️ <strong>Marc Levine</strong></p>

<p>The <a href="https://www.sfchronicle.com/opinion/editorials/article/Endorsement-Ricardo-Lara-has-to-go-Vote-Marc-17138169.php"><em>Chronicle</em> notes</a> incument Ricardo Lara’s ethical lapses
while in office, including swaying decisions to favor campaign donors.</p>

<p>The other candidates are not qualified because they’re unlikely to have the
right domain knowledge for insurance.</p>

<h3 id="united-states-senator">United States Senator</h3>

<p>➡️ <strong>Alex Padilla</strong> (both terms)</p>

<p>Padilla performed well as California’s Secretary of State. His no-nonsense
approach to election trust included prosecuting those setting up fake ballot
boxes.</p>

<h3 id="united-states-representative-district-11">United States Representative, District 11</h3>

<p>➡️ <strong>Nancy Pelosi</strong></p>

<p>It would be great to bring in some younger politicians so they can start
building their influence, etc. Unfortunately, Pelosi decided to run so we’re
stuck voting for the boomer again.</p>

<h3 id="state-assembly-district-17">State Assembly, District 17</h3>

<p>➡️ <strong>Matt Haney</strong></p>

<p>Haney supports housing development. Many local governments in California
obstruct development, so it’s important to have housing advocates in the state
legislature.</p>

<h2 id="san-francisco">San Francisco</h2>

<h3 id="city-attorney">City Attorney</h3>

<p>➡️ <strong>David Chiu</strong></p>

<p>There are no other candidates.</p>

<h2 id="san-francisco-ballot-measures">San Francisco Ballot Measures</h2>

<h3 id="navigating-ballot-propositions">Navigating ballot propositions</h3>

<p>Here’s what I wrote last year about California ballot measures. Similar dynamics
appear in local elections.</p>

<blockquote>
  <p><a href="https://en.wikipedia.org/wiki/California_ballot_proposition">California’s ballot proposition system</a> requires voter approval
for certain kinds of bills, including issuing bonds, amending the state
constitution, and amending previously passed propositions. Voters can also
introduce new laws and veto laws already passed by the legislature.</p>

  <p>There is a problem with direct democracy: people typically aren’t as informed
as their representatives. Suppose there is a measure to issue $5 billion in
bonds.  How do I know that’s the right amount? Why is it not $5.1 or $4.9
billion?  Because few voters are public policy experts, the proposition
section of the ballot has become a prime target of astroturfing campaigns and
<a href="https://en.wikipedia.org/wiki/1978_California_Proposition_13">populist policies</a>.</p>

  <p>Because of its tendency to produce bad ideas and make them hard to undo, my
heuristic is to vote “no” by default, especially when the proposition in
question seems complicated or has received funding from interest groups. I’ll
also watch out for propositions that could be passed as normal legislation and
hold them to a higher standard. They tend to be put on the ballot by special
interests or astroturf campaigns trying to trick voters into passing favorable
regulation.</p>
</blockquote>

<h3 id="a-muni-bond">A: MUNI Bond</h3>

<p>✅ <strong>Yes</strong></p>

<p>MUNI is not the most efficient when spending its budget. For example, the Van
Ness BRT project overran its budget multiple times. However, funding transit is
essential for a city where not everyone owns a car. We are probably below the
optimal amount of spending on MUNI.</p>

<h3 id="b-department-of-building-inspection-appointment-process">B: Department of Building Inspection appointment process</h3>

<p>❎ <strong>No</strong></p>

<p>Although reforming the Department of Building Inspection is important, giving
the control to the Board of Supervisors seems like it could cause gridlock.</p>

<h3 id="c-limit-recall-period">C: Limit recall period</h3>

<p>❎ <strong>No</strong></p>

<p>Since I voted in favor of the recalling the three Board of Education members, I
feel that the recall is still being properly used.</p>

<h3 id="d-office-of-victim-and-witness-rights">D: Office of Victim and Witness Rights</h3>

<p>❎ <strong>No</strong></p>

<p>Although it is important to guide victims in the crime reporting process, this
does not need to be a ballot measure. If the office ends up not working out, we
would need another proposition to remove it. (Note that the responsibilities can
be changed by the Board of Supervisors. I wasn’t sure what this meant
concretely.)</p>

<h3 id="e-behested-payments">E: Behested payments</h3>

<p>❎ <strong>No</strong></p>

<p>This also doesn’t need to be a ballot measure. In the meantime, behested
donations don’t directly benefit the politician and need to be reported above a
certain threshold.</p>

<h3 id="f-refuse-rate-board">F: Refuse Rate Board</h3>

<p>✅ <strong>Yes</strong></p>

<p>The rate is currently renegotiated every five years. It seems correct to do more
frequently.</p>

<h3 id="g-public-health-emergency-leave">G: Public health emergency leave</h3>

<p>❎ <strong>No</strong></p>

<p>Although the idea makes sense — codifying COVID-19 protections for a future
pandemic — the definition of public health emergencies is too broad. For
example, SPUR notes that Spare the Air days count as emergencies even though
they are triggered fairly often.</p>

<h3 id="h-chesa-boudin-recall">H: Chesa Boudin recall</h3>

<p>❎ <strong>No</strong></p>

<p>Voters recalled three members of its school board for incompetence. Boudin’s
case is different: he ran on a progressive agenda and delivered what he
promised.</p>

<p>Boudin has also been scapegoated for property crime rates. The reality is that
it’s a complicated problem with many causes, including the SFPD and broader
trends including a high cost of living and income inequality.</p>]]></content><author><name>Kevin Chen</name></author><category term="election" /><summary type="html"><![CDATA[Here’s how I’m voting in the June 2022 primary election. While preparing for this election, I consulted the San Francisco Chronicle and SPUR endorsements.]]></summary></entry><entry><title type="html">What’s on my ballot: November 2020 general election</title><link href="https://kevinchen.co/blog/my-ballot-november-2020-general-election/" rel="alternate" type="text/html" title="What’s on my ballot: November 2020 general election" /><published>2020-11-01T18:48:06-08:00</published><updated>2020-11-01T18:48:06-08:00</updated><id>https://kevinchen.co/blog/my-ballot-november-2020-general-election</id><content type="html" xml:base="https://kevinchen.co/blog/my-ballot-november-2020-general-election/"><![CDATA[<p>Here’s how I’m voting in the November 2020 general election in San Francisco:</p>

<h2 class="no_toc" id="contents">Contents</h2>

<ul id="markdown-toc">
  <li><a href="#federal" id="markdown-toc-federal">Federal</a>    <ul>
      <li><a href="#president--vice-president" id="markdown-toc-president--vice-president">President &amp; Vice President</a></li>
      <li><a href="#us-representative-district-12" id="markdown-toc-us-representative-district-12">US Representative, District 12</a></li>
    </ul>
  </li>
  <li><a href="#california-state-legislature" id="markdown-toc-california-state-legislature">California State Legislature</a>    <ul>
      <li><a href="#state-senator-district-11" id="markdown-toc-state-senator-district-11">State Senator, District 11</a></li>
      <li><a href="#state-assembly-district-17" id="markdown-toc-state-assembly-district-17">State Assembly, District 17</a></li>
    </ul>
  </li>
  <li><a href="#san-francisco-city--county" id="markdown-toc-san-francisco-city--county">San Francisco City &amp; County</a>    <ul>
      <li><a href="#member-board-of-education" id="markdown-toc-member-board-of-education">Member, Board of Education</a></li>
      <li><a href="#member-community-college-board" id="markdown-toc-member-community-college-board">Member, Community College Board</a></li>
      <li><a href="#bart-director-district-9" id="markdown-toc-bart-director-district-9">BART Director, District 9</a></li>
    </ul>
  </li>
  <li><a href="#state-ballot-measures" id="markdown-toc-state-ballot-measures">State ballot measures</a>    <ul>
      <li><a href="#navigating-californias-ballot-proposition-system" id="markdown-toc-navigating-californias-ballot-proposition-system">Navigating California’s ballot proposition system</a></li>
      <li><a href="#14-stem-cell-research-bonds" id="markdown-toc-14-stem-cell-research-bonds">14: Stem Cell Research Bonds</a></li>
      <li><a href="#15-assess-commercial-property-tax-with-current-value" id="markdown-toc-15-assess-commercial-property-tax-with-current-value">15: Assess Commercial Property Tax with Current Value</a></li>
      <li><a href="#16-diversity-as-a-factor-in-public-employment-education-contracting" id="markdown-toc-16-diversity-as-a-factor-in-public-employment-education-contracting">16: Diversity as a Factor in Public Employment, Education, Contracting</a></li>
      <li><a href="#17-restore-right-to-vote-after-completion-of-prison-term" id="markdown-toc-17-restore-right-to-vote-after-completion-of-prison-term">17: Restore Right to Vote after Completion of Prison Term</a></li>
      <li><a href="#18-permit-17-year-olds-to-vote-in-primary" id="markdown-toc-18-permit-17-year-olds-to-vote-in-primary">18: Permit 17-Year-Olds to Vote in Primary</a></li>
      <li><a href="#19-expand-elderly-property-tax-transfer-and-reduce-property-tax-inheritance-loopholes" id="markdown-toc-19-expand-elderly-property-tax-transfer-and-reduce-property-tax-inheritance-loopholes">19: Expand Elderly Property Tax Transfer and Reduce Property Tax Inheritance Loopholes</a></li>
      <li><a href="#20-restrict-parole-for-certain-non-violent-offenses" id="markdown-toc-20-restrict-parole-for-certain-non-violent-offenses">20: Restrict Parole for Certain Non-Violent Offenses</a></li>
      <li><a href="#21-expand-rent-control" id="markdown-toc-21-expand-rent-control">21: Expand Rent Control</a></li>
      <li><a href="#22-exempt-app-based-transportation-from-employee-benefits" id="markdown-toc-22-exempt-app-based-transportation-from-employee-benefits">22: Exempt App-based Transportation from Employee Benefits</a></li>
      <li><a href="#23-requirements-for-dialysis-clinics" id="markdown-toc-23-requirements-for-dialysis-clinics">23: Requirements for Dialysis Clinics</a></li>
      <li><a href="#24-amend-consumer-privacy-laws" id="markdown-toc-24-amend-consumer-privacy-laws">24: Amend Consumer Privacy Laws</a></li>
      <li><a href="#25-referendum-on-replacing-cash-bail-with-risk-assessment-procedure" id="markdown-toc-25-referendum-on-replacing-cash-bail-with-risk-assessment-procedure">25: Referendum on Replacing Cash Bail with Risk Assessment Procedure</a></li>
    </ul>
  </li>
  <li><a href="#san-francisco-ballot-measures" id="markdown-toc-san-francisco-ballot-measures">San Francisco ballot measures</a>    <ul>
      <li><a href="#a-health-and-recovery-bonds" id="markdown-toc-a-health-and-recovery-bonds">A: Health and Recovery Bonds</a></li>
      <li><a href="#b-create-department-of-sanitation-and-streets" id="markdown-toc-b-create-department-of-sanitation-and-streets">B: Create Department of Sanitation and Streets</a></li>
      <li><a href="#c-remove-citizenship-requirement-for-members-of-city-bodies" id="markdown-toc-c-remove-citizenship-requirement-for-members-of-city-bodies">C: Remove Citizenship Requirement for Members of City Bodies</a></li>
      <li><a href="#d-create-sheriff-inspector-general-and-oversight-board" id="markdown-toc-d-create-sheriff-inspector-general-and-oversight-board">D: Create Sheriff Inspector General and Oversight Board</a></li>
      <li><a href="#e-remove-police-department-minimum-staffing-requirement" id="markdown-toc-e-remove-police-department-minimum-staffing-requirement">E: Remove Police Department Minimum Staffing Requirement</a></li>
      <li><a href="#f-various-business-tax-changes" id="markdown-toc-f-various-business-tax-changes">F: Various Business Tax Changes</a></li>
      <li><a href="#g-permit-16-year-olds-to-vote-on-local-issues" id="markdown-toc-g-permit-16-year-olds-to-vote-on-local-issues">G: Permit 16-Year-Olds to Vote on Local Issues</a></li>
      <li><a href="#h-expedite-planning-process-in-commercial-districts" id="markdown-toc-h-expedite-planning-process-in-commercial-districts">H: Expedite Planning Process in Commercial Districts</a></li>
      <li><a href="#i-real-estate-transfer-tax-increase" id="markdown-toc-i-real-estate-transfer-tax-increase">I: Real Estate Transfer Tax Increase</a></li>
      <li><a href="#j-school-district-parcel-tax" id="markdown-toc-j-school-district-parcel-tax">J: School District Parcel Tax</a></li>
      <li><a href="#k-authorize-city-developed-affordable-housing" id="markdown-toc-k-authorize-city-developed-affordable-housing">K: Authorize City-developed Affordable Housing</a></li>
      <li><a href="#l-tax-companies-with-large-executive-pay-disparity" id="markdown-toc-l-tax-companies-with-large-executive-pay-disparity">L: Tax Companies with Large Executive Pay Disparity</a></li>
    </ul>
  </li>
  <li><a href="#district-ballot-measures" id="markdown-toc-district-ballot-measures">District ballot measures</a>    <ul>
      <li><a href="#rr-caltrain-sales-tax" id="markdown-toc-rr-caltrain-sales-tax">RR: Caltrain Sales Tax</a></li>
    </ul>
  </li>
</ul>

<h2 id="federal">Federal</h2>

<h3 id="president--vice-president">President &amp; Vice President</h3>

<p>➡️ <strong>Joseph R. Biden, Kamala D. Harris</strong></p>

<p>You already know why.</p>

<h3 id="us-representative-district-12">US Representative, District 12</h3>

<p>➡️ <strong>Nancy Pelosi</strong></p>

<p>Pelosi has a lot of clout in Congress. It would be against our interests to vote
her out.</p>

<h2 id="california-state-legislature">California State Legislature</h2>

<h3 id="state-senator-district-11">State Senator, District 11</h3>

<p>➡️ <strong>Scott Wiener</strong></p>

<p>Wiener is a high-profile housing advocate and housing is California’s top issue
in my opinion.</p>

<h3 id="state-assembly-district-17">State Assembly, District 17</h3>

<p>➡️ <strong>David Chiu</strong></p>

<p>The other candidate, who I won’t link here, is an extreme libertarian. Among
other things, the candidate’s platform includes the belief that “secession is a
civil right.”</p>

<h2 id="san-francisco-city--county">San Francisco City &amp; County</h2>

<h3 id="member-board-of-education">Member, Board of Education</h3>

<p>➡️ <strong>Kevine Bogess</strong><br />
➡️ <strong>Alida Fisher</strong><br />
➡️ <strong>Jenny Lam</strong><br />
➡️ <strong>Michelle Parker</strong></p>

<p>I’ll defer to the <a href="https://www.sfchronicle.com/opinion/editorials/article/Editorial-The-Chronicle-recommends-Boggess-15614291.php">San Francisco Chronicle’s
endorsements</a> on this one. Apart from the anti-vaxxer,
it’s difficult to distinguish the candidates in this race.</p>

<h3 id="member-community-college-board">Member, Community College Board</h3>

<p>➡️ <strong>Shanell Williams</strong><br />
➡️ <strong>Tom Temprano</strong><br />
➡️ <strong>Jeanette Quick</strong><br />
➡️ <strong>Marie Hurabiell</strong></p>

<p>I’m deferring again to the <a href="https://www.sfchronicle.com/opinion/editorials/article/Chronicle-recommends-Williams-Temprano-Quick-15617670.php#photo-20051748">San Francisco Chronicle’s
endorsements</a>. They’ve picked candidates with a good
mix of backgrounds: incumbents who have experience with the school board
(Williams, Temprano), a recent CCSF student (Quick), and a finance-focused
lawyer (Hurabiell).</p>

<h3 id="bart-director-district-9">BART Director, District 9</h3>

<p>➡️ <strong>Patrick Mortiere</strong></p>

<p><a href="https://www.bevandufty.com/">Bevan Dufty</a> is the incumbent and has the most
experience, but hasn’t provided any platform or proposed policies. He spent half
of his candidate statement telling a nice story about that time he personally
cleaned a BART station. I do not consider candidates unless they share
meaningful information.</p>

<p>Similarly, Michael Petrelis (no website) has only provided vague policy
proposals. To use COVID-19 as an example, the candidate’s statement says simply:
“Enhancing Covid-19 <em>[sic]</em> protections for all workers and riders.”</p>

<p>The remaining candidates are <a href="https://patrickforbart.com/">Patrick Mortiere</a> and
<a href="https://www.daveforbart.com/">David Wei Wen Young</a>. Their platforms are fairly
similar. Mortiere wants more bike-friendly stations and an expansion of
discounted fares for low-income customers. Young plans to keep drug abusers out
of BART with new fare gates, which I don’t think will really solve the problem,
and promises not to accept campaign contributions from BART vendors and unions.
Both candidates mention the need to cut spending to make up for ridership
decline, but neither offers the specifics of which items they would cut.</p>

<h2 id="state-ballot-measures">State ballot measures</h2>

<h3 id="navigating-californias-ballot-proposition-system">Navigating California’s ballot proposition system</h3>

<p><a href="https://en.wikipedia.org/wiki/California_ballot_proposition">California’s ballot proposition system</a> requires voter approval for
certain kinds of bills, including issuing bonds, amending the state
constitution, and amending previously passed propositions. Voters can also
introduce new laws and veto laws already passed by the legislature.</p>

<p>There is a problem with direct democracy: people typically aren’t as informed as
their representatives. Suppose there is a measure to issue $5 billion in bonds.
How do I know that’s the right amount? Why is it not $5.1 or $4.9 billion?
Because few voters are public policy experts, the proposition section of the
ballot has become a prime target of astroturfing campaigns and <a href="https://en.wikipedia.org/wiki/1978_California_Proposition_13">populist
policies</a>.</p>

<p>Because of its tendency to produce bad ideas and make them hard to undo, my
heuristic is to vote “no” by default, especially when the proposition in
question seems complicated or has received funding from interest groups. I’ll
also watch out for propositions that could be passed as normal legislation and
hold them to a higher standard. They tend to be put on the ballot by special
interests or astroturf campaigns trying to trick voters into passing favorable
regulation.</p>

<p>These resources can help too:</p>

<ul>
  <li><a href="https://ballotpedia.org/California_2020_ballot_propositions">Ballotpedia</a> provides neutral summaries of each
proposition, often in far greater detail than provided by the <a href="https://voterguide.sos.ca.gov/quick-reference-guide/">official
voter guide</a>. Ballotpedia also lists the top donors and their
spending, which can be used as a proxy for whether to support a proposition.</li>
  <li>Newspaper endorsements by the <a href="https://www.sfchronicle.com/projects/2020/voter-guide/endorsements/">San Francisco Chronicle</a> and
the <a href="https://www.latimes.com/opinion/story/2020-09-09/la-times-endorsements-november-2020-election-trump-biden">Los Angeles Times</a>.</li>
  <li><a href="https://www.spur.org/voter-guide/san-francisco-2020-11">SPUR</a>, a member-funded think tank that publishes in-depth analyses
with citations. Although they are opinionated, they also do a great job of
summarizing the context surrounding each issue.</li>
</ul>

<h3 id="14-stem-cell-research-bonds">14: Stem Cell Research Bonds</h3>

<p>❎ <strong>No</strong></p>

<p>Basic research, including stem-cell research, should be funded by the federal
government. I would prefer for a federal agency to fund projects on a more
granular basis, rather than as a huge lump sum by committed up front by voters.</p>

<h3 id="15-assess-commercial-property-tax-with-current-value">15: Assess Commercial Property Tax with Current Value</h3>

<p>✅ <strong>Yes</strong></p>

<p>California’s <a href="https://en.wikipedia.org/wiki/1978_California_Proposition_13">Proposition 13</a> is one of the worst tax policies in
history. It effectively locks in the property tax rate based on the sale price,
not the fair market value of the property. This simple rule causes a variety of
poor second-order effects in the residential market, including:</p>

<ul>
  <li>Making California more dependent on income and sales taxes, which are more
volatile.</li>
  <li>Regressive tax: the homeowners whose property values have increased the most
are taxed the least.</li>
  <li>Incentivizes holding onto property and increases “tenure” of homeowners
trying to avoid property tax reassessment. This reduces the market’s ability
to increase housing supply in popular areas, such as Silicon Valley, because
current residents don’t want to move out. For commercial properties, it
incentivizes businesses to structure their property deals to avoid
transferring ownership.</li>
</ul>

<p>Proposition 15 would phase out this policy for commercial properties worth more
than $3 million, starting in 2022. It’s an important first step in reversing the
harms done by Proposition 13.</p>

<h3 id="16-diversity-as-a-factor-in-public-employment-education-contracting">16: Diversity as a Factor in Public Employment, Education, Contracting</h3>

<p>✅ <strong>Yes</strong></p>

<p>In 1996, <a href="https://en.wikipedia.org/wiki/1996_California_Proposition_209">Proposition 209</a> banned affirmative action in public
employment, contracting, and education by constitutional amendment. This
proposition would allow public entities in the state to use affirmative action,
while not mandating that they do so.</p>

<p>To me, this seems like an appropriate cleanup of legislation that should not
have been created through propositions.</p>

<h3 id="17-restore-right-to-vote-after-completion-of-prison-term">17: Restore Right to Vote after Completion of Prison Term</h3>

<p>✅ <strong>Yes</strong></p>

<p>Currently, they must finish parole to be allowed to vote again. I’m in favor of
anything that expands the right to vote.</p>

<h3 id="18-permit-17-year-olds-to-vote-in-primary">18: Permit 17-Year-Olds to Vote in Primary</h3>

<p>✅ <strong>Yes</strong></p>

<p>This proposition would allow underage voters to vote in a primary or special
election if they would turn 18 by the date of the following general election. It
seems like an easy way to increase young voters’ engagement.</p>

<h3 id="19-expand-elderly-property-tax-transfer-and-reduce-property-tax-inheritance-loopholes">19: Expand Elderly Property Tax Transfer and Reduce Property Tax Inheritance Loopholes</h3>

<p>❎ <strong>No</strong></p>

<p>This proposition allows homeowners who are over 55, disabled, or disaster
victims to transfer their low property tax base (from Proposition 13) to their
new primary residence without restrictions. (They must currently move within the
same county and to a home with lower value.) They would be able to transfer
their low property taxes three times instead of just once. This may reduce the
cost of moving away from a high-demand area after retirement, freeing up housing
supply, and provides a path for wildfire victims to move to a more defensible
location.</p>

<p>Parents can pass their low Prop 13 tax rates to their children through
inheritance. In exchange for giving the elderly an additional tax break, this
proposition would limit such inheritance to the primary residence.</p>

<p>On balance, it seems like we’re not getting enough reform in exchange for
increased tax breaks for older homeowners who don’t need it. Presumably, if they
really wanted to move to a cheaper area, they could use the appreciation of
their current home’s value to pay for the increased property taxes.</p>

<p>I also don’t like that this proposition is <a href="https://ballotpedia.org/California_Proposition_19,_Property_Tax_Transfers,_Exemptions,_and_Revenue_for_Wildfire_Agencies_and_Counties_Amendment_(2020)">funded by
realtors</a>, who have spent over $40 million in cash
contributions to “Yes on 19” committees. Realtors are already doing pretty well
on the regulatory capture front.</p>

<h3 id="20-restrict-parole-for-certain-non-violent-offenses">20: Restrict Parole for Certain Non-Violent Offenses</h3>

<p>❎ <strong>No</strong></p>

<p>This proposition would limit parole for some non-violent offenses, such as
shoplifting. It would also allow some misdemeanors to receive felony sentences.
In addition, it disallows early release for child sex trafficking and felony
domestic violence.</p>

<p>Passing this proposition would reverse recent progress toward reducing the
prison population, which doesn’t make sense for minor offenses yet is very
expensive for taxpayers. Disallowing early release for certain crimes can be
implemented through the normal legislative process.</p>

<p>The <a href="https://ballotpedia.org/California_Proposition_20,_Criminal_Sentencing,_Parole,_and_DNA_Collection_Initiative_(2020)">donors in favor, according to Ballotpedia,</a> are mostly
police officers’ associations, plus a $300,000 contribution from Devin Nunes.</p>

<h3 id="21-expand-rent-control">21: Expand Rent Control</h3>

<p>❎ <strong>No</strong></p>

<p>The <a href="https://en.wikipedia.org/wiki/Costa–Hawkins_Rental_Housing_Act">Costa–Hawkins Housing Act</a> limited the ways cities can
enact rent control. This proposition would amend it in the following ways:</p>

<ul>
  <li>Instead of limiting rent control to properties built before 1995, allow
cities to enact rent control on any property that was built more than 15
years ago.</li>
  <li>Allow “vacancy control,” where the city limits the increase in rent charged
to a new tenant moving into a vacant unit. Currently, cities can’t place
limits on the rent charged to new tenants.</li>
</ul>

<p>Rent control raises housing prices in the long run by reducing new housing
construction and discouraging renters from moving. In addition, there is no
reason this needs to be a proposition, which makes it difficult to undo in case
of unintended consequences in the future. Costa–Hawkins could be amended or
repealed by the legislature instead.</p>

<h3 id="22-exempt-app-based-transportation-from-employee-benefits">22: Exempt App-based Transportation from Employee Benefits</h3>

<p>❎ <strong>No</strong></p>

<p><a href="https://en.wikipedia.org/wiki/California_Assembly_Bill_5_(2019)">AB 5</a> classified many independent contractors as employees. The state
later issued hundreds of exemptions, which means AB 5 basically only applies to
gig economy apps. Now Uber, Lyft, and DoorDash are funding a proposition to
exempt themselves too.</p>

<p>In exchange, drivers would receive a wage floor set at 1.2 times the minimum
wage, compensation for work-related injuries, and health insurance
contributions.</p>

<p>The proposition could be amended by seven-eights of the legislature, rather than
requiring another proposition. This is somewhat meaningless as the threshold is
set so high that is is unlikely to be reached.</p>

<p>In my opinion, Uber, Lyft, and DoorDash employ a spectrum of drivers. Some are
more employee-like while others are more contractor-like. I disagree with AB 5’s
classification of all drivers as employees.</p>

<p>But I also don’t think a ballot proposition is the right way to address this
issue. The regulation needs to change as we learn more about the economics of
these operations. For example, Uber CEO Dara Khosrowshahi <a href="https://www.theverge.com/2020/8/19/21376009/uber-ceo-interivew-california-ab5-drivers-khosrowshahi">claimed that prices
in San Francisco would rise by 20 percent</a> if California drivers
were employees. I’d like to run the experiment and see some evidence before
making permanent changes.</p>

<p>Finally, Proposition 22 also acts as a referendum on whether <a href="https://ballotpedia.org/California_Proposition_22,_App-Based_Drivers_as_Contractors_and_Labor_Policies_Initiative_(2020)">$200 million in
campaign contributions</a> can allow an industry to write its
own regulations in California. This is an absurd amount of money. To put it into
perspective, every second YouTube ad in the past couple months has been paid for
by the Uber–Lyft–DoorDash interest group. I don’t want to reward this type of
behavior.</p>

<h3 id="23-requirements-for-dialysis-clinics">23: Requirements for Dialysis Clinics</h3>

<p>❎ <strong>No</strong></p>

<p>This is a rerun of 2018 Proposition 8. It is also the result of a dialysis
clinic labor dispute. While it sucks to be on the same side as
<a href="https://www.youtube.com/watch?v=yw_nqzVfxFQ">DaVita</a>, the question of dialysis clinic regulation
should be resolved through the normal legislative process.</p>

<h3 id="24-amend-consumer-privacy-laws">24: Amend Consumer Privacy Laws</h3>

<p>❎ <strong>No</strong></p>

<p>This proposition would expand the GDPR-like <a href="https://en.wikipedia.org/wiki/California_Consumer_Privacy_Act">California Consumer Privacy
Act</a> that passed in 2018. It’s not clear to me why this needs to be a
ballot proposition. As I mentioned in the section about Proposition 22, I
believe privacy regulation needs to change as we learn more about the effects of
Internet services.</p>

<h3 id="25-referendum-on-replacing-cash-bail-with-risk-assessment-procedure">25: Referendum on Replacing Cash Bail with Risk Assessment Procedure</h3>

<p>✅ <strong>Yes</strong></p>

<p>The current cash bail system keeps defendants in jail if they are accused, but
not convicted, of a crime and can’t come up with enough money. Being stuck in
jail can cause further financial stress, such as losing one’s job, if ultimately
proven innocent. It is a regressive tax.</p>

<p><a href="https://en.wikipedia.org/wiki/1996_California_Proposition_209">SB 10</a> ended cash bail and replaced it
with a system of risk assessments to decide whether a defendant should be
released. Risk is assessed based many factors, including whether the crime is
violent and whether the defendant has a history of violence.</p>

<p>This proposition is a referendum on SB 10. A “yes” vote puts the (already
passed) law into effect, while a “no” vote repeals the law. It was placed onto
the ballot <a href="https://thehill.com/homenews/campaign/404395-bail-bond-industry-mobilizes-against-calif-law-eliminating-cash-bail">by interest groups representing the commercial bail bond
industry</a>.</p>

<h2 id="san-francisco-ballot-measures">San Francisco ballot measures</h2>

<h3 id="a-health-and-recovery-bonds">A: Health and Recovery Bonds</h3>

<p>✅ <strong>Yes</strong></p>

<p>Allows the city to issue $488 million in bonds to pay for parks, housing/drug
services, and infrastructure. The bonds would be repaid by increasing property
taxes.</p>

<h3 id="b-create-department-of-sanitation-and-streets">B: Create Department of Sanitation and Streets</h3>

<p>❎ <strong>No</strong></p>

<p>This proposition would split operations and cleaning responsibilities into the
Department of Sanitation and Streets, while the design and construction
responsibilities remain with the Department of Public Works. It also creates two
five-member oversight commissions, one for each department. Members would be
appointed by the Board of Supervisors.</p>

<p>It’s not clear to me that this reorganization would fix San Francisco’s
sanitation problems. For example, the argument in favor says that this will
allow “data-driven cleaning.” But they haven’t shown why that practice is
impossible to implement under the current organizational structure.</p>

<h3 id="c-remove-citizenship-requirement-for-members-of-city-bodies">C: Remove Citizenship Requirement for Members of City Bodies</h3>

<p>✅ <strong>Yes</strong></p>

<p>Currently, one must be a registered voter and US citizen to serve on city boards
and commissions. This proposition would remove that requirement.</p>

<p>San Francisco has a lot of immigrants and it’s a long, difficult process to
receive US citizenship. There could exist many non-citizens who have great ideas
on how to run our city.</p>

<h3 id="d-create-sheriff-inspector-general-and-oversight-board">D: Create Sheriff Inspector General and Oversight Board</h3>

<p>✅ <strong>Yes</strong></p>

<p>I’m deferring to <a href="https://www.spur.org/voter-guide/san-francisco-2020-11/prop-d-sheriff-oversight">SPUR’s
analysis</a>
because I don’t know much about how we handle police misconduct currently.</p>

<p>This detail stuck out to me:</p>

<blockquote>
  <p>In May of 2019, the Sheriff’s Department entered into an agreement with the
Department of Police Accountability (DPA) to investigate several existing
high-profile allegations of misconduct</p>

  <p><em>[…]</em></p>

  <p>In August of 2020, the relationship between DPA and Sheriff’s Department was
modified in an effort to strengthen the provision of oversight. A key addition
to the agreement includes the ability for incarcerated people and the public
to file complaints directly with DPA, as opposed to the sheriff assigning
cases to DPA.</p>
</blockquote>

<p>It seems better to codify the agreement between the Sheriff and DPA into law.</p>

<h3 id="e-remove-police-department-minimum-staffing-requirement">E: Remove Police Department Minimum Staffing Requirement</h3>

<p>✅ <strong>Yes</strong></p>

<p>The City Charter requires San Francisco to maintain at least 1,971 police
officers. This proposition would remove the requirement.</p>

<p>Regardless of your opinions on policing, having a fixed headcount number is a
nonsensical way to make staffing decisions.</p>

<h3 id="f-various-business-tax-changes">F: Various Business Tax Changes</h3>

<p>✅ <strong>Yes</strong></p>

<p>Among other things, this proposition primarily eliminates the payroll expense
tax in exchange for increasing the gross receipts tax. The city would make an
additional $97 million per year.</p>

<p>According to <a href="https://www.spur.org/voter-guide/san-francisco-2020-11/prop-f-business-tax-changes">SPUR</a> and the <a href="https://www.sfchronicle.com/opinion/editorials/article/Editorial-San-Francisco-ballot-recommendations-15620442.php">San Francisco
Chronicle</a>, this is a surprisingly good tax reform since
the payroll tax can discourage hiring, while the gross receipts tax is
progressive to reduce the burden on small businesses. It’s part of a long-term
shift away from payroll taxes.</p>

<h3 id="g-permit-16-year-olds-to-vote-on-local-issues">G: Permit 16-Year-Olds to Vote on Local Issues</h3>

<p>✅ <strong>Yes</strong></p>

<p>I am generally in favor of expanding voting. This seems like an easy way to
increase engagement among young people.</p>

<h3 id="h-expedite-planning-process-in-commercial-districts">H: Expedite Planning Process in Commercial Districts</h3>

<p>✅ <strong>Yes</strong></p>

<p>San Francisco’s approval process is <a href="https://www.sfchronicle.com/bayarea/heatherknight/article/Bid-to-open-S-F-ice-cream-shop-turns-into-a-15614815.php">disgustingly bad</a>. It
makes sense to expedite this process and remove the notification requirement for
some uses, reducing the barrier to entry for new businesses.</p>

<h3 id="i-real-estate-transfer-tax-increase">I: Real Estate Transfer Tax Increase</h3>

<p>❎ <strong>No</strong></p>

<p>The city levies a transfer tax on real estate sales. This proposition would
increase the tax on property valued at $10+ million from approximately 3 percent
to approximately 6 percent. According to <a href="https://www.spur.org/voter-guide/san-francisco-2020-11/prop-i-transfer-tax-increase">SPUR</a>, the transfer tax
is highly volatile income source because it depends on both property value and
transaction volume. And San Francisco already has a high transfer tax rate.</p>

<p>Some proceeds would be spent on compensating landlords whose tenants didn’t pay
rent due to COVID-19 hardships. In my opinion, this spending is dubious at best.
Being a professional landlord is a speculative business: those who can’t weather
a market downturn shouldn’t get into it.</p>

<h3 id="j-school-district-parcel-tax">J: School District Parcel Tax</h3>

<p>✅ <strong>Yes</strong></p>

<p>In 2018, voters passed a tax of $320 per parcel to fund the school district. The
measure was challenged in court over the required voter threshold to pass, and
until that dispute is resolved, the school district might not be allowed to
spend the money raised by the tax.</p>

<p>This proposition reduces the tax to $288 per parcel. It also requires a
two-thirds majority to pass, which would avoid the issue with the 2018 tax.</p>

<h3 id="k-authorize-city-developed-affordable-housing">K: Authorize City-developed Affordable Housing</h3>

<p>✅ <strong>Yes</strong></p>

<p>Article 34 of California’s Constitution requires cities to receive voter
approval before constructing low-income housing projects or paying private
organizations to do so. This proposition authorizes San Francisco to construct
up to 10,000 units under Article 34.</p>

<h3 id="l-tax-companies-with-large-executive-pay-disparity">L: Tax Companies with Large Executive Pay Disparity</h3>

<p>❎ <strong>No</strong></p>

<p>This proposition increases the payroll and gross receipts tax rate of companies
whose ratio of executive to worker pay for workers in San Francisco exceeds 100
to 1. The Controller notes that this would increase tax revenues by $60 to $140
million. <a href="https://www.spur.org/voter-guide/san-francisco-2020-11/prop-l-disproportionate-ceo-pay-tax">SPUR</a> notes that the increase is small enough not to
affect the decision to do business in San Francisco and that companies may find
other ways to compensate executives.</p>

<p>I am voting no because it seems to be difficult to enforce, while not bringing
in much tax revenue or offering much of an incentive for more equal pay.</p>

<h2 id="district-ballot-measures">District ballot measures</h2>

<h3 id="rr-caltrain-sales-tax">RR: Caltrain Sales Tax</h3>

<p>✅ <strong>Yes</strong></p>

<p>The US-101 corridor is the most economically productive road in the United
States. Caltrain is essential for moving workers between San Francisco and Santa
Clara counties, which will keep the region productive after the pandemic.
Caltrain currently faces a large budget shortfall from a decline in ridership.</p>

<p>This proposition would increase sales tax by 0.125 percentage points to fund
Caltrain’s operations and upcoming electrification.</p>]]></content><author><name>Kevin Chen</name></author><category term="election" /><summary type="html"><![CDATA[Here’s how I’m voting in the November 2020 general election in San Francisco:]]></summary></entry><entry><title type="html">Speeding up code with vectorization</title><link href="https://kevinchen.co/blog/speeding-up-code-with-vectorization/" rel="alternate" type="text/html" title="Speeding up code with vectorization" /><published>2020-04-17T01:42:28-07:00</published><updated>2020-04-17T01:42:28-07:00</updated><id>https://kevinchen.co/blog/speeding-up-code-with-vectorization</id><content type="html" xml:base="https://kevinchen.co/blog/speeding-up-code-with-vectorization/"><![CDATA[<p>I’ve been writing a lot of math code with latency requirements these days. When
I talk to people about my problems, they usually suggest multithreading and
general-purpose GPU computing.</p>

<p>These both have downsides. Multithreading might not be the best option in
systems that already have a lot of concurrent workloads. And GPU computing adds
the overhead of a round-trip copy to GPU memory, which might end up increasing
latency depending on the problem size.</p>

<p>Vectorization can offer a way out. Modern CPUs have several copies of key
components like arithmetic logic units (ALUs), allowing them to execute multiple
operations in parallel — even on the same CPU.<sup id="fnref:wp-superscalar" role="doc-noteref"><a href="#fn:wp-superscalar" class="footnote" rel="footnote">1</a></sup> Developers can
take advantage of this functionality through special vector instructions.</p>

<p>For the rest of this article, I’ll use Clang 10 and Intel’s <a href="https://en.wikipedia.org/wiki/Advanced_Vector_Extensions">AVX2</a>
extension in all examples. However, the same ideas apply to other compilers and
architectures.</p>

<p>Here are the rules I’ve developed for vectorization:</p>

<h2 id="option-1-make-a-library-do-it-for-you">Option 1: Make a library do it for you</h2>

<p>If we use a library like <a href="https://eigen.tuxfamily.org/">Eigen</a>, chances are it’s already
vectorizing code for us. We can write maintainable, high-level code using
Eigen’s <code class="language-plaintext highlighter-rouge">Matrix</code> type.</p>

<p>Let’s say we want to compute the Euclidean distance between two vectors:</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;Eigen/Core&gt;</span><span class="cp">
</span>
<span class="k">using</span> <span class="n">MyVector</span> <span class="o">=</span> <span class="n">Eigen</span><span class="o">::</span><span class="n">Matrix</span><span class="o">&lt;</span><span class="kt">float</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">1</span><span class="o">&gt;</span><span class="p">;</span>

<span class="kt">float</span> <span class="nf">Distance</span><span class="p">(</span><span class="k">const</span> <span class="n">MyVector</span><span class="o">&amp;</span> <span class="n">a</span><span class="p">,</span> <span class="k">const</span> <span class="n">MyVector</span><span class="o">&amp;</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="p">(</span><span class="n">a</span> <span class="o">-</span> <span class="n">b</span><span class="p">).</span><span class="n">norm</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Eigen overloads the subtraction operator with template magic that automatically
uses vector routines when they’re enabled.<sup id="fnref:eigen-magic" role="doc-noteref"><a href="#fn:eigen-magic" class="footnote" rel="footnote">2</a></sup> On x64, passing
<code class="language-plaintext highlighter-rouge">-mavx2</code> tells the compiler that it’s safe to generate a binary that uses AVX-2
vector instructions.<sup id="fnref:eigen-vect" role="doc-noteref"><a href="#fn:eigen-vect" class="footnote" rel="footnote">3</a></sup></p>

<p>We can now check the assembly to see whether Eigen vectorized for us. If you’re
not familiar with reading x64 assembly, that’s okay. All you need to know in
this step is:</p>

<ul>
  <li>Vector instruction names begin with the letter “v.”</li>
  <li>Vector registers begin with “xmm,” “ymm,” and “zmm.”</li>
  <li>Loops are implemented using branch (“b”) or jump (“j”) instructions.</li>
</ul>

<p>Notice that the entire function uses vector instructions and doesn’t contain any
loops despite working with 16-length vectors.</p>

<p>Eigen even decided to use the <a href="https://www.felixcloutier.com/x86/sqrtss"><code class="language-plaintext highlighter-rouge">vsqrtss</code></a> instruction instead of
calling the slower <code class="language-plaintext highlighter-rouge">std::sqrt</code>, which has the appropriate error handling for
negative inputs.</p>

<div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">Distance</span><span class="p">(</span><span class="nv">Eigen</span><span class="p">::</span><span class="nv">Matrix</span><span class="o">&lt;</span><span class="nv">float</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">1</span><span class="o">&gt;</span> <span class="nv">const</span><span class="o">&amp;</span><span class="p">,</span> <span class="nv">Eigen</span><span class="p">::</span><span class="nv">Matrix</span><span class="o">&lt;</span><span class="nv">float</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">1</span><span class="o">&gt;</span> <span class="nv">const</span><span class="o">&amp;</span><span class="p">):</span>
  <span class="nf">vmovaps</span> <span class="nv">ymm0</span><span class="p">,</span> <span class="nv">ymmword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rdi</span><span class="p">]</span>
  <span class="nf">vmovaps</span> <span class="nv">ymm1</span><span class="p">,</span> <span class="nv">ymmword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rdi</span> <span class="o">+</span> <span class="mi">32</span><span class="p">]</span>
  <span class="nf">vsubps</span> <span class="nv">ymm0</span><span class="p">,</span> <span class="nv">ymm0</span><span class="p">,</span> <span class="nv">ymmword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsi</span><span class="p">]</span>
  <span class="nf">vmulps</span> <span class="nv">ymm0</span><span class="p">,</span> <span class="nv">ymm0</span><span class="p">,</span> <span class="nv">ymm0</span>
  <span class="nf">vsubps</span> <span class="nv">ymm1</span><span class="p">,</span> <span class="nv">ymm1</span><span class="p">,</span> <span class="nv">ymmword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsi</span> <span class="o">+</span> <span class="mi">32</span><span class="p">]</span>
  <span class="nf">vmulps</span> <span class="nv">ymm1</span><span class="p">,</span> <span class="nv">ymm1</span><span class="p">,</span> <span class="nv">ymm1</span>
  <span class="nf">vaddps</span> <span class="nv">ymm0</span><span class="p">,</span> <span class="nv">ymm0</span><span class="p">,</span> <span class="nv">ymm1</span>
  <span class="nf">vextractf128</span> <span class="nv">xmm1</span><span class="p">,</span> <span class="nv">ymm0</span><span class="p">,</span> <span class="mi">1</span>
  <span class="nf">vaddps</span> <span class="nv">xmm0</span><span class="p">,</span> <span class="nv">xmm0</span><span class="p">,</span> <span class="nv">xmm1</span>
  <span class="nf">vpermilpd</span> <span class="nv">xmm1</span><span class="p">,</span> <span class="nv">xmm0</span><span class="p">,</span> <span class="mi">1</span>
  <span class="nf">vaddps</span> <span class="nv">xmm0</span><span class="p">,</span> <span class="nv">xmm0</span><span class="p">,</span> <span class="nv">xmm1</span>
  <span class="nf">vmovshdup</span> <span class="nv">xmm1</span><span class="p">,</span> <span class="nv">xmm0</span>
  <span class="nf">vaddss</span> <span class="nv">xmm0</span><span class="p">,</span> <span class="nv">xmm0</span><span class="p">,</span> <span class="nv">xmm1</span>
  <span class="nf">vsqrtss</span> <span class="nv">xmm0</span><span class="p">,</span> <span class="nv">xmm0</span><span class="p">,</span> <span class="nv">xmm0</span>
  <span class="nf">vzeroupper</span>
  <span class="nf">ret</span>
</code></pre></div></div>
<p><a href="https://gcc.godbolt.org/z/dL3uMu"><em>View on Compiler Explorer</em></a></p>

<h2 id="option-2-make-the-compiler-do-it-for-you">Option 2: Make the compiler do it for you</h2>

<p>Sometimes it’s not convenient to express an algorithm using an existing library,
or maybe the code is small enough that it doesn’t justify the work to bring in
another dependency.</p>

<p>In this case, we can write a normal loop and let Clang or GCC translate it into
platform-specific vector instructions. The resulting code is extremely flexible,
because it can be understood by anyone who writes C++, no library knowledge
required. However, the compiler’s vectorizer isn’t as smart as library authors,
and there are many situations where it doesn’t generate great code.</p>

<p>This code does computes the same distance, but with a handwritten loop:</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;array&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;cmath&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;numeric&gt;</span><span class="cp">
</span>
<span class="kt">float</span> <span class="nf">Distance</span><span class="p">(</span><span class="k">const</span> <span class="kt">float</span><span class="o">*</span> <span class="n">a</span><span class="p">,</span> <span class="k">const</span> <span class="kt">float</span><span class="o">*</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// Compute the squared differences between a and b.</span>
  <span class="n">std</span><span class="o">::</span><span class="n">array</span><span class="o">&lt;</span><span class="kt">float</span><span class="p">,</span> <span class="mi">16</span><span class="o">&gt;</span> <span class="n">diffs</span><span class="p">;</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">16</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">const</span> <span class="kt">float</span> <span class="n">diff</span> <span class="o">=</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">-</span> <span class="n">b</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
    <span class="n">diffs</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">diff</span> <span class="o">*</span> <span class="n">diff</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="c1">// Sum the squares.</span>
  <span class="kt">float</span> <span class="n">dist</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">accumulate</span><span class="p">(</span><span class="n">diffs</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">diffs</span><span class="p">.</span><span class="n">end</span><span class="p">(),</span> <span class="mf">0.0</span><span class="n">f</span><span class="p">);</span>
  <span class="n">dist</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">sqrt</span><span class="p">(</span><span class="n">dist</span><span class="p">);</span>
  <span class="k">return</span> <span class="n">dist</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We can again read the assembly to find out whether the compiler could vectorize
our code. Clang 10 figured it out, mostly.</p>

<ul>
  <li>It successfully vectorized the subtraction and multiplication to compute
squared differences.</li>
  <li>Unfortunately, Clang wasn’t smart enough to use vectorize the summation of
the array of squares, generating an add (<code class="language-plaintext highlighter-rouge">vaddss</code>) instruction for each of
the 16 items.</li>
  <li>It still generates an unnecessary branch with <code class="language-plaintext highlighter-rouge">call sqrtf</code> for when the sum
of squares is negative.</li>
</ul>

<div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">Distance</span><span class="p">(</span><span class="nv">float</span> <span class="nv">const</span><span class="o">*</span><span class="p">,</span> <span class="nv">float</span> <span class="nv">const</span><span class="o">*</span><span class="p">):</span>
  <span class="nf">sub</span> <span class="nb">rsp</span><span class="p">,</span> <span class="mi">72</span>
  <span class="nf">vmovups</span> <span class="nv">ymm0</span><span class="p">,</span> <span class="nv">ymmword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rdi</span><span class="p">]</span>
  <span class="nf">vsubps</span> <span class="nv">ymm0</span><span class="p">,</span> <span class="nv">ymm0</span><span class="p">,</span> <span class="nv">ymmword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsi</span><span class="p">]</span>
  <span class="nf">vmulps</span> <span class="nv">ymm0</span><span class="p">,</span> <span class="nv">ymm0</span><span class="p">,</span> <span class="nv">ymm0</span>
  <span class="nf">vmovups</span> <span class="nv">ymmword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsp</span> <span class="o">+</span> <span class="mi">8</span><span class="p">],</span> <span class="nv">ymm0</span>
  <span class="nf">vmovups</span> <span class="nv">ymm0</span><span class="p">,</span> <span class="nv">ymmword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rdi</span> <span class="o">+</span> <span class="mi">32</span><span class="p">]</span>
  <span class="nf">vsubps</span> <span class="nv">ymm0</span><span class="p">,</span> <span class="nv">ymm0</span><span class="p">,</span> <span class="nv">ymmword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsi</span> <span class="o">+</span> <span class="mi">32</span><span class="p">]</span>
  <span class="nf">vmulps</span> <span class="nv">ymm0</span><span class="p">,</span> <span class="nv">ymm0</span><span class="p">,</span> <span class="nv">ymm0</span>
  <span class="nf">vmovups</span> <span class="nv">ymmword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsp</span> <span class="o">+</span> <span class="mi">40</span><span class="p">],</span> <span class="nv">ymm0</span>
  <span class="nf">vxorps</span> <span class="nv">xmm1</span><span class="p">,</span> <span class="nv">xmm1</span><span class="p">,</span> <span class="nv">xmm1</span>
  <span class="nf">vaddss</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="nv">xmm1</span><span class="p">,</span> <span class="kt">dword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsp</span> <span class="o">+</span> <span class="mi">8</span><span class="p">]</span>
  <span class="nf">vaddss</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="kt">dword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsp</span> <span class="o">+</span> <span class="mi">12</span><span class="p">]</span>
  <span class="nf">vaddss</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="kt">dword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsp</span> <span class="o">+</span> <span class="mi">16</span><span class="p">]</span>
  <span class="nf">vaddss</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="kt">dword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsp</span> <span class="o">+</span> <span class="mi">20</span><span class="p">]</span>
  <span class="nf">vaddss</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="kt">dword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsp</span> <span class="o">+</span> <span class="mi">24</span><span class="p">]</span>
  <span class="nf">vaddss</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="kt">dword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsp</span> <span class="o">+</span> <span class="mi">28</span><span class="p">]</span>
  <span class="nf">vaddss</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="kt">dword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsp</span> <span class="o">+</span> <span class="mi">32</span><span class="p">]</span>
  <span class="nf">vaddss</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="kt">dword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsp</span> <span class="o">+</span> <span class="mi">36</span><span class="p">]</span>
  <span class="nf">vaddss</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="kt">dword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsp</span> <span class="o">+</span> <span class="mi">40</span><span class="p">]</span>
  <span class="nf">vaddss</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="kt">dword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsp</span> <span class="o">+</span> <span class="mi">44</span><span class="p">]</span>
  <span class="nf">vaddss</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="kt">dword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsp</span> <span class="o">+</span> <span class="mi">48</span><span class="p">]</span>
  <span class="nf">vaddss</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="kt">dword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsp</span> <span class="o">+</span> <span class="mi">52</span><span class="p">]</span>
  <span class="nf">vaddss</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="kt">dword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsp</span> <span class="o">+</span> <span class="mi">56</span><span class="p">]</span>
  <span class="nf">vaddss</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="kt">dword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsp</span> <span class="o">+</span> <span class="mi">60</span><span class="p">]</span>
  <span class="nf">vaddss</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="kt">dword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsp</span> <span class="o">+</span> <span class="mi">64</span><span class="p">]</span>
  <span class="nf">vextractf128</span> <span class="nv">xmm0</span><span class="p">,</span> <span class="nv">ymm0</span><span class="p">,</span> <span class="mi">1</span>
  <span class="nf">vpermilps</span> <span class="nv">xmm0</span><span class="p">,</span> <span class="nv">xmm0</span><span class="p">,</span> <span class="mi">231</span>
  <span class="nf">vaddss</span> <span class="nv">xmm0</span><span class="p">,</span> <span class="nv">xmm2</span><span class="p">,</span> <span class="nv">xmm0</span>
  <span class="nf">vucomiss</span> <span class="nv">xmm0</span><span class="p">,</span> <span class="nv">xmm1</span>
  <span class="nf">jb</span> <span class="nv">.LBB0_2</span>
  <span class="nf">vsqrtss</span> <span class="nv">xmm0</span><span class="p">,</span> <span class="nv">xmm0</span><span class="p">,</span> <span class="nv">xmm0</span>
  <span class="nf">add</span> <span class="nb">rsp</span><span class="p">,</span> <span class="mi">72</span>
  <span class="nf">vzeroupper</span>
  <span class="nf">ret</span>
<span class="nl">.LBB0_2:</span>
  <span class="nf">vzeroupper</span>
  <span class="nf">call</span> <span class="nv">sqrtf</span>
  <span class="nf">add</span> <span class="nb">rsp</span><span class="p">,</span> <span class="mi">72</span>
  <span class="nf">ret</span>
</code></pre></div></div>

<p><a href="https://gcc.godbolt.org/z/---RGY"><em>View on Compiler Explorer</em></a></p>

<h2 id="preventing-auto-vectorizer-regressions">Preventing auto-vectorizer regressions</h2>

<p>One downside of auto-vectorization is that it’s not obvious to the reader that
the compiler is generating vector instructions instead of the usual loop. We can
consider using compiler extensions to ensure that our code stays vectorized.</p>

<p>In Clang, we write <code class="language-plaintext highlighter-rouge">#pragma clang loop vectorize(enable)</code><sup id="fnref:llvm-vect" role="doc-noteref"><a href="#fn:llvm-vect" class="footnote" rel="footnote">4</a></sup> above any
loop we want the compiler to vectorize. The pragma also emits a warning if the
code fails to vectorize. For example, someone might inadvertently prevent
vectorization by adding a print statement inside the loop. To avoid merging such
a change, we can configure our build system to treat this warning as an error.</p>

<p><a href="https://www.llvm.org/docs/Vectorizers.html">LLVM’s auto-vectorization documentation</a> has more information about
the supported flags and use cases.</p>

<h3 id="designing-data-structures-for-easier-vectorization">Designing data structures for easier vectorization</h3>

<p>When we vectorized with Eigen, the library’s organization around its <code class="language-plaintext highlighter-rouge">Matrix</code>
type made vectorization easier. This is because all the values for each operand
are stored in contiguous memory.</p>

<p>Writing normal C++ code doesn’t force us to comply with this constraint. We
could have stored each pair values from <code class="language-plaintext highlighter-rouge">a</code> and <code class="language-plaintext highlighter-rouge">b</code> as members of a struct:</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="nc">Input</span> <span class="p">{</span>
  <span class="kt">float</span> <span class="n">a</span> <span class="o">=</span> <span class="mf">0.0</span><span class="n">f</span><span class="p">;</span>
  <span class="kt">float</span> <span class="n">b</span> <span class="o">=</span> <span class="mf">0.0</span><span class="n">f</span><span class="p">;</span>
<span class="p">};</span>
<span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">Input</span><span class="o">&gt;</span> <span class="n">my_inputs</span> <span class="o">=</span> <span class="p">...;</span>
</code></pre></div></div>

<p><a href="https://gcc.godbolt.org/z/XqnTqB"><em>View on Compiler Explorer</em></a></p>

<p>The vector <code class="language-plaintext highlighter-rouge">my_inputs</code> stores the values interleaved: a, b, a, b, and so on.
These values will need to be deinterleaved when they are loaded into a vector
register so that we can have one register which only contains values from <code class="language-plaintext highlighter-rouge">a</code>,
and another with values from <code class="language-plaintext highlighter-rouge">b</code>.</p>

<p>In the best case, the compiler wastes a few instructions shuffling data around.
In the worst case, the interleaved data prevents the compiler from vectorizing
the code at all.</p>

<h2 id="option-3-write-vector-code-by-hand">Option 3: Write vector code by hand</h2>

<p>If all else fails, then we’re stuck with writing vector code by hand. This
doesn’t mean writing assembly. Compilers offer built-in functions, most of which
map directly to vector instructions.</p>

<p>The benefit of this approach is fine-grained control: for example, we can choose
faster math instructions if our algorithm can tolerate an approximate answer.
This comes at the cost of flexibility. We must reimplement the algorithm for
each target architecture. We also need to rewrite if we want the performance
improvements offered by future instruction set updates.</p>

<p>The process usually starts with opening the <a href="https://software.intel.com/sites/landingpage/IntrinsicsGuide/">Intel Intrinsics
Guide</a> to find the relevant functions. We can filter only
instructions available on our target processor, then use search to find the ones
(subtraction, multiplication, and square root) needed to implement our
algorithm.</p>

<p>This handwritten code generates the same assembly as the first example:</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;immintrin.h&gt;</span><span class="cp">
</span>
<span class="kt">float</span> <span class="nf">Distance</span><span class="p">(</span><span class="k">const</span> <span class="kt">float</span><span class="o">*</span> <span class="n">a</span><span class="p">,</span> <span class="k">const</span> <span class="kt">float</span><span class="o">*</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// Load the 16-length inputs into two 8-length registers each.</span>
  <span class="n">__m256</span> <span class="n">a1</span> <span class="o">=</span> <span class="n">_mm256_load_ps</span><span class="p">(</span><span class="n">a</span><span class="p">);</span>
  <span class="n">__m256</span> <span class="n">a2</span> <span class="o">=</span> <span class="n">_mm256_load_ps</span><span class="p">(</span><span class="n">a</span> <span class="o">+</span> <span class="mi">8</span><span class="p">);</span>
  <span class="n">__m256</span> <span class="n">b1</span> <span class="o">=</span> <span class="n">_mm256_load_ps</span><span class="p">(</span><span class="n">b</span><span class="p">);</span>
  <span class="n">__m256</span> <span class="n">b2</span> <span class="o">=</span> <span class="n">_mm256_load_ps</span><span class="p">(</span><span class="n">b</span> <span class="o">+</span> <span class="mi">8</span><span class="p">);</span>

  <span class="c1">// Take the square of difference of the first 8 items.</span>
  <span class="n">__m256</span> <span class="n">diff1</span> <span class="o">=</span> <span class="n">_mm256_sub_ps</span><span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="n">b1</span><span class="p">);</span>
  <span class="n">diff1</span> <span class="o">=</span> <span class="n">_mm256_mul_ps</span><span class="p">(</span><span class="n">diff1</span><span class="p">,</span> <span class="n">diff1</span><span class="p">);</span>
  <span class="c1">// Take the square of difference of the second 8 items.</span>
  <span class="n">__m256</span> <span class="n">diff2</span> <span class="o">=</span> <span class="n">_mm256_sub_ps</span><span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="n">b2</span><span class="p">);</span>
  <span class="n">diff2</span> <span class="o">=</span> <span class="n">_mm256_mul_ps</span><span class="p">(</span><span class="n">diff2</span><span class="p">,</span> <span class="n">diff2</span><span class="p">);</span>

  <span class="c1">// Sum the first and second differences.</span>
  <span class="n">__m256</span> <span class="n">sum_diffs</span> <span class="o">=</span> <span class="n">_mm256_add_ps</span><span class="p">(</span><span class="n">diff1</span><span class="p">,</span> <span class="n">diff2</span><span class="p">);</span>

  <span class="c1">// Split 8-length register into two 4-length registers.</span>
  <span class="n">__m128</span> <span class="n">upper</span> <span class="o">=</span> <span class="n">_mm256_extractf128_ps</span><span class="p">(</span><span class="n">sum_diffs</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
  <span class="n">__m128</span> <span class="n">lower</span> <span class="o">=</span> <span class="n">_mm256_extractf128_ps</span><span class="p">(</span><span class="n">sum_diffs</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
  <span class="c1">// Add the upper and lower sums.</span>
  <span class="n">__m128</span> <span class="n">sum1</span> <span class="o">=</span> <span class="n">_mm_add_ps</span><span class="p">(</span><span class="n">lower</span><span class="p">,</span> <span class="n">upper</span><span class="p">);</span>

  <span class="c1">// Swap the upper and lower two items.</span>
  <span class="n">__m128</span> <span class="n">sum2</span> <span class="o">=</span> <span class="n">_mm_permute_pd</span><span class="p">(</span><span class="n">sum1</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
  <span class="c1">// Add the swapped vectors.</span>
  <span class="n">sum1</span> <span class="o">=</span> <span class="n">_mm_add_ps</span><span class="p">(</span><span class="n">sum1</span><span class="p">,</span> <span class="n">sum2</span><span class="p">);</span>

  <span class="c1">// Move the 2nd item into the 1st position.</span>
  <span class="n">sum2</span> <span class="o">=</span> <span class="n">_mm_movehdup_ps</span><span class="p">(</span><span class="n">sum1</span><span class="p">);</span>
  <span class="c1">// Add the swapped vectors.</span>
  <span class="n">sum1</span> <span class="o">=</span> <span class="n">_mm_add_ps</span><span class="p">(</span><span class="n">sum1</span><span class="p">,</span> <span class="n">sum2</span><span class="p">);</span>

  <span class="c1">// Take square root.</span>
  <span class="n">sum1</span> <span class="o">=</span> <span class="n">_mm_sqrt_ps</span><span class="p">(</span><span class="n">sum1</span><span class="p">);</span>

  <span class="c1">// Extract the result to a scalar array.</span>
  <span class="kt">float</span> <span class="n">result</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span>
  <span class="n">_mm_storeu_ps</span><span class="p">(</span><span class="o">&amp;</span><span class="n">result</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">sum1</span><span class="p">);</span>
  <span class="k">return</span> <span class="n">result</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
<span class="p">}</span>
</code></pre></div></div>

<p><a href="https://gcc.godbolt.org/z/BKa6fP"><em>View on Compiler Explorer</em></a></p>

<p>It’s a good idea to keep the scalar implementation around so that a unit test
can that we didn’t introduce any bugs from manual vectorization.</p>

<p>We can also consider a portable SIMD wrapper library like <a href="https://github.com/xtensor-stack/xsimd">xsimd</a> or
<a href="https://github.com/aff3ct/MIPP">MIPP</a>. These libraries provide their own vector types, which the library
maps to architecture-specific intrinsics. Keep in mind that libraries might not
expose obscure operations that are only available on one architecture, so the
portability can have a performance cost.</p>

<h2 id="conclusion">Conclusion</h2>

<p>There are many ways to use vector instructions on modern processors. To write
the most maintainable code possible for an application, try the options in this
order:</p>

<ol>
  <li><strong>Use an existing library, like Eigen or OpenCV.</strong> The code is extremely
readable and fast, if the algorithm can be written in terms of the library’s
high-level operations.</li>
  <li><strong>Use the compiler’s auto-vectorizer.</strong> The code is mostly readable and
mostly fast, though complicated operations can confuse the compiler.</li>
  <li><strong>Write the vector code by hand.</strong> The code is tedious to write and
maintain, but can express exactly what we want.</li>
</ol>

<hr />
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:wp-superscalar" role="doc-endnote">
      <p><a href="https://en.wikipedia.org/wiki/Superscalar_processor">Superscalar processor</a>, Wikipedia. <a href="#fnref:wp-superscalar" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:eigen-magic" role="doc-endnote">
      <p>For more information on how Eigen is implemented, check out
<a href="https://eigen.tuxfamily.org/dox/TopicInsideEigenExample.html">What happens inside Eigen, on a simple example</a>. <a href="#fnref:eigen-magic" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:eigen-vect" role="doc-endnote">
      <p>See <a href="https://eigen.tuxfamily.org/index.php?title=FAQ#Vectorization">Eigen Vectorization FAQ</a> for details on how to
enable vectorization. <a href="#fnref:eigen-vect" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:llvm-vect" role="doc-endnote">
      <p><a href="https://www.llvm.org/docs/Vectorizers.html">Auto-Vectorization in LLVM</a>. <a href="#fnref:llvm-vect" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Kevin Chen</name></author><summary type="html"><![CDATA[Take advantage of vector instructions, the easy way]]></summary></entry><entry><title type="html">!!Con West 2019 Notes</title><link href="https://kevinchen.co/blog/bang-bang-con-west-notes-2019/" rel="alternate" type="text/html" title="!!Con West 2019 Notes" /><published>2019-02-23T16:51:21-08:00</published><updated>2019-02-23T16:51:21-08:00</updated><id>https://kevinchen.co/blog/bang-bang-con-west-notes-2019</id><content type="html" xml:base="https://kevinchen.co/blog/bang-bang-con-west-notes-2019/"><![CDATA[<p><a href="https://bangbangcon.com/">!!Con</a> is a conference held every spring in New York
City. It’s two days of lightning talks that can be about anything related to
computers!</p>

<p>At the beginning of last year’s !!Con, <a href="/blog/bang-bang-con-notes-2018/">I wrote</a>:</p>

<blockquote>
  <p>This conference is a great showcase of the diverse backgrounds of the NYC tech
scene. I’m really going to miss it when I move back to the Bay Area.</p>
</blockquote>

<p>Fortunately, I spoke too soon! This year, we got !!Con West, held at UC Santa
Cruz, which might be the UC campus with the most redwood trees. Here are my
notes.</p>

<p><img class="extrawide" src="/assets/blog/bang-bang-con-west-2019.jpg" width="1200" height="900" alt="A staircase from a building leads to a trail head." /></p>

<h2 class="no_toc" id="contents">Contents</h2>

<ul id="markdown-toc">
  <li><a href="#day-1-keynote" id="markdown-toc-day-1-keynote">Day 1 Keynote</a>    <ul>
      <li><a href="#the-best-parts-of-my-favorite-things" id="markdown-toc-the-best-parts-of-my-favorite-things">The Best Parts! Of My Favorite Things!</a></li>
    </ul>
  </li>
  <li><a href="#session-1" id="markdown-toc-session-1">Session 1</a>    <ul>
      <li><a href="#imus-ftw-building-imu-based-gesture-recognition" id="markdown-toc-imus-ftw-building-imu-based-gesture-recognition">IMUs FTW!! Building IMU-based gesture recognition!</a></li>
      <li><a href="#earthbounds-almost-turing-complete-text-system" id="markdown-toc-earthbounds-almost-turing-complete-text-system">EarthBound’s almost-Turing-complete text system!</a></li>
      <li><a href="#etcservices-is-made-of-people-and-also-ports" id="markdown-toc-etcservices-is-made-of-people-and-also-ports"><code class="language-plaintext highlighter-rouge">/etc/services</code> is made of people! (and also ports!)</a></li>
      <li><a href="#wheels-within-whiles-or-possibly-whiles-within-wheels" id="markdown-toc-wheels-within-whiles-or-possibly-whiles-within-wheels">“Wheels within whiles!” or possibly “Whiles within wheels!”</a></li>
    </ul>
  </li>
  <li><a href="#day-2-keynote" id="markdown-toc-day-2-keynote">Day 2 Keynote</a>    <ul>
      <li><a href="#glitch-nuggets-of-resistance" id="markdown-toc-glitch-nuggets-of-resistance">Glitch Nuggets of Resistance!</a></li>
    </ul>
  </li>
  <li><a href="#session-5" id="markdown-toc-session-5">Session 5</a>    <ul>
      <li><a href="#guiding-a-starship-with-noise-and-blinking" id="markdown-toc-guiding-a-starship-with-noise-and-blinking">Guiding a starship with noise! And blinking!</a></li>
      <li><a href="#the-secret-life-of-not-a-number" id="markdown-toc-the-secret-life-of-not-a-number">The secret life of Not-a-Number!</a></li>
      <li><a href="#hacking-lego-computer-generated-lego-instructions" id="markdown-toc-hacking-lego-computer-generated-lego-instructions">Hacking Lego! Computer generated Lego instructions!</a></li>
      <li><a href="#the-worlds-first-racing-the-beam-ray-tracer-on-discarded-fpga-hardware" id="markdown-toc-the-worlds-first-racing-the-beam-ray-tracer-on-discarded-fpga-hardware">The world’s first racing-the-beam ray tracer on discarded FPGA hardware!!!</a></li>
    </ul>
  </li>
  <li><a href="#session-6" id="markdown-toc-session-6">Session 6</a>    <ul>
      <li><a href="#robots-rockets-and-more-control-theory-in-10-minutes" id="markdown-toc-robots-rockets-and-more-control-theory-in-10-minutes">Robots, rockets, and more! Control theory in 10 minutes</a></li>
      <li><a href="#minimax-search-and-the-structure-of-cognition" id="markdown-toc-minimax-search-and-the-structure-of-cognition">Minimax search and the structure of cognition!</a></li>
      <li><a href="#postgres-plays-pokémon" id="markdown-toc-postgres-plays-pokémon">Postgres plays Pokémon!</a></li>
      <li><a href="#software-patterns-from-the-9th-century" id="markdown-toc-software-patterns-from-the-9th-century">Software patterns… from the 9th century?!!</a></li>
    </ul>
  </li>
  <li><a href="#session-7" id="markdown-toc-session-7">Session 7</a>    <ul>
      <li><a href="#how-to-throw-out-95-of-pixels-in-virtual-reality-without-anyone-noticing" id="markdown-toc-how-to-throw-out-95-of-pixels-in-virtual-reality-without-anyone-noticing">How to throw out 95% of pixels in virtual reality, without anyone noticing!!</a></li>
      <li><a href="#how-to-calculate-the-phase-of-the-moon-very-very-badly" id="markdown-toc-how-to-calculate-the-phase-of-the-moon-very-very-badly">How to calculate the phase of the moon very, very badly!</a></li>
      <li><a href="#value-your-types" id="markdown-toc-value-your-types">Value Your Types!</a></li>
      <li><a href="#the-conjuring-ransomware-edition" id="markdown-toc-the-conjuring-ransomware-edition">The Conjuring: ransomware edition!!</a></li>
    </ul>
  </li>
  <li><a href="#session-8" id="markdown-toc-session-8">Session 8</a>    <ul>
      <li><a href="#observability-in-the-kitchen-improve-your-breadmaking-skills-with-open-source-monitoring" id="markdown-toc-observability-in-the-kitchen-improve-your-breadmaking-skills-with-open-source-monitoring">Observability in the Kitchen: Improve Your Breadmaking Skills with Open-Source Monitoring!!</a></li>
      <li><a href="#computers-are-fast-but-how-come-they-sometimes-feel-slow" id="markdown-toc-computers-are-fast-but-how-come-they-sometimes-feel-slow">Computers are fast! But how come they sometimes feel slow?</a></li>
      <li><a href="#my-my-tty" id="markdown-toc-my-my-tty">My, my, TTY!</a></li>
    </ul>
  </li>
</ul>

<h2 id="day-1-keynote">Day 1 Keynote</h2>

<h3 id="the-best-parts-of-my-favorite-things">The Best Parts! Of My Favorite Things!</h3>

<p>Lynn Cyrin</p>

<ul>
  <li>Version control
    <ul>
      <li>Branches like having three thoughts at once</li>
    </ul>
  </li>
  <li>Semantic versioning
    <ul>
      <li>Quickly tell which releases are likely to be broken</li>
    </ul>
  </li>
  <li>Public and private APIs
    <ul>
      <li>Progressive disclosure: read public APIs to understand quickly; private
when you need to know the details</li>
    </ul>
  </li>
  <li>Infrastructure as code
    <ul>
      <li>Communicate changes by diffing code</li>
    </ul>
  </li>
  <li>Automated documentation generators
    <ul>
      <li>“Trick programmers into writing more” because you just write directly
into the code</li>
    </ul>
  </li>
  <li>Copy paste
    <ul>
      <li>Put all the potentially copyable code into a repo so you can copy-paste
them in the future</li>
    </ul>
  </li>
  <li>Autocomplete</li>
  <li>Postgres
    <ul>
      <li>Document model and relational model</li>
      <li>Popular so you can always get help</li>
    </ul>
  </li>
  <li>Package Managers
    <ul>
      <li>“Automated copy pasters”</li>
      <li>Node business cards: “websites but for terminals”</li>
    </ul>
  </li>
  <li>Web micro-frameworks
    <ul>
      <li>Just do one job and not try to take over the whole project</li>
    </ul>
  </li>
  <li>Web (macro-)frameworks
    <ul>
      <li>When you need most of the website done for you</li>
    </ul>
  </li>
  <li>Error contexts
    <ul>
      <li>Good messages save time</li>
    </ul>
  </li>
  <li>Containers</li>
  <li>Programming languages
    <ul>
      <li>Python: “My literal mom”</li>
      <li>Ruby on Rails: Makes your code look cool</li>
      <li>JavaScript: Learning async</li>
      <li>Rust: learn memory by fighting the borrow checker (hope you don’t have a
deadline)</li>
      <li>Golang: binaries in regular build process</li>
    </ul>
  </li>
  <li>Conclusion: tell your friends about your favorite things</li>
</ul>

<h2 id="session-1">Session 1</h2>

<h3 id="imus-ftw-building-imu-based-gesture-recognition">IMUs FTW!! Building IMU-based gesture recognition!</h3>

<p>Jennifer Wang</p>

<ul>
  <li>Harry Potter gesture recognition wand</li>
  <li>Compute to run the algorithms
    <ul>
      <li>Raspberry Pi is big but runs TensorFlow, easy to prototype</li>
      <li>Cannot run Python on Arduino</li>
    </ul>
  </li>
  <li>In hardware, experimentation costs money instead of just time</li>
</ul>

<h3 id="earthbounds-almost-turing-complete-text-system">EarthBound’s almost-Turing-complete text system!</h3>

<p>Alex Rasmussen</p>

<ul>
  <li>EarthBound’s text system
    <ul>
      <li>EarthBound is a Super Nintendo role-playing game</li>
      <li>Looks like normal game with normal text options</li>
      <li>Actually a tiny virtual machine that’s quite complicated for its job</li>
    </ul>
  </li>
  <li>Instruction set
    <ul>
      <li>Registers: working, argumentary, secondary. 4 bytes each. 2 sets of
registers that can be swapped.</li>
      <li>Normal instructions: text input/output, set flags, jump if flag set,
jump and return</li>
      <li>Game-specific: set HP/level of players, movement in the game world, show
sprites</li>
      <li>Extremely CISC: summon bicycle, summon photographer, teleport</li>
    </ul>
  </li>
  <li>Why study it?
    <ul>
      <li>Mod the game</li>
      <li>Make a patch that turns it into a totally different game</li>
      <li>Build high-level language that compiles to this virtual machine</li>
    </ul>
  </li>
</ul>

<h3 id="etcservices-is-made-of-people-and-also-ports"><code class="language-plaintext highlighter-rouge">/etc/services</code> is made of people! (and also ports!)</h3>

<p>Breanne Boland</p>

<ul>
  <li>List of services, their ports, protocols, and… email address?
    <ul>
      <li>Email addresses are the people who wrote the RFCs to reserve those ports
for their services</li>
    </ul>
  </li>
  <li>API for getting service names: can map back and forth
    <ul>
      <li><code class="language-plaintext highlighter-rouge">telnet</code> lets you use both <code class="language-plaintext highlighter-rouge">22</code> and <code class="language-plaintext highlighter-rouge">ssh</code></li>
    </ul>
  </li>
  <li>How do you get into <code class="language-plaintext highlighter-rouge">/etc/services</code>?
    <ul>
      <li>Becoming less well known because many services restrict
themselves to 80 and 443 to get through firewalls</li>
      <li>About 400 ports left – not too late!</li>
      <li>Fill out a form</li>
    </ul>
  </li>
</ul>

<h3 id="wheels-within-whiles-or-possibly-whiles-within-wheels">“Wheels within whiles!” or possibly “Whiles within wheels!”</h3>

<p>Michael Albaugh</p>

<ul>
  <li>“I’m about the same age as the stored-program electronic computer”</li>
  <li>“Could the analytical engine emulate the difference engine?”
    <ul>
      <li>Difference engine: stack of adding machines for producing math lookup
tables of 7th order polynomial approximation</li>
      <li>Analytical engine: more complex, closer to modern computers</li>
    </ul>
  </li>
  <li>Why emulate?
    <ul>
      <li>PowerPC Mac to Intel: run your old software on your new computer</li>
      <li>To learn about old computers or games that no longer exist</li>
      <li>As an easier way to study old computers even if they exist</li>
      <li>Try out future designs</li>
    </ul>
  </li>
</ul>

<p>Sadly, I had to leave early on the first day.</p>

<h2 id="day-2-keynote">Day 2 Keynote</h2>

<h3 id="glitch-nuggets-of-resistance">Glitch Nuggets of Resistance!</h3>

<p>VJ Um Amel</p>

<ul>
  <li>Data body (as opposed to real body): made of SSN, GPA, SAT score,
immigration papers, …
    <ul>
      <li>“VJ Um Amel” is the name of Laila’s data body, which is her performative
project over the past few years</li>
    </ul>
  </li>
  <li>Media theory and practice professor</li>
  <li>Design
    <ul>
      <li>Studies the artificial world</li>
      <li>Concerned with suitability for a purpose</li>
    </ul>
  </li>
  <li>Arab Spring: young people designing society through social media
    <ul>
      <li>Group of young Arab techies developing things</li>
    </ul>
  </li>
  <li>Glitch: problem in a computer system
    <ul>
      <li>Maspero incident: protesters interrupted (“glitched”) Egyptian state’s
propaganda broadcast</li>
    </ul>
  </li>
</ul>

<h2 id="session-5">Session 5</h2>

<h3 id="guiding-a-starship-with-noise-and-blinking">Guiding a starship with noise! And blinking!</h3>

<p>Simon Porter</p>

<ul>
  <li>How does New Horizons navigate to take pictures of things?
    <ul>
      <li>1.6 kbps downlink best case, 0.5 kbps worst, 6 hours one way</li>
    </ul>
  </li>
  <li>Picked the first Kuiper Belt Object they saw</li>
  <li>Determining the flyby parameters
    <ul>
      <li>Need to preprogram the flight plan</li>
      <li>Spacecraft location from radio tracking</li>
      <li>Onboard optical navigation to control perpendicular motion</li>
      <li>Don’t know the range! Guess based on limited orbit data for the object</li>
    </ul>
  </li>
  <li>Using Hubble images to find the orbit
    <ul>
      <li>Combine uncertainty of Hubble direction &amp; uncertainty of object location
in image</li>
      <li>Combine multiple observations from many amateur telescopes on earth to
get the size uncertainty down to kilometers</li>
    </ul>
  </li>
</ul>

<h3 id="the-secret-life-of-not-a-number">The secret life of Not-a-Number!</h3>

<p>Annie Cherkaev</p>

<ul>
  <li>IEEE 754 allows implementers to provide diagnostic information</li>
  <li>NaN:
    <ul>
      <li>Set all exponent bits to 1</li>
      <li>For the mantissa, set the first bit to 0 if it’s a signaling NaN, which
should cause an exception when touched</li>
      <li>Other bits can provide diagnostic information (51 bits of space for a
<code class="language-plaintext highlighter-rouge">double</code>!)</li>
    </ul>
  </li>
  <li>Use case: JavaScriptCore NaN Boxing
    <ul>
      <li>JavaScript needs to track the types of variables</li>
      <li>If valid floating point, do the computation</li>
      <li>Otherwise, look at the NaN bits which tell the type of the variable
        <ul>
          <li>Booleans will fit because only have two values</li>
          <li>Pointers: 48 bits max, so it works</li>
          <li>Integers: JavaScript arrays can only have 2<sup>32</sup> values so
they can be indexed by a 32-bit integer</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h3 id="hacking-lego-computer-generated-lego-instructions">Hacking Lego! Computer generated Lego instructions!</h3>

<p>Michael Knowles</p>

<ul>
  <li>Likes working with Lego, but unfortunately it doesn’t pay well</li>
  <li>Lego mosaics: building low bit depth images with Lego
    <ul>
      <li>Can we do this in 3D?</li>
      <li>Build a tool to convert models to Lego using voxel grid</li>
      <li>Merge voxels with the same color into taller bricks</li>
      <li>Export each layer to Lego CAD software</li>
    </ul>
  </li>
</ul>

<h3 id="the-worlds-first-racing-the-beam-ray-tracer-on-discarded-fpga-hardware">The world’s first racing-the-beam ray tracer on discarded FPGA hardware!!!</h3>

<p>Tom Verbeure</p>

<ul>
  <li>Pano Logic G1 thin client
    <ul>
      <li>Had no CPU! Everything is “in hardware” (FPGA)</li>
      <li>Very cheap because the company went out of business</li>
    </ul>
  </li>
  <li>Reverse engineer the board &amp; want to render graphics on it
    <ul>
      <li>“When you buy a big GPU, you have this urge to make it work at capacity.
It doesn’t matter what it does.”</li>
      <li>Racing the beam: Atari frame buffer had only two lines &amp; had to render
one line while the other got sent to the CRT</li>
      <li>Prototype in C code to count how many operations needed per pixel and
know how much hardware you need</li>
    </ul>
  </li>
</ul>

<h2 id="session-6">Session 6</h2>

<h3 id="robots-rockets-and-more-control-theory-in-10-minutes">Robots, rockets, and more! Control theory in 10 minutes</h3>

<p>Wesley Aptekar-Cassels</p>

<ul>
  <li>Control theory is everywhere, even non-obvious applications like queue
length in a network</li>
</ul>

<h3 id="minimax-search-and-the-structure-of-cognition">Minimax search and the structure of cognition!</h3>

<p>Zack M. Davis</p>

<ul>
  <li>Minimax search
    <ul>
      <li>Could look at all possible moves &amp; pick the one that results in the best
board position</li>
      <li>To have better long-term decision making, then search the opponent’s
best possible responses, then your best possible response, …</li>
      <li>Simple algorithm that implicitly encodes many chess behaviors</li>
    </ul>
  </li>
</ul>

<h3 id="postgres-plays-pokémon">Postgres plays Pokémon!</h3>

<p>Liz Frost</p>

<ul>
  <li>Query your Pokémon in Postgre
    <ul>
      <li>Read the Gameboy emulator memory in a Postgres extension</li>
      <li>Works because Pokémon info is stored at known addresses</li>
    </ul>
  </li>
</ul>

<h3 id="software-patterns-from-the-9th-century">Software patterns… from the 9th century?!!</h3>

<p>Michael Arntzenius</p>

<ul>
  <li>70s garbage collection paper: once half of memory is used, copy all the live
objects to the other half of memory</li>
  <li>Medieval farmers: split field into two parts, alternating back and forth to
allow farmland to recover
    <ul>
      <li>Why should it be half? Why not plant 2/3?</li>
      <li>Dependencies of this change: plant legumes to restore nitrogen, deeper
plows, horses, oats for horses…which needs better farm productivity</li>
      <li>Cycle in dependency graph</li>
    </ul>
  </li>
  <li>Not just a technological change — important social considerations
    <ul>
      <li>Rearrange field ownership to allow longer fields for easier plowing</li>
      <li>People don’t want to make a change that risks starvation</li>
      <li>Our technology can support many things, but people don’t always want to
make big changes</li>
    </ul>
  </li>
</ul>

<h2 id="session-7">Session 7</h2>

<h3 id="how-to-throw-out-95-of-pixels-in-virtual-reality-without-anyone-noticing">How to throw out 95% of pixels in virtual reality, without anyone noticing!!</h3>

<p>Amrita Mazumdar</p>

<ul>
  <li>95% of the eye’s photoreceptors are concentrated at the fovea</li>
  <li>In virtual reality, your fovea covers a small percentage of the
screen size (compared to phones)</li>
  <li>Use neural net to guess where people might look, then compress everything
else</li>
</ul>

<h3 id="how-to-calculate-the-phase-of-the-moon-very-very-badly">How to calculate the phase of the moon very, very badly!</h3>

<p>André Arko</p>

<ul>
  <li>Calendar app for werewolves required calculating the phase of the moon</li>
  <li>Look up the day of a full moon, then add 27.321661 days every time
    <ul>
      <li>Worked for awhile!</li>
      <li>2 years later, it was about 3 days wrong</li>
      <li>Using the wrong number &amp; can’t use the average</li>
    </ul>
  </li>
  <li>User interface
    <ul>
      <li>28 moon icons, so just bucket the floating point moon phase</li>
      <li>Multiple days could have full moon icon depending on rounding</li>
      <li>Solution: assign full moon icon to the day that contains the full moon</li>
    </ul>
  </li>
</ul>

<h3 id="value-your-types">Value Your Types!</h3>

<p>Eric Weinstein</p>

<ul>
  <li>Dependent type: a type whose definition <em>depends</em> on a value
    <ul>
      <li>Example: list of integers where each value is <em>larger than the value
before it</em></li>
    </ul>
  </li>
</ul>

<h3 id="the-conjuring-ransomware-edition">The Conjuring: ransomware edition!!</h3>

<p>Pranshu Bajpai</p>

<ul>
  <li>Goal: show that demonic posession (e.g. in <em>The Conjuring</em>) is similar to
randomware
    <ul>
      <li>Initial entry: always looking to (posess, infect) vulnerable hosts</li>
      <li>Needs a unique (item, encryption key) to perform the ritual
        <ul>
          <li>Symmetric key: malevolent entity uses a secret word to posess host,
where the same key is used to decrypt</li>
          <li>Some randomware uses the date and time to generate keys, which are easy
to guess</li>
        </ul>
      </li>
      <li>(Demons, malware authors) don’t like (exorcists, malware analysts)</li>
    </ul>
  </li>
  <li>Ransomware developers sell their malwre to ransomware opeartors, either
one-time fee or revenue share</li>
</ul>

<h2 id="session-8">Session 8</h2>

<h3 id="observability-in-the-kitchen-improve-your-breadmaking-skills-with-open-source-monitoring">Observability in the Kitchen: Improve Your Breadmaking Skills with Open-Source Monitoring!!</h3>

<p>Daisy Tsang</p>

<ul>
  <li>Sourdough healthier than regular bread, but requires long fermentation
process (about 1 day) and could mold if you’re not careful</li>
  <li>Prometheus: open source system monitoring server written in Go</li>
  <li>Raspberry Pi with temperature &amp; humidity sensors</li>
  <li>Write exporter for sensor to Prometheus</li>
  <li>Highly over-engineered sourdough starter process!</li>
</ul>

<h3 id="computers-are-fast-but-how-come-they-sometimes-feel-slow">Computers are fast! But how come they sometimes feel slow?</h3>

<p>Mike Lazer-Walker</p>

<ul>
  <li>Latency matters to making computers feel instant, letting people use
computers more effectively</li>
  <li>Input latency of a game depends on many things: human reaction time,
keyboard interrupt, OS, application drawing stuff, display buffer &amp; response</li>
</ul>

<h3 id="my-my-tty">My, my, TTY!</h3>

<p>Tabitha Sable</p>

<ul>
  <li>1960s–70s: paper teletypes let you write on one typewriter and print on
another connected typewriter</li>
  <li>1970s: UNIX written on PDP-11; login always local because teletype was
directly wired to server</li>
  <li>1970s–80s: video display terminals (“glass teletypes”) emulated paper
teletypes with more features, like control sequences, introducing complexity
    <ul>
      <li>BSD shipped database of terminals and supported features</li>
    </ul>
  </li>
  <li>1980: BSD added curses library, allowing easy GUI building in terminal</li>
  <li>Late 1980s: Stanford University Network workstation (or <em>SUN</em> for short),
Macs, Windows PCs, X11 killed video terminals by bundling terminal emulator
apps
    <ul>
      <li>New features: setting title, colors, mouse clicks</li>
      <li>“If you absent mindedly click on the menu, it actually works, which was
kind of horrifying to me the first time it happened”</li>
    </ul>
  </li>
  <li>People switch from telnet to SSH</li>
  <li>Stuff we use today is influenced by a long history
    <ul>
      <li>Bell used paper teletypes, which is why they made <code class="language-plaintext highlighter-rouge">ed</code> (actually a good
editor when you have paper)</li>
      <li>Berkeley bought video terminals, which is why we got <code class="language-plaintext highlighter-rouge">vi</code></li>
    </ul>
  </li>
</ul>]]></content><author><name>Kevin Chen</name></author><summary type="html"><![CDATA[A conference about the joy, excitement, and surprise of computing!]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kevinchen.co/blog/bang-bang-con-west-2019.jpg" /><media:content medium="image" url="https://kevinchen.co/blog/bang-bang-con-west-2019.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Supporting macOS Mojave’s Dark Mode on the web</title><link href="https://kevinchen.co/blog/support-macos-mojave-dark-mode-on-websites/" rel="alternate" type="text/html" title="Supporting macOS Mojave’s Dark Mode on the web" /><published>2018-10-25T23:51:59-07:00</published><updated>2018-10-25T23:51:59-07:00</updated><id>https://kevinchen.co/blog/support-macos-mojave-dark-mode-on-websites</id><content type="html" xml:base="https://kevinchen.co/blog/support-macos-mojave-dark-mode-on-websites/"><![CDATA[<p>macOS Mojave adds a Dark Mode for native apps that makes you look approximately
78 percent cooler when using the computer. In <a href="https://webkit.org/blog/8475/release-notes-for-safari-technology-preview-68/">Safari Technology Preview
68</a>, it’s now available on webpages too! Here’s how I added support to this
website.</p>

<figure>
    <video class="extrawide" width="1200" height="675" src="https://files.kevinchen.co/website/assets/blog/dark-mode-web/dark-mode-demo.mp4" poster="/assets/blog/dark-mode-web/dark-mode-demo-poster.jpg" preload="" autoplay="" loop="" muted="" playsinline="">
        <p><a href="https://files.kevinchen.co/website/assets/blog/dark-mode-web/dark-mode-demo.mp4">Download video</a></p>
    </video>
</figure>

<h2 id="using-the-prefers-color-scheme-css-media-query">Using the <code class="language-plaintext highlighter-rouge">prefers-color-scheme</code> CSS media query</h2>

<p>The release notes mention a new CSS media query for Dark Mode without saying how
to use it. We can try to use a unit test from the WebKit repo as sample code
instead.</p>

<p>In revision <a href="https://trac.webkit.org/changeset/237156/webkit">r237156</a>, a test named
<a href="https://trac.webkit.org/browser/webkit/trunk/LayoutTests/css-dark-mode/prefers-color-scheme.html?rev=237156">prefers-color-scheme.html</a> looks promising. It shows that the new
<code class="language-plaintext highlighter-rouge">prefer-color-scheme</code> media query can either be <code class="language-plaintext highlighter-rouge">light</code> or <code class="language-plaintext highlighter-rouge">dark</code>.</p>

<p>Let’s assume our CSS already looks good in light mode. We can use the media
query to add overrides for some rules in Dark Mode:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">/* The CSS rules we had before. */</span>
<span class="nt">body</span> <span class="p">{</span>
    <span class="py">line-spacing</span><span class="p">:</span> <span class="m">1.2em</span><span class="p">;</span>
    <span class="nl">color</span><span class="p">:</span> <span class="no">black</span><span class="p">;</span>
    <span class="nl">background</span><span class="p">:</span> <span class="no">white</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">@media</span> <span class="p">(</span><span class="n">prefers-color-scheme</span><span class="p">:</span> <span class="n">dark</span><span class="p">)</span> <span class="p">{</span>
    <span class="c">/* Overrides for Dark Mode. */</span>
    <span class="nt">body</span> <span class="p">{</span>
        <span class="nl">color</span><span class="p">:</span> <span class="no">white</span><span class="p">;</span>
        <span class="nl">background</span><span class="p">:</span> <span class="no">black</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We can even put CSS variables in the media query. Unfortunately, it’s a pretty
cutting edge feature: as of October 2018, only 91 percent of US web traffic
supports it.<sup id="fnref:caniuse" role="doc-noteref"><a href="#fn:caniuse" class="footnote" rel="footnote">1</a></sup> That’s not enough for something as fundamental as setting
the colors on a website.</p>

<p>Why don’t we use the media query for light mode too? Most browsers don’t know
about <code class="language-plaintext highlighter-rouge">prefers-color-scheme</code> yet. If we had enclosed the light mode rules with
<code class="language-plaintext highlighter-rouge">@media (prefers-color-scheme: light) { }</code>, none of the CSS rules would apply in
those browsers. Our styles would only show up in Safari!</p>

<h2 id="designing-for-dark-mode">Designing for Dark Mode</h2>

<p>In the code above, we simply swapped the text and background colors. But that
isn’t enough to make your site look great in Dark Mode. Apple’s WWDC talk
<a href="https://developer.apple.com/videos/play/wwdc2018/210/">Introducing Dark Mode</a> is a fun, lightweight video that
outlines their design philosophy and offers helpful app design tips. The same
rules apply to websites.</p>

<p>Here are my main takeaways (but you should really watch the video because the
presenter is more eloquent):</p>

<ul>
  <li>Since text becomes white, links and other colored text should also become
lighter.
    <ul>
      <li>Similarly, visual cues that normally become darker (like an active
button) should become lighter in Dark Mode.</li>
    </ul>
  </li>
  <li>Don’t mindlessly flip all the colors — not everything looks good inverted.</li>
  <li>Dark Mode is supposed to let the content shine, so don’t darken or invert
things like images.</li>
  <li>Redraw icons to fill in areas that should be white.</li>
</ul>

<h2 id="optional-add-more-javascript">Optional: Add more JavaScript</h2>

<p>On this website, I already had a dark mode for the photo gallery. My site
generator would create the gallery page with <code class="language-plaintext highlighter-rouge">&lt;body id="dark"&gt;</code> to trigger the
dark CSS rules:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">/* How Kevin's photo gallery works. */</span>
<span class="nt">body</span> <span class="p">{</span>
    <span class="py">line-spacing</span><span class="p">:</span> <span class="m">1.2em</span><span class="p">;</span>
    <span class="nl">color</span><span class="p">:</span> <span class="no">black</span><span class="p">;</span>
    <span class="nl">background</span><span class="p">:</span> <span class="no">white</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">body</span><span class="nf">#dark</span> <span class="p">{</span>
    <span class="c">/* Overrides for dark mode. */</span>
    <span class="nl">color</span><span class="p">:</span> <span class="no">black</span><span class="p">;</span>
    <span class="nl">background</span><span class="p">:</span> <span class="no">white</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I wanted to use the CSS rules I already had — duplicating all the rules from
<code class="language-plaintext highlighter-rouge">body#dark</code> into a media query would be tedious and error-prone, especially
since the media query is an experimental feature.</p>

<p>Experts agree that any computer science problem can be solved by adding more
JavaScript. So I’ll listen to changes in the media query using JavaScript, then
modify the <code class="language-plaintext highlighter-rouge">&lt;body&gt;</code> tag to match.</p>

<p>First, do the media query:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">mql</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nf">matchMedia</span><span class="p">(</span><span class="dl">'</span><span class="s1">(prefers-color-scheme: dark)</span><span class="dl">'</span><span class="p">);</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">mql.matches</code> flag will be true when Dark Mode is set. Add a callback to
<code class="language-plaintext highlighter-rouge">mql</code> that runs when the media query changes. (We don’t have to animate the
transition. The system captures a screenshot of the entire desktop before the
transition and gracefully fades to the new appearance.)</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nf">setDark</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
    <span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">id</span> <span class="o">=</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">matches</span> <span class="p">?</span> <span class="dl">"</span><span class="s2">dark</span><span class="dl">"</span> <span class="p">:</span> <span class="dl">""</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">mql</span><span class="p">.</span><span class="nf">addListener</span><span class="p">(</span><span class="nx">setDark</span><span class="p">);</span>
</code></pre></div></div>

<p>So far, our code only runs when the Dark Mode setting changes. We also need to
set the initial light/dark state when the page loads:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">document</span><span class="p">.</span><span class="nf">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">DOMContentLoaded</span><span class="dl">"</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    <span class="nf">setDark</span><span class="p">(</span><span class="nx">mql</span><span class="p">);</span>
<span class="p">});</span>
</code></pre></div></div>

<p>Here’s the whole thing:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">mql</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nf">matchMedia</span><span class="p">(</span><span class="dl">"</span><span class="s2">(prefers-color-scheme: dark)</span><span class="dl">"</span><span class="p">);</span>
<span class="kd">function</span> <span class="nf">setDark</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
    <span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">id</span> <span class="o">=</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">matches</span> <span class="p">?</span> <span class="dl">"</span><span class="s2">dark</span><span class="dl">"</span> <span class="p">:</span> <span class="dl">""</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">mql</span><span class="p">.</span><span class="nf">addListener</span><span class="p">(</span><span class="nx">setDark</span><span class="p">);</span>
<span class="nb">document</span><span class="p">.</span><span class="nf">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">DOMContentLoaded</span><span class="dl">"</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    <span class="nf">setDark</span><span class="p">(</span><span class="nx">mql</span><span class="p">);</span>
<span class="p">});</span>
</code></pre></div></div>

<p>That’s it!</p>

<p><img class="extrawide" src="/assets/blog/dark-mode-web/dark-mode-side-by-side.jpg" width="1200" height="675" alt="Light and dark mode shown side by side" /></p>

<hr />
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:caniuse" role="doc-endnote">
      <p><a href="https://caniuse.com/#feat=css-variables">Can I use…CSS Variables?</a> <a href="#fnref:caniuse" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Kevin Chen</name></author><summary type="html"><![CDATA[macOS Mojave adds a Dark Mode for native apps that makes you look approximately 78 percent cooler when using the computer. In Safari Technology Preview 68, it’s now available on webpages too! Here’s how I added support to this website.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kevinchen.co/blog/dark-mode-web/dark-mode-side-by-side.jpg" /><media:content medium="image" url="https://kevinchen.co/blog/dark-mode-web/dark-mode-side-by-side.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">What I learned in 2017</title><link href="https://kevinchen.co/blog/learned-in-2017/" rel="alternate" type="text/html" title="What I learned in 2017" /><published>2018-06-08T00:00:00-07:00</published><updated>2018-06-08T00:00:00-07:00</updated><id>https://kevinchen.co/blog/learned-in-2017</id><content type="html" xml:base="https://kevinchen.co/blog/learned-in-2017/"><![CDATA[<p>At the end of 2017, I wrote down a few important things I’d learned that year.
And now that we’re more than halfway through 2018, I decided to stop
procrastinating and flesh out the details. Some of these things are probably
obvious to you, but they were new to me!</p>

<h3 id="being-right-is-worth-nothing">Being right is worth nothing</h3>

<p>School rewards us for being right: for knowing the right facts and using them to
come to the right conclusions. I personally also put a lot of value on being
right. But that’s not how the world works.</p>

<p>People might value the emotional aspect of a decision. They might prefer not to
change because of inertia, or because the matter at hand isn’t very important.
They might not want to admit that they were wrong. There are many other things
people consider before the hard facts.</p>

<p>So it’s not always useful to point out the facts. Things often turn out better
if I don’t. And in situations where correctness matters, because <em>being right is
worth nothing,</em> being right needs to be combined with something valuable, like
an emotionally intelligent presentation.</p>

<p>And even if other people don’t value being right, I can still value it if that
makes me happy.</p>

<h3 id="curiosity-before-criticism">Curiosity before criticism</h3>

<p>In a new situation, I find it easy to fall into the trap of pointing out things
that are wrong. But it doesn’t help me learn. And the people who can change
things usually aren’t receptive to hearing feedback in this way, because <em>being
right is worth nothing.</em> (That saying really does apply to everything.)</p>

<p>I can rewrite each criticism of the format “you shouldn’t do this” into a
question — “why is it done this way?” This changes the tone from
confrontational to collaborative, which leads to better conversations.</p>

<h3 id="everyones-a-role-model">Everyone’s a role model</h3>

<p>Over the summer, I watched an interview with Michelle Obama where they asked her
how to bring about change without influence. She pointed out that everyone
already has lots of influence. It doesn’t seem obvious, but we each have people
who are silently watching and learning from us.</p>

<p>It sounded corny then, and it still does now. But it’s true.</p>

<p>As far back as I can remember, there have always been examples of people who
were watching and learning from me. This seems to be true for my friends too. It
means that my words and behavior have more potential for positive or negative
consequences than I think.</p>

<h3 id="vulnerability-leads-to-closeness">Vulnerability leads to closeness</h3>

<p>This seems obvious in the context of one’s personal life, but I’d never
considered applying the same idea to the workplace.</p>

<p>I had the opportunity to work on a really special team last year. Members of
this team were comfortable with sharing just about anything over lunch: the
usual workplace gossip, sure, but also life philosophy, deep-seated anxieties,
horror stories of weekend trips gone wrong…you name it, we talked about it.</p>

<p>Sharing so much about yourself in front of people that you don’t know very well
takes a lot of courage. But bringing your whole self to work also builds trust
and connection. Watching this happen in an unexpected environment (the
workplace) helped me realize how well it works.</p>

<h2 id="conclusions">Conclusions</h2>

<p>I wrote these ideas out individually. But looking back now, an unexpected thread
ties these lessons together: I learned all of them at work. I was super lucky to
have worked alongside great people who taught me things in the context of the
workplace that also apply to life in general.</p>

<p>If you’d told me these things at the beginning of 2017, I would’ve agreed with
all of them. But it took the specific experiences of the past year to make me
really understand.</p>]]></content><author><name>Kevin Chen</name></author><summary type="html"><![CDATA[A computer tries to figure out the world]]></summary></entry><entry><title type="html">!!Con 2018 Notes</title><link href="https://kevinchen.co/blog/bang-bang-con-notes-2018/" rel="alternate" type="text/html" title="!!Con 2018 Notes" /><published>2018-05-12T14:16:17-07:00</published><updated>2018-05-12T14:16:17-07:00</updated><id>https://kevinchen.co/blog/bang-bang-con-notes-2018</id><content type="html" xml:base="https://kevinchen.co/blog/bang-bang-con-notes-2018/"><![CDATA[<p><a href="https://bangbangcon.com/">!!Con</a> is a conference held every spring in New York
City. It’s two days of lightning talks that can be about anything related to
computers!</p>

<p>This conference is a great showcase of the diverse backgrounds of the NYC tech
scene. I’m really going to miss it when I move back to the Bay Area.</p>

<p><img class="extrawide" src="/assets/blog/bang-bang-con-2018.jpg" width="1200" height="675" alt="A stage with a projector showing the name of the conference on a blue background" /></p>

<h2 class="no_toc" id="contents">Contents</h2>

<ul id="markdown-toc">
  <li><a href="#day-1-keynote" id="markdown-toc-day-1-keynote">Day 1 Keynote</a>    <ul>
      <li><a href="#-" id="markdown-toc--">{ }</a></li>
    </ul>
  </li>
  <li><a href="#session-1" id="markdown-toc-session-1">Session 1</a>    <ul>
      <li><a href="#telling-stories-with-traceroute" id="markdown-toc-telling-stories-with-traceroute">Telling stories with traceroute!</a></li>
      <li><a href="#tales-of--can-you-tell-your-story-when-your-character-is-undefined" id="markdown-toc-tales-of--can-you-tell-your-story-when-your-character-is-undefined">Tales of ⌧! Can You Tell Your Story When Your Character Is Undefined?!</a></li>
      <li><a href="#turning-google-earth-into-simcity-2000-from-light-to-pixels-to-impossible-perspectives" id="markdown-toc-turning-google-earth-into-simcity-2000-from-light-to-pixels-to-impossible-perspectives">Turning Google Earth into SimCity 2000! (From Light to Pixels to Impossible Perspectives!)</a></li>
      <li><a href="#we-built-a-map-to-aggregate-real-time-flood-data-in-under-two-days" id="markdown-toc-we-built-a-map-to-aggregate-real-time-flood-data-in-under-two-days">We built a map to aggregate real-time flood data in under two days!</a></li>
    </ul>
  </li>
  <li><a href="#session-2" id="markdown-toc-session-2">Session 2</a>    <ul>
      <li><a href="#moving-towards-dialogue-collaborating-with-your-computer-using-typed-holes" id="markdown-toc-moving-towards-dialogue-collaborating-with-your-computer-using-typed-holes">Moving towards dialogue: collaborating with your computer using typed holes!</a></li>
      <li><a href="#compressing-the-library-of-babel" id="markdown-toc-compressing-the-library-of-babel">Compressing the Library of Babel!</a></li>
      <li><a href="#its-super-effective-solving-pokémon-blue-with-a-single-huge-regular-expression" id="markdown-toc-its-super-effective-solving-pokémon-blue-with-a-single-huge-regular-expression">It’s super effective! Solving Pokémon Blue with a single, huge regular expression</a></li>
      <li><a href="#transform-live-video-streams-with-code-and-a-repl" id="markdown-toc-transform-live-video-streams-with-code-and-a-repl">Transform live video streams with code and a REPL!!</a></li>
    </ul>
  </li>
  <li><a href="#session-3" id="markdown-toc-session-3">Session 3</a>    <ul>
      <li><a href="#creating-an-arabic-programming-language" id="markdown-toc-creating-an-arabic-programming-language">Creating an Arabic Programming Language!</a></li>
      <li><a href="#evil-twins-and-the-secret-lives-of-linkers" id="markdown-toc-evil-twins-and-the-secret-lives-of-linkers">Evil Twins and the Secret Lives of Linkers!</a></li>
      <li><a href="#satellites-are-talking-to-us-lets-hear-them-out" id="markdown-toc-satellites-are-talking-to-us-lets-hear-them-out">Satellites are talking to us! Let’s hear them out!</a></li>
      <li><a href="#the-joys-of-pico-8-token-crunching-or-what-i-learned-about-programming-from-being-restricted-on-every-side" id="markdown-toc-the-joys-of-pico-8-token-crunching-or-what-i-learned-about-programming-from-being-restricted-on-every-side">The joys of PICO-8 token crunching!! Or: what I learned about programming from being restricted on every side!</a></li>
    </ul>
  </li>
  <li><a href="#session-4" id="markdown-toc-session-4">Session 4</a>    <ul>
      <li><a href="#ux-for-cats-and-dogs" id="markdown-toc-ux-for-cats-and-dogs">UX for Cats and Dogs!</a></li>
      <li><a href="#if-you-could-solve-this-word-tile-puzzle-you-could-solve-the-halting-problem-too-bad-you-cant" id="markdown-toc-if-you-could-solve-this-word-tile-puzzle-you-could-solve-the-halting-problem-too-bad-you-cant">If you could solve this word tile puzzle, you could solve the halting problem! (Too bad you can’t!)</a></li>
      <li><a href="#so-thats-how-my-phone-knows-where-i-am" id="markdown-toc-so-thats-how-my-phone-knows-where-i-am">So THAT’S how my phone knows where I am!</a></li>
    </ul>
  </li>
  <li><a href="#day-2-keynote" id="markdown-toc-day-2-keynote">Day 2 Keynote</a>    <ul>
      <li><a href="#build-skills-through-hobbies-bring-them-to-work" id="markdown-toc-build-skills-through-hobbies-bring-them-to-work">Build skills through hobbies! Bring them to work!</a></li>
    </ul>
  </li>
  <li><a href="#session-5" id="markdown-toc-session-5">Session 5</a>    <ul>
      <li><a href="#relativistic-software-calendars-its-about-time" id="markdown-toc-relativistic-software-calendars-its-about-time">Relativistic Software Calendars: It’s About Time!</a></li>
      <li><a href="#undo-all-the-things" id="markdown-toc-undo-all-the-things">Undo all the things!</a></li>
      <li><a href="#estimating-the-value-of-pi-with-a-dartboard-and-not-so-much-luck" id="markdown-toc-estimating-the-value-of-pi-with-a-dartboard-and-not-so-much-luck">Estimating the Value of Pi with a Dartboard and (Not so Much) Luck!</a></li>
      <li><a href="#ray-tracing-and-special-relativity-rendering-objects-near-the-speed-of-light" id="markdown-toc-ray-tracing-and-special-relativity-rendering-objects-near-the-speed-of-light">Ray-tracing and special relativity: Rendering objects near the speed of light!</a></li>
    </ul>
  </li>
  <li><a href="#session-6" id="markdown-toc-session-6">Session 6</a>    <ul>
      <li><a href="#talking-to-my-past-self-without-introducing-temporal-paradoxes" id="markdown-toc-talking-to-my-past-self-without-introducing-temporal-paradoxes">Talking to my past self (without introducing temporal paradoxes!)</a></li>
      <li><a href="#four-fake-filesystems" id="markdown-toc-four-fake-filesystems">Four fake filesystems!</a></li>
      <li><a href="#using-postgres-to-watch-star-wars" id="markdown-toc-using-postgres-to-watch-star-wars">Using Postgres to <code class="language-plaintext highlighter-rouge">\watch</code> Star Wars!</a></li>
      <li><a href="#if-at-first-you-dont-succeed-at-beating-hq-trivia-try-cheating" id="markdown-toc-if-at-first-you-dont-succeed-at-beating-hq-trivia-try-cheating">If at first you don’t succeed at beating HQ Trivia, try cheating!!</a></li>
    </ul>
  </li>
  <li><a href="#session-7" id="markdown-toc-session-7">Session 7</a>    <ul>
      <li><a href="#the-man-comes-around-and-so-does-his-sound" id="markdown-toc-the-man-comes-around-and-so-does-his-sound">The Man Comes Around: and so does his sound!</a></li>
      <li><a href="#step-by-step-algorithms-that-teach-you-math" id="markdown-toc-step-by-step-algorithms-that-teach-you-math">Step by Step: Algorithms that teach you math!</a></li>
      <li><a href="#whoa-pictures-a-visual-history-of-visual-programming-languages" id="markdown-toc-whoa-pictures-a-visual-history-of-visual-programming-languages">Whoa, pictures! A visual history of visual programming languages!</a></li>
      <li><a href="#fast-but-not-too-fast-what-17th-century-windmills-can-teach-us-about-database-migrations" id="markdown-toc-fast-but-not-too-fast-what-17th-century-windmills-can-teach-us-about-database-migrations">Fast, but not too fast! What 17th-century windmills can teach us about database migrations</a></li>
    </ul>
  </li>
  <li><a href="#session-8" id="markdown-toc-session-8">Session 8</a>    <ul>
      <li><a href="#the-itty-bitty-tiny-bytes-that-make-up-a-pokémon" id="markdown-toc-the-itty-bitty-tiny-bytes-that-make-up-a-pokémon">The itty, bitty, tiny bytes that make up a Pokémon!</a></li>
      <li><a href="#pseudofractals-accidental-aesthetics-where-math-meets-pixels" id="markdown-toc-pseudofractals-accidental-aesthetics-where-math-meets-pixels">Pseudofractals! Accidental aesthetics where math meets pixels</a></li>
    </ul>
  </li>
</ul>

<h2 id="day-1-keynote">Day 1 Keynote</h2>

<h3 id="-">{ }</h3>

<p>Mimi Onuoha</p>

<ul>
  <li>Exploring the implications of machine-readable world through art &amp;
programming</li>
  <li>“Embedded in every technology there is a powerful idea” —Neil Postman
    <ul>
      <li>Oral culture: prioritize memory.</li>
      <li>Written culture: prioritize logical organization of thought.</li>
      <li>Digital culture: prioritize data!</li>
    </ul>
  </li>
  <li>Obsession with data as the object
    <ul>
      <li>Does it mean what you think it means?</li>
      <li>Hard to tell when you don’t have context</li>
    </ul>
  </li>
  <li>Pathways
    <ul>
      <li>Tracked location data of several groups for 2 weeks</li>
      <li>Met in person to collect: make the collection relationship explicit,
forcing people to think about it</li>
    </ul>
  </li>
  <li>Unable to collect? Common reasons of missing data
    <ul>
      <li>Those with resources to collect don’t have an incentive to know</li>
      <li>Resists “metrification”: things are hard to categorize or don’t
inherently generate data</li>
      <li>Nonexistence benefits or protects someone</li>
    </ul>
  </li>
  <li>Art piece: cabinet of missing datasets
    <ul>
      <li>Filing cabinet with empty folders labeled with names of datasets that
don’t exist</li>
      <li>Flip through all the things we don’t know</li>
    </ul>
  </li>
</ul>

<h2 id="session-1">Session 1</h2>

<h3 id="telling-stories-with-traceroute">Telling stories with traceroute!</h3>

<p>Karla Burnett</p>

<ul>
  <li>How traceroute works
    <ul>
      <li>Packets have a time-to-live (TTL) to prevent getting stuck in routing
loops</li>
      <li>traceroute is a hack that sends packets with different TTLs to make
different routers along the path respond</li>
    </ul>
  </li>
  <li>Telling stories through traceroute
    <ul>
      <li>Send fake TTL expired messages from spoofed IPs</li>
      <li>Reverse DNS maps IP to hostname, but the hostname doesn’t have to be
real</li>
      <li>Each line of the story needs a new IP: $3 per month</li>
    </ul>
  </li>
</ul>

<h3 id="tales-of--can-you-tell-your-story-when-your-character-is-undefined">Tales of ⌧! Can You Tell Your Story When Your Character Is Undefined?!</h3>

<p>Persa Zula</p>

<ul>
  <li>Small boxes that show up in text when characters not defined
    <ul>
      <li><code class="language-plaintext highlighter-rouge">[ ]</code>: “tofu,” so <code class="language-plaintext highlighter-rouge">[X]</code>: “not tofu”</li>
    </ul>
  </li>
  <li>Displaying characters
    <ul>
      <li>Characters are encoded as codepoints in Unicode</li>
      <li>Font’s <code class="language-plaintext highlighter-rouge">cmap</code> table maps codepoints to glyph identifier</li>
    </ul>
  </li>
</ul>

<h3 id="turning-google-earth-into-simcity-2000-from-light-to-pixels-to-impossible-perspectives">Turning Google Earth into SimCity 2000! (From Light to Pixels to Impossible Perspectives!)</h3>

<p>Logan Williams</p>

<ul>
  <li>Perspective projection
    <ul>
      <li>Farther objects are smaller</li>
    </ul>
  </li>
  <li>Orthographic projection
    <ul>
      <li>Satellite photo pointing sideways has no perspective: all lines are
parallel</li>
      <li>Used in drafting, art, and SimCity 2000</li>
      <li>Only possible when image sensor is same size as object (flatbed scanner)</li>
      <li>In rendering software, set field of view very small so input rays of
light are almost parallel</li>
    </ul>
  </li>
  <li>Doing this in real life?
    <ul>
      <li>Fly over the scene and take the same row of pixels from each frame</li>
      <li>Ensures all pixels formed by light coming from the same direction</li>
      <li>Produce images that represent time in addition to space</li>
    </ul>
  </li>
</ul>

<h3 id="we-built-a-map-to-aggregate-real-time-flood-data-in-under-two-days">We built a map to aggregate real-time flood data in under two days!</h3>

<p>Aruna Sankaranarayanan</p>

<ul>
  <li>Chennai floods in 2015
    <ul>
      <li>Bad maps made it hard to communicate which roads were navigable</li>
      <li>Google only lets you annotate locations, not streets</li>
      <li>Used MapBox to crowdsource mapping during the floods</li>
      <li>600,000 people used the software, showing that simple tools can be
really useful</li>
    </ul>
  </li>
</ul>

<h2 id="session-2">Session 2</h2>

<h3 id="moving-towards-dialogue-collaborating-with-your-computer-using-typed-holes">Moving towards dialogue: collaborating with your computer using typed holes!</h3>

<p>Vaibhav Sagar</p>

<ul>
  <li>Typed hole: something where you know the type but not the contents of the
value
    <ul>
      <li>Can specify in Haskell using underscore <code class="language-plaintext highlighter-rouge">_1</code>, <code class="language-plaintext highlighter-rouge">_2</code></li>
      <li>Type inference tells you what the type should be</li>
      <li>In Idris, a more powerful type system also includes list length, so
type inference can even help you write snippets of code</li>
    </ul>
  </li>
  <li><em>Untyped</em> holes: don’t know what the type is, but still want help writing
program
    <ul>
      <li>Open research topic</li>
    </ul>
  </li>
</ul>

<h3 id="compressing-the-library-of-babel">Compressing the Library of Babel!</h3>

<p>David Turner</p>

<ul>
  <li>Compressing the infinite library described in Borges’ short story</li>
  <li>gzip?
    <ul>
      <li>Works by finding identical sequences and inserting references back to
them</li>
      <li>On Babel: compression ratio is 1:1 because of pigeonhole principle</li>
    </ul>
  </li>
  <li>Write a program?
    <ul>
      <li>Kolmogorov Complexity = size of compressed data + size of decompressor</li>
      <li>Generation program only a few lines of Python</li>
      <li>Suppose some pages are missing: pigeonhole principle means representing
missing books requires storing entire book</li>
    </ul>
  </li>
</ul>

<h3 id="its-super-effective-solving-pokémon-blue-with-a-single-huge-regular-expression">It’s super effective! Solving Pokémon Blue with a single, huge regular expression</h3>

<p>Alex Clemmer</p>

<ul>
  <li>Pokémon Blue
    <ul>
      <li>Little boy leaves home to wander the world and catch Pokémon</li>
      <li>Mostly wandering around in the grass (very tedious)</li>
      <li>Create a regex which accepts iff the moves are a winning game</li>
    </ul>
  </li>
  <li>How to solve?
    <ul>
      <li>Pokémon Blue is a finite state machine</li>
      <li>Equivalence between FSM and regex (according to “nice book” by “this
guy” Michael Sipser)</li>
      <li>Simplify Pokémon to use ASCII world maps</li>
      <li>World map → FSM → regex</li>
    </ul>
  </li>
  <li>Differences from actual Pokémon game
    <ul>
      <li>Items can have state, player interaction</li>
      <li>Players can hold items</li>
      <li>Game has random encounters (use probabilistic regex)</li>
    </ul>
  </li>
</ul>

<h3 id="transform-live-video-streams-with-code-and-a-repl">Transform live video streams with code and a REPL!!</h3>

<p>Mark Wunsch</p>

<ul>
  <li><a href="https://en.wikipedia.org/wiki/Live_coding">Live coding</a>: code improv! Code
as the user interface, applied to video streaming.</li>
  <li>Use Racket language and GStreamer multimedia framework</li>
  <li>Write in Racket REPL to manipulate video stream, draw stuff, PIP</li>
</ul>

<h2 id="session-3">Session 3</h2>

<h3 id="creating-an-arabic-programming-language">Creating an Arabic Programming Language!</h3>

<p>Ahmed Abdalla</p>

<ul>
  <li>Learning to program requires English proficiency
    <ul>
      <li>Not just keywords, but also error messages &amp; documentation</li>
      <li>English proficiency requires privileged backgrounds in some countries</li>
    </ul>
  </li>
  <li>Noor: Imperative, Algol-style programming language for kids
    <ul>
      <li>Keywords are simple &amp; informal so kids understand</li>
      <li>Used his Sudanese dialect because languages reflect the aesthetics of
their creators</li>
    </ul>
  </li>
  <li>Arabic editor challenges
    <ul>
      <li>Bidirectional language is overall right-to-left, but English words
within Arabic text are left-to-right</li>
      <li>Ligatures required in the language, not just aesthetic</li>
    </ul>
  </li>
</ul>

<h3 id="evil-twins-and-the-secret-lives-of-linkers">Evil Twins and the Secret Lives of Linkers!</h3>

<p>Josh Bowman-Matthews</p>

<ul>
  <li>Symbol defined twice in different <code class="language-plaintext highlighter-rouge">.o</code> files. What happens when you link?
    <ul>
      <li>If functions are not inline, linker complains about duplicate symbols</li>
      <li>Inlining makes a copy of the function inside each <code class="language-plaintext highlighter-rouge">.o</code> file</li>
      <li>Therefore, linker is allowed to pick one arbitrarily &amp; not complain</li>
    </ul>
  </li>
  <li>Don’t accidentally write the same function twice</li>
</ul>

<h3 id="satellites-are-talking-to-us-lets-hear-them-out">Satellites are talking to us! Let’s hear them out!</h3>

<p>Ed Medvedev</p>

<ul>
  <li>“There are only a handful of things cooler than satellites”</li>
  <li>Radio amateur satellites: tiny satellites for ham radio</li>
  <li>Listening to satellites
    <ul>
      <li>You will need radio dongle for computer &amp; antenna</li>
      <li>Look up online when the ISS will be overhead</li>
    </ul>
  </li>
  <li>ARISS: Amateur Radio on ISS
    <ul>
      <li>Runs BBS so you can chat with people relayed through space</li>
      <li>Broadcast images &amp; talk to astronauts sometimes</li>
    </ul>
  </li>
</ul>

<h3 id="the-joys-of-pico-8-token-crunching-or-what-i-learned-about-programming-from-being-restricted-on-every-side">The joys of PICO-8 token crunching!! Or: what I learned about programming from being restricted on every side!</h3>

<p>Ayla Myers</p>

<ul>
  <li>PICO-8: game engine with retro graphics &amp; sound, but modern programming
    <ul>
      <li>Artificial limits on bit depth, memory, code size (tokens), …</li>
      <li>Built-in editors for everything</li>
    </ul>
  </li>
  <li>Economics of the PICO-8 device
    <ul>
      <li>Trade-offs: smaller sprite sheet leads to more complex code</li>
      <li>“Spent” code tokens to “buy” more sprites</li>
      <li>Can draw <a href="https://en.wikipedia.org/wiki/Production–possibility_frontier">PPF</a>
curve for tradeoff between e.g. code complexity &amp; number of sprites</li>
    </ul>
  </li>
</ul>

<h2 id="session-4">Session 4</h2>

<h3 id="ux-for-cats-and-dogs">UX for Cats and Dogs!</h3>

<p>Joel Potischman</p>

<ul>
  <li>Problem: daughter moved to college but misses cat
    <ul>
      <li>Manually send cat photos</li>
      <li>Can the cat send us selfies?</li>
    </ul>
  </li>
  <li>Solution
    <ul>
      <li>Train cats to come eat when the sound is played</li>
      <li>Take a picture of cats while they wait for food</li>
      <li>Feed the cats</li>
    </ul>
  </li>
</ul>

<h3 id="if-you-could-solve-this-word-tile-puzzle-you-could-solve-the-halting-problem-too-bad-you-cant">If you could solve this word tile puzzle, you could solve the halting problem! (Too bad you can’t!)</h3>

<p>Kamal Marhubi</p>

<ul>
  <li>Tile puzzle (Post Correspondence Problem)
    <ul>
      <li>Tiles have symbols on top and bottom</li>
      <li>Goal: order the tiles so that top and bottom say the same thing (can
repeat tiles)</li>
      <li>Not possible to tell whether a solution exists</li>
    </ul>
  </li>
  <li>Reducing Turing machine to tile puzzle
    <ul>
      <li>Make a tile from each state somehow</li>
      <li>???</li>
      <li>Solving the tile puzzle is the same as running the Turing machine</li>
    </ul>
  </li>
  <li>Wikipedia has a really unclear explanation
    <ul>
      <li>“Some things are actually hard, and some things just have too many Greek
letters”</li>
    </ul>
  </li>
</ul>

<h3 id="so-thats-how-my-phone-knows-where-i-am">So THAT’S how my phone knows where I am!</h3>

<p>Mike Lazer-Walker</p>

<ul>
  <li>Positioning throughout history
    <ul>
      <li>Latitude is easy because you can look at the sun</li>
      <li>Longitude much harder, so people dead reckoned based on speed and time</li>
      <li>Solved with accurate clock that allowed using time zone as a proxy for
distance</li>
    </ul>
  </li>
  <li>Urban canyon problem: GPS error gets really bad in cities
    <ul>
      <li>Skyscrapers bounce the signal, messing with the distance</li>
    </ul>
  </li>
</ul>

<h2 id="day-2-keynote">Day 2 Keynote</h2>

<h3 id="build-skills-through-hobbies-bring-them-to-work">Build skills through hobbies! Bring them to work!</h3>

<p>Liz Fong-Jones</p>

<ul>
  <li>Many years ago, a disowned trans woman who “almost didn’t make it”</li>
  <li>Growth comes from learning new skills</li>
  <li>Playing Puzzle Pirates game (2004–2008)
    <ul>
      <li>Some people made new players work without teaching the game</li>
      <li>Learning mentorship: encouraging people to help newcomers by rewarding
them</li>
      <li>Organizing people: conquering an island requires multiple levels of
coordinators to solve puzzles &amp; distribute booty as reward</li>
      <li>Active on community forums building diagnostic tools led to first tech
job at Puzzle Pirates!</li>
    </ul>
  </li>
  <li>World of Warcraft (2008–2012)
    <ul>
      <li>Building up new players’ skills to have a stronger team in next raid</li>
      <li>Not all leaders are blameless after the team loses</li>
    </ul>
  </li>
  <li>Eve Online (2012–present)
    <ul>
      <li>After fleet commander died, there was no communication &amp; everyone else
died</li>
      <li>In incident response, “any call is better than no call”</li>
      <li>Value your trustworthiness: not scamming in the game pays off when other
players trust with other opportunities</li>
    </ul>
  </li>
  <li>Factorio (2017–present)
    <ul>
      <li>Learning what’s the most important to automate <em>right now</em></li>
      <li>Refactoring a complex, running system</li>
    </ul>
  </li>
  <li>Playing these games taught her management skills in a safe environment
before she became a manager for real
    <ul>
      <li>Don’t be afraid to list non-traditional background on your resume</li>
    </ul>
  </li>
  <li>Intentional skill building
    <ul>
      <li>What skills will your next project require?</li>
      <li>Does this activity help me get closer? (Or just mindless play?)</li>
    </ul>
  </li>
</ul>

<h2 id="session-5">Session 5</h2>

<h3 id="relativistic-software-calendars-its-about-time">Relativistic Software Calendars: It’s About Time!</h3>

<p>John Feminella</p>

<ul>
  <li>Software makes assumptions
    <ul>
      <li>About the machine (“integers are 32 bits”)</li>
      <li>About the world (“everyone has a real name”)</li>
    </ul>
  </li>
  <li>Time is one of these assumptions</li>
  <li>What is time?
    <ul>
      <li>Originally measured by motion of earth &amp; moon</li>
      <li>Calendar is an abstract representation of this motion</li>
      <li>Software calendars bake in assumptions about being on earth</li>
    </ul>
  </li>
  <li>Enter relativity
    <ul>
      <li>Time distorts depending on the velocity of observer</li>
      <li>GPS is one of the few programs that accounts for relativity</li>
    </ul>
  </li>
</ul>

<h3 id="undo-all-the-things">Undo all the things!</h3>

<p>Tom Ballinger</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">bpython</code> is a Python interpreter that can undo the effects of expressions
    <ul>
      <li>Works by saving commands &amp; rerunning only some of them</li>
      <li>Could be slow</li>
      <li>Doesn’t work if there are side effects
        <ul>
          <li>Random numbers, time, reading files</li>
          <li>Non-idempotent actions: saving to files, buying shoes online</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Use <code class="language-plaintext highlighter-rouge">fork()</code> to save state
    <ul>
      <li>Fork every time you run a command</li>
      <li>Undo goes back to previous process</li>
      <li>Build this into <code class="language-plaintext highlighter-rouge">readline()</code> &amp; run with <code class="language-plaintext highlighter-rouge">LD_PRELOAD</code>
        <ul>
          <li>Works with unmodified interpreters!</li>
          <li><code class="language-plaintext highlighter-rouge">readline()</code> now forks your program lol</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Building an undo-able Lisp interpreter from scratch
    <ul>
      <li>Save periodic snapshots</li>
      <li>When code changes, roll back to the snapshot before that code was run</li>
      <li>JavaScript version because “the word has to be on the left side of the
parentheses or the programming language will never be popular”</li>
    </ul>
  </li>
</ul>

<h3 id="estimating-the-value-of-pi-with-a-dartboard-and-not-so-much-luck">Estimating the Value of Pi with a Dartboard and (Not so Much) Luck!</h3>

<p>Stephen Tu</p>

<ul>
  <li>Estimating pi
    <ul>
      <li>Sample a square uniformly at random</li>
      <li>Percentage inside circle is \( \pi / 2 \)</li>
    </ul>
  </li>
  <li>Estimating with error
    <ul>
      <li>Define an epsilon \( \epsilon \) which is the error on our estimate</li>
      <li>Define a probability (95%) that the estimate is within \( \epsilon \)</li>
      <li>Need 100x more samples for 10x reduction in \( \epsilon \)</li>
    </ul>
  </li>
</ul>

<h3 id="ray-tracing-and-special-relativity-rendering-objects-near-the-speed-of-light">Ray-tracing and special relativity: Rendering objects near the speed of light!</h3>

<p>Lucy Zhang</p>

<ul>
  <li>If an object is moving near the speed of light, what would a photo look
like?</li>
  <li>Computer graphics
    <ul>
      <li>Ray casting: for each pixel, figure out where the light ray from the
camera hits in the scene</li>
    </ul>
  </li>
  <li>Relativity
    <ul>
      <li>Light can never exceed speed of light, even if the light is on a
fast-moving train</li>
      <li>Lorentz transformation converts between two coordinate systems that are
moving relative to each other</li>
    </ul>
  </li>
  <li>Relativistic raytracing
    <ul>
      <li>Define the rays toward the object with a 4th parameter, time</li>
      <li>Use Lorentz transformation</li>
      <li>Do intersection normally</li>
      <li>As cube moves faster, it compresses more (expected) &amp; appears to rotate
(why?)</li>
    </ul>
  </li>
  <li>Terrell rotation
    <ul>
      <li>Rotates because farther light takes longer to reach the camera
        <ul>
          <li>Like rolling shutter effect but for relativity!</li>
        </ul>
      </li>
      <li>Anyone with a computer can “rediscover” Terrell rotation with a bit of
code!</li>
    </ul>
  </li>
</ul>

<h2 id="session-6">Session 6</h2>

<h3 id="talking-to-my-past-self-without-introducing-temporal-paradoxes">Talking to my past self (without introducing temporal paradoxes!)</h3>

<p>Andrew Louis</p>

<ul>
  <li>Stores <a href="https://hyfen.net/memex/">all data about himself</a>
    <ul>
      <li>“MSN was this cool software package that taught high schoolers how to
type really fast”</li>
    </ul>
  </li>
  <li>Training a bot on chat logs
    <ul>
      <li>Train RNN to reproduce chat logs from high school</li>
      <li>Sequence-to-sequence to respond to other messages
        <ul>
          <li>Originally developed for machine translation</li>
        </ul>
      </li>
      <li>Bot tends to converge on safe responses like “lol” and “ya” which work
in any situation</li>
      <li>Tends to respond with nonsense</li>
    </ul>
  </li>
  <li>Why is it hard?
    <ul>
      <li>Training data won’t capture general knowledge about the world</li>
      <li>Needs working memory, not just the previous message</li>
      <li>“Before submitting a conference talk, make sure you’re not committing to
solving an open research problem”</li>
    </ul>
  </li>
</ul>

<h3 id="four-fake-filesystems">Four fake filesystems!</h3>

<p>Omar Rizwan</p>

<ul>
  <li>Files are anything that you can open, read, write, close using a path</li>
  <li>GrabFS
    <ul>
      <li>Contains screenshots of each process’s windows</li>
      <li>Use shell script with <code class="language-plaintext highlighter-rouge">cp</code> to take screenshot every second</li>
    </ul>
  </li>
  <li>btfs (BitTorrent filesystem)
    <ul>
      <li>Downloads blocks as requested</li>
      <li>Opening part of a file doesn’t require downloading whole thing</li>
    </ul>
  </li>
  <li>ytfs (YouTube filesystem)
    <ul>
      <li>Making directory is a search query</li>
      <li>Directory populated with movie files of search results</li>
    </ul>
  </li>
  <li>Git filesystem
    <ul>
      <li>Exposes remote repository as a directory</li>
    </ul>
  </li>
  <li>Get to reuse existing tools &amp; UI
    <ul>
      <li>Plan 9 operating system takes filesystem idea to the extreme</li>
    </ul>
  </li>
</ul>

<h3 id="using-postgres-to-watch-star-wars">Using Postgres to <code class="language-plaintext highlighter-rouge">\watch</code> Star Wars!</h3>

<p>Will Leinweber</p>

<ul>
  <li>Using <code class="language-plaintext highlighter-rouge">psql</code>, the command-line Postgres program, to watch
<a href="https://asciimation.co.nz/">ASCII Star Wars</a>
    <ul>
      <li>Import frames as database rows</li>
      <li>Store a function in the database that prints each row</li>
    </ul>
  </li>
</ul>

<h3 id="if-at-first-you-dont-succeed-at-beating-hq-trivia-try-cheating">If at first you don’t succeed at beating HQ Trivia, try cheating!!</h3>

<p>Hung Truong</p>

<ul>
  <li>HQ Trivia is fun but we want to <strong><em>win</em></strong></li>
  <li>Pipeline
    <ul>
      <li>iOS Vision framework to detect text</li>
      <li>Tessaract library for OCR</li>
      <li>Google the question with all answers</li>
      <li>Pick the answer with the most occurrences</li>
    </ul>
  </li>
</ul>

<h2 id="session-7">Session 7</h2>

<h3 id="the-man-comes-around-and-so-does-his-sound">The Man Comes Around: and so does his sound!</h3>

<p>Vince Allen</p>

<ul>
  <li>Johnny Cash did gospel music at the beginning &amp; end of his career
    <ul>
      <li>What are the differences between the two periods?</li>
    </ul>
  </li>
  <li>Identifying gospel songs
    <ul>
      <li>Topic modeling: assigns words to categories to classify the entire text</li>
      <li>Apply topic modeling to lyrics</li>
      <li>Pick the category with religious terms</li>
    </ul>
  </li>
  <li>Comparing songs acoustically
    <ul>
      <li>Take a spectrogram of the song</li>
      <li>Convert this to a vector</li>
      <li>I didn’t really understand the rest :-(</li>
    </ul>
  </li>
</ul>

<h3 id="step-by-step-algorithms-that-teach-you-math">Step by Step: Algorithms that teach you math!</h3>

<p>Evy Kassirer</p>

<ul>
  <li>Building a step-by-step solver for teaching math</li>
  <li>Computer algebra systems
    <ul>
      <li>SymPy can simplify expressions symbolically, which sounds like a solver</li>
      <li>Goal is to get the answer, so they represent division \( a / b \) as
\( a \times b^{-1} \)</li>
      <li>This would be confusing for explaining stuff</li>
    </ul>
  </li>
  <li>Building their own solver
    <ul>
      <li>Search the parse tree for patterns like “number + number”</li>
      <li>Apply rules to transform to equivalent tree</li>
      <li>Identified which rules were applied to find relevant lecture videos</li>
    </ul>
  </li>
</ul>

<h3 id="whoa-pictures-a-visual-history-of-visual-programming-languages">Whoa, pictures! A visual history of visual programming languages!</h3>

<p>Emily Nakashima</p>

<ul>
  <li>Visual Programming Languages (VPLs)
    <ul>
      <li>Programming &amp; documentation are all in words</li>
      <li>Our brains are really good at visual stuff</li>
    </ul>
  </li>
  <li>Examples
    <ul>
      <li>Scratch: Build programs with blocks</li>
      <li>GRAIL (1968): Draw flowcharts which are converted to programs with OCR</li>
      <li>Pygmalion (1975): Visually show state on screen</li>
      <li>Cube (1995): 3D flowcharts?</li>
    </ul>
  </li>
  <li>Challenges
    <ul>
      <li>Diffing &amp; merging visual programs</li>
      <li>Humans can handle more unique words (symbol names) than unique shapes</li>
      <li>Hard to represent complexity without losing the simplicity of visual
programming</li>
      <li>Unclear whether VPLs actually make you program differently
        <ul>
          <li>People prefer whatever they learned first</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Good applications of VPLs
    <ul>
      <li>Simple systems: Twilio phone tree designer</li>
      <li>Kids &amp; learning</li>
    </ul>
  </li>
</ul>

<h3 id="fast-but-not-too-fast-what-17th-century-windmills-can-teach-us-about-database-migrations">Fast, but not too fast! What 17th-century windmills can teach us about database migrations</h3>

<p>Wander Hillen</p>

<ul>
  <li>Windmill design
    <ul>
      <li>Whole windmill rotates to match wind direction</li>
      <li>When windmill moves too quickly, slats automatically open to catch less
wind</li>
      <li>Big changes in load: wind energy increases with \( \vert v \vert^2 \)</li>
    </ul>
  </li>
  <li>WeTransfer depends on automation to handle load changes
    <ul>
      <li>Throttle expensive background tasks when the system is busy</li>
    </ul>
  </li>
</ul>

<h2 id="session-8">Session 8</h2>

<h3 id="the-itty-bitty-tiny-bytes-that-make-up-a-pokémon">The itty, bitty, tiny bytes that make up a Pokémon!</h3>

<p>Jan Mitsuko Cash</p>

<ul>
  <li>Pokémon data structure
    <ul>
      <li>232 bytes each</li>
      <li>Stores properties like species ID, experience, nickname</li>
      <li>Backward compatible between generations 1–2, and gen 3 on</li>
      <li>Gen 3 added encryption</li>
    </ul>
  </li>
</ul>

<h3 id="pseudofractals-accidental-aesthetics-where-math-meets-pixels">Pseudofractals! Accidental aesthetics where math meets pixels</h3>

<p>Jes Wolfe!</p>

<ul>
  <li>Generate topographical map
    <ul>
      <li>For each pixel, compute color as \( (x^2 + y^2) \bmod 2 \)</li>
      <li>Weird artifacts! Doesn’t look like a parabolic topo map at all</li>
    </ul>
  </li>
  <li>Aliasing
    <ul>
      <li>Caused by <a href="https://en.wikipedia.org/wiki/Nyquist_rate">Nyquist limit</a></li>
      <li>If you zoom out too far, frequency of pixels (sampling) is no longer
high enough to show frequency of topo map lines</li>
    </ul>
  </li>
  <li>Making art
    <ul>
      <li>Zoom out slowly to watch the aliasing pattern change</li>
      <li>Use \( (x^2 + y^2) \bmod N \) and assign to colormap of N colors</li>
    </ul>
  </li>
</ul>]]></content><author><name>Kevin Chen</name></author><summary type="html"><![CDATA[A conference about the joy, excitement, and surprise of computing!]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kevinchen.co/blog/bang-bang-con-2018.jpg" /><media:content medium="image" url="https://kevinchen.co/blog/bang-bang-con-2018.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Why we still can’t stop plagiarism in undergraduate computer science</title><link href="https://kevinchen.co/blog/cant-stop-plagiarism-in-computer-science/" rel="alternate" type="text/html" title="Why we still can’t stop plagiarism in undergraduate computer science" /><published>2018-03-22T09:35:20-07:00</published><updated>2018-03-22T09:35:20-07:00</updated><id>https://kevinchen.co/blog/cant-stop-plagiarism-in-computer-science</id><content type="html" xml:base="https://kevinchen.co/blog/cant-stop-plagiarism-in-computer-science/"><![CDATA[<p>Imagine that you’re hired to work at your local public library. As an eagle-eyed
checkout clerk, you soon realize that half the patrons leave without actually
checking out their books! This leaves everyone else scratching their heads when
the catalog doesn’t match the shelves. But conveniently, the library has an
unused anti-theft alarm sitting in the back room.</p>

<p>There’s just one problem: your supervisor, though sympathetic to your cause,
doesn’t want you using the alarm. You see, people take books for many reasons,
and not all of them are malicious. Besides, who cares if the shelves are in
disarray? They’ve been like that for years, but everyone who works at the
library still gets their paychecks the all the same.  If you want to set up the
alarm, you’ll have to do so on your own time, in addition to your other
responsibilities.</p>

<p>In many undergraduate computer science programs, this is the absurd reality we
face when trying to combat plagiarism. Everyone agrees plagiarism is wrong.
Everyone wishes they could stop it. Everyone has access to the tools that find
it. But no one seems willing to take any action.</p>

<h2 id="why-bother">Why bother?</h2>

<p>The most important goal is to keep the course fair for students who do honest
work. Instructors must assign grades that accurately reflect performance. A
student who grapples with a problem — becoming a stronger programmer in the
process — should never receive a lower grade than one who copies and pastes.</p>

<p>Finally, as educators, we also hope that the accused student can learn difficult
lessons about ethical behavior in the classroom rather than the workplace.</p>

<h2 id="understanding-the-scope-of-plagiarism">Understanding the scope of plagiarism</h2>

<p>I’ve been a teaching assistant in a lower-division computer science course for
the past two years. Each semester, in a class of 200 to 300 students, we
typically discover 20 to 40 blatant cases of plagiarism on homework. And because
of the nature of our process, even more cases go undetected. Here’s how it
works:</p>

<p>We begin by uploading our students’ code to an online <a href="https://en.wikipedia.org/wiki/Plagiarism_detection">plagiarism detection
tool</a>. The tool compares the submissions with each other and with
our massive back catalog of previous solutions, flagging pairs of similar
programs. We’ll manually review nearly 1,000 of these pairs over the course of
the semester, throwing out all but the most suspicious cases. These cases end up
representing about 100 students.</p>

<p>Then, we apply <em>another</em> filter, keeping only the cases that contain
indisputable evidence — for example, hundreds of lines copied right down to
the last whitespace error. We have virtually eliminated false positives at this
point. (In the course’s entire history, only one such example exists.)</p>

<p>That means we aren’t catching cases where there’s plausible deniability. And we
certainly can’t find students who transform someone else’s homework beyond
recognition. (They’ve demonstrated a better grasp of programming than your
average copy-paste-rename job, but still haven’t learned what the homework was
trying to teach.)</p>

<p>Because our process heavily favors <a href="https://en.wikipedia.org/wiki/Precision_and_recall">precision over recall</a>,
the 20 to 40 cases at the end represent a <em>lower bound</em> on plagiarism in the
course. This is corroborated by the results from Lisa Yan et al, the authors of
a new plagiarism tool named TMOSS. In a Stanford course, 43 percent of true
positives detected by TMOSS wouldn’t have been discovered by a traditional tool
similar to ours.<sup id="fnref:sigcse-tmoss" role="doc-noteref"><a href="#fn:sigcse-tmoss" class="footnote" rel="footnote">1</a></sup></p>

<p>The large number of students suspected of plagiarism isn’t unique to our course.
In fact, if a university or course doesn’t have comparable numbers, it most
likely reflects their (lack of) plagiarism detection, rather than the actual
rate of plagiarism.</p>

<p>According to a 2017 <em>New York Times</em> report,<sup id="fnref:nyt-cs50" role="doc-noteref"><a href="#fn:nyt-cs50" class="footnote" rel="footnote">2</a></sup> a course at UC Berkeley
found about 1 in 7 students in violation of policies on copying code. At
Stanford, one course suspected up to 20 percent of its students. And in spring
2017, Harvard’s CS50 reported nearly 60 of 600 students for cheating.</p>

<h2 id="the-aftermath">The aftermath</h2>

<p>What happens after an instructor confronts a student about plagiarism?</p>

<p>Some admit their mistake right away. Others deny it for awhile before coming
clean. And every semester, there will be a few conversations that go something
like this:</p>

<blockquote>
  <p>“The TAs think you copied a few homeworks. You need to tell me what’s going
on.”</p>

  <p>“I didn’t cheat. I don’t know what you’re talking about.”</p>

  <p>“Look, Bob, you have three screens of code that are exactly the same as
Alice’s, except she called the parameter <code class="language-plaintext highlighter-rouge">searchQuery</code> and you renamed it to
<code class="language-plaintext highlighter-rouge">info_strings</code> in your final commit.”</p>

  <p>“Those similarities are a coincidence that happens fairly often in
programming. Stop harassing me!” <em>*files complaint with department*</em></p>
</blockquote>

<p>Trying to gaslight the computer science department about computer science makes
no sense, but let’s move on.</p>

<p><strong>The long tail of case resolution times.</strong> After confronting students,
conversations like the above take up the vast majority of the teaching staff’s
time. A small but vocal minority of students hope to avoid consequences by
dragging out their cases.</p>

<p>They send endless emails to the teaching staff, sometimes even getting parents
involved. They appeal to the relevant offices in the university, claiming that
in computer programming, independently writing hundreds of identical lines of
code is a common occurrence. To refute these claims, the teaching staff might
then be asked to provide a nontechnical explanation of the similarities,
sometimes months after the semester has ended.</p>

<p>Combined with the time it takes to find cases in the first place, the time sink
alone is enough to discourage many instructors from looking. But don’t worry,
because there’s more:</p>

<p><strong>Lack of support from the university.</strong> When there’s enough noise,
administrators in high places start getting spooked. No one wants to end up <a href="https://www.nytimes.com/2017/05/29/us/computer-science-cheating.html">on
the front page of the <em>New York Times</em></a>. So rather than offer their
support and influence, they do nothing. Teaching staffs are left to deal with
the issue using their own time and resources.</p>

<p><strong>Becoming the bad guy.</strong> At the end of it all, students take out their anger in
course evaluations. Instructors become “terrible human beings” and worse for
daring to find out why so many programs written by different people have the
exact same bugs.</p>

<p>These reviews matter: the hiring process for faculty positions often requires
candidates to submit their past student evaluations. And like any other form of
anonymous online abuse, repeatedly reading personal attacks and other vitriol
about yourself <em>as part of your job</em> carries a large mental health cost.
(Imagine if your boss outsourced a portion of your quarterly performance review
to a panel of YouTube commenters and Twitter trolls.)</p>

<p>On a more personal level, <strong>it hurts to know which students are plagiarizing.</strong>
We teach because we want to help people learn. It might be a little
disappointing when, for instance, a student comes to office hours at the end of
a semester not knowing how to compile programs. But it’s far worse to find out
that they’d asked for compilation help because they were trying to submit a
friend’s solution.</p>

<h3 id="competing-against-inaction">Competing against inaction</h3>

<p>It’s clear that instructors who choose to pursue plagiarism cases encounter a
wide variety of disincentives, ranging from slightly unpleasant to truly
dreadful. But on an individual level, nothing bad really happens to those who
don’t bother confronting plagiarism directly. Research-track faculty can still
publish their papers. And with fewer negative reviews, teaching-track lecturers
might even improve their chances of being hired in the future.</p>

<p>So it should come as no surprise that most professors avoid doing anything about
plagiarism directly.</p>

<h2 id="many-creative-solutions">Many creative solutions</h2>

<p>I attended a <a href="https://easychair.org/smart-program/SIGCSE2018/2018-02-22.html#session:21605">discussion on plagiarism</a> at this year’s
<a href="/blog/sigcse-2018-notes/">SIGCSE</a>, a computer science education conference. It opened my
eyes to all the ideas faculty have to avoid facing plagiarism directly.</p>

<p>Some of them simply don’t generalize, like creating new homework assignments
from scratch each semester. People who bring this up have likely never asked an
instructor of an introductory course whether they’d like to spend large chunks
of time rushing out assignments in exchange for also reducing their quality. And
it still wouldn’t address plagiarism among students in the same semester.</p>

<p>But what really bothered me were all the moralistic solutions.</p>

<p>Making students sign an honor pledge. Offering leniency to those who turn
themselves in during the 72-hour “regret period” after the deadline. Or even
inviting students to email the instructor at any time if they are about to
cheat, so that the instructor can talk them out of it one-on-one.</p>

<p>These are great ideas — for supporting students in courses with large
enrollments. But they cannot be the only solution to combating plagiarism,
because they do nothing to address the underlying incentive structure:</p>

<ul>
  <li><strong>Benefit:</strong> Copying and pasting someone else’s code saves a ton of
development time, and ensures a higher grade. Good grades might be tied to
desirable outcomes like graduation, financial aid, and job prospects.</li>
  <li><strong>Cost:</strong> Zero! If the teaching staff doesn’t look for plagiarism directly,
students know they’ll never be caught.</li>
</ul>

<p>Empirical evidence shows that this is true. Our students sign an honor pledge at
the beginning of the semester, but we did not see any change in the rate of
plagiarism. Even for Harvard’s CS50, the birthplace of the “regret period,”
David Malan reports that it “has not materially impacted CS50’s number of
cases.”<sup id="fnref:cs50-cheating" role="doc-noteref"><a href="#fn:cs50-cheating" class="footnote" rel="footnote">3</a></sup></p>

<h2 id="finding-our-way-out">Finding our way out</h2>

<h3 id="student-incentives-come-from-faculty">Student incentives come from faculty</h3>

<p>Student incentives are set by the faculty: for example, if the instructor
assigns a larger weight to some assignment, students usually spend more time
working on it. This means instructors have a lot of influence here! They can
combat plagiarism by making it costly.</p>

<p>If students knew that there would be a consistent, non-zero cost to
plagiarizing, many of them wouldn’t do it. Copying homework would no longer be
in their best interest. To have an effect, the policy would have to be
implemented consistently across all courses, beginning with the introductory
courses, which play the role of communicating the department’s standards to new
students.</p>

<p>Achieving this level of uniformity means we need to provide a strong incentive
every instructor — not just the ones who care the most — to enforce the
rules.</p>

<h3 id="fixing-faculty-incentives">Fixing faculty incentives</h3>

<p><strong>University administrators should communicate their support.</strong> Instructors
should know that, not only will they suffer no retaliation, but that the
university encourages them to enforce university policies. This might require
administrators to acknowledge the inconvenient truth of widespread plagiarism.</p>

<p>Next, we need to reduce the cost of looking for plagiarism.</p>

<p><strong>Efficiently deploying plagiarism detection software.</strong> Most instructors I’ve
spoken with use <a href="https://theory.stanford.edu/~aiken/moss/">MOSS</a> (if they use automated detection at all).
However, each teaching staff must learn to use the software on their own,
sometimes writing additional code to format the inputs or aggregate the results.
We’ve somehow managed to turn plagiarism software — which should be written
once and deployed widely at zero marginal cost — into something that is very
expensive to use!</p>

<p>Instead of deploying the software on a per-class basis, the university should
pay to integrate it into their learning management systems. The software could
even automatically run on all programming assignments by default.</p>

<p><strong>Spreading the workload among more TAs.</strong> Universities can further decrease the
burden by increasing the TA headcounts of courses that enforce plagiarism rules.
This ensures that time spent combing through the results of the software doesn’t
come at the cost of teaching.</p>

<p><strong>Improving the quality of results from plagiarism detection software.</strong> The
software returns more false positives as class sizes increase, since the number
of pairs of students grows quadratically. But because MOSS is proprietary, the
community’s open-source development revolves around creating tools that wrap
MOSS, rather than improving the core algorithm. Anyone who has an idea for
improving the algorithm must first reimplement all existing functionality
starting from the pseudocode in the paper.<sup id="fnref:moss-paper" role="doc-noteref"><a href="#fn:moss-paper" class="footnote" rel="footnote">4</a></sup> There is an opportunity
to have a big impact by creating a free and open source alternative.</p>

<p><strong>We won’t be able to fix everything.</strong> For example, personal attacks in course
evaluations may be interleaved with genuine criticisms, making them difficult
for moderators to separate.</p>

<hr />

<p>I’m not smart enough to have all the answers. These are just some of my ideas
for moving incentives in the right direction. It’s not even clear which ideas
will work. But one thing’s for certain: students will learn to care when their
teachers start caring.</p>

<p><em>Thanks to <a href="https://www.ngomez.me/">Nelson Gomez</a>, Joshua Zweig, John Hui, Vivian Shen,
Edward Wang, and two anonymous readers for their feedback and discussion.</em></p>

<hr />
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:sigcse-tmoss" role="doc-endnote">
      <p>Lisa Yan, Nick McKeown, Mehran Sahami, and Chris Piech. <a href="https://dl.acm.org/citation.cfm?id=3159490">TMOSS:
Using Intermediate Assignment Work to Understand Excessive Collaboration
in Large Classes</a>.
<em>SIGCSE 2018.</em> <a href="#fnref:sigcse-tmoss" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:nyt-cs50" role="doc-endnote">
      <p>Jess Bidgood and Jeremy B. Merrill. <a href="https://www.nytimes.com/2017/05/29/us/computer-science-cheating.html">As Computer Coding Classes
Swell, So Does Cheating</a>. <em>The New York Times.</em> 29 May 2017. <a href="#fnref:nyt-cs50" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:cs50-cheating" role="doc-endnote">
      <p>David J. Malan. <a href="https://medium.com/@cs50/teaching-academic-honesty-in-cs50-965653520caf">Teaching Academic Honesty in CS50</a>.
6 March 2018. <a href="#fnref:cs50-cheating" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:moss-paper" role="doc-endnote">
      <p>Saul Schleimer, Daniel S. Wilkerson, and Alex Aiken.
<a href="https://theory.stanford.edu/~aiken/publications/papers/sigmod03.pdf">Winnowing: local algorithms for document fingerprinting</a>.
<em>SIGMOD 2003.</em> <a href="#fnref:moss-paper" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Kevin Chen</name></author><category term="starred" /><summary type="html"><![CDATA[Lessons on incentives and scaling from two years as a teaching assistant]]></summary></entry><entry><title type="html">SIGCSE 2018 notes</title><link href="https://kevinchen.co/blog/sigcse-2018-notes/" rel="alternate" type="text/html" title="SIGCSE 2018 notes" /><published>2018-02-22T14:59:03-08:00</published><updated>2018-02-22T14:59:03-08:00</updated><id>https://kevinchen.co/blog/sigcse-2018-notes</id><content type="html" xml:base="https://kevinchen.co/blog/sigcse-2018-notes/"><![CDATA[<figure>
    <img class="extrawide" src="/assets/blog/sigcse-2018-columbia-group.jpg" width="1200" height="675" alt="Twelve people smiling and holding various signs taken from the registration desk" />
    <figcaption><p>SIGCSE attendees from Columbia’s Computer Science department.</p></figcaption>
</figure>

<p>Over the weekend, I attended <a href="https://sigcse2018.sigcse.org">SIGCSE</a> — the ACM’s conference on
computer science education — with the teaching staff of Columbia’s
<a href="https://www.cs.columbia.edu/~jae/3157/">Advanced Programming</a> course. We learned about everything from rubric
design to creating community in large classes to catching plagiarism at scale.
These are the notes from the more interesting sessions I attended!</p>

<h2 class="no_toc" id="contents">Contents</h2>

<ul id="markdown-toc">
  <li><a href="#thursday-february-22" id="markdown-toc-thursday-february-22">Thursday, February 22</a>    <ul>
      <li><a href="#tmoss-using-intermediate-assignment-work-to-understand-excessive-collaboration-in-large-classes" id="markdown-toc-tmoss-using-intermediate-assignment-work-to-understand-excessive-collaboration-in-large-classes">TMOSS: Using Intermediate Assignment Work to Understand Excessive Collaboration in Large Classes</a></li>
      <li><a href="#lightweight-techniques-to-support-students-in-large-classes" id="markdown-toc-lightweight-techniques-to-support-students-in-large-classes">Lightweight Techniques to Support Students in Large Classes</a></li>
      <li><a href="#pvc-visualizing-memory-space-on-web-browsers-for-c-novices" id="markdown-toc-pvc-visualizing-memory-space-on-web-browsers-for-c-novices">PVC: Visualizing Memory Space on Web Browsers for C Novices</a></li>
      <li><a href="#special-session-watch-them-teach" id="markdown-toc-special-session-watch-them-teach">Special Session: Watch them Teach</a>        <ul>
          <li><a href="#abstraction" id="markdown-toc-abstraction">Abstraction</a></li>
        </ul>
      </li>
      <li><a href="#we-should-give-messy-problems-and-make-students-reflect-on-what-they-learn" id="markdown-toc-we-should-give-messy-problems-and-make-students-reflect-on-what-they-learn">We Should Give Messy Problems and Make Students Reflect on What They Learn</a></li>
      <li><a href="#teaching-students-a-systematic-approach-to-debugging" id="markdown-toc-teaching-students-a-systematic-approach-to-debugging">Teaching Students a Systematic Approach to Debugging</a></li>
      <li><a href="#providing-meaningful-feedback-for-autograding-of-programming-assignments" id="markdown-toc-providing-meaningful-feedback-for-autograding-of-programming-assignments">Providing Meaningful Feedback for Autograding of Programming Assignments</a></li>
      <li><a href="#an-explicit-strategy-to-scaffold-novice-program-tracing" id="markdown-toc-an-explicit-strategy-to-scaffold-novice-program-tracing">An Explicit Strategy to Scaffold Novice Program Tracing</a></li>
      <li><a href="#discussion-combating-the-wide-web-of-plagiarism" id="markdown-toc-discussion-combating-the-wide-web-of-plagiarism">Discussion: Combating the Wide Web of Plagiarism</a></li>
    </ul>
  </li>
  <li><a href="#friday-february-23" id="markdown-toc-friday-february-23">Friday, February 23</a>    <ul>
      <li><a href="#how-am-i-going-to-grade-all-these-assignments-thinking-about-rubrics-in-the-large" id="markdown-toc-how-am-i-going-to-grade-all-these-assignments-thinking-about-rubrics-in-the-large">How am I Going to Grade All These Assignments? Thinking About Rubrics in the Large</a></li>
      <li><a href="#bluebook-a-computerized-replacement-for-paper-tests-in-computer-science" id="markdown-toc-bluebook-a-computerized-replacement-for-paper-tests-in-computer-science">BlueBook: A Computerized Replacement for Paper Tests in Computer Science</a></li>
      <li><a href="#using-a-computer-based-testing-facility-to-improve-student-learning-in-a-programming-languages-and-compilers-course" id="markdown-toc-using-a-computer-based-testing-facility-to-improve-student-learning-in-a-programming-languages-and-compilers-course">Using a computer-based testing facility to improve student learning in a programming languages and compilers course</a></li>
    </ul>
  </li>
</ul>

<h2 id="thursday-february-22">Thursday, February 22</h2>

<h3 id="tmoss-using-intermediate-assignment-work-to-understand-excessive-collaboration-in-large-classes">TMOSS: Using Intermediate Assignment Work to Understand Excessive Collaboration in Large Classes</h3>

<p>Lisa Yan, Nick McKeown, Mehran Sahami, Chris Piech</p>

<ul>
  <li>“Excessive collaboration” is cheating, such as copying too much from online
solutions or classmates</li>
  <li>Cheating detection is hard because of different patterns
    <ul>
      <li>Submitting an online solution</li>
      <li>Viewing online solution, then modifying it</li>
      <li>Viewing online solution, then working independently</li>
    </ul>
  </li>
  <li>Intermediate code snapshots captured through Eclipse plugin at every
compilation</li>
  <li>TMOSS comparison algorithm
    <ul>
      <li>Moss does \( O(N^2) \) pairwise comparison of tokenized programs</li>
      <li>At average \( M = 250 \) snapshots per student, \( O(N^2 M^2) \) is
too much</li>
      <li>For each student, compare each snapshot against all <em>final</em> submissions:
\( O(N^2 M) \)</li>
      <li>Plot similarity score over “time” (aka snapshot index)</li>
      <li>In TMOSS, the distribution of scores overlaps much less than in normal
MOSS, showing it’s better at distinguishing cheaters</li>
    </ul>
  </li>
  <li>Applications of TMOSS data
    <ul>
      <li>Students who cheat also start homework later, do worse on exams</li>
      <li>Online matches start even later than classmate matches</li>
      <li>TMOSS-only students (those who modified the code to fool MOSS) are 43
percent of students!</li>
    </ul>
  </li>
  <li><a href="https://github.com/yanlisa/tmoss">TMOSS on GitHub</a></li>
</ul>

<h3 id="lightweight-techniques-to-support-students-in-large-classes">Lightweight Techniques to Support Students in Large Classes</h3>

<p>Mia Minnes, Christine Alvarado, Leo Porter</p>

<ul>
  <li>Huge class sizes
    <ul>
      <li>Enrollment is growing but we can’t hire faculty to match</li>
      <li>Students feel anonymous, perhaps more for URMs</li>
    </ul>
  </li>
  <li>Their previous work
    <ul>
      <li>Enhance discussion sections as “micro-classes” with the 1 TA, 2-3
tutors, who built a relationship over the entire semester</li>
      <li>Students felt more included, but academic performance didn’t change!</li>
      <li>Very expensive so couldn’t justify poor ROI</li>
    </ul>
  </li>
  <li>This work: lightweight (cheap or free) first order approximations</li>
  <li>From “micro-classes,” students liked having a smaller community
    <ul>
      <li>Students liked familiar faces in assigned zone seating, about 50 per
zone</li>
      <li>Assigned specific seating led to backlash
        <ul>
          <li>Zones give some choice</li>
          <li>Can also say “next time, your seat choice is your zone for the rest
of the term”</li>
        </ul>
      </li>
      <li>Create physical space between zones, using walkways or barriers</li>
      <li>Took some convincing, for example asking students to move away from the
empty area, reminding people about zones</li>
    </ul>
  </li>
  <li>Personal tutors (not TAs)
    <ul>
      <li>In class: tutor stays in the same part of the classroom
        <ul>
          <li>Listen to students whispering in class to ask questions or provoke a
discussion if students are confused</li>
          <li>Continuity from tutors coming to class helps build connection</li>
          <li>“Remember when we talked about XYZ in class?”</li>
        </ul>
      </li>
      <li>Out of class: personalized congrats emails throughout the term</li>
      <li>Students reported a more welcoming environment</li>
      <li>Some tutors offered 15-minute 1:1 sessions and cut back on other
responsibilities</li>
      <li>Tutors have “their own” students</li>
    </ul>
  </li>
  <li>TA said it wasn’t much more work and helped get to know students and class
dynamics better</li>
</ul>

<h3 id="pvc-visualizing-memory-space-on-web-browsers-for-c-novices">PVC: Visualizing Memory Space on Web Browsers for C Novices</h3>

<p>Ryosuke Ishizue, Kazunori Sakamoto, Hironori Washizaki, Yoshiaki Fukazawa</p>

<ul>
  <li>Why visualization?
    <ul>
      <li>Hard for newcomers to use debugging tools like gdb because they’re
text-only</li>
      <li>Existing tools: Python Tutor, SeeC</li>
      <li>When a program changes, SeeC requires a compile and execute cycle to
update the visualization</li>
    </ul>
  </li>
  <li>PlayVisualizerC (PVC)
    <ul>
      <li>Web-based <em>interpreter</em> that doesn’t support full C syntax
        <ul>
          <li>Interpreter is an interesting trade-off — they don’t need to
modify C compiler, but also don’t get all potentially helpful
diagnostics for free</li>
          <li>I wonder if this would be easier in clang</li>
        </ul>
      </li>
      <li>Interpreter runs on server and sends program state to client</li>
    </ul>
  </li>
</ul>

<h3 id="special-session-watch-them-teach">Special Session: Watch them Teach</h3>

<h4 id="abstraction">Abstraction</h4>

<p>Mehran Sahami</p>

<ul>
  <li>Context: CS1, written small programs using Karel the Robot, but only written
methods with no parameters or return value</li>
  <li>Abstraction: “Civilization advances by extending the number of operations we can perform
without thinking about them.” —Alfred Whitehead
    <ul>
      <li>Use the <code class="language-plaintext highlighter-rouge">sqrt()</code> function without remembering how it works</li>
      <li>Toaster analogy: put in bread, get out toast</li>
      <li>One toaster works by heating coils, but if it worked by magic, we
wouldn’t use it any differently</li>
    </ul>
  </li>
  <li>Reuse
    <ul>
      <li>CD player does not require us to change the electronics to play a new
song</li>
      <li>If you buy an iPod, can reuse the same headphones rather than replacing
whole system</li>
      <li>Then show the code</li>
    </ul>
  </li>
  <li>Teaching abstraction in a concrete way</li>
</ul>

<h3 id="we-should-give-messy-problems-and-make-students-reflect-on-what-they-learn">We Should Give Messy Problems and Make Students Reflect on What They Learn</h3>

<p>Paul Dickson</p>

<ul>
  <li>We give students large projects because we want them to learn by figuring it
out</li>
  <li>People learn a lot from reflection, but everyone hates it
    <ul>
      <li>Only done once</li>
      <li>Not a big part of the grade</li>
    </ul>
  </li>
</ul>

<h3 id="teaching-students-a-systematic-approach-to-debugging">Teaching Students a Systematic Approach to Debugging</h3>

<p>Roman Lysecky, Frank Vahid</p>

<ul>
  <li>Students encounter problems and try to fix them by changing random stuff
    <ul>
      <li>Instructors try to teach how to use a debugger, but the problem isn’t
debugging</li>
      <li>Students should learn a systematic approach to troubleshooting</li>
    </ul>
  </li>
  <li>Teach troubleshooting with everyday things — no code
    <ul>
      <li>“My lights do not turn on”</li>
      <li>Create a hypothesis about the cause</li>
      <li>Design a test to validate or reject the hypothesis</li>
    </ul>
  </li>
  <li>Then move to programming
    <ul>
      <li>Experiments include reading the code, executing the code, etc</li>
      <li>Web environment for writing and testing</li>
    </ul>
  </li>
  <li>Book: <a href="https://www.zybooks.com/catalog/troubleshooting-basics/"><em>Troubleshooting Basics</em></a></li>
</ul>

<h3 id="providing-meaningful-feedback-for-autograding-of-programming-assignments">Providing Meaningful Feedback for Autograding of Programming Assignments</h3>

<p>Georgiana Haldeman, Andrew Tjang, Monica Babes-Vroman, Stephen Bartos, Jay Shah,
Danielle Yucht, Thu Nguyen</p>

<ul>
  <li>Instructors use automatic grading to deal with CS enrollment growth
    <ul>
      <li>Students code for a test harness</li>
      <li>Instructors write test cases, which the student perceives as feedback</li>
    </ul>
  </li>
  <li>Impact of feedback: past research
    <ul>
      <li>“Binary instant feedback,” like unit tests, leads to trial-and-error
programming and more cheating</li>
      <li>Students and instructors both benefit from “directed feedback”</li>
    </ul>
  </li>
  <li>Concepts and Skills Based Feedback Generation Framework (CSF^2)
    <ul>
      <li>Cluster student submissions based on unit test pass/fail vectors</li>
      <li>Manually inspect the programs in each cluster and write a hint
        <ul>
          <li>Mapping of test vector to hint is known as <em>knowledge map</em></li>
        </ul>
      </li>
      <li>During the next term, classify the pass/fail vector of the test cases to
decide which hint to display to the student</li>
      <li>Classification accuracy around 90%</li>
    </ul>
  </li>
</ul>

<h3 id="an-explicit-strategy-to-scaffold-novice-program-tracing">An Explicit Strategy to Scaffold Novice Program Tracing</h3>

<p>Benjamin Xie, Greg L. Nelson, Andrew J. Ko</p>

<ul>
  <li>Programming instruction lacks strategy to apply learned knowledge
    <ul>
      <li>Their approach: teach people to trace code line by line</li>
    </ul>
  </li>
  <li>Why do people struggle to trace?
    <ul>
      <li>Inaccurate knowledge of programming language semantics</li>
      <li>Don’t use external representations of program state (hold all the
variables in your head)</li>
      <li>Weak problem solving strategies</li>
      <li>Not familiar enough to put all the above pieces together</li>
    </ul>
  </li>
  <li>Previous work
    <ul>
      <li>Visualization tools: requires learning another piece of software</li>
      <li>“Sketching,” or writing down state of variables: students don’t use this
on their own even after seeing it during lecture</li>
    </ul>
  </li>
  <li>Their explicit instruction approach
    <ul>
      <li>Give explicit steps of how to solve the problem using line-by-line
tracing
        <ul>
          <li>Don’t depend on students coming up with these strategies ad-hoc</li>
          <li>Student: having a strategy “forces you not to skip around”</li>
        </ul>
      </li>
      <li>Use <em>memory table,</em> which is sketching with a printed template (columns
for variable name and value history)
        <ul>
          <li>Not going to magically fix poor knowledge of programming semantics</li>
        </ul>
      </li>
      <li>Those who learned the strategy did better on practice exam questions,
but didn’t better on midterm by a significant amount</li>
    </ul>
  </li>
  <li>Possible extensions
    <ul>
      <li>Grade the memory table on an exam? (To provide partial credit?)</li>
      <li>Online tool to fill in the memory table?</li>
    </ul>
  </li>
</ul>

<h3 id="discussion-combating-the-wide-web-of-plagiarism">Discussion: Combating the Wide Web of Plagiarism</h3>

<ul>
  <li>Solutions may appear on websites outside the purview of US copyright law</li>
  <li>Tutors who give away the answers, maybe because they’re trying to save time
on the student</li>
  <li>Is cheating a problem to be solved, or a fact of life?</li>
  <li>Exams in a computer lab with real compilers and documentation (Google
cache), which makes the homework exactly like the exam</li>
  <li>Grading system where you cannot pass the course without having a passing
grade in the exam category</li>
  <li>Intro students believe there is only one solution to programming problem, so
have them do a peer review after turning in the homework</li>
  <li>Students who design their own project seem to cheat less
    <ul>
      <li>Maybe harder to find a solution after writing a unique problem</li>
    </ul>
  </li>
</ul>

<h2 id="friday-february-23">Friday, February 23</h2>

<h3 id="how-am-i-going-to-grade-all-these-assignments-thinking-about-rubrics-in-the-large">How am I Going to Grade All These Assignments? Thinking About Rubrics in the Large</h3>

<ul>
  <li>College Board has experience grading massive Advanced Placement CS exams
consistently
    <ul>
      <li>Can apply this reading experience in the classroom</li>
    </ul>
  </li>
  <li>Question development
    <ul>
      <li>Development meeting is 3 days long!</li>
      <li>AP exam questions give away a lot of information because students can’t
ask questions of the proctors</li>
      <li>This seems like the easy part</li>
    </ul>
  </li>
  <li>Rubric development
    <ul>
      <li>Decide what skills you are testing and want to reward</li>
      <li>Write a canonical solution</li>
      <li>Give points for each part of the algorithm that the student writes</li>
      <li>Write general scoring guidelines to discourage shotgunning and cover
cases outside the rubric
        <ul>
          <li>Extraneous code with side effects</li>
          <li>Not declaring local variables</li>
        </ul>
      </li>
      <li>Don’t start grading — test the rubric on a sample of student work,
focusing on solutions that test your edge cases
        <ul>
          <li>Simple oversights: forgetting a semicolon, incorrect syntax,
misspelling</li>
          <li>Incorrect execution</li>
          <li>Correct solution but different approach, which may or may not
violate the assignment spec</li>
          <li>Student wrote almost nothing but demonstrated some knowledge</li>
        </ul>
      </li>
      <li>Write down how to handle those edge cases in the rubric</li>
    </ul>
  </li>
  <li>Applying the rubric
    <ul>
      <li>Training to ensure consistency among graders, and over time (first/last
exam graded)</li>
      <li>Grade each individual point on the rubric, not looking at the whole
thing and making up a number</li>
      <li>Once a student loses points for something, continue evaluating the rest
of the code as if they’d gotten that part</li>
      <li>Review samples with graders first</li>
      <li>Grade in pairs so people can ask questions</li>
      <li>Start with grading each solution twice, then only spot check
        <ul>
          <li>Positive feedback after double checking is important but often
overlooked</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h3 id="bluebook-a-computerized-replacement-for-paper-tests-in-computer-science">BlueBook: A Computerized Replacement for Paper Tests in Computer Science</h3>

<p>Chris Piech, Chris Gregg</p>

<ul>
  <li>Qualitatively, there are students who understand the homework but do poorly
on handwritten exams
    <ul>
      <li>Writing solutions from top to bottom, rather than interatively</li>
      <li>“Strange transfer task” to perform well on exams</li>
    </ul>
  </li>
  <li>BlueBook app user experience
    <ul>
      <li>Takes over screen to prevent looking at notes
        <ul>
          <li>Detects when you switch to another window</li>
          <li>No defense against VMs — relies on trusting that students won’t be
“truly adversarial”</li>
        </ul>
      </li>
      <li>Enter password to decrypt exam</li>
      <li>Editor with syntax highlighting
        <ul>
          <li>Periodic snapshots submitted to server in case of hardware failure</li>
          <li>Doesn’t compile code: found that students would spend too much time
trying to get it perfect</li>
        </ul>
      </li>
      <li>Problem spec in HTML, CSS, JS
        <ul>
          <li>Can include interactive examples and even a demo solution</li>
        </ul>
      </li>
      <li>Submit electronically and no way to go back</li>
    </ul>
  </li>
  <li>Potential extensions
    <ul>
      <li>Automatic grading
        <ul>
          <li>98% of student code doesn’t compile: TAs fix the syntax errors</li>
          <li>Could let students compile but not run</li>
          <li>Automatic syntax correction</li>
        </ul>
      </li>
      <li>Better security
        <ul>
          <li>Nothing prevents students from sending exam to a friend offsite</li>
          <li>Laptops make it easier to look at screens in front of you</li>
        </ul>
      </li>
      <li>Equation editor, drawing diagrams</li>
    </ul>
  </li>
</ul>

<h3 id="using-a-computer-based-testing-facility-to-improve-student-learning-in-a-programming-languages-and-compilers-course">Using a computer-based testing facility to improve student learning in a programming languages and compilers course</h3>

<p>Terence Nip, Elsa Gunter, Geoffrey Herman, Jason Morphew, Matthew West</p>

<ul>
  <li>Prairie Learn: similar platform as BlueBook</li>
  <li>Testing model
    <ul>
      <li>Students schedule exam appointment at computer lab over multi-day exam
period
        <ul>
          <li>Take the exam at any time</li>
          <li>Generic proctor, since students from multiple classes can take the
exam at the same time</li>
          <li>Allows preventing internet access more effectively, unlike BYO
hardware</li>
        </ul>
      </li>
      <li>Numbers in exam questions are randomized</li>
      <li>Grades go down over the exam period, showing that questions may not be
leaking
        <ul>
          <li>Selection bias issue: students who need to cheat won’t come on the
first few days</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Results
    <ul>
      <li>Instructor give more exams (quizzes) because they’re cheaper now
        <ul>
          <li>Achieves scale because the exams/automatic graders becomes a fixed
cost for the instructor</li>
        </ul>
      </li>
      <li>Grades have gone up at the low end</li>
    </ul>
  </li>
</ul>]]></content><author><name>Kevin Chen</name></author><summary type="html"><![CDATA[Learning to teach computer science]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kevinchen.co/blog/sigcse-2018-columbia-group.jpg" /><media:content medium="image" url="https://kevinchen.co/blog/sigcse-2018-columbia-group.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Ideas for fooling Amazon Go</title><link href="https://kevinchen.co/blog/fooling-amazon-go-ideas/" rel="alternate" type="text/html" title="Ideas for fooling Amazon Go" /><published>2018-01-23T20:19:42-08:00</published><updated>2018-01-23T20:19:42-08:00</updated><id>https://kevinchen.co/blog/fooling-amazon-go-ideas</id><content type="html" xml:base="https://kevinchen.co/blog/fooling-amazon-go-ideas/"><![CDATA[<p><a href="https://www.recode.net/2018/1/21/16913984/what-does-photos-amazon-jeff-bezos-seattle-new-no-cashier-line-grocery-story-amazon-go">Amazon Go</a> is a grocery store that does away with checkout lines by
using computer vision to figure out what you purchased.</p>

<p>What happens if you…</p>

<ul>
  <li>Take an item off the shelf and give it to someone else.</li>
  <li>Go shopping with your identical twin.</li>
  <li>Use the restroom to put on a face mask.</li>
  <li>Take two products off the shelf, swap the labels, and put the cheaper one
back.</li>
  <li>Bring an empty food container from your last visit, take the same item from
the shelf, and put the empty container back.
    <ul>
      <li>An optimization: open prepackaged food, eat it in the store, then reseal
the packaging and put it back.</li>
    </ul>
  </li>
</ul>

<p><em>Thanks Chaiwen Chou and Mitchell Gouzenko for the inspiration.</em></p>]]></content><author><name>Kevin Chen</name></author><summary type="html"><![CDATA[I’d plan a trip to Seattle just to try these out]]></summary></entry><entry><title type="html">How to create a digital Suica card in Apple Pay (2024 Update)</title><link href="https://kevinchen.co/blog/create-suica-for-apple-pay/" rel="alternate" type="text/html" title="How to create a digital Suica card in Apple Pay (2024 Update)" /><published>2017-11-05T23:28:23-08:00</published><updated>2017-11-05T23:28:23-08:00</updated><id>https://kevinchen.co/blog/create-suica-for-apple-pay</id><content type="html" xml:base="https://kevinchen.co/blog/create-suica-for-apple-pay/"><![CDATA[<p><a href="https://en.wikipedia.org/wiki/Suica">Suica</a> is a smart card used to pay at train stations and convenience
stores in Japan. In 2016, Apple added support for Suica to Apple Pay on iPhone 7
devices sold in Japan. In 2017, Apple added support to all iPhone 8/X and later, regardless of where they’re sold.<sup id="fnref:use-suica" role="doc-noteref"><a href="#fn:use-suica" class="footnote" rel="footnote">1</a></sup></p>

<figure>
    <img alt="Screenshot of a green card with the word Suica and an image of a penguin" src="/assets/blog/suica-apple-pay/suica-in-apple-pay-crop.png" width="432" height="480" />
    <figcaption><p>Virtual Suica card in the iOS Wallet app.</p></figcaption>
</figure>

<p>But there are some limitations. When a physical Suica is
<a href="https://support.apple.com/en-us/HT207153">transferred</a> to Apple Pay, the physical card cannot be used
anymore. This could be inconvenient if you prefer to keep your physical card
active as a backup. To have the best of both worlds, you can generate a new
Suica just for Apple Pay.</p>

<h2 id="update-2024">Generating a new card with the Suica app (2024)</h2>

<p><em>This section was last updated on <time datetime="2024-03-21T12:09:09-07:00">21 Mar 2024</time>.</em></p>

<p>Download the <a href="https://itunes.apple.com/us/app/suica/id1156875272?mt=8">Suica app on the App Store</a>.<sup id="fnref:fn-why-app" role="doc-noteref"><a href="#fn:fn-why-app" class="footnote" rel="footnote">2</a></sup></p>

<p>Note that the Suica app is only available in Japanese as the English version has
been discontinued. However, you can still use the app by following the
screenshots below.</p>

<p>The app opens to a grayed-out Suica card. Tap the plus (+) button in the top
right corner.</p>

<p><img alt="Screenshot of an app with an image of a Suica card and several buttons" src="/assets/blog/suica-apple-pay/setup-2024-update-01.jpg" width="390" height="844" /></p>

<p>You can now choose between several types of Suica. Scroll to the last option,
which is the anonymous (無記名) Suica. Tap the green button at the bottom of the
screen:</p>

<p><img alt="Screenshot of an app with three pages showing the final page" src="/assets/blog/suica-apple-pay/setup-2024-update-02.jpg" width="390" height="844" /></p>

<p>The next screen reminds you about the restrictions of anonymous cards. You will
not be able to receive these services at the customer service counter:</p>

<ul>
  <li>Suspending the card in case of a lost or stolen device</li>
  <li>Refunds</li>
  <li>Purchasing commuter passes</li>
</ul>

<p>Tap the button in the top right corner to continue.</p>

<p><img alt="Screenshot of an app displaying a warning in red text" src="/assets/blog/suica-apple-pay/setup-2024-update-03.jpg" width="390" height="844" /></p>

<p>The next screen shows the terms of service. There are two agreements in the
list. Tap each one to read it.</p>

<p><img alt="Screenshot of an app with a list of two items" src="/assets/blog/suica-apple-pay/setup-2024-update-04.jpg" width="390" height="844" /></p>

<p>Scroll to the bottom of each agreement, then tap the button in the top right
corner to accept the terms.</p>

<p><img alt="Screenshot of an app displaying the end of a long document" src="/assets/blog/suica-apple-pay/setup-2024-update-05.jpg" width="390" height="844" /></p>

<p>Tap the white table cell to set the card’s initial balance.</p>

<p><img alt="Screenshot of an app with a white button" src="/assets/blog/suica-apple-pay/setup-2024-update-06.jpg" width="390" height="844" /></p>

<p>Once you have selected a balance, tap the Apple Pay button to load the initial
funds. You will be charged in Japanese yen. If possible, use a card without
foreign transaction fees.</p>

<p><img alt="Screenshot of app with a white button labeled 10,000 yen and a black Apple Pay button" src="/assets/blog/suica-apple-pay/setup-2024-update-07.jpg" width="390" height="844" /></p>

<p>You will then be prompted to add the card to the Wallet app. It might take a few
minutes before the card is ready to use.</p>

<h2 id="generating-a-new-card-with-the-suica-app-2017">Generating a new card with the Suica app (2017)</h2>

<div class="message-box">
    <p>
        ⚠️ The information in this section is out of date.
        <strong><a href="#update-2024">See 2024 update above</a>.</strong>
    </p>
</div>

<p><strong>Update:</strong> There’s now an English version of the Suica app,
<a href="https://itunes.apple.com/us/app/suicaeng/id1304852119?mt=8">SuicaEng</a>. You can download that instead of following these
instructions for using the Japanese app.</p>

<p><strong>Update 2:</strong> The SuicaEng app has been discontinued.</p>

<hr />

<p>Download the <a href="https://itunes.apple.com/us/app/suica/id1156875272?mt=8">Suica app on the App Store</a>, then follow the
screenshots below because the app doesn’t have an English localization.</p>

<p>Tap the + button in the top right corner. It will take you to the page below.
Tap the first option:</p>

<p><img alt="Screenshot of the Suica app with several options for adding cards" class="" src="/assets/blog/suica-apple-pay/setup-01.png" width="375" height="812" /></p>

<p>Tap the button in the top right corner to continue. In the alert that appears,
tap the second option.</p>

<p><img alt="Screenshot of the Suica app with Japanese text in alert" class="" src="/assets/blog/suica-apple-pay/setup-02.png" width="375" height="812" /></p>

<p>Tap the table cell to set the initial balance of the card.</p>

<p><img alt="Screenshot of the Suica app initial balance page" class="" src="/assets/blog/suica-apple-pay/setup-03.png" width="375" height="812" /></p>

<p>After this, tap the Apple Pay button to pay for your new card. You’ll be charged
for the initial balance, and maybe a foreign transaction fee depending on your
bank. Follow the instructions to add the card to the Wallet app.</p>

<h2 id="if-you-need-to-move-your-suica-to-a-new-phone">If you need to move your Suica to a new phone</h2>

<p>On the old phone:</p>

<ol>
  <li>In the Wallet app, tap the (i) button next to the Suica.</li>
  <li>Scroll to the bottom and delete the Suica from this phone.</li>
</ol>

<p>On the new phone:</p>

<ol>
  <li>Open Settings &gt; General &gt; Language &amp; Region. Make sure your Region is set to
Japan.
    <ul>
      <li>You don’t need to change the Language setting.</li>
    </ul>
  </li>
  <li>In the Wallet app, tap the + button.</li>
  <li>Don’t forget to change your region back to what it was before!</li>
</ol>

<hr />
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:use-suica" role="doc-endnote">
      <p><a href="https://support.apple.com/en-us/HT207154">Use Suica, PASMO, or ICOCA cards on iPhone or Apple Watch in Japan</a>. Apple Support. <a href="#fnref:use-suica" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:fn-why-app" role="doc-endnote">
      <p>The iOS Wallet app can also create a new Suica card without needing to install the Suica app. However, the Wallet app requires a Japanese payment card to load the Suica, making this approach not suitable for travelers. <a href="#fnref:fn-why-app" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Kevin Chen</name></author><summary type="html"><![CDATA[Board the train in Japan with just your phone]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kevinchen.co/blog/suica-apple-pay/suica-in-apple-pay-thumbnail.png" /><media:content medium="image" url="https://kevinchen.co/blog/suica-apple-pay/suica-in-apple-pay-thumbnail.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>