<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Reflections]]></title><description><![CDATA[Reflections]]></description><link>https://arnavverma.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 02 Sep 2026 13:21:31 GMT</lastBuildDate><atom:link href="https://arnavverma.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Building a Semantic Video Search Engine]]></title><description><![CDATA[Point it at a folder of videos. Type "a red car driving at night". Get back the exact timestamps, across every video you own, each one playable at the moment it matched.
This is a write-up of sv-engin]]></description><link>https://arnavverma.hashnode.dev/building-a-semantic-video-search-engine</link><guid isPermaLink="true">https://arnavverma.hashnode.dev/building-a-semantic-video-search-engine</guid><dc:creator><![CDATA[Arnav Verma]]></dc:creator><pubDate>Mon, 24 Aug 2026 20:52:17 GMT</pubDate><content:encoded><![CDATA[<p><em>Point it at a folder of videos. Type "a red car driving at night". Get back the exact timestamps, across every video you own, each one playable at the moment it matched.</em></p>
<p>This is a write-up of <code>sv-engine</code> , what it does, how it is built, which decisions were made by measurement rather than by argument, and the bugs that were only found because something was measured. It is a small system, roughly 3,000 lines of Python and 1,500 lines of TypeScript, but almost every piece of it exists because a simpler version of it broke in a specific way.</p>
<p>Link for Code: <a href="https://github.com/namesarnav/semantic-video-search-engine">https://github.com/namesarnav/semantic-video-search-engine</a></p>
<h2>1. The problem</h2>
<p>Video is the least searchable data most people own. A folder of 200 clips is a folder of 200 opaque blobs. The tools that exist mostly search <em>around</em> the video , the filename, the transcript, the description someone typed , not the picture itself.</p>
<p>The question I wanted to answer was the one you actually ask yourself when hunting through footage:</p>
<blockquote>
<p><em>Where is the bit where someone opens a laptop?</em></p>
</blockquote>
<p>Not "which file is called laptop.mp4". Not "where does someone say the word laptop". Where is the <strong>moment that looks like that</strong>.</p>
<p>So the scope was drawn tightly and deliberately:</p>
<ul>
<li><p><strong>In scope:</strong> visual semantic search. Natural-language query in, ranked (video, timestamp) pairs out, with a thumbnail and a seekable player.</p>
</li>
<li><p><strong>Explicitly out of scope:</strong> OCR / text-in-frame, transcript or speech search, audio of any kind. If a change starts pulling toward Whisper, that is a different project.</p>
</li>
</ul>
<p>That second list matters more than it looks. "Semantic video search" is a phrase that expands to fill any amount of engineering time. Writing down what it is <em>not</em> was what kept the project finishable.</p>
<h3>The targets</h3>
<p>Three numbers, agreed before any code:</p>
<table>
<thead>
<tr>
<th>Target</th>
<th>Value</th>
<th>Why this one</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Correctness</strong></td>
<td>a distinctive moment lands in the <strong>top 5</strong></td>
<td>This is the real metric. Top-50 is not a search engine, it is a haystack.</td>
</tr>
<tr>
<td><strong>Latency</strong></td>
<td>search p95 &lt; 500ms over hundreds of videos</td>
<td>Fast enough to feel interactive.</td>
</tr>
<tr>
<td><strong>Storage</strong></td>
<td>never store every raw frame</td>
<td>A minute of 30fps 4K is 1,800 frames. That does not scale and mostly stores duplicates.</td>
</tr>
</tbody></table>
<p>Ingestion throughput was explicitly <em>not</em> a target. A "few videos a day" workflow was the bar. Correctness was the thing worth optimising, and every time those two competed, correctness won.</p>
<hr />
<h2>2. The core idea: one shared embedding space</h2>
<p>The whole system rests on a single property of CLIP.</p>
<p>CLIP is trained on image–caption pairs with a contrastive objective, so it learns <strong>two encoders that land in the same vector space</strong>: an image encoder and a text encoder. A picture of a sunset and the string <code>"a sunset"</code> end up as nearby vectors. That is not a coincidence of the architecture, it is the training objective.</p>
<p>Which means the entire search engine is this:</p>
<p><strong>Ingest path</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/6557ff28afe2c15e65f8d100/52b66d87-7a89-4018-b7c0-52677f03defd.png" alt="" style="display:block;margin:0 auto" />

<p><strong>Query path</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/6557ff28afe2c15e65f8d100/0bc2b7f9-68ca-46fc-aac5-e43ef5d53acb.png" alt="" style="display:block;margin:0 auto" />

<p>Two pipelines, one vector space, and the space is what makes comparing words to pictures meaningful at all. Everything else in this document is plumbing around that one idea , but the plumbing is where all the interesting failures live.</p>
<p>One implementation detail that saves a step: every vector is <strong>L2-normalised on the way out of the embedder</strong>. That makes FAISS inner-product search exactly equivalent to cosine similarity, so <code>IndexFlatIP</code> does the right thing with no extra work at query time.</p>
<pre><code class="language-python">def _normalize(self, tensor: torch.Tensor) -&gt; np.ndarray:
    tensor = tensor / tensor.norm(dim=-1, keepdim=True)
    return tensor.cpu().numpy().astype(np.float32)
</code></pre>
<hr />
<h2>3. The stack, and why each piece</h2>
<table>
<thead>
<tr>
<th>Component</th>
<th>Choice</th>
<th>Why</th>
</tr>
</thead>
<tbody><tr>
<td>Embedding</td>
<td>CLIP ViT-B/32 via <code>open_clip</code></td>
<td>joint image/text space; the whole trick</td>
</tr>
<tr>
<td>Vector index</td>
<td>FAISS, flat (<code>IndexFlatIP</code>)</td>
<td>in-process, no extra service, exact search</td>
</tr>
<tr>
<td>Metadata</td>
<td>SQLite</td>
<td>zero-ops, genuinely fine at this scale, transactional</td>
</tr>
<tr>
<td>API</td>
<td>FastAPI</td>
<td>background tasks, automatic docs, thread-pool semantics I could reason about</td>
</tr>
<tr>
<td>Frame extraction</td>
<td>OpenCV</td>
<td>standard, and the histogram tooling is right there</td>
</tr>
<tr>
<td>Frontend</td>
<td>React + TypeScript + Tailwind + Vite</td>
<td>small client, no state library needed</td>
</tr>
<tr>
<td>Packaging</td>
<td>Docker, single container</td>
<td><code>docker compose up --build</code> is the only setup step</td>
</tr>
<tr>
<td>Python tooling</td>
<td><code>uv</code>, pinned to 3.12</td>
<td>fast, lockfile-truthful; <code>faiss-cpu</code> wheels for macOS arm64 are unreliable on 3.13</td>
</tr>
</tbody></table>
<h3>Things deliberately <em>not</em> used</h3>
<ul>
<li><p><strong>Sentence Transformers.</strong> CLIP already ships its own text encoder, in the same space as the images. Adding a second text model would mean two spaces that cannot be compared.</p>
</li>
<li><p><strong>ONNX Runtime.</strong> Pre-optimising before measuring. Latency turned out to be ~40× under target, so this never became justified.</p>
</li>
<li><p><strong>Postgres / Chroma / any vector service.</strong> FAISS in-process with SQLite beside it has no operational cost at all. The escape hatch is written down , FAISS → Chroma if metadata filtering ever gets painful , but taking it requires a measured reason, not a feeling.</p>
</li>
<li><p><strong>IVF/HNSW approximate indexes.</strong> A flat index is exhaustive and exact. It measured p50 11ms / p95 12ms at 4,415 vectors. Trading recall for speed the system does not need would be a strict loss.</p>
</li>
</ul>
<p>Every one of these is a decision <em>not</em> to add a moving part. At this scale the boring choice is the right one, and the cost of the boring choice is zero.</p>
<hr />
<h2>4. Architecture</h2>
<pre><code class="language-plaintext">src/sv_engine/
  sampler.py      video → frames worth embedding
  embedder.py     frames/text → normalised CLIP vectors
  index.py        FAISS: vectors and nothing else
  db.py           SQLite: everything else, source of truth
  ingest.py       glues sampler → embedder → index + db
  search.py       text → vectors → FAISS → join back to metadata
  recovery.py     repairs a store left inconsistent by a crash
  compaction.py   removes a video without corrupting every id after it
  evaluation.py   Recall@K against hand-labelled ground truth
  cli.py  api.py  two front ends over the same core

web/              React client; knows nothing but the HTTP API

eval/             labels + methodology

scripts/          corpus builders, A/B runners, report comparison
</code></pre>
<p>The shape that matters: <code>cli.py</code> <strong>and</strong> <code>api.py</code> <strong>are peers</strong>, both thin, and the core modules know about neither. <code>web/</code> is a client over the HTTP API and knows nothing beyond it. Every layer can be tested without the one above it.</p>
<h3>The single sharpest failure mode</h3>
<p>FAISS holds vectors. SQLite holds everything needed to turn a vector back into something meaningful. They are joined on one column:</p>
<pre><code class="language-sql">frames.vector_index_id   -- the vector's *position* in the FAISS index
</code></pre>
<p>A flat FAISS index assigns positions implicitly. First vector added is 0, next is 1, and so on. Which means:</p>
<blockquote>
<p><strong>Any code that rebuilds or mutates the index must keep that mapping consistent , or search returns the wrong video at the wrong timestamp, with full confidence, and never raises.</strong></p>
</blockquote>
<p>This is the thing that shaped most of the system's design. A crash that <em>errors</em> is fine. A desync that silently answers wrong is not, because nothing in the system or the UI can tell you it happened. Two whole modules (<code>recovery.py</code>, <code>compaction.py</code>) exist only to make that failure impossible, and <code>index.py</code> deliberately has <strong>no</strong> "remove vector at position i" method, because there is no safe implementation of one.</p>
<hr />
<h2>5. Frame sampling: the first real design decision</h2>
<p>Naive options, both bad:</p>
<ul>
<li><p><strong>Every frame.</strong> 30fps means 1,800 embeddings a minute, mostly near-identical. Wasted storage, wasted index, wasted search time.</p>
</li>
<li><p><strong>Every N seconds.</strong> Cheap, but misses short distinct moments entirely. A 0.6-second shot in a fast-cut sequence simply does not exist to the index.</p>
</li>
</ul>
<p>The strategy used is <strong>scene-change-aware sampling</strong>: a fixed baseline rate of ~1 frame/sec, <em>plus</em> extra samples at points where the picture actually changes.</p>
<p>Cut detection compares <strong>downscaled HSV histograms</strong> using <strong>Bhattacharyya distance</strong>:</p>
<pre><code class="language-python">def scene_distance(prev, curr) -&gt; float:
    """Bhattacharyya distance between two frame histograms, in [0, 1]."""
    return float(cv2.compareHist(_histogram(prev), _histogram(curr),
                                 cv2.HISTCMP_BHATTACHARYYA))
</code></pre>
<p>Three choices inside that one function:</p>
<ol>
<li><p><strong>Frames are downscaled to 160px wide first.</strong> A scene cut is a global property of the picture. Comparing 4K pixels buys nothing and costs a lot.</p>
</li>
<li><p><strong>HSV, not BGR.</strong> Hue/saturation is more robust to lighting drift than raw channel intensity.</p>
</li>
<li><p><strong>Bhattacharyya, not correlation.</strong> Bhattacharyya is already bounded to [0, 1], so the threshold is interpretable and needs no per-video calibration. Correlation would have meant tuning per clip, which is not a threshold, it is a chore.</p>
</li>
</ol>
<h3>The threshold is measured, not guessed</h3>
<p>On 4-shot test footage:</p>
<table>
<thead>
<tr>
<th>threshold</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>0.10</td>
<td>26 false positives , triggered by camera motion</td>
</tr>
<tr>
<td><strong>0.20 – 0.60</strong></td>
<td><strong>all cuts found, zero false positives</strong></td>
</tr>
<tr>
<td>0.75</td>
<td>starts missing real cuts</td>
</tr>
</tbody></table>
<p>0.35 sits in the middle of a wide plateau. That plateau is the point: a threshold that only works at exactly one value is a threshold that will break on the next video.</p>
<h3>The bug that only a test found</h3>
<p>There is a <code>min_gap_sec</code> throttle so a shaky or fast-cutting sequence cannot emit a burst of samples. The first version throttled a candidate cut against <em>any</em> previous sample.</p>
<p>That is wrong, and wrong in the worst direction. A cut landing just after a baseline tick is the <strong>least</strong> duplicative frame available , the picture just changed, which is the entire reason it was flagged. Throttling against baseline left the opening of every new scene unrepresented until the next tick, silently dropping exactly the frames the whole feature exists to capture.</p>
<p>The fix is one word in a condition. Finding it required writing a test that asserted the expected frame count on footage with known cuts:</p>
<pre><code class="language-python"># Throttle cuts against the previous *cut* only.
if (scene_distance(prev_frame, frame) &gt;= scene_threshold
        and timestamp - last_cut_ts &gt;= min_gap_sec):
    reason = "scene_cut"
</code></pre>
<p><code>min_gap_sec</code> throttles <strong>cut-against-cut only, never cut-against-baseline</strong>. There are regression tests pinning this in <code>test_sampler.py</code>, because the code looked completely reasonable while being wrong.</p>
<p>One more subtlety in the same loop: <code>prev_frame</code> updates on <strong>every</strong> iteration, not only when a frame is emitted. Cut detection compares against the previous <em>compared</em> frame, not the previous <em>sampled</em> one. Getting that backwards turns a cut detector into a slow-drift detector.</p>
<hr />
<h2>6. Idempotency: content hash, not filename</h2>
<pre><code class="language-python">def content_hash(path) -&gt; str:
    """SHA-256 of the file's bytes, truncated to 16 hex chars."""
</code></pre>
<p><code>videos.id</code> <strong>is</strong> the hash. Re-ingesting the same file is a no-op or a clean overwrite, never a duplicate set of frames. Rename a file and it is still the same video. Copy it into a second folder and it does not double.</p>
<p>Files are hashed in 1MB chunks , these are hundreds of megabytes and do not belong in memory.</p>
<p>The trade-off is honest and worth stating: re-encoding a video changes its hash, so it re-ingests as a new video. That is the correct behaviour for a <em>visual</em> search engine (re-encoded pixels really are different pixels), but it is a choice, not a law.</p>
<hr />
<h2>7. Status is persisted, not in-memory</h2>
<pre><code class="language-sql">videos.status ∈ queued | processing | done | failed
videos.error                        -- so a failure says *why*
</code></pre>
<p>The temptation with a background-task API is to hold job state in a dict. That dict does not survive a restart, and the failure it produces is the invisible kind: a video that is silently stuck forever with no way to notice.</p>
<p>So status is a column. <code>sv-engine videos</code> lists it. <code>GET /videos/{id}/status</code> serves it. And a video that fails records the reason next to the status, because "failed" with no explanation is only marginally better than silence.</p>
<p>Two real bugs on this project were caught only by tests written <em>after</em> the code was already called "working": the <code>min_gap_sec</code> cut-suppression bug above, and a missing <code>failed</code> row for unreadable files. Both were invisible failures. Both are now pinned by tests.</p>
<hr />
<h2>8. Crash recovery: the part I'm most pleased with</h2>
<p><code>kill -9</code> runs no <code>except</code> block. Neither does an OOM kill, nor a power cut. So nothing the ingest path does <em>on the way down</em> can be relied on. What survives on disk has to be repairable <strong>from the disk alone</strong>.</p>
<p>The approach: rather than try to handle every crash window, <strong>order the writes so the damage always takes one repairable shape.</strong></p>
<h3>The write order</h3>
<ol>
<li><p>A writer holds <code>index.appending()</code> across <strong>add → persist → commit</strong>, so no second ingest can interleave its vectors. (Embedding stays <em>outside</em> that lock , it is the slow part, and holding a lock across it would make one ingest block every other. Searches take a different, finer lock and are never held up.)</p>
</li>
<li><p>The index is <strong>saved before the rows are committed</strong>, and the save is atomic , temp file plus <code>os.replace</code> , so a half-written index can never replace a good one. Recovery can repair a <em>stale</em> index; it cannot repair an unreadable one.</p>
</li>
<li><p>The frame rows and the <code>done</code> status are <strong>one SQLite transaction</strong>. Otherwise a crash between them leaves a complete frame set on a <code>processing</code> video, and recovery would re-ingest it and double every frame.</p>
</li>
</ol>
<h3>The invariant those three buy</h3>
<blockquote>
<p>A crash leaves <strong>at most surplus vectors at the tail of the index.</strong></p>
</blockquote>
<p>And a tail is the one thing that can be dropped without shifting a single surviving <code>vector_index_id</code>. Hence the only removal primitive on the index:</p>
<pre><code class="language-python">def truncate(self, size: int) -&gt; None:
    """Drop every vector from `size` onwards.

    The only safe way to remove vectors from a flat index: positions below the
    cut keep their ids. Removing from the middle would shift ids and silently
    mis-answer every later query, which is why there is no such method.
    """
</code></pre>
<h3>What recovery does at startup</h3>
<ol>
<li><p><strong>Sweep.</strong> Every video left in <code>processing</code> or <code>queued</code> is marked <code>failed</code>, with the reason recorded. After a restart neither status is true any more , the worker died with the process and nothing re-queues it.</p>
</li>
<li><p><strong>Reconcile.</strong> Rows whose vectors are past the end of the index are unrecoverable, so that video is failed <em>wholesale</em> and its rows dropped. Then vectors no row points at are truncated away.</p>
</li>
</ol>
<p>That "wholesale" is deliberate. <strong>Half a video in the index is worse than none</strong> , it answers queries confidently using whichever frames happened to survive. A partial result that looks complete is the failure mode this entire subsystem exists to prevent.</p>
<p>Recovery runs before the CLI ingests anything, in the API's lifespan hook, and on demand via <code>sv-engine recover</code>. It is startup-only and assumes it is the only process running , sweeping while another process ingests would fail a live job.</p>
<hr />
<h2>9. Compacting removal: the operation with no safe write order</h2>
<p><code>--force</code> on a video that still has frames used to just raise an error. Making it work was the hardest correctness problem in the project.</p>
<p>The issue: <strong>a flat FAISS index cannot delete a vector.</strong> Removing position 3 shifts 4, 5, 6 down by one, and every stored <code>vector_index_id</code> above it then points at the wrong frame. So removal means rebuilding the index <em>and</em> rewriting the mapping, together.</p>
<p>Unlike an append, <strong>a compaction has no safe write order</strong>:</p>
<ul>
<li><p>Save the index first → a compacted index against stale ids.</p>
</li>
<li><p>Commit the rows first → new ids against the old index.</p>
</li>
</ul>
<p>Both are silent corruption. And neither is the shape M4's recovery repairs , <code>truncate</code> drops from the <em>end</em>, compaction renumbers from the <em>middle</em>.</p>
<p>The solution is a <strong>write-ahead marker</strong>: the operation announces its intent before it swaps.</p>
<ol>
<li><p>Build the compacted index and <strong>stage</strong> it beside the live one.</p>
</li>
<li><p>In <strong>one</strong> SQLite transaction: renumber the survivors, delete the video, and record that a swap is owed.</p>
</li>
<li><p><code>os.replace</code> the staged file over the live one.</p>
</li>
<li><p>Clear the marker.</p>
</li>
</ol>
<p>Every interruption then lands somewhere repairable, and <code>recovery.repair</code> finishes the job at startup:</p>
<table>
<thead>
<tr>
<th>crash point</th>
<th>state on disk</th>
<th>repair</th>
</tr>
</thead>
<tbody><tr>
<td>before (2)</td>
<td>marker absent, staged file present</td>
<td>staged file is an orphan , delete it, old store intact</td>
</tr>
<tr>
<td>between (2) and (3)</td>
<td>marker set, staged file present</td>
<td>the DB already describes the compacted index , <strong>complete the swap</strong></td>
</tr>
<tr>
<td>between (3) and (4)</td>
<td>marker set, staged file gone</td>
<td>swap already happened , clear the marker. Idempotent.</td>
</tr>
</tbody></table>
<p>The marker stores the <em>staged filename</em> rather than a boolean, precisely so repair can tell those last two cases apart by asking whether the file exists.</p>
<p>Compaction repair also runs <strong>first</strong> in the recovery pass, before reconcile , it decides which file is the live index, so reconciling before it would compare the database against a file that is about to be replaced, and conclude (correctly but uselessly) that they disagree.</p>
<h3>Two small details with big consequences</h3>
<p><strong>Renumbering passes through negative ids.</strong> <code>vector_index_id</code> has a <code>UNIQUE</code> constraint, so assigning final values directly would collide with rows that have not moved yet. Two passes , write <code>-(new+1)</code>, then flip the sign , cannot collide, because negatives and positives are disjoint.</p>
<p><code>drop_video</code> <strong>takes a required</strong> <code>index_dir</code> <strong>with no default.</strong> It briefly defaulted to <code>config.INDEX_DIR</code>. A unit test that omitted the argument then compacted my real 205-video index out from under me. The lesson is written into the code and pinned by a test:</p>
<blockquote>
<p>A destructive file operation must never be able to reach a global path because a caller left an argument off.</p>
</blockquote>
<hr />
<h2>10. The API layer, and two rules that break silently</h2>
<pre><code class="language-plaintext">POST   /videos                # {"path": "..."} -&gt; 202 queued; ingests in background
POST   /videos/upload         # multipart file -&gt; 202 queued
GET    /videos[?status=...]   # list with per-video frame counts
GET    /videos/{id}/status    # ingestion status
POST   /search                # {query, top_k, collapse_window_sec} -&gt; ranked results
GET    /videos/{id}/file      # streams the source video, with byte ranges
GET    /thumbnails/{frame_id} # serves the JPEG
GET    /health                # corpus size + device
</code></pre>
<p><strong>Rule 1: every handler that can reach CLIP is a plain</strong> <code>def</code><strong>, never</strong> <code>async def</code><strong>.</strong></p>
<p>FastAPI runs <code>async def</code> handlers on the single event-loop thread and plain <code>def</code> handlers in a worker thread. CLIP inference is CPU-bound and never yields. An <code>async def</code> search handler would stall the <em>entire server</em> for the length of an ingest. Measured with the rule in place: searches held ~13ms while a 40-second video ingested.</p>
<p>This is the kind of bug that does not show up in development, where you are the only user, and shows up immediately in a demo.</p>
<p><strong>Rule 2:</strong> <code>VectorIndex</code> <strong>locks internally, around the FAISS call only.</strong></p>
<p>The lock lives inside the index rather than in callers, so it cannot be forgotten. It is held for the microseconds of the FAISS call , never around sampling or embedding, which would freeze search for the whole of an ingest.</p>
<p>There is a second, coarser writer lock (<code>appending()</code>) held across an entire add-persist-commit unit. It blocks other <em>writers</em> only. Searches take the fine lock and are never held up by it, so rule 2 still holds.</p>
<p>And a constraint that falls out of threading: <strong>SQLite connections cannot cross threads</strong>, so each request and each background task opens its own.</p>
<h3>Byte ranges, by hand</h3>
<p><code>GET /videos/{id}/file</code> implements HTTP range requests manually rather than delegating to <code>FileResponse</code>, for two reasons:</p>
<ol>
<li><p><strong>Seeking is the entire point.</strong> A result is a moment <em>inside</em> a video. Without a 206 response the browser must download the whole file before it can jump to 4:32.</p>
</li>
<li><p><code>FileResponse</code> answers <strong>400</strong> for a range unit it does not recognise, where RFC 9110 §14.2 requires an unknown unit to be <em>ignored</em> and the full representation sent.</p>
</li>
</ol>
<p>Ranges are streamed in 256KB blocks: a seek into a 120MB 4K clip should not cost 120MB of RSS per viewer. Only paths recorded in <code>videos.path</code> are reachable, and the id is a content hash rather than a caller-supplied filename, so there is no path-traversal surface.</p>
<h3>URLs, not paths</h3>
<p>Results return <code>thumbnail_url</code> and <code>video_url</code>, never a filesystem path. A server path is useless to a browser and leaks the server's layout.</p>
<hr />
<h2>11. The web UI, and one opinionated call</h2>
<p>Vite + React + TypeScript + Tailwind, with shadcn-style components vendored into <code>components/ui/</code> (shadcn ships source, not a runtime dependency). Four tabs: Search, Library, How to use, About. Tested with Vitest and Testing Library , no browser needed.</p>
<p><strong>Results are grouped by video, not listed as frames.</strong></p>
<p>The engine ranks <em>frames</em>, because frames are what get embedded, and <code>/search</code> still returns that flat ranked list , the honest shape of what the index computed. But a frame is <strong>evidence</strong>, not the answer. The answer is "this video, at these times."</p>
<p>So <code>lib/group.ts</code> folds hits into one card per video: filename at the head, the video itself playable, and its matched moments beneath as a strip of seek targets. A video is ranked by its <strong>best</strong> moment rather than by how many it has , one strong match should beat five weak ones, and counting would only reward long videos.</p>
<p>Grouping is a presentation concern and stays in the client. The API keeps returning frames.</p>
<h3>The client holds no API base URL, and must not grow one</h3>
<p><code>/search</code> returns <code>thumbnail_url</code> as a <em>relative</em> path, so every URL the app touches is relative. In development the Vite proxy forwards <code>/search</code>, <code>/videos</code>, <code>/thumbnails</code> and <code>/health</code> to <code>:8000</code>; in production FastAPI serves the built files itself, so they are same-origin.</p>
<p>Introducing a <code>VITE_API_URL</code> would trade that for a setting that can be wrong in two environments instead of a thing that cannot be wrong in either.</p>
<h3>One route-ordering trap</h3>
<p><code>create_app</code> mounts <code>web/dist</code> at <code>/</code> <strong>last, and nowhere else</strong>. A mount at <code>/</code> matches every path, and Starlette resolves routes in registration order , so anything registered after it becomes unreachable, while everything before it (every API route, plus <code>/docs</code>) still wins. Move that block and you get a page that loads and whose every request 404s. <code>test_web.py</code> pins it.</p>
<p>An absent build is not an error, either. A headless deployment is legitimate, so <code>/</code> returns a 404 with the command to build the UI rather than failing at startup.</p>
<hr />
<h2>12. Evaluation: the part that made everything else decidable</h2>
<p>This is the piece I would keep if I had to throw the rest away.</p>
<p>Everything above is an <em>opinion</em> until it is measured. <code>sv-engine eval</code> scores the store against hand-labelled ground truth and reports <strong>Recall@1 / @5 / @10</strong> plus latency p50/p95. Every design decision in this project was settled by A/B against it.</p>
<h3>Recall over <em>queries</em>, not relevant items</h3>
<p>Each label names the moments that answer a query. A query counts as found if <strong>any</strong> of them lands in the top K. Finding two is worth no more than finding one.</p>
<p>That is <strong>known-item retrieval</strong>, which is the actual shape of this product: a person hunting one moment they remember. It is deliberately not the mean-average-precision framing , there is no notion of "all relevant frames" to be complete against, and inventing one would mean labelling every frame of every video.</p>
<h3>Three design points, each because the alternative silently corrupts the metric</h3>
<ol>
<li><p><strong>A label carries a <em>list</em> of targets.</strong> The test corpus includes <code>multishot_4cuts_720p.mp4</code>, a concatenation of four other clips , so most footage has two correct answers. A single-target schema would score a perfect retrieval as a miss and cap Recall@1 for reasons unrelated to retrieval.</p>
</li>
<li><p><strong>Labels key on filename, and an unknown filename is an error, not a miss.</strong> A typo'd or un-ingested video would score zero, which looks <em>exactly</em> like a broken retriever. The same reasoning drives the strict loader: unknown keys, backwards ranges and empty label sets all raise, because each would otherwise surface only as a lower score.</p>
</li>
<li><p><strong>A tolerance (default 1.0s = one baseline interval) widens each range.</strong> Sampling is ~1 frame/sec, so the nearest sampled frame can sit that far from the moment a human read off the clock. Scoring strictly charges the retriever for the sampler's grid.</p>
</li>
</ol>
<p>Every report also records <strong>which sampling arm built the store it measured</strong>, inferred from whether any frame has <code>reason = scene_cut</code>. Two A/B reports without that are two numbers with no record of what they compare.</p>
<p>And the labels are <strong>committed to git</strong>, anchored to the checkout rather than the gitignored data directory. A metric whose ground truth is not version-controlled cannot be re-derived, and a metric that cannot be re-derived is not a metric.</p>
<h3>Two eval sets, for two different questions</h3>
<table>
<thead>
<tr>
<th>set</th>
<th>size</th>
<th>how made</th>
<th>what it measures</th>
</tr>
</thead>
<tbody><tr>
<td><code>eval/labels.json</code></td>
<td>12 queries</td>
<td>hand-authored, someone looked at the frames</td>
<td><strong>moment precision</strong> , narrow timestamp targets. The headline number, and the only one supporting an absolute claim.</td>
</tr>
<tr>
<td><code>eval/labels-corpus.json</code></td>
<td>187 queries</td>
<td>generated from the corpus manifest; the query is the uploader's own description</td>
<td><strong>video selection</strong> , whole-video targets. Weak supervision; use for <em>comparison</em>, never as a quality claim.</td>
</tr>
</tbody></table>
<p>The corpus set's absolute number understates quality badly, and it is worth understanding why: with ~20 near-identical clips per category, returning a <em>different</em> cat video than the labelled one scores zero while being a perfectly good answer. Its value is that a <strong>paired</strong> comparison over identical queries cancels per-query noise , which is exactly what an A/B needs.</p>
<h3>Current baseline</h3>
<p><strong>205 videos, 4,415 frames, ~70 minutes of footage, scene-aware, ViT-B/32:</strong></p>
<table>
<thead>
<tr>
<th>set</th>
<th>R@1</th>
<th>R@5</th>
<th>R@10</th>
</tr>
</thead>
<tbody><tr>
<td>hand-labelled (12)</td>
<td>66.7%</td>
<td>75.0%</td>
<td>83.3%</td>
</tr>
<tr>
<td>corpus (187, weak)</td>
<td>38.0%</td>
<td>52.4%</td>
<td>58.8%</td>
</tr>
</tbody></table>
<p>Latency <strong>p50 11ms / p95 12ms</strong> against a 500ms target , roughly 40× headroom. The flat index is nowhere near being the bottleneck, so IVF/HNSW stays unjustified.</p>
<p>One number worth reporting honestly: the hand-labelled R@5 <strong>fell from 91.7% to 75.0%</strong> when the corpus grew from 5 videos to 205. That is not a regression. It is the honest effect of 200 distractors; the earlier figure was measured against a corpus with almost nothing to confuse it. A benchmark that only ever improves is usually a benchmark that is being gamed.</p>
<hr />
<h2>13. What the measurements settled</h2>
<h3>A. Scene-aware sampling beats fixed-interval , and the corpus problem</h3>
<p><strong>Result: Recall@5 100% vs 85.7%, +14.3 points.</strong></p>
<p>But the interesting part is that <strong>this could not be measured on the real corpus.</strong> Scene-aware sampling contributes exactly 3 frames out of 87 there, because four of the five original videos are single continuous shots. Both arms tied , and the tie says nothing whatsoever about the design.</p>
<blockquote>
<p><strong>A corpus that cannot exhibit the phenomenon cannot measure it.</strong></p>
</blockquote>
<p>So <code>scripts/make_cut_dense_corpus.py</code> builds one that can: 16 shots (8 sub-second, 8 sustained) cut from four source clips crossed with four visual treatments, so every shot is uniquely addressable by one query. Boundaries are known by construction.</p>
<table>
<thead>
<tr>
<th>arm</th>
<th>frames</th>
<th>shot coverage</th>
<th>R@1</th>
<th>R@5</th>
<th>R@10</th>
</tr>
</thead>
<tbody><tr>
<td><strong>scene-aware</strong></td>
<td>43</td>
<td><strong>100.0%</strong></td>
<td>81.2%</td>
<td><strong>93.8%</strong></td>
<td>100.0%</td>
</tr>
<tr>
<td>fixed-interval</td>
<td>28</td>
<td>81.2%</td>
<td>68.8%</td>
<td>81.2%</td>
<td>81.2%</td>
</tr>
<tr>
<td>dense control (10fps)</td>
<td>277</td>
<td>100.0%</td>
<td>68.8%</td>
<td>87.5%</td>
<td>87.5%</td>
</tr>
</tbody></table>
<p>Two queries changed outcome, both sub-second shots (0.63s and 0.57s), findable <em>only</em> by the scene-aware arm. Fixed-interval put no frame inside them at all.</p>
<p><strong>Three things hold that result up</strong>, and each exists because its absence silently corrupts the answer:</p>
<ol>
<li><p><strong>Coverage is reported next to recall.</strong> Recall alone cannot distinguish "captured shots that were missed, and retrieval improved" from "captured them and retrieval didn't improve" , the second is a finding about CLIP, not about sampling. Here they agree, and that agreement is the actual evidence.</p>
</li>
<li><p><strong>A dense 10fps control arm decides which labels are answerable.</strong> Some queries fail because CLIP simply cannot see "sepia". Filtering with either test arm's own successes would bias the comparison toward it.</p>
</li>
<li><p><strong>Shot boundaries are quantised to whole frames.</strong> The first version rounded durations to 1/100s while the renderer wrote whole frames. The declared boundaries drifted from the rendered ones, ground truth pointed at the <em>neighbouring</em> shot, and the run produced a coherent-looking, wholly false result , 100% coverage with <em>worse</em> recall. <strong>Generated ground truth still has to be verified against the artefact it describes.</strong> That one cost a day and was the most valuable mistake in the project.</p>
</li>
</ol>
<h3>B. Near-duplicate collapsing helps, and the safe window is 3–5s</h3>
<p>A long static shot floods the results with near-identical entries. Collapsing merges hits from the same video within N seconds, keeping the best-scoring one. (The search over-fetches <code>top_k * 5</code> when collapsing, so <code>top_k</code> distinct moments still come back.)</p>
<table>
<thead>
<tr>
<th>collapse</th>
<th>hand R@5</th>
<th>hand R@10</th>
<th>corpus R@5</th>
</tr>
</thead>
<tbody><tr>
<td>off</td>
<td>75.0%</td>
<td>83.3%</td>
<td>52.4%</td>
</tr>
<tr>
<td><strong>3s</strong></td>
<td><strong>83.3%</strong></td>
<td><strong>91.7%</strong></td>
<td>56.7%</td>
</tr>
<tr>
<td><strong>5s</strong></td>
<td><strong>83.3%</strong></td>
<td><strong>91.7%</strong></td>
<td>63.1%</td>
</tr>
<tr>
<td>20s</td>
<td>75.0%</td>
<td>75.0%</td>
<td><strong>71.7%</strong></td>
</tr>
</tbody></table>
<p><strong>The two sets disagree above ~5s, and the disagreement is the finding, not a problem.</strong> The corpus set improves monotonically because its targets are <em>whole videos</em> , collapsing frees top-K slots for distinct videos, so it can only help a video-selection task. The hand-labelled set degrades past 10s because aggressive merging deletes the correct <em>moment</em> inside the right video.</p>
<p>So: collapse hard if you only care which video; keep it at 3–5s if timestamps matter. The 3s default sits in that plateau.</p>
<h3>C. ViT-L/14 beats ViT-B/32 by ~7 points , and the default did not change</h3>
<p>Measured at 205 videos on both eval sets. The comparison is clean: <code>laion2b_s32b_b82k</code> is the LAION-2B counterpart to B/32's <code>laion2b_s34b_b79k</code>, so this isolates <strong>model capacity</strong> rather than confounding it with training data , and both arms indexed the identical 4,415 frames, with only the embedder differing.</p>
<table>
<thead>
<tr>
<th>set</th>
<th>arm</th>
<th>R@1</th>
<th>R@5</th>
<th>R@10</th>
</tr>
</thead>
<tbody><tr>
<td>corpus (187, weak)</td>
<td>ViT-B/32</td>
<td>38.0%</td>
<td>52.4%</td>
<td>58.8%</td>
</tr>
<tr>
<td>corpus (187, weak)</td>
<td><strong>ViT-L/14</strong></td>
<td>43.9%</td>
<td><strong>59.4%</strong></td>
<td>65.8%</td>
</tr>
<tr>
<td>hand-labelled (12)</td>
<td>ViT-B/32</td>
<td>66.7%</td>
<td>75.0%</td>
<td>83.3%</td>
</tr>
<tr>
<td>hand-labelled (12)</td>
<td><strong>ViT-L/14</strong></td>
<td>75.0%</td>
<td><strong>83.3%</strong></td>
<td>91.7%</td>
</tr>
</tbody></table>
<p><strong>Read the churn, not just the delta.</strong> At R@5 on the 187-query set, ViT-L/14 <strong>gained 26 queries and lost 13</strong>. A net +13 built from a 2:1 win ratio is a real effect rather than noise , but a model swap is <em>not</em> a strict improvement, and thirteen queries genuinely got worse. The 12-query set moves +8.3 everywhere, which is one query each: corroboration, not evidence.</p>
<p>Costs: search p50 11ms → 22ms (still ~20× under target), ingest 198s → 370s for 70 minutes of footage, index 8.6MB → 13MB (768-d vs 512-d), and the Docker image would grow ~1.1GB.</p>
<p><strong>Why the default did not change:</strong> the vector dimension changes with the checkpoint, so <strong>every existing index becomes unreadable</strong> and needs a full <code>--rebuild</code>. That is an operational decision, not a tuning knob. The mismatch is caught loudly rather than silently mis-served:</p>
<pre><code class="language-plaintext">existing index has dim 512, embedder produces 768.
The checkpoint changed -- rebuild the index.
</code></pre>
<p>Switching is one env var away (<code>SV_CLIP_MODEL=ViT-L-14</code>), and the measurement says it is worth it , it is just not something that should happen to someone by surprise.</p>
<p><code>scripts/compare_reports.py</code> reproduces the comparison, pairing two <code>eval --json</code> reports by query text and reporting <strong>wins and losses separately</strong>. It refuses to compare reports scored against different label sets rather than silently intersecting them.</p>
<hr />
<h2>14. Packaging</h2>
<pre><code class="language-bash">docker compose up --build      # API + UI on :8000
</code></pre>
<p>A <strong>four-stage build</strong>: the React UI (so the image does not depend on a host <code>npm run build</code>), Python deps from <code>uv.lock</code> with <code>--frozen</code>, the CLIP checkpoint baked in, then a slim runtime. Verified in-container: identical Recall to the host, UI at <code>/</code>, <code>/docs</code> still 200, thumbnails served. Latency is ~3× the host (p50 34ms vs 11ms) , CPU-only, as designed.</p>
<p>Five things turned out to be load-bearing, each found by the container failing without it:</p>
<ol>
<li><p><code>pyproject.toml</code> <strong>pins torch to the CPU index on linux.</strong> The default wheels drag in the entire CUDA toolkit , gigabytes, for hardware the container cannot reach. Scoped by a <code>sys_platform</code> marker so macOS resolution is untouched; <code>uv.lock</code> carries both.</p>
</li>
<li><p><code>/data</code> <strong>is created and chowned in the image <em>before</em></strong> <code>VOLUME</code><strong>.</strong> Docker seeds a fresh named volume from the image path, ownership included. Without it the volume arrives root-owned and the unprivileged process dies in <code>ensure_dirs()</code>.</p>
</li>
<li><p><strong>Host footage mounts at</strong> <code>/videos</code><strong>, not over</strong> <code>/data/videos</code><strong>.</strong> That directory must stay writable , <code>POST /videos/upload</code> saves into it, so a read-only bind there breaks the endpoint at <em>runtime</em> rather than at startup.</p>
</li>
<li><p><code>SV_WEB_DIST</code> <strong>and</strong> <code>SV_DEVICE</code> <strong>are set explicitly.</strong> <code>config.py</code> derives the UI path from the source-tree layout, which is not a safe assumption inside an image; and there is no MPS or CUDA in the container, so say so rather than letting device selection fall through and look like a choice.</p>
</li>
<li><p><code>.dockerignore</code> <strong>is not optional.</strong> The build context is 1.2GB without it , 258MB of source videos plus a macOS-built <code>.venv</code> that must never enter a linux image.</p>
</li>
</ol>
<p>One known wrinkle, documented rather than hidden: <code>docker compose run ... index</code> writes to the shared volume immediately, but the already-running server loaded its index at startup and will not see the new vectors until a restart. Ingesting through <code>POST /videos</code> avoids it entirely.</p>
<h3>Local dev vs Docker</h3>
<p>Develop natively with <code>uv</code>. <strong>Docker on macOS has no MPS/GPU passthrough</strong>, so CLIP inference in a container is CPU-only and dramatically slower , fine for correctness and for the <code>docker compose up</code> done-criterion, painful for iterating on ingestion. Device selection is <code>mps → cuda → cpu</code>, overridable by <code>SV_DEVICE</code> so containers and tests can force <code>cpu</code>.</p>
<hr />
<h2>15. The stupidest bug: two OpenMP runtimes</h2>
<p><code>faiss</code> and <code>torch</code> each bundle their own <code>libomp.dylib</code>. Loading both in one process on macOS aborts with <code>OMP: Error #15</code>. Import order does not fix it.</p>
<p>The internet's answer is <code>KMP_DUPLICATE_LIB_OK=TRUE</code>. <strong>That was not acceptable here.</strong> OpenMP's own documentation says it can silently produce incorrect results , which is disqualifying for a retrieval system, where "silently incorrect" is the exact failure class the entire architecture is built to avoid.</p>
<p>The fix is <code>scripts/fix_openmp.py</code>: it repoints faiss's copy at torch's (same LLVM libomp, ABI 5.0.0), and <strong>refuses to link if the ABI versions ever diverge</strong>. It must be re-run after any <code>uv sync</code>.</p>
<p>This does not arise on linux , there is one system <code>libgomp</code> and both libraries use it , so the script is macOS-only.</p>
<p>Not an interesting bug. But a good illustration: the <em>convenient</em> workaround traded a loud crash for possible silent wrongness, which is always the wrong trade in this project.</p>
<hr />
<h2>16. How it was built</h2>
<p>Six milestones, each independently demoable , the rule was <strong>never leave a half-built pile.</strong></p>
<table>
<thead>
<tr>
<th></th>
<th>milestone</th>
<th></th>
</tr>
</thead>
<tbody><tr>
<td>M1</td>
<td>CLI only: extract, embed, index, query one video</td>
<td>Done</td>
</tr>
<tr>
<td>M2</td>
<td>Multi-video + SQLite; cross-video results map to the right video/timestamp</td>
<td>Done</td>
</tr>
<tr>
<td>M3</td>
<td>FastAPI wrapper; background ingestion with status tracking</td>
<td>Done</td>
</tr>
<tr>
<td>M4</td>
<td>Content-hash idempotency, persisted status, crash handling</td>
<td>Done</td>
</tr>
<tr>
<td>M5</td>
<td>React search UI</td>
<td>Done</td>
</tr>
<tr>
<td>M6</td>
<td>Near-duplicate collapsing, sampling refinement, latency work</td>
<td>Done (gated on the eval harness)</td>
</tr>
</tbody></table>
<p>31 commits over roughly two months of evenings.</p>
<h3>Test-first, and why it stopped being optional</h3>
<p>The rule: <strong>write the tests that define the behaviour before the implementation</strong>, run them to confirm they fail for the right reason, then make them pass.</p>
<p>This started as discipline and became non-negotiable after two bugs , the <code>min_gap_sec</code> cut suppression and the missing <code>failed</code> row for unreadable files , were found <em>only</em> by tests written after the code had already been called "working." Both were silent. Neither would have been noticed in normal use.</p>
<p>The suite is 225 tests. The fast suite (<code>pytest -m "not slow"</code>) is 217 of them and runs in <strong>~2 seconds</strong>, because it is kept free of CLIP loads. End-to-end tests that load the checkpoint are marked <code>slow</code>. A test suite you avoid running is a test suite you do not have.</p>
<hr />
<h2>17. What I'd take from this</h2>
<p><strong>Measure the thing you are arguing about, or stop arguing.</strong> Sampling strategy, collapse window, and checkpoint choice were all live debates that took ten minutes each to settle once there was a Recall@K number. The eval harness was the highest-leverage code in the project and it was written <em>after</em> M5 , which is later than it should have been.</p>
<p><strong>A corpus that cannot exhibit the phenomenon cannot measure it.</strong> The sampling A/B tied on real footage and the tie meant nothing. Recognising that a null result was <em>uninformative</em> rather than <em>negative</em> was the difference between shipping the right default and shipping a coin flip.</p>
<p><strong>Generated ground truth is not ground truth until you check it.</strong> A rounding mismatch of one hundredth of a second produced a completely coherent, completely false experimental result.</p>
<p><strong>Order writes so failure has one shape.</strong> Trying to handle every crash window is unbounded work. Constraining the damage to "surplus vectors at the tail" made recovery a dozen lines instead of a subsystem , and where a single shape was impossible (compaction), a write-ahead marker made the three remaining shapes enumerable, and the table of what to do in each fits in a paragraph.</p>
<p><strong>The worst bug is the one that does not raise.</strong> Almost every hard decision here , failing a video wholesale rather than half-serving it, refusing <code>KMP_DUPLICATE_LIB_OK</code>, making <code>index_dir</code> a required argument, treating an unknown label filename as an error rather than a miss, erroring loudly on a dimension mismatch , is the same decision made repeatedly: <strong>prefer a loud failure to a quiet wrong answer.</strong> In a retrieval system, a confident wrong answer is indistinguishable from a right one, and that is the only failure mode the user can never catch for you.</p>
<hr />
<h2>Appendix: running it</h2>
<pre><code class="language-bash">docker compose up --build          # everything, on :8000
</code></pre>
<p>Or natively:</p>
<pre><code class="language-bash">uv sync
uv run python scripts/fix_openmp.py       # macOS only

uv run python -m sv_engine.cli index data/videos      # ingest a file or folder
uv run python -m sv_engine.cli search "a red car at night" -k 10
uv run python -m sv_engine.cli videos                 # status per video
uv run python -m sv_engine.cli recover                # repair after a crash
uv run python -m sv_engine.cli eval                   # Recall@K
uv run python -m sv_engine.cli serve --port 8000      # API + UI, docs at /docs
</code></pre>
<p>Useful flags: <code>--rebuild</code> (drop index, database <strong>and</strong> thumbnails together , a rebuilt index against a stale database is exactly the desync that produces confident wrong answers), <code>--force</code> (re-ingest a <code>done</code> video, compacting the old frames out), <code>--fixed-interval</code> (the sampling control arm), <code>--collapse 3.0</code>.</p>
<pre><code class="language-bash">uv run pytest -m "not slow"      # 217 tests, ~2s, no CLIP
uv run pytest -m slow            # end-to-end, loads the checkpoint
npm --prefix web test            # UI tests, no browser needed
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Kubernetes Pods]]></title><description><![CDATA[1. Understanding Pods as the Smallest Deployable Unit
What Exactly Is a Pod?
A Pod is Kubernetes' atomic unit of deployment. You cannot deploy anything smaller than a Pod. While you might think of containers as the fundamental building block (since t...]]></description><link>https://arnavverma.hashnode.dev/kubernetes-pods</link><guid isPermaLink="true">https://arnavverma.hashnode.dev/kubernetes-pods</guid><category><![CDATA[Kubernetes]]></category><dc:creator><![CDATA[Arnav Verma]]></dc:creator><pubDate>Wed, 18 Feb 2026 22:36:29 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-1-understanding-pods-as-the-smallest-deployable-unit">1. Understanding Pods as the Smallest Deployable Unit</h2>
<h3 id="heading-what-exactly-is-a-pod">What Exactly Is a Pod?</h3>
<p>A Pod is Kubernetes' atomic unit of deployment. You cannot deploy anything smaller than a Pod. While you might think of containers as the fundamental building block (since that's what actually runs your code), Kubernetes wraps containers in Pods for important architectural reasons.</p>
<p><strong>The Technical Reality:</strong></p>
<p>When you create a Pod, Kubernetes actually creates something called a <strong>pause container</strong> (also called the infrastructure container) first. This hidden container:</p>
<ol>
<li><p>Holds the network namespace for the Pod</p>
</li>
<li><p>Acquires the Pod's IP address</p>
</li>
<li><p>Stays running for the Pod's entire lifetime</p>
</li>
<li><p>Allows your application containers to restart without losing network identity</p>
</li>
</ol>
<p>Your application containers then join this pause container's namespaces. This is why all containers in a Pod share the same IP address and network stack.</p>
<pre><code class="lang-plaintext">┌─────────────────────────────────────────────────────────────┐
│                           POD                               │
│  ┌────────────────────────────────────────────────────────┐ │
│  │              Shared Network Namespace                  │ │
│  │                   (pause container)                    │ │
│  │                    IP: 10.244.1.5                      │ │
│  └────────────────────────────────────────────────────────┘ │
│                            │                                │
│       ┌────────────────────┼────────────────────┐           │
│       │                    │                    │           │
│       ▼                    ▼                    ▼           │
│  ┌─────────┐         ┌─────────┐         ┌─────────┐        │
│  │Container│         │Container│         │Container│        │
│  │    A    │         │    B    │         │    C    │        │
│  │ :8080   │         │ :9090   │         │ :3000   │        │
│  └─────────┘         └─────────┘         └─────────┘        │
│       │                    │                    │           │
│       └────────────────────┴────────────────────┘           │
│                            │                                │
│                    Shared Volumes                           │
└─────────────────────────────────────────────────────────────┘
</code></pre>
<h3 id="heading-what-does-smallest-deployable-unit-actually-mean">What Does "Smallest Deployable Unit" Actually Mean?</h3>
<p>It means:</p>
<ol>
<li><p><strong>Scheduling granularity</strong> — The Kubernetes scheduler places entire Pods on nodes, never individual containers. If you have 3 containers in a Pod, they all go to the same node together.</p>
</li>
<li><p><strong>Scaling granularity</strong> — When you scale up, you add more Pods, not more containers within a Pod. If you need 5 replicas of your web server, you get 5 Pods, each with its own web server container.</p>
</li>
<li><p><strong>Failure granularity</strong> — If a node fails, the entire Pod fails. Kubernetes doesn't try to relocate individual containers from a Pod to different nodes.</p>
</li>
</ol>
<h3 id="heading-why-not-deploy-containers-directly">Why Not Deploy Containers Directly?</h3>
<p>Consider what you'd need to manage if Kubernetes deployed raw containers:</p>
<ul>
<li><p><strong>Networking:</strong> How would containers find each other? How would they share <a target="_blank" href="http://localhost">localhost</a>?</p>
</li>
<li><p><strong>Storage:</strong> How would you share files between containers?</p>
</li>
<li><p><strong>Lifecycle:</strong> What happens when one container depends on another?</p>
</li>
<li><p><strong>Identity:</strong> How do you refer to a group of related containers as one thing?</p>
</li>
</ul>
<p>The Pod abstraction solves all of these:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Problem</td><td>Pod Solution</td></tr>
</thead>
<tbody>
<tr>
<td>Containers need same IP</td><td>Pod provides shared network namespace</td></tr>
<tr>
<td>Containers need shared files</td><td>Pod provides shared volume mounts</td></tr>
<tr>
<td>Containers have dependencies</td><td>Init containers and lifecycle hooks</td></tr>
<tr>
<td>Need atomic deployment</td><td>Pod is scheduled/deployed as one unit</td></tr>
<tr>
<td>Need health tracking</td><td>Pod-level status and conditions</td></tr>
</tbody>
</table>
</div><h3 id="heading-the-pod-to-container-relationship-real-world-analogy">The Pod-to-Container Relationship: Real-World Analogy</h3>
<p>Imagine shipping containers (the metal boxes on cargo ships). A Pod is like a shipping container, and application containers are like the packages inside it.</p>
<ul>
<li><p>The shipping container (Pod) provides the outer boundary and tracking number</p>
</li>
<li><p>The packages (containers) inside share the same journey</p>
</li>
<li><p>When the shipping container moves, everything inside moves together</p>
</li>
<li><p>The shipping manifest (Pod spec) lists all packages inside</p>
</li>
<li><p>Customs (Kubernetes) deals with shipping containers, not individual packages</p>
</li>
</ul>
<p><strong>When to Use Single-Container Pods:</strong></p>
<p>Most Pods contain exactly one container. Use single-container Pods when:</p>
<ul>
<li><p>The container is self-sufficient</p>
</li>
<li><p>It doesn't need to share files or network with a helper</p>
</li>
<li><p>Scaling means running more instances of the same thing</p>
</li>
</ul>
<p><strong>When to Use Multi-Container Pods:</strong></p>
<p>Use multi-container Pods only when containers are <strong>tightly coupled</strong>:</p>
<ul>
<li><p>They must run on the same machine</p>
</li>
<li><p>They must share files</p>
</li>
<li><p>They communicate over <a target="_blank" href="http://localhost">localhost</a></p>
</li>
<li><p>They scale together as a unit</p>
</li>
</ul>
<hr />
<h2 id="heading-2-pod-lifecycle-and-phases">2. Pod Lifecycle and Phases</h2>
<h3 id="heading-the-complete-pod-lifecycle-journey">The Complete Pod Lifecycle Journey</h3>
<p>When you create a Pod, it goes through a predictable sequence of events. Understanding this deeply helps you debug problems.</p>
<pre><code class="lang-mermaid">---
title: Pod Lifecycle Phases
---
flowchart TD
    PC["Pod Created (via API call)"] --&gt; PENDING

    subgraph PENDING["PENDING PHASE"]
        P1["1. Pod stored in etcd"]
        P2["2. Scheduler assigns Pod to Node"]
        P3["3. Kubelet receives Pod spec"]
        P4["4. Kubelet pulls container images"]
        P5["5. Init containers run (if any)"]
        P1 --&gt; P2 --&gt; P3 --&gt; P4 --&gt; P5
    end

    PENDING --&gt;|"All init containers complete&lt;br/&gt;Main containers starting"| RUNNING

    subgraph RUNNING["RUNNING PHASE"]
        R1["At least one container running"]
        R2["Pod bound to node"]
        R3["Container States: Running, Waiting, or Terminated"]
    end

    RUNNING --&gt;|"All containers exit&lt;br/&gt;with code 0"| SUCCEEDED
    RUNNING --&gt;|"At least one container&lt;br/&gt;exits with non-zero"| FAILED

    subgraph SUCCEEDED["SUCCEEDED"]
        S1["All containers terminated"]
        S2["All exit code 0"]
        S3["Won't restart"]
        S4["Common for: Jobs"]
    end

    subgraph FAILED["FAILED"]
        F1["All containers terminated"]
        F2["At least one exit code != 0"]
        F3["Won't restart (unless policy)"]
    end
</code></pre>
<h3 id="heading-deep-dive-into-each-phase">Deep Dive into Each Phase</h3>
<h4 id="heading-pending-phase">PENDING Phase</h4>
<p>A Pod enters Pending immediately after creation and stays there until containers start running. Many things happen in this phase:</p>
<p><strong>Step 1: API Server Processing</strong></p>
<pre><code class="lang-plaintext">kubectl apply -f pod.yaml
        │
        ▼
┌─────────────────┐
│   API Server    │
│                 │
│ - Authenticates │
│ - Authorizes    │
│ - Validates     │
│ - Stores in     │
│   etcd          │
└────────┬────────┘
         │
         ▼
   Pod exists in cluster
   (but not scheduled yet)
</code></pre>
<p><strong>Step 2: Scheduling</strong></p>
<pre><code class="lang-plaintext">┌─────────────────────┐
│     Scheduler       │
│                     │
│ Evaluates:          │
│ - Resource requests │
│ - Node selectors    │
│ - Affinity rules    │
│ - Taints/tolerations│
│ - Available nodes   │
└──────────┬──────────┘
           │
           ▼
   Pod assigned to Node
   (nodeName field set)
</code></pre>
<p><strong>What Can Cause Prolonged Pending:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Symptom</td><td>Cause</td><td>How to Debug</td></tr>
</thead>
<tbody>
<tr>
<td>No node assigned</td><td>No node has enough resources</td><td><code>kubectl describe pod</code> shows "Insufficient cpu/memory"</td></tr>
<tr>
<td>No node assigned</td><td>No node matches node selector</td><td>Check node labels vs pod's nodeSelector</td></tr>
<tr>
<td>No node assigned</td><td>Taints blocking all nodes</td><td>Check tolerations</td></tr>
<tr>
<td>Image pull stuck</td><td>Wrong image name or tag</td><td><code>kubectl describe pod</code> shows ImagePullBackOff</td></tr>
<tr>
<td>Image pull stuck</td><td>Private registry, no credentials</td><td>Check imagePullSecrets</td></tr>
<tr>
<td>Init container running</td><td>Init containers taking time</td><td>Normal if init does real work</td></tr>
</tbody>
</table>
</div><p><strong>Example Pending Debugging:</strong></p>
<pre><code class="lang-bash">$ kubectl get pods
NAME      READY   STATUS    RESTARTS   AGE
my-pod    0/1     Pending   0          5m

$ kubectl describe pod my-pod
...
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  5m    default-scheduler  0/3 nodes are available: 
                                                       3 Insufficient memory.
</code></pre>
<p>This tells you: No node has enough memory for this Pod's requests.</p>
<h4 id="heading-running-phase">RUNNING Phase</h4>
<p>Once at least one main container is running, the Pod transitions to Running. But "Running" doesn't mean "working correctly" — your app could be crashing in a loop.</p>
<p><strong>Container States Within Running Pod:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-comment"># kubectl get pod my-pod -o yaml (simplified)</span>
<span class="hljs-attr">status:</span>
  <span class="hljs-attr">phase:</span> <span class="hljs-string">Running</span>
  <span class="hljs-attr">containerStatuses:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nginx</span>
    <span class="hljs-attr">state:</span>
      <span class="hljs-attr">running:</span>
        <span class="hljs-attr">startedAt:</span> <span class="hljs-string">"2024-01-15T10:00:00Z"</span>
    <span class="hljs-attr">ready:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">restartCount:</span> <span class="hljs-number">0</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">sidecar</span>
    <span class="hljs-attr">state:</span>
      <span class="hljs-attr">waiting:</span>
        <span class="hljs-attr">reason:</span> <span class="hljs-string">CrashLoopBackOff</span>
        <span class="hljs-attr">message:</span> <span class="hljs-string">"back-off 5m0s restarting failed container"</span>
    <span class="hljs-attr">ready:</span> <span class="hljs-literal">false</span>
    <span class="hljs-attr">restartCount:</span> <span class="hljs-number">5</span>
</code></pre>
<p>This Pod is "Running" but has a problem — the sidecar container keeps crashing.</p>
<p><strong>Container State Details:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>State</td><td>Fields</td><td>Meaning</td></tr>
</thead>
<tbody>
<tr>
<td><code>waiting</code></td><td>reason, message</td><td>Container not running yet. Reasons: ContainerCreating, ImagePullBackOff, CrashLoopBackOff</td></tr>
<tr>
<td><code>running</code></td><td>startedAt</td><td>Container executing. Has timestamp of start</td></tr>
<tr>
<td><code>terminated</code></td><td>exitCode, reason, startedAt, finishedAt</td><td>Container finished. exitCode 0 = success, else failure</td></tr>
</tbody>
</table>
</div><h4 id="heading-succeeded-phase">SUCCEEDED Phase</h4>
<p>Pods reach Succeeded when:</p>
<ul>
<li><p>All containers have terminated</p>
</li>
<li><p>All containers exited with code 0</p>
</li>
<li><p>The Pod won't be restarted</p>
</li>
</ul>
<p>This is normal for <strong>Jobs</strong> — workloads designed to run to completion:</p>
<pre><code class="lang-bash">$ kubectl get pods
NAME              READY   STATUS      RESTARTS   AGE
backup-job-xyz    0/1     Completed   0          1h
</code></pre>
<h4 id="heading-failed-phase">FAILED Phase</h4>
<p>Pods reach Failed when:</p>
<ul>
<li><p>All containers have terminated</p>
</li>
<li><p>At least one container exited with non-zero code</p>
</li>
<li><p>restartPolicy prevents restart (Never or OnFailure with Succeeded)</p>
</li>
</ul>
<pre><code class="lang-bash">$ kubectl get pods
NAME            READY   STATUS   RESTARTS   AGE
broken-job-abc  0/1     Error    0          5m

$ kubectl logs broken-job-abc
Error: database connection failed
</code></pre>
<h3 id="heading-restart-policies">Restart Policies</h3>
<p>The <code>restartPolicy</code> field determines what happens when containers exit:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">spec:</span>
  <span class="hljs-attr">restartPolicy:</span> <span class="hljs-string">Always</span>  <span class="hljs-comment"># Options: Always, OnFailure, Never</span>
</code></pre>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Policy</td><td>Behavior</td><td>Use Case</td></tr>
</thead>
<tbody>
<tr>
<td><code>Always</code></td><td>Always restart containers, regardless of exit code</td><td>Long-running services (web servers, daemons)</td></tr>
<tr>
<td><code>OnFailure</code></td><td>Restart only if exit code is non-zero</td><td>Jobs that should retry on failure</td></tr>
<tr>
<td><code>Never</code></td><td>Never restart containers</td><td>Jobs where failure should not retry</td></tr>
</tbody>
</table>
</div><p><strong>How Restart Backoff Works:</strong></p>
<p>When a container keeps failing, Kubernetes doesn't just hammer restarts. It uses exponential backoff:</p>
<pre><code class="lang-plaintext">First failure:    restart immediately
Second failure:   wait 10s, then restart
Third failure:    wait 20s, then restart
Fourth failure:   wait 40s, then restart
Fifth failure:    wait 80s, then restart
...continues...   up to 5 minutes max
</code></pre>
<p>This is why you see "CrashLoopBackOff" — Kubernetes is backing off between restart attempts.</p>
<h3 id="heading-pod-conditions">Pod Conditions</h3>
<p>Beyond the simple phase, Pods have detailed <strong>conditions</strong>:</p>
<pre><code class="lang-bash">$ kubectl describe pod my-pod
Conditions:
  Type              Status
  Initialized       True    <span class="hljs-comment"># All init containers completed</span>
  Ready             True    <span class="hljs-comment"># Pod is ready to serve traffic</span>
  ContainersReady   True    <span class="hljs-comment"># All containers are ready</span>
  PodScheduled      True    <span class="hljs-comment"># Pod has been scheduled to a node</span>
</code></pre>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Condition</td><td>True Means</td></tr>
</thead>
<tbody>
<tr>
<td><code>PodScheduled</code></td><td>Pod assigned to a node</td></tr>
<tr>
<td><code>Initialized</code></td><td>All init containers completed successfully</td></tr>
<tr>
<td><code>ContainersReady</code></td><td>All containers have passed readiness probes</td></tr>
<tr>
<td><code>Ready</code></td><td>Pod can receive traffic (used by Services)</td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-3-creating-pods-imperative-vs-declarative">3. Creating Pods: Imperative vs Declarative</h2>
<p>These represent two fundamentally different approaches to managing infrastructure.</p>
<h3 id="heading-imperative-approach-telling-kubernetes-what-to-do">Imperative Approach: Telling Kubernetes What To Do</h3>
<p>With imperative commands, you issue direct orders to Kubernetes: "Run this container," "Delete this Pod," "Scale this deployment."</p>
<p><strong>The Imperative Mindset:</strong></p>
<ul>
<li><p>You are the operator</p>
</li>
<li><p>You execute commands in sequence</p>
</li>
<li><p>The cluster state is the result of your actions</p>
</li>
<li><p>No record exists of how you got to current state</p>
</li>
</ul>
<p><strong>Basic Pod Creation:</strong></p>
<pre><code class="lang-bash">kubectl run nginx-pod --image=nginx
</code></pre>
<p>This single command:</p>
<ol>
<li><p>Creates a Pod resource</p>
</li>
<li><p>Names it "nginx-pod"</p>
</li>
<li><p>Uses the nginx image</p>
</li>
<li><p>Applies default settings for everything else</p>
</li>
</ol>
<p><strong>Adding More Options:</strong></p>
<pre><code class="lang-bash">kubectl run nginx-pod \
  --image=nginx:1.19 \
  --port=80 \
  --labels=<span class="hljs-string">"app=web,env=prod"</span> \
  --env=<span class="hljs-string">"ENV=production"</span> \
  --restart=Never
</code></pre>
<p><strong>Common Imperative Commands:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Command</td><td>Purpose</td></tr>
</thead>
<tbody>
<tr>
<td><code>kubectl run NAME --image=IMAGE</code></td><td>Create a Pod</td></tr>
<tr>
<td><code>kubectl create deployment NAME --image=IMAGE</code></td><td>Create a Deployment</td></tr>
<tr>
<td><code>kubectl expose pod NAME --port=80</code></td><td>Create a Service</td></tr>
<tr>
<td><code>kubectl delete pod NAME</code></td><td>Delete a Pod</td></tr>
<tr>
<td><code>kubectl edit pod NAME</code></td><td>Edit live resource</td></tr>
<tr>
<td><code>kubectl scale deployment NAME --replicas=3</code></td><td>Scale deployment</td></tr>
</tbody>
</table>
</div><p><strong>Imperative Object Configuration:</strong></p>
<p>There's a middle ground — using imperative verbs with files:</p>
<pre><code class="lang-bash">kubectl create -f pod.yaml    <span class="hljs-comment"># Create (fails if exists)</span>
kubectl delete -f pod.yaml    <span class="hljs-comment"># Delete</span>
kubectl replace -f pod.yaml   <span class="hljs-comment"># Replace (must exist)</span>
</code></pre>
<p>This is still imperative because you're telling Kubernetes what to do, but you're using files to define the objects.</p>
<h3 id="heading-declarative-approach-telling-kubernetes-what-you-want">Declarative Approach: Telling Kubernetes What You Want</h3>
<p>With declarative configuration, you describe the desired state, and Kubernetes figures out how to achieve it.</p>
<p><strong>The Declarative Mindset:</strong></p>
<ul>
<li><p>You define the target state</p>
</li>
<li><p>Kubernetes continuously works toward that state</p>
</li>
<li><p>The definition IS the documentation</p>
</li>
<li><p>Changes are tracked through version control</p>
</li>
</ul>
<p><strong>The Core Command:</strong></p>
<pre><code class="lang-bash">kubectl apply -f pod.yaml
</code></pre>
<p><code>apply</code> is intelligent:</p>
<ul>
<li><p>If resource doesn't exist → create it</p>
</li>
<li><p>If resource exists → update it to match the file</p>
</li>
<li><p>If resource is unchanged → do nothing</p>
</li>
</ul>
<p><strong>Declarative Workflow:</strong></p>
<pre><code class="lang-plaintext">┌─────────────────┐
│   pod.yaml      │   Version controlled
│   (v1)          │   in Git
└────────┬────────┘
         │
         ▼
    kubectl apply -f pod.yaml
         │
         ▼
┌─────────────────┐
│   Kubernetes    │   Creates Pod
│   Cluster       │
└─────────────────┘

    ... time passes ...
    ... you need to make changes ...

┌─────────────────┐
│   pod.yaml      │   Edit the file
│   (v2)          │   Commit to Git
└────────┬────────┘
         │
         ▼
    kubectl apply -f pod.yaml
         │
         ▼
┌─────────────────┐
│   Kubernetes    │   Updates Pod to
│   Cluster       │   match new spec
└─────────────────┘
</code></pre>
<h3 id="heading-comparison-table">Comparison Table</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Aspect</td><td>Imperative</td><td>Declarative</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Command</strong></td><td><code>kubectl run</code>, <code>create</code>, <code>delete</code></td><td><code>kubectl apply</code></td></tr>
<tr>
<td><strong>State tracking</strong></td><td>None (you remember)</td><td>Git history</td></tr>
<tr>
<td><strong>Repeatability</strong></td><td>Run same commands</td><td>Apply same files</td></tr>
<tr>
<td><strong>Collaboration</strong></td><td>Share commands (error-prone)</td><td>Share files (reliable)</td></tr>
<tr>
<td><strong>Audit trail</strong></td><td>None</td><td>Git commits</td></tr>
<tr>
<td><strong>Rollback</strong></td><td>Remember previous commands</td><td>Revert Git commit</td></tr>
<tr>
<td><strong>Partial updates</strong></td><td>Tricky</td><td>Automatic</td></tr>
<tr>
<td><strong>Learning curve</strong></td><td>Lower</td><td>Higher</td></tr>
<tr>
<td><strong>CKAD exams</strong></td><td>Essential for speed</td><td>Required for complex tasks</td></tr>
</tbody>
</table>
</div><h3 id="heading-the-secret-weapon-generating-yaml-from-imperative-commands">The Secret Weapon: Generating YAML from Imperative Commands</h3>
<p>Here's the technique that makes you fast in CKAD:</p>
<pre><code class="lang-bash">kubectl run nginx --image=nginx --dry-run=client -o yaml &gt; pod.yaml
</code></pre>
<p><strong>Breaking this down:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Flag</td><td>Meaning</td></tr>
</thead>
<tbody>
<tr>
<td><code>--dry-run=client</code></td><td>Don't actually create anything; just simulate</td></tr>
<tr>
<td><code>-o yaml</code></td><td>Output the would-be resource as YAML</td></tr>
<tr>
<td><code>&gt; pod.yaml</code></td><td>Redirect output to a file</td></tr>
</tbody>
</table>
</div><p><strong>Generated output:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Pod</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">creationTimestamp:</span> <span class="hljs-literal">null</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">run:</span> <span class="hljs-string">nginx</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">nginx</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">containers:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">image:</span> <span class="hljs-string">nginx</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">nginx</span>
    <span class="hljs-attr">resources:</span> {}
  <span class="hljs-attr">dnsPolicy:</span> <span class="hljs-string">ClusterFirst</span>
  <span class="hljs-attr">restartPolicy:</span> <span class="hljs-string">Always</span>
<span class="hljs-attr">status:</span> {}
</code></pre>
<p>Now you can edit this file to add anything you need (environment variables, volumes, etc.) and apply it.</p>
<p><strong>More Examples:</strong></p>
<pre><code class="lang-bash"><span class="hljs-comment"># Generate Pod with port exposed</span>
kubectl run nginx --image=nginx --port=80 --dry-run=client -o yaml

<span class="hljs-comment"># Generate Pod with environment variable</span>
kubectl run nginx --image=nginx --env=<span class="hljs-string">"DB_HOST=mysql"</span> --dry-run=client -o yaml

<span class="hljs-comment"># Generate Pod that runs a command</span>
kubectl run busybox --image=busybox --dry-run=client -o yaml \
  --<span class="hljs-built_in">command</span> -- sleep 3600

<span class="hljs-comment"># Generate Pod with resource limits</span>
kubectl run nginx --image=nginx --dry-run=client -o yaml \
  --requests=<span class="hljs-string">'cpu=100m,memory=128Mi'</span> \
  --limits=<span class="hljs-string">'cpu=200m,memory=256Mi'</span>
</code></pre>
<hr />
<h2 id="heading-4-yaml-manifest-structure">4. YAML Manifest Structure</h2>
<h3 id="heading-the-four-required-top-level-fields">The Four Required Top-Level Fields</h3>
<p>Every Kubernetes resource YAML follows this structure:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">&lt;api-version&gt;</span>   <span class="hljs-comment"># 1. Which API version</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">&lt;resource-type&gt;</span>       <span class="hljs-comment"># 2. What kind of resource</span>
<span class="hljs-attr">metadata:</span>                   <span class="hljs-comment"># 3. Resource identification</span>
  <span class="hljs-comment"># ...</span>
<span class="hljs-attr">spec:</span>                       <span class="hljs-comment"># 4. Desired state</span>
  <span class="hljs-comment"># ...</span>
</code></pre>
<p>Let's examine each in detail.</p>
<h3 id="heading-field-1-apiversion">Field 1: apiVersion</h3>
<p>This tells Kubernetes which schema to use for parsing this resource. Different resources live in different API groups.</p>
<p><strong>How to find the right apiVersion:</strong></p>
<pre><code class="lang-bash"><span class="hljs-comment"># List all API resources and their versions</span>
kubectl api-resources

<span class="hljs-comment"># Output (partial):</span>
NAME          SHORTNAMES   APIVERSION   NAMESPACED   KIND
pods          po           v1           <span class="hljs-literal">true</span>         Pod
deployments   deploy       apps/v1      <span class="hljs-literal">true</span>         Deployment
services      svc          v1           <span class="hljs-literal">true</span>         Service
configmaps    cm           v1           <span class="hljs-literal">true</span>         ConfigMap
secrets                    v1           <span class="hljs-literal">true</span>         Secret
<span class="hljs-built_in">jobs</span>                       batch/v1     <span class="hljs-literal">true</span>         Job
cronjobs      cj           batch/v1     <span class="hljs-literal">true</span>         CronJob
</code></pre>
<p><strong>API Version Patterns:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Pattern</td><td>Example</td><td>Meaning</td></tr>
</thead>
<tbody>
<tr>
<td><code>v1</code></td><td><code>v1</code></td><td>Core API group, stable</td></tr>
<tr>
<td><code>GROUP/VERSION</code></td><td><code>apps/v1</code></td><td>Named group, stable</td></tr>
<tr>
<td><code>GROUP/v1beta1</code></td><td><a target="_blank" href="http://networking.k8s.io/v1beta1"><code>networking.k8s.io/v1beta1</code></a></td><td>Beta, may change</td></tr>
<tr>
<td><code>GROUP/v1alpha1</code></td><td><a target="_blank" href="http://example.io/v1alpha1"><code>example.io/v1alpha1</code></a></td><td>Alpha, experimental</td></tr>
</tbody>
</table>
</div><p><strong>Common apiVersions for CKAD:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Resource</td><td>apiVersion</td></tr>
</thead>
<tbody>
<tr>
<td>Pod, Service, ConfigMap, Secret, PersistentVolume, PersistentVolumeClaim</td><td><code>v1</code></td></tr>
<tr>
<td>Deployment, ReplicaSet, DaemonSet, StatefulSet</td><td><code>apps/v1</code></td></tr>
<tr>
<td>Job, CronJob</td><td><code>batch/v1</code></td></tr>
<tr>
<td>Ingress</td><td><a target="_blank" href="http://networking.k8s.io/v1"><code>networking.k8s.io/v1</code></a></td></tr>
<tr>
<td>NetworkPolicy</td><td><a target="_blank" href="http://networking.k8s.io/v1"><code>networking.k8s.io/v1</code></a></td></tr>
</tbody>
</table>
</div><h3 id="heading-field-2-kind">Field 2: kind</h3>
<p>The type of resource you're creating. This must match one of Kubernetes' registered resource types.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">kind:</span> <span class="hljs-string">Pod</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Service</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span>
</code></pre>
<p>The kind determines what fields are valid in the spec section.</p>
<h3 id="heading-field-3-metadata">Field 3: metadata</h3>
<p>This section provides the resource's identity and organizational information.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">my-pod</span>                    <span class="hljs-comment"># Required: unique name within namespace</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">default</span>              <span class="hljs-comment"># Optional: which namespace (default if omitted)</span>
  <span class="hljs-attr">labels:</span>                         <span class="hljs-comment"># Optional: key-value pairs for organization</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">myapp</span>
    <span class="hljs-attr">environment:</span> <span class="hljs-string">production</span>
    <span class="hljs-attr">version:</span> <span class="hljs-string">v1.2.3</span>
  <span class="hljs-attr">annotations:</span>                    <span class="hljs-comment"># Optional: non-identifying metadata</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">"Main application pod"</span>
    <span class="hljs-attr">owner:</span> <span class="hljs-string">"platform-team@company.com"</span>
    <span class="hljs-attr">git-commit:</span> <span class="hljs-string">"abc123def456"</span>
</code></pre>
<p><strong>Understanding Labels:</strong></p>
<p>Labels are key-value pairs that identify resources. They're used for:</p>
<ol>
<li><p><strong>Selection</strong> — Find resources matching criteria</p>
</li>
<li><p><strong>Grouping</strong> — Organize resources logically</p>
</li>
<li><p><strong>Service routing</strong> — Services use label selectors to find Pods</p>
</li>
</ol>
<pre><code class="lang-bash"><span class="hljs-comment"># Find all pods with label app=myapp</span>
kubectl get pods -l app=myapp

<span class="hljs-comment"># Find pods matching multiple labels</span>
kubectl get pods -l app=myapp,environment=production

<span class="hljs-comment"># Find pods where version is NOT v1</span>
kubectl get pods -l <span class="hljs-string">'version!=v1'</span>
</code></pre>
<p><strong>Label Best Practices:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Label Key</td><td>Purpose</td><td>Example</td></tr>
</thead>
<tbody>
<tr>
<td><code>app</code></td><td>Application name</td><td><code>app: frontend</code></td></tr>
<tr>
<td><code>environment</code></td><td>Deployment environment</td><td><code>environment: production</code></td></tr>
<tr>
<td><code>version</code></td><td>Application version</td><td><code>version: v2.1.0</code></td></tr>
<tr>
<td><code>tier</code></td><td>Architectural tier</td><td><code>tier: backend</code></td></tr>
<tr>
<td><code>team</code></td><td>Owning team</td><td><code>team: payments</code></td></tr>
</tbody>
</table>
</div><p><strong>Understanding Annotations:</strong></p>
<p>Annotations are for non-identifying information:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">annotations:</span>
  <span class="hljs-comment"># Documentation</span>
  <span class="hljs-attr">description:</span> <span class="hljs-string">"Handles user authentication"</span>

  <span class="hljs-comment"># Tooling</span>
  <span class="hljs-attr">prometheus.io/scrape:</span> <span class="hljs-string">"true"</span>
  <span class="hljs-attr">prometheus.io/port:</span> <span class="hljs-string">"9090"</span>

  <span class="hljs-comment"># Audit trail</span>
  <span class="hljs-attr">kubernetes.io/change-cause:</span> <span class="hljs-string">"Update to fix CVE-2024-1234"</span>

  <span class="hljs-comment"># Build information</span>
  <span class="hljs-attr">build.company.com/git-sha:</span> <span class="hljs-string">"abc123"</span>
  <span class="hljs-attr">build.company.com/pipeline:</span> <span class="hljs-string">"main-123"</span>
</code></pre>
<p><strong>Labels vs Annotations:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Aspect</td><td>Labels</td><td>Annotations</td></tr>
</thead>
<tbody>
<tr>
<td>Used for selection</td><td>Yes</td><td>No</td></tr>
<tr>
<td>Character limits</td><td>Key: 63, Value: 63</td><td>Key: 253, Value: 256KB</td></tr>
<tr>
<td>Purpose</td><td>Identify and group</td><td>Store metadata</td></tr>
<tr>
<td>Example use</td><td><code>app=nginx</code></td><td>Long JSON config</td></tr>
</tbody>
</table>
</div><h3 id="heading-field-4-spec">Field 4: spec</h3>
<p>This is where you define what you actually want. The structure depends on the <code>kind</code> of resource.</p>
<p><strong>Pod spec structure:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">spec:</span>
  <span class="hljs-comment"># Container definitions (required)</span>
  <span class="hljs-attr">containers:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">main</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">nginx</span>
    <span class="hljs-comment"># ... container-specific settings</span>

  <span class="hljs-comment"># Init containers (optional)</span>
  <span class="hljs-attr">initContainers:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">init</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">busybox</span>

  <span class="hljs-comment"># Volume definitions (optional)</span>
  <span class="hljs-attr">volumes:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">data</span>
    <span class="hljs-attr">emptyDir:</span> {}

  <span class="hljs-comment"># Pod-level settings</span>
  <span class="hljs-attr">restartPolicy:</span> <span class="hljs-string">Always</span>
  <span class="hljs-attr">serviceAccountName:</span> <span class="hljs-string">default</span>
  <span class="hljs-attr">nodeName:</span> <span class="hljs-string">specific-node</span>  <span class="hljs-comment"># Manual scheduling</span>
  <span class="hljs-attr">nodeSelector:</span>            <span class="hljs-comment"># Node selection by labels</span>
    <span class="hljs-attr">disktype:</span> <span class="hljs-string">ssd</span>
</code></pre>
<h3 id="heading-container-spec-deep-dive">Container Spec Deep Dive</h3>
<p>The container definition is the heart of a Pod spec:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">containers:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nginx</span>                        <span class="hljs-comment"># Required: container name</span>
  <span class="hljs-attr">image:</span> <span class="hljs-string">nginx:1.19</span>                  <span class="hljs-comment"># Required: image to run</span>

  <span class="hljs-comment"># Command and arguments</span>
  <span class="hljs-attr">command:</span> [<span class="hljs-string">"/bin/sh"</span>]               <span class="hljs-comment"># Override ENTRYPOINT</span>
  <span class="hljs-attr">args:</span> [<span class="hljs-string">"-c"</span>, <span class="hljs-string">"echo hello"</span>]         <span class="hljs-comment"># Override CMD</span>

  <span class="hljs-comment"># Environment variables</span>
  <span class="hljs-attr">env:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">DB_HOST</span>
    <span class="hljs-attr">value:</span> <span class="hljs-string">"mysql.default.svc"</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">DB_PASSWORD</span>
    <span class="hljs-attr">valueFrom:</span>
      <span class="hljs-attr">secretKeyRef:</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">db-secret</span>
        <span class="hljs-attr">key:</span> <span class="hljs-string">password</span>

  <span class="hljs-comment"># Port definitions</span>
  <span class="hljs-attr">ports:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">http</span>
    <span class="hljs-attr">containerPort:</span> <span class="hljs-number">80</span>
    <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span>

  <span class="hljs-comment"># Resource management</span>
  <span class="hljs-attr">resources:</span>
    <span class="hljs-attr">requests:</span>                        <span class="hljs-comment"># Minimum guaranteed</span>
      <span class="hljs-attr">memory:</span> <span class="hljs-string">"128Mi"</span>
      <span class="hljs-attr">cpu:</span> <span class="hljs-string">"100m"</span>
    <span class="hljs-attr">limits:</span>                          <span class="hljs-comment"># Maximum allowed</span>
      <span class="hljs-attr">memory:</span> <span class="hljs-string">"256Mi"</span>
      <span class="hljs-attr">cpu:</span> <span class="hljs-string">"200m"</span>

  <span class="hljs-comment"># Volume mounts</span>
  <span class="hljs-attr">volumeMounts:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">data</span>
    <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/var/data</span>
    <span class="hljs-attr">readOnly:</span> <span class="hljs-literal">false</span>

  <span class="hljs-comment"># Health checks</span>
  <span class="hljs-attr">livenessProbe:</span>
    <span class="hljs-attr">httpGet:</span>
      <span class="hljs-attr">path:</span> <span class="hljs-string">/health</span>
      <span class="hljs-attr">port:</span> <span class="hljs-number">80</span>
    <span class="hljs-attr">initialDelaySeconds:</span> <span class="hljs-number">10</span>
    <span class="hljs-attr">periodSeconds:</span> <span class="hljs-number">5</span>

  <span class="hljs-attr">readinessProbe:</span>
    <span class="hljs-attr">httpGet:</span>
      <span class="hljs-attr">path:</span> <span class="hljs-string">/ready</span>
      <span class="hljs-attr">port:</span> <span class="hljs-number">80</span>
    <span class="hljs-attr">initialDelaySeconds:</span> <span class="hljs-number">5</span>
    <span class="hljs-attr">periodSeconds:</span> <span class="hljs-number">3</span>

  <span class="hljs-comment"># Security settings</span>
  <span class="hljs-attr">securityContext:</span>
    <span class="hljs-attr">runAsUser:</span> <span class="hljs-number">1000</span>
    <span class="hljs-attr">runAsNonRoot:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">readOnlyRootFilesystem:</span> <span class="hljs-literal">true</span>
</code></pre>
<hr />
<h2 id="heading-5-multi-container-pods">5. Multi-Container Pods</h2>
<h3 id="heading-when-and-why-to-use-multi-container-pods">When and Why to Use Multi-Container Pods</h3>
<p>The general rule is: <strong>one container per Pod</strong>. But there are legitimate cases for multiple containers:</p>
<p><strong>Use Multi-Container Pods When:</strong></p>
<ul>
<li><p>Containers are tightly coupled and must share resources</p>
</li>
<li><p>One container enhances/supports another</p>
</li>
<li><p>Containers must run on the same node</p>
</li>
<li><p>They need to communicate via <a target="_blank" href="http://localhost">localhost</a></p>
</li>
<li><p>They share the same lifecycle</p>
</li>
</ul>
<p><strong>Don't Use Multi-Container Pods When:</strong></p>
<ul>
<li><p>Containers scale independently</p>
</li>
<li><p>Containers have different lifecycle needs</p>
</li>
<li><p>They could run on different nodes</p>
</li>
<li><p>Communication can happen over the network</p>
</li>
</ul>
<h3 id="heading-how-multi-container-pods-work">How Multi-Container Pods Work</h3>
<p>All containers in a Pod share:</p>
<p><strong>1. Network Namespace</strong></p>
<pre><code class="lang-plaintext">┌──────────────────────────────────────────────────┐
│                 Pod: my-app                      │
│              IP: 10.244.1.15                     │
│                                                  │
│  Container A                  Container B        │
│  Listens on :8080             Listens on :9090   │
│                                                  │
│  A can reach B at:            B can reach A at:  │
│  localhost:9090               localhost:8080     │
└──────────────────────────────────────────────────┘

External communication to either container uses 10.244.1.15
</code></pre>
<p><strong>2. Shared Volumes</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Pod</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">shared-volume-pod</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">containers:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">writer</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">busybox</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">'sh'</span>, <span class="hljs-string">'-c'</span>, <span class="hljs-string">'while true; do date &gt;&gt; /shared/log.txt; sleep 5; done'</span>]
    <span class="hljs-attr">volumeMounts:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">shared-data</span>
      <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/shared</span>

  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">reader</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">busybox</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">'sh'</span>, <span class="hljs-string">'-c'</span>, <span class="hljs-string">'tail -f /shared/log.txt'</span>]
    <span class="hljs-attr">volumeMounts:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">shared-data</span>
      <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/shared</span>

  <span class="hljs-attr">volumes:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">shared-data</span>
    <span class="hljs-attr">emptyDir:</span> {}
</code></pre>
<p>In this example:</p>
<ul>
<li><p><code>writer</code> appends timestamps to <code>/shared/log.txt</code></p>
</li>
<li><p><code>reader</code> continuously reads from the same file</p>
</li>
<li><p>Both see the same filesystem at <code>/shared</code></p>
</li>
</ul>
<p><strong>3. Same Node</strong></p>
<p>The scheduler places the entire Pod on one node. You're guaranteed both containers run on the same machine.</p>
<h3 id="heading-lifecycle-behavior">Lifecycle Behavior</h3>
<p><strong>Startup Order:</strong></p>
<ol>
<li><p>All init containers run first (sequentially)</p>
</li>
<li><p>All main containers start simultaneously</p>
</li>
<li><p>There's no guaranteed order among main containers</p>
</li>
</ol>
<p><strong>If One Container Crashes:</strong></p>
<ul>
<li><p>The crashed container is restarted (based on restartPolicy)</p>
</li>
<li><p>Other containers keep running</p>
</li>
<li><p>The Pod status reflects the issue</p>
</li>
<li><p>If crashes continue, you see CrashLoopBackOff</p>
</li>
</ul>
<p><strong>Example: Handling Container Dependencies</strong></p>
<p>If container B depends on container A being ready, use readiness probes:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">containers:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">database</span>
  <span class="hljs-attr">image:</span> <span class="hljs-string">postgres</span>
  <span class="hljs-attr">readinessProbe:</span>
    <span class="hljs-attr">tcpSocket:</span>
      <span class="hljs-attr">port:</span> <span class="hljs-number">5432</span>
    <span class="hljs-attr">initialDelaySeconds:</span> <span class="hljs-number">5</span>
    <span class="hljs-attr">periodSeconds:</span> <span class="hljs-number">5</span>

<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">app</span>
  <span class="hljs-attr">image:</span> <span class="hljs-string">myapp</span>
  <span class="hljs-comment"># App should handle DB not being ready initially</span>
  <span class="hljs-comment"># or use an init container to wait</span>
</code></pre>
<h3 id="heading-practical-multi-container-example">Practical Multi-Container Example</h3>
<p><strong>Log shipping pattern:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Pod</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">app-with-logging</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">containers:</span>
  <span class="hljs-comment"># Main application</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">app</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">myapp:latest</span>
    <span class="hljs-attr">volumeMounts:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">logs</span>
      <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/var/log/app</span>

  <span class="hljs-comment"># Sidecar that ships logs</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">log-shipper</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">fluentd:latest</span>
    <span class="hljs-attr">volumeMounts:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">logs</span>
      <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/var/log/app</span>
      <span class="hljs-attr">readOnly:</span> <span class="hljs-literal">true</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">fluentd-config</span>
      <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/fluentd/etc</span>

  <span class="hljs-attr">volumes:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">logs</span>
    <span class="hljs-attr">emptyDir:</span> {}
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">fluentd-config</span>
    <span class="hljs-attr">configMap:</span>
      <span class="hljs-attr">name:</span> <span class="hljs-string">fluentd-config</span>
</code></pre>
<hr />
<h2 id="heading-6-init-containers">6. Init Containers</h2>
<h3 id="heading-what-are-init-containers">What Are Init Containers?</h3>
<p>Init containers are specialized containers that run <strong>before</strong> your main application containers start. They run to completion, one at a time, in order.</p>
<p><strong>Key Characteristics:</strong></p>
<ul>
<li><p>Run sequentially, not in parallel</p>
</li>
<li><p>Each must complete successfully before the next starts</p>
</li>
<li><p>If any fails, the Pod restarts (based on restartPolicy)</p>
</li>
<li><p>Only after ALL init containers succeed do main containers start</p>
</li>
<li><p>Can have different images than main containers</p>
</li>
<li><p>Don't support readiness probes (they're not long-running)</p>
</li>
</ul>
<h3 id="heading-init-container-lifecycle">Init Container Lifecycle</h3>
<pre><code class="lang-plaintext">Pod Created
     │
     ▼
┌──────────────────┐
│ Init Container 1 │
│    Running       │
└────────┬─────────┘
         │ Exit 0
         ▼
┌──────────────────┐
│ Init Container 2 │
│    Running       │
└────────┬─────────┘
         │ Exit 0
         ▼
┌──────────────────┐
│ Init Container 3 │
│    Running       │
└────────┬─────────┘
         │ Exit 0
         ▼
┌──────────────────────────────────┐
│        Main Containers           │
│  (all start simultaneously)      │
└──────────────────────────────────┘
</code></pre>
<h3 id="heading-common-use-cases">Common Use Cases</h3>
<p><strong>1. Wait for a Dependency</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">initContainers:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">wait-for-database</span>
  <span class="hljs-attr">image:</span> <span class="hljs-string">busybox</span>
  <span class="hljs-attr">command:</span> [<span class="hljs-string">'sh'</span>, <span class="hljs-string">'-c'</span>, 
    <span class="hljs-string">'until nc -z database-service 5432; do 
       echo "Waiting for database..."; 
       sleep 2; 
     done; 
     echo "Database is ready"'</span>]
</code></pre>
<p>This ensures your app doesn't start until the database is reachable.</p>
<p><strong>2. Clone Code or Configuration</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">initContainers:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">clone-repo</span>
  <span class="hljs-attr">image:</span> <span class="hljs-string">alpine/git</span>
  <span class="hljs-attr">command:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">git</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">clone</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">--depth=1</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">https://github.com/company/config.git</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">/config</span>
  <span class="hljs-attr">volumeMounts:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">config</span>
    <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/config</span>
<span class="hljs-attr">containers:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">app</span>
  <span class="hljs-attr">image:</span> <span class="hljs-string">myapp</span>
  <span class="hljs-attr">volumeMounts:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">config</span>
    <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/app/config</span>
<span class="hljs-attr">volumes:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">config</span>
  <span class="hljs-attr">emptyDir:</span> {}
</code></pre>
<p><strong>3. Setup File Permissions</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">initContainers:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">fix-permissions</span>
  <span class="hljs-attr">image:</span> <span class="hljs-string">busybox</span>
  <span class="hljs-attr">command:</span> [<span class="hljs-string">'sh'</span>, <span class="hljs-string">'-c'</span>, <span class="hljs-string">'chown -R 1000:1000 /data'</span>]
  <span class="hljs-attr">volumeMounts:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">data</span>
    <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/data</span>
  <span class="hljs-attr">securityContext:</span>
    <span class="hljs-attr">runAsUser:</span> <span class="hljs-number">0</span>  <span class="hljs-comment"># Run as root to change ownership</span>
</code></pre>
<p><strong>4. Generate Configuration</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">initContainers:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">generate-config</span>
  <span class="hljs-attr">image:</span> <span class="hljs-string">busybox</span>
  <span class="hljs-attr">command:</span> [<span class="hljs-string">'sh'</span>, <span class="hljs-string">'-c'</span>, <span class="hljs-string">'echo "server_id=$(hostname)" &gt; /config/server.conf'</span>]
  <span class="hljs-attr">volumeMounts:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">config</span>
    <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/config</span>
</code></pre>
<p><strong>5. Database Migrations</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">initContainers:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">db-migrate</span>
  <span class="hljs-attr">image:</span> <span class="hljs-string">myapp:latest</span>
  <span class="hljs-attr">command:</span> [<span class="hljs-string">'./migrate.sh'</span>]
  <span class="hljs-attr">env:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">DATABASE_URL</span>
    <span class="hljs-attr">valueFrom:</span>
      <span class="hljs-attr">secretKeyRef:</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">db-credentials</span>
        <span class="hljs-attr">key:</span> <span class="hljs-string">url</span>
</code></pre>
<h3 id="heading-complete-init-container-example">Complete Init Container Example</h3>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Pod</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">myapp-pod</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">initContainers:</span>
  <span class="hljs-comment"># First: wait for database</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">wait-db</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">busybox:1.35</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">'sh'</span>, <span class="hljs-string">'-c'</span>, <span class="hljs-string">'until nc -z db-service 3306; do sleep 2; done'</span>]

  <span class="hljs-comment"># Second: wait for cache</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">wait-cache</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">busybox:1.35</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">'sh'</span>, <span class="hljs-string">'-c'</span>, <span class="hljs-string">'until nc -z redis-service 6379; do sleep 2; done'</span>]

  <span class="hljs-comment"># Third: download configuration</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">download-config</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">curlimages/curl:latest</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">'sh'</span>, <span class="hljs-string">'-c'</span>, <span class="hljs-string">'curl -o /config/app.json http://config-service/config'</span>]
    <span class="hljs-attr">volumeMounts:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">config</span>
      <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/config</span>

  <span class="hljs-attr">containers:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">app</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">myapp:v2</span>
    <span class="hljs-attr">ports:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">8080</span>
    <span class="hljs-attr">volumeMounts:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">config</span>
      <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/app/config</span>
    <span class="hljs-attr">env:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">CONFIG_PATH</span>
      <span class="hljs-attr">value:</span> <span class="hljs-string">"/app/config/app.json"</span>

  <span class="hljs-attr">volumes:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">config</span>
    <span class="hljs-attr">emptyDir:</span> {}
</code></pre>
<h3 id="heading-debugging-init-containers">Debugging Init Containers</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># See init container status</span>
kubectl get pod myapp-pod

<span class="hljs-comment"># Output might show:</span>
<span class="hljs-comment"># NAME        READY   STATUS     RESTARTS   AGE</span>
<span class="hljs-comment"># myapp-pod   0/1     Init:1/3   0          30s</span>

<span class="hljs-comment"># This means: 1 of 3 init containers completed</span>

<span class="hljs-comment"># Get logs from specific init container</span>
kubectl logs myapp-pod -c wait-db

<span class="hljs-comment"># If init container is currently running:</span>
kubectl logs myapp-pod -c wait-cache

<span class="hljs-comment"># Describe pod to see init container details</span>
kubectl describe pod myapp-pod
</code></pre>
<p><strong>Init Status Meanings:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Status</td><td>Meaning</td></tr>
</thead>
<tbody>
<tr>
<td><code>Init:0/3</code></td><td>0 of 3 init containers completed</td></tr>
<tr>
<td><code>Init:1/3</code></td><td>1 of 3 completed, second running</td></tr>
<tr>
<td><code>Init:Error</code></td><td>Current init container failed</td></tr>
<tr>
<td><code>Init:CrashLoopBackOff</code></td><td>Init container keeps crashing</td></tr>
<tr>
<td><code>PodInitializing</code></td><td>All init containers done, main starting</td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-7-static-pods">7. Static Pods</h2>
<h3 id="heading-what-are-static-pods">What Are Static Pods?</h3>
<p>Static Pods are managed directly by the kubelet on a specific node, without any involvement from the Kubernetes API server, scheduler, or controller manager.</p>
<p><strong>The key difference:</strong></p>
<ul>
<li><p>Normal Pods: API server → Scheduler → Kubelet</p>
</li>
<li><p>Static Pods: Kubelet watches a directory → Creates Pod directly</p>
</li>
</ul>
<h3 id="heading-how-static-pods-work">How Static Pods Work</h3>
<pre><code class="lang-plaintext">Normal Pod Flow:
┌─────────────┐    ┌───────────┐    ┌─────────┐    ┌─────────┐
│ kubectl     │───▶│API Server │───▶│Scheduler│───▶│ Kubelet │
│ apply       │    │  (etcd)   │    │         │    │         │
└─────────────┘    └───────────┘    └─────────┘    └─────────┘

Static Pod Flow:
┌─────────────────────────────────────────────────────────────┐
│                         Node                                │
│                                                             │
│  ┌─────────────────────────────────┐                        │
│  │  /etc/kubernetes/manifests/     │                        │
│  │    ├── etcd.yaml                │                        │
│  │    ├── kube-apiserver.yaml      │◀─── You put files here │
│  │    └── kube-scheduler.yaml      │                        │
│  └───────────────┬─────────────────┘                        │
│                  │                                          │
│                  │ Kubelet watches                          │
│                  ▼ this directory                           │
│  ┌─────────────────────────────────┐                        │
│  │           Kubelet               │                        │
│  │                                 │                        │
│  │  Automatically creates and      │                        │
│  │  manages Pods from manifests    │                        │
│  └─────────────────────────────────┘                        │
└─────────────────────────────────────────────────────────────┘
</code></pre>
<h3 id="heading-why-do-static-pods-exist">Why Do Static Pods Exist?</h3>
<p><strong>The Bootstrap Problem:</strong></p>
<p>When a Kubernetes cluster starts, you face a chicken-and-egg problem:</p>
<ul>
<li><p>The API server is a container that needs to be scheduled</p>
</li>
<li><p>But the scheduler is also a container that needs the API server</p>
</li>
<li><p>And etcd (the database) is also a container</p>
</li>
</ul>
<p>Static Pods solve this. The control plane components run as static Pods managed by kubelet directly, without needing the full cluster to be running.</p>
<p><strong>On a master node, you typically find:</strong></p>
<pre><code class="lang-plaintext">/etc/kubernetes/manifests/
├── etcd.yaml
├── kube-apiserver.yaml
├── kube-controller-manager.yaml
└── kube-scheduler.yaml
</code></pre>
<h3 id="heading-characteristics-of-static-pods">Characteristics of Static Pods</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Aspect</td><td>Static Pod Behavior</td></tr>
</thead>
<tbody>
<tr>
<td>Creation</td><td>Kubelet creates when manifest appears in watched directory</td></tr>
<tr>
<td>Deletion</td><td>Kubelet deletes when manifest is removed</td></tr>
<tr>
<td>Updates</td><td>Kubelet updates when manifest file changes</td></tr>
<tr>
<td>API visibility</td><td>A mirror Pod appears in API (read-only)</td></tr>
<tr>
<td>Scheduling</td><td>No scheduling—runs on the node where manifest exists</td></tr>
<tr>
<td>Pod naming</td><td>Node name is appended: <code>my-pod-node01</code></td></tr>
<tr>
<td>Control via kubectl</td><td>Cannot delete via API (recreates immediately)</td></tr>
</tbody>
</table>
</div><h3 id="heading-finding-the-static-pod-directory">Finding the Static Pod Directory</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Check kubelet configuration</span>
cat /var/lib/kubelet/config.yaml | grep staticPodPath

<span class="hljs-comment"># Or</span>
ps aux | grep kubelet | grep -- --pod-manifest-path

<span class="hljs-comment"># Common locations:</span>
<span class="hljs-comment"># /etc/kubernetes/manifests/</span>
<span class="hljs-comment"># /etc/kubelet.d/</span>
</code></pre>
<h3 id="heading-creating-a-static-pod">Creating a Static Pod</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># 1. Create a manifest file</span>
cat &lt;&lt;EOF &gt; /etc/kubernetes/manifests/static-nginx.yaml
apiVersion: v1
kind: Pod
metadata:
  name: static-nginx
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
EOF

<span class="hljs-comment"># 2. Kubelet automatically creates it</span>
<span class="hljs-comment"># Wait a few seconds...</span>

<span class="hljs-comment"># 3. Verify</span>
kubectl get pods
<span class="hljs-comment"># Shows: static-nginx-&lt;node-name&gt;</span>

<span class="hljs-comment"># 4. Try to delete it</span>
kubectl delete pod static-nginx-&lt;node-name&gt;
<span class="hljs-comment"># Pod immediately recreates!</span>

<span class="hljs-comment"># 5. Actually delete it by removing the file</span>
rm /etc/kubernetes/manifests/static-nginx.yaml
<span class="hljs-comment"># Now it's gone</span>
</code></pre>
<h3 id="heading-mirror-pods">Mirror Pods</h3>
<p>When kubelet creates a static Pod, it also creates a "mirror Pod" in the API server. This is read-only and lets you see the static Pod via <code>kubectl get pods</code>.</p>
<p><strong>Mirror Pod characteristics:</strong></p>
<ul>
<li><p>Has annotation: <a target="_blank" href="http://kubernetes.io/config.mirror"><code>kubernetes.io/config.mirror</code></a></p>
</li>
<li><p>Cannot be modified via API</p>
</li>
<li><p>Deleting it via kubectl does nothing (kubelet recreates)</p>
</li>
<li><p>Reflects the real Pod's status</p>
</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-comment"># Identify a mirror pod</span>
kubectl get pod static-nginx-node01 -o yaml | grep -A2 annotations
<span class="hljs-comment"># annotations:</span>
<span class="hljs-comment">#   kubernetes.io/config.mirror: "..."</span>
</code></pre>
<h3 id="heading-ckad-relevance">CKAD Relevance</h3>
<p>For CKAD, you need to know:</p>
<ul>
<li><p>What static Pods are</p>
</li>
<li><p>How to identify them</p>
</li>
<li><p>Where manifests are stored</p>
</li>
<li><p>That you can't manage them via kubectl</p>
</li>
</ul>
<p>You probably won't create static Pods in the exam, but you might be asked to identify them or understand why a Pod keeps recreating after deletion.</p>
<hr />
<h2 id="heading-8-pod-design-patterns">8. Pod Design Patterns</h2>
<p>These patterns describe standard ways to structure multi-container Pods. They come from distributed systems design and solve common problems.</p>
<h3 id="heading-pattern-1-sidecar">Pattern 1: Sidecar</h3>
<p><strong>Purpose:</strong> Extend or enhance the main container's functionality without modifying it.</p>
<p><strong>The Sidecar Analogy:</strong></p>
<p>Think of a motorcycle with a sidecar. The motorcycle (main container) does the primary work—driving. The sidecar (sidecar container) adds capability—carrying a passenger or cargo. They move together, share the journey, but have different jobs.</p>
<pre><code class="lang-plaintext">┌──────────────────────────────────────────────────────────────┐
│                            POD                               │
│                                                              │
│  ┌─────────────────────┐     ┌─────────────────────┐        │
│  │   Main Container    │     │  Sidecar Container  │        │
│  │                     │     │                     │        │
│  │   - Business logic  │     │   - Log shipping    │        │
│  │   - Writes logs to  │────▶│   - Watches /logs   │        │
│  │     /logs volume    │     │   - Ships to ELK    │        │
│  │                     │     │                     │        │
│  └─────────────────────┘     └─────────────────────┘        │
│            │                           │                     │
│            └───────────────────────────┘                     │
│                      Shared Volume                           │
│                        (/logs)                               │
└──────────────────────────────────────────────────────────────┘
</code></pre>
<p><strong>Common Sidecar Use Cases:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Use Case</td><td>Main Container</td><td>Sidecar</td></tr>
</thead>
<tbody>
<tr>
<td>Logging</td><td>App writes logs</td><td>Fluentd ships logs</td></tr>
<tr>
<td>Monitoring</td><td>App runs</td><td>Prometheus exporter</td></tr>
<tr>
<td>Security</td><td>App serves traffic</td><td>mTLS proxy (Istio)</td></tr>
<tr>
<td>Sync</td><td>App uses config</td><td>Git-sync updates config</td></tr>
<tr>
<td>Compression</td><td>App serves files</td><td>Compressor processes uploads</td></tr>
</tbody>
</table>
</div><p><strong>Complete Sidecar Example: Log Collection</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Pod</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">app-with-log-sidecar</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">containers:</span>
  <span class="hljs-comment"># Main application</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">app</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">myapp:latest</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">'sh'</span>, <span class="hljs-string">'-c'</span>, <span class="hljs-string">'while true; do echo "$(date) - App running" &gt;&gt; /var/log/app/app.log; sleep 5; done'</span>]
    <span class="hljs-attr">volumeMounts:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">log-volume</span>
      <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/var/log/app</span>

  <span class="hljs-comment"># Sidecar: ships logs to external system</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">log-shipper</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">busybox</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">'sh'</span>, <span class="hljs-string">'-c'</span>, <span class="hljs-string">'tail -f /var/log/app/app.log'</span>]  <span class="hljs-comment"># Simplified; real would send to logging system</span>
    <span class="hljs-attr">volumeMounts:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">log-volume</span>
      <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/var/log/app</span>
      <span class="hljs-attr">readOnly:</span> <span class="hljs-literal">true</span>

  <span class="hljs-attr">volumes:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">log-volume</span>
    <span class="hljs-attr">emptyDir:</span> {}
</code></pre>
<p><strong>Real-World Sidecar: Service Mesh (Istio)</strong></p>
<p>In Istio, every Pod gets an Envoy proxy sidecar automatically:</p>
<pre><code class="lang-plaintext">┌────────────────────────────────────────────────────────────────┐
│                            POD                                 │
│                                                                │
│  ┌──────────────────┐          ┌──────────────────────────┐   │
│  │    Your App      │          │    Envoy Sidecar         │   │
│  │                  │◀────────▶│                          │   │
│  │  Listens on      │          │  - mTLS encryption       │   │
│  │  localhost:8080  │          │  - Traffic management    │   │
│  │                  │          │  - Observability         │   │
│  │  No security     │          │  - Circuit breaking      │   │
│  │  code needed!    │          │                          │   │
│  └──────────────────┘          └────────────┬─────────────┘   │
│                                             │                  │
└─────────────────────────────────────────────┼──────────────────┘
                                              │
                                    All external traffic
                                    goes through Envoy
</code></pre>
<h3 id="heading-pattern-2-ambassador">Pattern 2: Ambassador</h3>
<p><strong>Purpose:</strong> Proxy outbound connections from the main container, simplifying how the app connects to external services.</p>
<p><strong>The Ambassador Analogy:</strong></p>
<p>An ambassador represents you in a foreign country. They handle the complexity of diplomacy, translation, and protocol. Your main container just talks to the ambassador (<a target="_blank" href="http://localhost">localhost</a>), and the ambassador handles the complex external communication.</p>
<pre><code class="lang-plaintext">┌──────────────────────────────────────────────────────────────────────────┐
│                                  POD                                     │
│                                                                          │
│  ┌─────────────────────┐       ┌───────────────────────┐                 │
│  │   Main Container    │       │   Ambassador          │                 │
│  │                     │       │                       │                 │
│  │   Connects to       │──────▶│   localhost:5432      │                 │
│  │   localhost:5432    │       │                       │                 │
│  │                     │       │   Handles:            │                 │
│  │   Simple config:    │       │   - Service discovery │                 │
│  │   DB_HOST=localhost │       │   - Load balancing    │                 │
│  │   DB_PORT=5432      │       │   - Failover          │                 │
│  │                     │       │   - Connection pooling│                 │
│  └─────────────────────┘       └───────────┬───────────┘                 │
│                                            │                             │
└────────────────────────────────────────────┼─────────────────────────────┘
                                             │
                                             ▼
                                    ┌────────────────────┐
                                    │ Database Cluster   │
                                    │  - Primary         │
                                    │  - Replica 1       │
                                    │  - Replica 2       │
                                    └────────────────────┘
</code></pre>
<p><strong>Why Use Ambassador Pattern:</strong></p>
<p>Without ambassador:</p>
<pre><code class="lang-python"><span class="hljs-comment"># App needs complex logic</span>
db = connect_with_retry(
    hosts=[<span class="hljs-string">'db-1.prod'</span>, <span class="hljs-string">'db-2.prod'</span>, <span class="hljs-string">'db-3.prod'</span>],
    load_balance=<span class="hljs-literal">True</span>,
    ssl=<span class="hljs-literal">True</span>,
    ...
)
</code></pre>
<p>With ambassador:</p>
<pre><code class="lang-python"><span class="hljs-comment"># App has simple logic</span>
db = connect(<span class="hljs-string">'localhost:5432'</span>)  <span class="hljs-comment"># Ambassador handles everything</span>
</code></pre>
<p><strong>Ambassador Example: Database Proxy</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Pod</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">app-with-ambassador</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">containers:</span>
  <span class="hljs-comment"># Main app - connects to localhost</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">app</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">myapp:latest</span>
    <span class="hljs-attr">env:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">DATABASE_HOST</span>
      <span class="hljs-attr">value:</span> <span class="hljs-string">"localhost"</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">DATABASE_PORT</span>
      <span class="hljs-attr">value:</span> <span class="hljs-string">"5432"</span>

  <span class="hljs-comment"># Ambassador - proxies to real database</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">db-ambassador</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">haproxy:latest</span>
    <span class="hljs-attr">ports:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">5432</span>
    <span class="hljs-attr">volumeMounts:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">haproxy-config</span>
      <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/usr/local/etc/haproxy</span>

  <span class="hljs-attr">volumes:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">haproxy-config</span>
    <span class="hljs-attr">configMap:</span>
      <span class="hljs-attr">name:</span> <span class="hljs-string">db-proxy-config</span>
</code></pre>
<p><strong>Common Ambassador Use Cases:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Scenario</td><td>Ambassador Does</td></tr>
</thead>
<tbody>
<tr>
<td>Database cluster</td><td>Routes to correct shard, handles failover</td></tr>
<tr>
<td>Redis cluster</td><td>Manages cluster topology, routes by key</td></tr>
<tr>
<td>External APIs</td><td>Handles retries, rate limiting, auth</td></tr>
<tr>
<td>Legacy systems</td><td>Protocol translation</td></tr>
</tbody>
</table>
</div><h3 id="heading-pattern-3-adapter">Pattern 3: Adapter</h3>
<p><strong>Purpose:</strong> Transform the main container's output into a format that external systems expect.</p>
<p><strong>The Adapter Analogy:</strong></p>
<p>Think of a power adapter. Your laptop (main container) outputs one type of power/data. The adapter transforms it to what the wall socket (external system) expects. The main container doesn't need to know about the external system's requirements.</p>
<pre><code class="lang-plaintext">┌──────────────────────────────────────────────────────────────────────────────┐
│                                  POD                                         │
│                                                                              │
│  ┌─────────────────────┐       ┌───────────────────────┐                     │
│  │   Main Container    │       │       Adapter         │                     │
│  │                     │       │                       │                     │
│  │   Outputs custom    │──────▶│   Reads custom format │                     │
│  │   log format        │       │   Converts to JSON    │────▶ Logging System │
│  │                     │       │                       │      (expects JSON) │
│  │   2024-01-15 ERROR  │       │   {"time":"2024...",  │                     │
│  │   User login failed │       │    "level":"ERROR",   │                     │
│  │                     │       │    "msg":"User..."}   │                     │
│  └─────────────────────┘       └───────────────────────┘                     │
│           │                              │                                   │
│           └──────────────────────────────┘                                   │
│                        Shared Volume                                         │
└──────────────────────────────────────────────────────────────────────────────┘
</code></pre>
<p><strong>Common Adapter Use Cases:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Input (Main Container)</td><td>Output (Adapter)</td><td>Consumer</td></tr>
</thead>
<tbody>
<tr>
<td>Custom log format</td><td>JSON logs</td><td>ELK Stack</td></tr>
<tr>
<td>Application metrics</td><td>Prometheus format</td><td>Prometheus</td></tr>
<tr>
<td>Custom health check</td><td>HTTP endpoint</td><td>Kubernetes probes</td></tr>
<tr>
<td>XML data</td><td>JSON data</td><td>Modern APIs</td></tr>
</tbody>
</table>
</div><p><strong>Adapter Example: Prometheus Exporter</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Pod</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">app-with-metrics-adapter</span>
  <span class="hljs-attr">annotations:</span>
    <span class="hljs-attr">prometheus.io/scrape:</span> <span class="hljs-string">"true"</span>
    <span class="hljs-attr">prometheus.io/port:</span> <span class="hljs-string">"9090"</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">containers:</span>
  <span class="hljs-comment"># Main app - writes metrics in custom format</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">app</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">legacy-app:latest</span>
    <span class="hljs-attr">volumeMounts:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">metrics</span>
      <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/var/metrics</span>

  <span class="hljs-comment"># Adapter - converts to Prometheus format</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">metrics-adapter</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">prom/statsd-exporter:latest</span>
    <span class="hljs-attr">ports:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">9090</span>
      <span class="hljs-attr">name:</span> <span class="hljs-string">metrics</span>
    <span class="hljs-attr">volumeMounts:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">metrics</span>
      <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/var/metrics</span>
      <span class="hljs-attr">readOnly:</span> <span class="hljs-literal">true</span>

  <span class="hljs-attr">volumes:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">metrics</span>
    <span class="hljs-attr">emptyDir:</span> {}
</code></pre>
<h3 id="heading-comparing-the-patterns">Comparing the Patterns</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Pattern</td><td>Direction</td><td>Purpose</td><td>Example</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Sidecar</strong></td><td>Same direction as main</td><td>Enhance/extend main container</td><td>Log shipping, config reload</td></tr>
<tr>
<td><strong>Ambassador</strong></td><td>Outbound</td><td>Simplify external connections</td><td>DB proxy, API gateway</td></tr>
<tr>
<td><strong>Adapter</strong></td><td>Outbound</td><td>Transform output format</td><td>Metrics conversion</td></tr>
</tbody>
</table>
</div><p><strong>Visual Comparison:</strong></p>
<pre><code class="lang-plaintext">SIDECAR (enhances):
┌─────────────────────────────────────┐
│ Main ────▶ Sidecar ────▶ External   │
│  │          (helps)                 │
│  └──────────────────────▶ External  │
└─────────────────────────────────────┘

AMBASSADOR (proxies outbound):
┌─────────────────────────────────────┐
│ Main ────▶ Ambassador ────▶ External│
│            (proxies)                │
└─────────────────────────────────────┘

ADAPTER (transforms output):
┌─────────────────────────────────────┐
│ Main ────▶ Adapter ────▶ External   │
│            (transforms)             │
└─────────────────────────────────────┘
</code></pre>
<hr />
<h2 id="heading-summary">Summary</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Topic</td><td>Key Takeaways</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Pod basics</strong></td><td>Smallest deployable unit; wraps containers; provides shared network/storage</td></tr>
<tr>
<td><strong>Lifecycle</strong></td><td>Pending → Running → Succeeded/Failed; containers have Waiting/Running/Terminated states</td></tr>
<tr>
<td><strong>Restart policies</strong></td><td>Always (services), OnFailure (jobs with retry), Never (one-shot jobs)</td></tr>
<tr>
<td><strong>Imperative</strong></td><td><code>kubectl run</code> — fast, no audit trail, good for quick tasks</td></tr>
<tr>
<td><strong>Declarative</strong></td><td>YAML + <code>kubectl apply</code> — reproducible, version-controlled, production standard</td></tr>
<tr>
<td><strong>YAML structure</strong></td><td>apiVersion, kind, metadata, spec — every resource follows this</td></tr>
<tr>
<td><strong>Multi-container</strong></td><td>Shared network (<a target="_blank" href="http://localhost">localhost</a>), shared volumes, same lifecycle</td></tr>
<tr>
<td><strong>Init containers</strong></td><td>Run before main containers, sequential, must succeed</td></tr>
<tr>
<td><strong>Static Pods</strong></td><td>Managed by kubelet directly, used for control plane, mirror pods in API</td></tr>
<tr>
<td><strong>Sidecar</strong></td><td>Helper that enhances main container (logs, mesh proxy)</td></tr>
<tr>
<td><strong>Ambassador</strong></td><td>Proxy for outbound connections (DB proxy)</td></tr>
<tr>
<td><strong>Adapter</strong></td><td>Transforms output format (metrics exporter)</td></tr>
</tbody>
</table>
</div>]]></content:encoded></item><item><title><![CDATA[Advanced Graph Theory]]></title><description><![CDATA[PART 1: ADVANCED STRONGLY CONNECTED COMPONENTS ALGORITHMS
Tarjan's Algorithm for Strongly Connected Components (Full Detail)
Tarjan's algorithm finds all strongly connected components in a directed graph in a single depth-first search pass. It's more...]]></description><link>https://arnavverma.hashnode.dev/advanced-graph-theory</link><guid isPermaLink="true">https://arnavverma.hashnode.dev/advanced-graph-theory</guid><dc:creator><![CDATA[Arnav Verma]]></dc:creator><pubDate>Sat, 20 Dec 2025 10:40:58 GMT</pubDate><content:encoded><![CDATA[<h3 id="heading-part-1-advanced-strongly-connected-components-algorithms">PART 1: ADVANCED STRONGLY CONNECTED COMPONENTS ALGORITHMS</h3>
<h4 id="heading-tarjans-algorithm-for-strongly-connected-components-full-detail">Tarjan's Algorithm for Strongly Connected Components (Full Detail)</h4>
<p>Tarjan's algorithm finds all strongly connected components in a directed graph in a single depth-first search pass. It's more elegant than Kosaraju's two-pass approach.</p>
<p><strong>Core Concepts:</strong></p>
<p>The algorithm maintains several pieces of information for each vertex:</p>
<ul>
<li><strong>index</strong>: The order in which vertices are visited (discovery time)</li>
<li><strong>lowlink</strong>: The smallest index of any vertex reachable from v, including v itself</li>
<li><strong>onStack</strong>: Boolean indicating if vertex is currently on the stack</li>
</ul>
<p><strong>Key Insight:</strong> A vertex v is the root of an SCC if and only if lowlink[v] = index[v]. This is because if lowlink[v] &lt; index[v], then v can reach some vertex discovered earlier, meaning v is part of an SCC rooted at that earlier vertex.</p>
<p><strong>Detailed Algorithm:</strong></p>
<pre><code>Global variables:
    index_counter = <span class="hljs-number">0</span>
    stack = empty stack
    index = {} (maps vertex to discovery time)
    lowlink = {} (maps vertex to lowlink value)
    onStack = {} (maps vertex to boolean)
    sccs = [] (list to store SCCs)

TARJAN-SCC(graph G):
    <span class="hljs-keyword">for</span> each vertex v <span class="hljs-keyword">in</span> G:
        index[v] = <span class="hljs-literal">undefined</span>
        lowlink[v] = <span class="hljs-literal">undefined</span>
        onStack[v] = <span class="hljs-literal">false</span>

    <span class="hljs-keyword">for</span> each vertex v <span class="hljs-keyword">in</span> G:
        <span class="hljs-keyword">if</span> index[v] is <span class="hljs-literal">undefined</span>:
            STRONGCONNECT(v)

    <span class="hljs-keyword">return</span> sccs

STRONGCONNECT(v):
    <span class="hljs-comment">// Set the depth index for v to the smallest unused index</span>
    index[v] = index_counter
    lowlink[v] = index_counter
    index_counter = index_counter + <span class="hljs-number">1</span>
    stack.push(v)
    onStack[v] = <span class="hljs-literal">true</span>

    <span class="hljs-comment">// Consider successors of v</span>
    <span class="hljs-keyword">for</span> each edge (v, w) <span class="hljs-keyword">in</span> G.edges:
        <span class="hljs-keyword">if</span> index[w] is <span class="hljs-literal">undefined</span>:
            <span class="hljs-comment">// Successor w has not yet been visited; recurse on it</span>
            STRONGCONNECT(w)
            <span class="hljs-comment">// After returning from recursion, update lowlink[v]</span>
            <span class="hljs-comment">// v can reach everything w can reach</span>
            lowlink[v] = min(lowlink[v], lowlink[w])
        <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> onStack[w]:
            <span class="hljs-comment">// Successor w is in stack S and hence in the current SCC</span>
            <span class="hljs-comment">// If w is not on stack, then (v, w) is a cross-edge in the DFS tree</span>
            <span class="hljs-comment">// and must be ignored</span>
            <span class="hljs-comment">// Note: The next line may look odd - we are taking the min of lowlink[v]</span>
            <span class="hljs-comment">// and index[w], not lowlink[w]. This is because of how the algorithm works.</span>
            lowlink[v] = min(lowlink[v], index[w])

    <span class="hljs-comment">// If v is a root node, pop the stack and print an SCC</span>
    <span class="hljs-keyword">if</span> lowlink[v] == index[v]:
        <span class="hljs-comment">// Start a new strongly connected component</span>
        current_scc = []
        <span class="hljs-attr">repeat</span>:
            w = stack.pop()
            onStack[w] = <span class="hljs-literal">false</span>
            current_scc.append(w)
        until w == v
        sccs.append(current_scc)
</code></pre><p><strong>Why This Works - Deep Explanation:</strong></p>
<ol>
<li><p><strong>The lowlink invariant</strong>: After STRONGCONNECT(v) returns, lowlink[v] is the smallest index of any vertex on the stack that is reachable from v through vertices in the subtree rooted at v in the DFS tree.</p>
</li>
<li><p><strong>Stack management</strong>: The stack contains vertices that have been visited but not yet assigned to an SCC. Vertices are pushed in DFS order and popped when an SCC root is identified.</p>
</li>
<li><p><strong>The crucial line</strong>: <code>lowlink[v] = min(lowlink[v], index[w])</code> when w is on the stack. We use index[w] not lowlink[w] because:</p>
<ul>
<li>If w is on the stack and we haven't finished processing it, then index[w] represents the earliest vertex we can reach</li>
<li>Using lowlink[w] would be wrong because lowlink[w] might reference vertices not in the current path</li>
</ul>
</li>
<li><p><strong>SCC identification</strong>: When lowlink[v] == index[v], vertex v cannot reach any vertex discovered earlier, so v is the root of an SCC containing all vertices above it on the stack.</p>
</li>
</ol>
<p><strong>Example Walkthrough:</strong></p>
<p>Consider graph: 1→2, 2→3, 3→1, 3→4, 4→5, 5→4</p>
<pre><code>Step-by-step execution:

Start <span class="hljs-keyword">with</span> vertex <span class="hljs-number">1</span>:
- index[<span class="hljs-number">1</span>] = <span class="hljs-number">0</span>, lowlink[<span class="hljs-number">1</span>] = <span class="hljs-number">0</span>, push <span class="hljs-number">1</span> to stack
- Visit <span class="hljs-number">2</span>: index[<span class="hljs-number">2</span>] = <span class="hljs-number">1</span>, lowlink[<span class="hljs-number">2</span>] = <span class="hljs-number">1</span>, push <span class="hljs-number">2</span> to stack
- Visit <span class="hljs-number">3</span>: index[<span class="hljs-number">3</span>] = <span class="hljs-number">2</span>, lowlink[<span class="hljs-number">3</span>] = <span class="hljs-number">2</span>, push <span class="hljs-number">3</span> to stack
- Edge <span class="hljs-number">3</span>→<span class="hljs-number">1</span>: <span class="hljs-number">1</span> is on stack, lowlink[<span class="hljs-number">3</span>] = min(<span class="hljs-number">2</span>, <span class="hljs-number">0</span>) = <span class="hljs-number">0</span>
- Edge <span class="hljs-number">3</span>→<span class="hljs-number">4</span>: Visit <span class="hljs-number">4</span>
  - index[<span class="hljs-number">4</span>] = <span class="hljs-number">3</span>, lowlink[<span class="hljs-number">4</span>] = <span class="hljs-number">3</span>, push <span class="hljs-number">4</span> to stack
  - Visit <span class="hljs-number">5</span>: index[<span class="hljs-number">5</span>] = <span class="hljs-number">4</span>, lowlink[<span class="hljs-number">5</span>] = <span class="hljs-number">4</span>, push <span class="hljs-number">5</span> to stack
  - Edge <span class="hljs-number">5</span>→<span class="hljs-number">4</span>: <span class="hljs-number">4</span> is on stack, lowlink[<span class="hljs-number">5</span>] = min(<span class="hljs-number">4</span>, <span class="hljs-number">3</span>) = <span class="hljs-number">3</span>
  - Return <span class="hljs-keyword">from</span> <span class="hljs-number">5</span>: lowlink[<span class="hljs-number">5</span>] = <span class="hljs-number">3</span>, not equal to index[<span class="hljs-number">5</span>] = <span class="hljs-number">4</span>, so not root
  - Back <span class="hljs-keyword">in</span> <span class="hljs-number">4</span>: lowlink[<span class="hljs-number">4</span>] = min(<span class="hljs-number">3</span>, <span class="hljs-number">3</span>) = <span class="hljs-number">3</span>
  - Return <span class="hljs-keyword">from</span> <span class="hljs-number">4</span>: lowlink[<span class="hljs-number">4</span>] = <span class="hljs-number">3</span> == index[<span class="hljs-number">4</span>] = <span class="hljs-number">3</span>, SO <span class="hljs-number">4</span> IS ROOT
  - Pop stack until we reach <span class="hljs-number">4</span>: SCC = {<span class="hljs-number">5</span>, <span class="hljs-number">4</span>}
- Back <span class="hljs-keyword">in</span> <span class="hljs-number">3</span>: lowlink[<span class="hljs-number">3</span>] = min(<span class="hljs-number">0</span>, <span class="hljs-number">3</span>) = <span class="hljs-number">0</span> (no update <span class="hljs-keyword">from</span> <span class="hljs-number">4</span> since it finished)
- Return <span class="hljs-keyword">from</span> <span class="hljs-number">3</span>: lowlink[<span class="hljs-number">3</span>] = <span class="hljs-number">0</span>, not equal to index[<span class="hljs-number">3</span>] = <span class="hljs-number">2</span>
- Back <span class="hljs-keyword">in</span> <span class="hljs-number">2</span>: lowlink[<span class="hljs-number">2</span>] = min(<span class="hljs-number">1</span>, <span class="hljs-number">0</span>) = <span class="hljs-number">0</span>
- Return <span class="hljs-keyword">from</span> <span class="hljs-number">2</span>: lowlink[<span class="hljs-number">2</span>] = <span class="hljs-number">0</span>, not equal to index[<span class="hljs-number">2</span>] = <span class="hljs-number">1</span>
- Back <span class="hljs-keyword">in</span> <span class="hljs-number">1</span>: lowlink[<span class="hljs-number">1</span>] = min(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>) = <span class="hljs-number">0</span>
- Return <span class="hljs-keyword">from</span> <span class="hljs-number">1</span>: lowlink[<span class="hljs-number">1</span>] = <span class="hljs-number">0</span> == index[<span class="hljs-number">1</span>] = <span class="hljs-number">0</span>, SO <span class="hljs-number">1</span> IS ROOT
- Pop stack until we reach <span class="hljs-number">1</span>: SCC = {<span class="hljs-number">3</span>, <span class="hljs-number">2</span>, <span class="hljs-number">1</span>}

<span class="hljs-attr">Result</span>: Two SCCs: {<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>} and {<span class="hljs-number">4</span>, <span class="hljs-number">5</span>}
</code></pre><p><strong>Time Complexity Analysis:</strong></p>
<ul>
<li>Each vertex is visited exactly once: O(V)</li>
<li>Each edge is examined exactly once: O(E)</li>
<li>Stack operations are O(1) amortized</li>
<li>Total: O(V + E)</li>
</ul>
<p><strong>Space Complexity:</strong></p>
<ul>
<li>Stack: O(V) worst case (all vertices on stack)</li>
<li>Hash maps: O(V)</li>
<li>Recursion depth: O(V) worst case</li>
<li>Total: O(V)</li>
</ul>
<hr />
<h3 id="heading-part-2-maximum-bipartite-matching-hopcroft-karp-algorithm">PART 2: MAXIMUM BIPARTITE MATCHING - HOPCROFT-KARP ALGORITHM</h3>
<p>The Hopcroft-Karp algorithm finds maximum cardinality matching in bipartite graphs in O(E√V) time, which is significantly faster than using Ford-Fulkerson (O(VE)) for dense graphs.</p>
<p><strong>Key Innovation:</strong> Instead of finding one augmenting path at a time, find a maximal set of shortest vertex-disjoint augmenting paths simultaneously.</p>
<p><strong>Definitions:</strong></p>
<ul>
<li><strong>Free vertex</strong>: A vertex not included in the current matching</li>
<li><strong>Augmenting path</strong>: A path that starts and ends at free vertices, alternating between non-matching and matching edges</li>
<li><strong>Level</strong>: Distance from free vertices in U (left partition)</li>
</ul>
<p><strong>Data Structures:</strong></p>
<pre><code>G = (U ∪ V, E)  <span class="hljs-comment">// Bipartite graph</span>
match_U = {}    <span class="hljs-comment">// match_U[u] = v means u is matched to v</span>
match_V = {}    <span class="hljs-comment">// match_V[v] = u means v is matched to u</span>
dist = {}       <span class="hljs-comment">// Distance from free vertices during BFS</span>
NIL = special <span class="hljs-literal">null</span> vertex
</code></pre><p><strong>Detailed Algorithm:</strong></p>
<pre><code>HOPCROFT-KARP(G):
    <span class="hljs-comment">// Initialize: no vertices matched</span>
    <span class="hljs-keyword">for</span> each u <span class="hljs-keyword">in</span> U:
        match_U[u] = NIL
    <span class="hljs-keyword">for</span> each v <span class="hljs-keyword">in</span> V:
        match_V[v] = NIL

    matching = <span class="hljs-number">0</span>

    <span class="hljs-comment">// Repeat while we can find augmenting paths</span>
    <span class="hljs-keyword">while</span> BFS():
        <span class="hljs-keyword">for</span> each u <span class="hljs-keyword">in</span> U:
            <span class="hljs-keyword">if</span> match_U[u] == NIL:
                <span class="hljs-keyword">if</span> DFS(u):
                    matching = matching + <span class="hljs-number">1</span>

    <span class="hljs-keyword">return</span> matching

BFS():
    <span class="hljs-comment">// Build level graph using BFS from all free vertices in U</span>
    queue = empty queue

    <span class="hljs-keyword">for</span> each u <span class="hljs-keyword">in</span> U:
        <span class="hljs-keyword">if</span> match_U[u] == NIL:
            dist[u] = <span class="hljs-number">0</span>
            queue.enqueue(u)
        <span class="hljs-attr">else</span>:
            dist[u] = INFINITY

    dist[NIL] = INFINITY

    <span class="hljs-keyword">while</span> queue is not empty:
        u = queue.dequeue()

        <span class="hljs-keyword">if</span> dist[u] &lt; dist[NIL]:
            <span class="hljs-keyword">for</span> each v <span class="hljs-keyword">in</span> Adj[u]:
                <span class="hljs-comment">// v is either free or matched to some u'</span>
                u_prime = match_V[v]

                <span class="hljs-keyword">if</span> dist[u_prime] == INFINITY:
                    <span class="hljs-comment">// We haven't seen u_prime yet</span>
                    dist[u_prime] = dist[u] + <span class="hljs-number">1</span>
                    queue.enqueue(u_prime)

    <span class="hljs-comment">// Return true if we found at least one augmenting path</span>
    <span class="hljs-keyword">return</span> dist[NIL] != INFINITY

DFS(u):
    <span class="hljs-comment">// Try to find an augmenting path from u using only edges in level graph</span>
    <span class="hljs-keyword">if</span> u != NIL:
        <span class="hljs-keyword">for</span> each v <span class="hljs-keyword">in</span> Adj[u]:
            u_prime = match_V[v]

            <span class="hljs-comment">// Only follow edges that go to the next level</span>
            <span class="hljs-keyword">if</span> dist[u_prime] == dist[u] + <span class="hljs-number">1</span>:
                <span class="hljs-keyword">if</span> DFS(u_prime):
                    <span class="hljs-comment">// Found augmenting path, update matching</span>
                    match_V[v] = u
                    match_U[u] = v
                    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>

        <span class="hljs-comment">// No augmenting path found through u</span>
        dist[u] = INFINITY
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>

    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>  <span class="hljs-comment">// Reached NIL, found augmenting path</span>
</code></pre><p><strong>How It Works - Phase by Phase:</strong></p>
<p><strong>Phase Structure:</strong>
Each phase consists of one BFS followed by multiple DFS calls. The BFS builds a level graph, and the DFS finds all maximal vertex-disjoint augmenting paths of the minimum length.</p>
<p><strong>Phase 1 Example:</strong></p>
<pre><code>Initial state: No matching
U = {u1, u2, u3}
V = {v1, v2, v3}
<span class="hljs-attr">Edges</span>: u1-v1, u1-v2, u2-v2, u2-v3, u3-v1

BFS Phase:
- Start <span class="hljs-keyword">from</span> all free vertices <span class="hljs-keyword">in</span> U: {u1, u2, u3}
- dist[u1] = dist[u2] = dist[u3] = <span class="hljs-number">0</span>
- Level <span class="hljs-number">0</span>: {u1, u2, u3}
- From u1: can reach v1, v2 (both free)
- From u2: can reach v2, v3 (both free)
- From u3: can reach v1 (free)
- All reached vertices <span class="hljs-keyword">in</span> V are free, so dist[NIL] = <span class="hljs-number">1</span>

DFS Phase:
- Try u1: finds path u1-v1, match them
- Try u2: v2 is now free, finds path u2-v2, match them
- Try u3: v1 is matched, but path through v3 works: u3 is already matched <span class="hljs-keyword">in</span> <span class="hljs-built_in">this</span> phase? No wait...
- Actually: Try u3: v1 is matched to u1. For v3: u2-v2 is matched.
  We can find u3-v3? Check edges: u3-v1 only. So u3 stays free.

Result after Phase <span class="hljs-number">1</span>: Matching = {(u1,v1), (u2,v2)}, size = <span class="hljs-number">2</span>
</code></pre><p><strong>Why O(E√V)?</strong></p>
<p><strong>Key Lemmas:</strong></p>
<ol>
<li><p><strong>The length of shortest augmenting paths increases</strong>: Each phase finds augmenting paths of length L, and the next phase finds paths of length at least L+2 (strictly increasing by 2 because paths alternate).</p>
</li>
<li><p><strong>Number of phases</strong>: At most √V phases. Proof:</p>
<ul>
<li>Let M* be the maximum matching size</li>
<li>After k phases, suppose matching size is M</li>
<li>Number of remaining augmenting paths ≤ M* - M</li>
<li>Average length of augmenting path in phase k: at least 2k + 1</li>
<li>By a counting argument, when k &gt; √V, we've found most of the matching</li>
<li>Specifically, after √V phases, remaining paths are so long that there can't be many of them</li>
</ul>
</li>
<li><p><strong>Work per phase</strong>: O(E)</p>
<ul>
<li>BFS: O(E) to examine all edges</li>
<li>DFS: Each edge examined once per phase (because we mark vertices as done with dist[u] = INFINITY)</li>
</ul>
</li>
<li><p><strong>Total complexity</strong>: O(√V) phases × O(E) per phase = O(E√V)</p>
</li>
</ol>
<p><strong>Detailed Proof of √V Bound on Phases:</strong></p>
<p>Let M be current matching size and M* be maximum matching size.</p>
<p>Consider the symmetric difference M ⊕ M<em> (edges in M or M</em> but not both). This forms a collection of vertex-disjoint paths and cycles where:</p>
<ul>
<li>Cycles have equal numbers of M and M* edges</li>
<li>Paths have one more edge from either M or M*</li>
</ul>
<p>The paths with more M<em> edges are augmenting paths with respect to M. There are M</em> - M such paths (since they account for the difference in matching sizes).</p>
<p>If the shortest augmenting path has length L ≥ 2k + 1, then the total number of edges in M ⊕ M* is at least:</p>
<ul>
<li>(M* - M) × (2k + 1) edges in augmenting paths</li>
<li>Plus edges in cycles and paths favoring M</li>
</ul>
<p>But M ⊕ M<em> has exactly M + M</em> edges (each matching contributes all its edges).</p>
<p>Therefore: (M<em> - M)(2k + 1) ≤ M + M</em> ≤ 2M*</p>
<p>This gives us: M<em> - M ≤ 2M</em>/(2k + 1)</p>
<p>When k &gt; √V, we have 2k + 1 &gt; 2√V, so:
M<em> - M ≤ 2M</em>/(2√V) ≤ V/√V = √V</p>
<p>So after √V phases, we're within √V edges of the maximum matching. But each subsequent phase must add at least one edge, and there are at most √V remaining, so at most √V more phases.</p>
<p>Total: at most 2√V phases, which is O(√V).</p>
<hr />
<h3 id="heading-part-3-hungarian-algorithm-kuhn-munkres-algorithm">PART 3: HUNGARIAN ALGORITHM (KUHN-MUNKRES ALGORITHM)</h3>
<p>The Hungarian algorithm solves the assignment problem: given a weighted bipartite graph, find a perfect matching with minimum total weight (or maximum, for profits).</p>
<p><strong>Problem Statement:</strong>
Given n workers, n jobs, and cost c[i][j] for assigning worker i to job j, find an assignment minimizing total cost.</p>
<p><strong>Graph Formulation:</strong></p>
<ul>
<li>Bipartite graph G = (U ∪ V, E) where |U| = |V| = n</li>
<li>Weight function w: E → ℝ</li>
<li>Find perfect matching M minimizing Σ w(e) for e ∈ M</li>
</ul>
<p><strong>Core Concepts:</strong></p>
<p><strong>Feasible Labeling:</strong> A function l: U ∪ V → ℝ such that:
l(u) + l(v) ≥ w(u,v) for all edges (u,v)</p>
<p>Think of l(u) as the "price" worker u demands, and l(v) as the "budget" job v offers. The edge is "tight" (affordable) if l(u) + l(v) = w(u,v).</p>
<p><strong>Equality Subgraph:</strong> Gl = graph containing only tight edges:
Gl = {(u,v) : l(u) + l(v) = w(u,v)}</p>
<p><strong>Key Theorem (Kuhn-Munkres):</strong> If l is a feasible labeling and M is a perfect matching in Gl, then M is a minimum-weight perfect matching in G.</p>
<p><strong>Proof:</strong> Let M* be any perfect matching. Then:
w(M) = Σ(u,v)∈M w(u,v) = Σ(u,v)∈M [l(u) + l(v)] (tight edges)
     = Σu∈U l(u) + Σv∈V l(v)</p>
<p>w(M<em>) = Σ(u,v)∈M</em> w(u,v) ≥ Σ(u,v)∈M<em> [l(u) + l(v)] (feasible labeling)
      = Σu∈U l(u) + Σv∈V l(v) (M</em> is perfect matching)</p>
<p>Therefore w(M) ≤ w(M*), so M is optimal.</p>
<p><strong>Algorithm Strategy:</strong></p>
<ol>
<li>Start with a feasible labeling</li>
<li>Find maximum matching in equality subgraph</li>
<li>If not perfect, improve labeling to add more tight edges</li>
<li>Repeat until perfect matching found</li>
</ol>
<p><strong>Detailed Algorithm:</strong></p>
<pre><code>HUNGARIAN-ALGORITHM(cost_matrix C[<span class="hljs-number">1.</span>.n][<span class="hljs-number">1.</span>.n]):
    <span class="hljs-comment">// Step 1: Initialize feasible labeling</span>
    <span class="hljs-keyword">for</span> each worker u <span class="hljs-keyword">in</span> <span class="hljs-number">1.</span>.n:
        l[u] = max{C[u][v] : v <span class="hljs-keyword">in</span> <span class="hljs-number">1.</span>.n}  <span class="hljs-comment">// maximum cost in row</span>
    <span class="hljs-keyword">for</span> each job v <span class="hljs-keyword">in</span> <span class="hljs-number">1.</span>.n:
        l[v] = <span class="hljs-number">0</span>

    <span class="hljs-comment">// Initially, for each worker u, there exists at least one tight edge</span>
    <span class="hljs-comment">// because l[u] is set to the max cost in their row</span>

    match_U = {} <span class="hljs-comment">// match_U[u] = v means worker u matched to job v</span>
    match_V = {} <span class="hljs-comment">// match_V[v] = u means job v matched to worker u</span>

    <span class="hljs-keyword">for</span> each u <span class="hljs-keyword">in</span> match_U:
        match_U[u] = NIL
    <span class="hljs-keyword">for</span> each v <span class="hljs-keyword">in</span> match_V:
        match_V[v] = NIL

    <span class="hljs-comment">// Step 2: Iteratively build matching</span>
    <span class="hljs-keyword">for</span> each worker u <span class="hljs-keyword">in</span> <span class="hljs-number">1.</span>.n:
        AUGMENT(u)

    <span class="hljs-keyword">return</span> match_U

AUGMENT(root):
    <span class="hljs-comment">// Try to find augmenting path for 'root' worker</span>
    <span class="hljs-comment">// Using BFS/DFS on equality subgraph</span>

    visited_U = {root}
    visited_V = {}
    slack = {}  <span class="hljs-comment">// slack[v] = minimum amount to make edge to v tight</span>
    slack_source = {}  <span class="hljs-comment">// which u gives slack[v]</span>

    <span class="hljs-keyword">for</span> each job v:
        slack[v] = INFINITY

    <span class="hljs-keyword">while</span> <span class="hljs-literal">true</span>:
        <span class="hljs-comment">// Step A: Try to find augmenting path with current labeling</span>
        found_augmenting_path = <span class="hljs-literal">false</span>

        <span class="hljs-keyword">for</span> each u <span class="hljs-keyword">in</span> visited_U:
            <span class="hljs-keyword">for</span> each v <span class="hljs-keyword">in</span> <span class="hljs-number">1.</span>.n:
                <span class="hljs-keyword">if</span> v not <span class="hljs-keyword">in</span> visited_V:
                    gap = l[u] + l[v] - C[u][v]

                    <span class="hljs-keyword">if</span> gap == <span class="hljs-number">0</span>:  <span class="hljs-comment">// Tight edge in equality graph</span>
                        visited_V.add(v)
                        <span class="hljs-keyword">if</span> match_V[v] == NIL:
                            <span class="hljs-comment">// Found augmenting path! Update matching</span>
                            UPDATE-MATCHING(root, v)
                            <span class="hljs-keyword">return</span>
                        <span class="hljs-keyword">else</span>:
                            <span class="hljs-comment">// v is matched, add its match to visited_U</span>
                            visited_U.add(match_V[v])
                            found_augmenting_path = <span class="hljs-literal">true</span>
                    <span class="hljs-attr">else</span>:
                        <span class="hljs-comment">// Update slack for v</span>
                        <span class="hljs-keyword">if</span> l[u] + l[v] - C[u][v] &lt; slack[v]:
                            slack[v] = l[u] + l[v] - C[u][v]
                            slack_source[v] = u

        <span class="hljs-keyword">if</span> found_augmenting_path:
            <span class="hljs-keyword">continue</span>  <span class="hljs-comment">// Try again with expanded tree</span>

        <span class="hljs-comment">// Step B: No augmenting path with current labeling</span>
        <span class="hljs-comment">// Update labels to add more tight edges</span>

        <span class="hljs-comment">// Find minimum slack among unvisited jobs</span>
        delta = min{slack[v] : v not <span class="hljs-keyword">in</span> visited_V}

        <span class="hljs-comment">// Update labels</span>
        <span class="hljs-keyword">for</span> each u <span class="hljs-keyword">in</span> visited_U:
            l[u] = l[u] - delta
        <span class="hljs-keyword">for</span> each v <span class="hljs-keyword">in</span> visited_V:
            l[v] = l[v] + delta
        <span class="hljs-keyword">for</span> each v not <span class="hljs-keyword">in</span> visited_V:
            slack[v] = slack[v] - delta

        <span class="hljs-comment">// After update, at least one new edge becomes tight</span>
        <span class="hljs-comment">// Add all newly tight edges to the equality graph</span>
        <span class="hljs-keyword">for</span> each v not <span class="hljs-keyword">in</span> visited_V:
            <span class="hljs-keyword">if</span> slack[v] == <span class="hljs-number">0</span>:
                visited_V.add(v)
                <span class="hljs-keyword">if</span> match_V[v] == NIL:
                    UPDATE-MATCHING(root, v)
                    <span class="hljs-keyword">return</span>
                <span class="hljs-keyword">else</span>:
                    visited_U.add(match_V[v])

UPDATE-MATCHING(start_u, end_v):
    <span class="hljs-comment">// Reconstruct augmenting path and flip matching</span>
    <span class="hljs-comment">// This is a simplified version; full implementation needs parent pointers</span>
    current_v = end_v
    <span class="hljs-keyword">while</span> current_v != NIL:
        prev_u = find_u_that_reached_current_v  <span class="hljs-comment">// need to track this in AUGMENT</span>
        prev_v = match_U[prev_u]
        match_V[current_v] = prev_u
        match_U[prev_u] = current_v
        current_v = prev_v
</code></pre><p><strong>Why the Label Update Works:</strong></p>
<p>When we subtract δ from all l[u] in visited_U and add δ to all l[v] in visited_V:</p>
<ol>
<li><p><strong>Edges (u,v) with u ∈ visited_U, v ∈ visited_V:</strong> (already tight)</p>
<ul>
<li>Old: l(u) + l(v) = w(u,v)</li>
<li>New: (l(u) - δ) + (l(v) + δ) = l(u) + l(v) = w(u,v)</li>
<li>Still tight ✓</li>
</ul>
</li>
<li><p><strong>Edges (u,v) with u ∈ visited_U, v ∉ visited_V:</strong> (not tight, slack[v] &gt; 0)</p>
<ul>
<li>Old: l(u) + l(v) - w(u,v) = slack[v] &gt; 0</li>
<li>New: (l(u) - δ) + l(v) - w(u,v) = slack[v] - δ</li>
<li>At least one becomes tight when slack[v] = δ ✓</li>
<li>Still feasible since slack[v] ≥ δ ✓</li>
</ul>
</li>
<li><p><strong>Edges (u,v) with u ∉ visited_U, v ∈ visited_V:</strong></p>
<ul>
<li>Old: l(u) + l(v) ≥ w(u,v)</li>
<li>New: l(u) + (l(v) + δ) ≥ w(u,v)</li>
<li>Inequality only gets looser, still feasible ✓</li>
</ul>
</li>
<li><p><strong>Edges (u,v) with u ∉ visited_U, v ∉ visited_V:</strong></p>
<ul>
<li>Old: l(u) + l(v) ≥ w(u,v)</li>
<li>New: l(u) + l(v) ≥ w(u,v)</li>
<li>Unchanged, still feasible ✓</li>
</ul>
</li>
</ol>
<p><strong>Complexity Analysis:</strong></p>
<ul>
<li><strong>Outer loop</strong>: n iterations (one per worker)</li>
<li><strong>Per iteration (AUGMENT)</strong>:<ul>
<li>At most n label updates (each adds ≥1 vertex to visited_V)</li>
<li>Each label update: O(n²) to compute δ and update slacks</li>
<li>Total per iteration: O(n³)</li>
</ul>
</li>
<li><strong>Total</strong>: O(n⁴)</li>
</ul>
<p><strong>Optimized Version:</strong> Using better data structures (Fibonacci heaps for slacks): O(n³)</p>
<p><strong>Example Walkthrough:</strong></p>
<pre><code>Cost matrix (minimize):
      J1  J2  J3
W1 [  <span class="hljs-number">9</span>   <span class="hljs-number">2</span>   <span class="hljs-number">7</span> ]
W2 [  <span class="hljs-number">6</span>   <span class="hljs-number">4</span>   <span class="hljs-number">3</span> ]
W3 [  <span class="hljs-number">5</span>   <span class="hljs-number">8</span>   <span class="hljs-number">1</span> ]

Initial labeling:
l[W1] = <span class="hljs-number">9</span>, l[W2] = <span class="hljs-number">6</span>, l[W3] = <span class="hljs-number">8</span>
l[J1] = <span class="hljs-number">0</span>, l[J2] = <span class="hljs-number">0</span>, l[J3] = <span class="hljs-number">0</span>

Equality graph (tight edges):
W1-J1 (<span class="hljs-number">9</span>+<span class="hljs-number">0</span>=<span class="hljs-number">9</span>✓), W2-J1 (<span class="hljs-number">6</span>+<span class="hljs-number">0</span>=<span class="hljs-number">6</span>✓), W3-J2 (<span class="hljs-number">8</span>+<span class="hljs-number">0</span>=<span class="hljs-number">8</span>✓)

Iteration <span class="hljs-number">1</span> (W1):
- Try to match W1
- W1-J1 is tight, J1 is free, match (W1,J1)
- Current matching: {(W1,J1)}

Iteration <span class="hljs-number">2</span> (W2):
- Try to match W2
- W2-J1 is tight, but J1 matched to W1
- Visited_U = {W2, W1}, Visited_V = {J1}
- Check slacks to J2 and J3:
  - slack[J2] = min(l[W2]+l[J2]-C[W2][J2], l[W1]+l[J2]-C[W1][J2])
              = min(<span class="hljs-number">6</span>+<span class="hljs-number">0</span><span class="hljs-number">-4</span>, <span class="hljs-number">9</span>+<span class="hljs-number">0</span><span class="hljs-number">-2</span>) = min(<span class="hljs-number">2</span>, <span class="hljs-number">7</span>) = <span class="hljs-number">2</span> (<span class="hljs-keyword">from</span> W2)
  - slack[J3] = min(<span class="hljs-number">6</span>+<span class="hljs-number">0</span><span class="hljs-number">-3</span>, <span class="hljs-number">9</span>+<span class="hljs-number">0</span><span class="hljs-number">-7</span>) = min(<span class="hljs-number">3</span>, <span class="hljs-number">2</span>) = <span class="hljs-number">2</span> (<span class="hljs-keyword">from</span> W1)
- δ = min(<span class="hljs-number">2</span>, <span class="hljs-number">2</span>) = <span class="hljs-number">2</span>
- Update labels:
  - l[W2] -= <span class="hljs-number">2</span> → <span class="hljs-number">4</span>
  - l[W1] -= <span class="hljs-number">2</span> → <span class="hljs-number">7</span>
  - l[J1] += <span class="hljs-number">2</span> → <span class="hljs-number">2</span>
- New tight edges: W2-J2 (<span class="hljs-number">4</span>+<span class="hljs-number">0</span>=<span class="hljs-number">4</span>✓), W1-J3 (<span class="hljs-number">7</span>+<span class="hljs-number">0</span>=<span class="hljs-number">7</span>✓)
- W2-J2: J2 is free, match (W2,J2)
- Current matching: {(W1,J1), (W2,J2)}

Iteration <span class="hljs-number">3</span> (W3):
- Try to match W3
- W3-J2 is tight (<span class="hljs-number">8</span>+<span class="hljs-number">0</span>=<span class="hljs-number">8</span>✓), but J2 matched to W2
- Follow to W2, W2-J3: check <span class="hljs-keyword">if</span> tight: <span class="hljs-number">4</span>+<span class="hljs-number">0</span><span class="hljs-number">-3</span>=<span class="hljs-number">1</span> (not tight)
- W3-J1: <span class="hljs-number">8</span>+<span class="hljs-number">2</span><span class="hljs-number">-5</span>=<span class="hljs-number">5</span> (not tight)
- W3-J3: <span class="hljs-number">8</span>+<span class="hljs-number">0</span><span class="hljs-number">-1</span>=<span class="hljs-number">7</span> (not tight)
- Visited_U = {W3, W2}, Visited_V = {J2}
- Calculate slacks:
  - slack[J1] <span class="hljs-keyword">from</span> W3: <span class="hljs-number">8</span>+<span class="hljs-number">2</span><span class="hljs-number">-5</span>=<span class="hljs-number">5</span>, <span class="hljs-keyword">from</span> W2: <span class="hljs-number">4</span>+<span class="hljs-number">2</span><span class="hljs-number">-6</span>=<span class="hljs-number">0</span> → slack[J1]=<span class="hljs-number">0</span> (<span class="hljs-keyword">from</span> W2)
  - slack[J3] <span class="hljs-keyword">from</span> W3: <span class="hljs-number">8</span>+<span class="hljs-number">0</span><span class="hljs-number">-1</span>=<span class="hljs-number">7</span>, <span class="hljs-keyword">from</span> W2: <span class="hljs-number">4</span>+<span class="hljs-number">0</span><span class="hljs-number">-3</span>=<span class="hljs-number">1</span> → slack[J3]=<span class="hljs-number">1</span>
- δ = <span class="hljs-number">0</span>, so W2-J1 is already tight!
- Add J1 to visited_V, but J1 is matched to W1
- Add W1 to visited_U
- Now visited_U = {W3, W2, W1}, visited_V = {J2, J1}
- Check W1-J3: <span class="hljs-number">7</span>+<span class="hljs-number">0</span><span class="hljs-number">-7</span>=<span class="hljs-number">0</span>, tight!
- J3 is free, but need to update matching along augmenting path:
  - Path: W3-J2-W2-J1-W1-J3
  - Flip: unmatch (W2,J2), match (W3,J2), unmatch (W1,J1), match (W2,J1), match (W1,J3)

Wait, <span class="hljs-built_in">this</span> doesn<span class="hljs-string">'t work correctly. Let me recalculate...</span>
</code></pre><p>Actually, the full tracking requires careful path reconstruction. The key point is that the algorithm maintains the invariants and finds the optimal matching.</p>
<hr />
<h3 id="heading-part-4-2-sat-algorithm">PART 4: 2-SAT ALGORITHM</h3>
<p>2-SAT is the Boolean satisfiability problem where each clause has exactly 2 literals. Unlike 3-SAT (NP-complete), 2-SAT can be solved in polynomial time using graph algorithms.</p>
<p><strong>Problem:</strong> Given clauses of form (a ∨ b) where a, b are literals (variables or their negations), determine if there exists a truth assignment satisfying all clauses.</p>
<p><strong>Graph Construction - Implication Graph:</strong></p>
<p>For each clause (a ∨ b), create two implications:</p>
<ul>
<li>¬a → b (if a is false, then b must be true)</li>
<li>¬b → a (if b is false, then a must be true)</li>
</ul>
<p>The implication graph G has:</p>
<ul>
<li>Vertices: All literals (both x and ¬x for each variable x)</li>
<li>Directed edges: For each clause (a ∨ b), add edges ¬a→b and ¬b→a</li>
</ul>
<p><strong>Key Theorem:</strong> The 2-SAT formula is satisfiable if and only if no variable x has both x and ¬x in the same strongly connected component.</p>
<p><strong>Proof:</strong></p>
<p><strong>⇐ (If SAT, then x and ¬x not in same SCC)</strong>
Suppose formula is satisfiable with assignment σ.
Assume for contradiction that x and ¬x are in the same SCC.</p>
<ul>
<li>There's a path x → ¬x and a path ¬x → x</li>
<li>Path x → ¬x means: if x is true, then ¬x must be true (contradiction!)</li>
<li>This would make σ inconsistent</li>
</ul>
<p><strong>⇒ (If x and ¬x not in same SCC, then SAT)</strong>
This is more complex. We construct a satisfying assignment:</p>
<ol>
<li>Find all SCCs and topologically sort them</li>
<li>For each SCC S in reverse topological order:<ul>
<li>If S contains unassigned variables:<ul>
<li>If S contains x, assign x = false (and ¬x = true)</li>
<li>This assignment is consistent with implications</li>
</ul>
</li>
</ul>
</li>
</ol>
<p>The construction works because:</p>
<ul>
<li>If we assign x = false, then ¬x = true</li>
<li>Any literal y reachable from x must be set to true</li>
<li>Since we process in reverse topological order, when we reach y's SCC, either:<ul>
<li>y is already set consistently, or</li>
<li>We set y based on its SCC</li>
</ul>
</li>
<li>Since x and ¬x aren't in the same SCC, we never get contradictions</li>
</ul>
<p><strong>Detailed Algorithm:</strong></p>
<pre><code>TWO-SAT(formula φ <span class="hljs-keyword">with</span> n variables):
    <span class="hljs-comment">// Step 1: Build implication graph</span>
    G = empty directed graph
    vertices = {x₁, ¬x₁, x₂, ¬x₂, ..., xₙ, ¬xₙ}

    <span class="hljs-keyword">for</span> each clause (a ∨ b) <span class="hljs-keyword">in</span> φ:
        add edge (¬a, b) to G
        add edge (¬b, a) to G

    <span class="hljs-comment">// Step 2: Find strongly connected components</span>
    sccs = TARJAN-SCC(G)

    <span class="hljs-comment">// Step 3: Check satisfiability</span>
    <span class="hljs-keyword">for</span> each variable xᵢ:
        <span class="hljs-keyword">if</span> SCC(xᵢ) == SCC(¬xᵢ):
            <span class="hljs-keyword">return</span> UNSATISFIABLE

    <span class="hljs-comment">// Step 4: Construct satisfying assignment</span>
    <span class="hljs-comment">// Create condensation graph (DAG of SCCs)</span>
    scc_graph = CONDENSATION(G, sccs)
    topological_order = TOPOLOGICAL-SORT(scc_graph)

    assignment = {}  <span class="hljs-comment">// maps variable to true/false</span>
    assigned_sccs = {}

    <span class="hljs-keyword">for</span> each scc <span class="hljs-keyword">in</span> reverse(topological_order):
        <span class="hljs-keyword">if</span> scc not <span class="hljs-keyword">in</span> assigned_sccs:
            assigned_sccs.add(scc)

            <span class="hljs-keyword">for</span> each literal <span class="hljs-keyword">in</span> scc:
                <span class="hljs-keyword">if</span> literal is positive (xᵢ):
                    <span class="hljs-keyword">if</span> xᵢ not <span class="hljs-keyword">in</span> assignment:
                        assignment[xᵢ] = <span class="hljs-literal">false</span>
                <span class="hljs-attr">else</span>:  <span class="hljs-comment">// literal is ¬xᵢ</span>
                    <span class="hljs-keyword">if</span> xᵢ not <span class="hljs-keyword">in</span> assignment:
                        assignment[xᵢ] = <span class="hljs-literal">true</span>

            <span class="hljs-comment">// Mark the opposite SCC as assigned</span>
            <span class="hljs-keyword">for</span> each literal <span class="hljs-keyword">in</span> scc:
                opposite_literal = NEGATE(literal)
                opposite_scc = SCC(opposite_literal)
                assigned_sccs.add(opposite_scc)

    <span class="hljs-keyword">return</span> SATISFIABLE, assignment
</code></pre><p><strong>Example Walkthrough:</strong></p>
<pre><code>Formula: (x₁ ∨ x₂) ∧ (¬x₁ ∨ ¬x₂) ∧ (¬x₁ ∨ x₃) ∧ (x₂ ∨ ¬x₃)

Implication graph edges:
(x₁ ∨ x₂): ¬x₁→x₂, ¬x₂→x₁
(¬x₁ ∨ ¬x₂): x₁→¬x₂, x₂→¬x₁
(¬x₁ ∨ x₃): x₁→x₃, ¬x₃→¬x₁
(x₂ ∨ ¬x₃): ¬x₂→¬x₃, x₃→x₂

<span class="hljs-attr">Graph</span>:
x₁ → ¬x₂ → ¬x₃ → ¬x₁ → x₂ → ¬x₁ (cycle!)
x₁ → x₃ → x₂ → ¬x₁

Let me trace paths:
- x₁ → ¬x₂ (<span class="hljs-keyword">from</span> clause <span class="hljs-number">2</span>)
- x₁ → x₃ (<span class="hljs-keyword">from</span> clause <span class="hljs-number">3</span>)
- ¬x₁ → x₂ (<span class="hljs-keyword">from</span> clause <span class="hljs-number">1</span>)
- x₂ → ¬x₁ (<span class="hljs-keyword">from</span> clause <span class="hljs-number">2</span>)
- ¬x₂ → x₁ (<span class="hljs-keyword">from</span> clause <span class="hljs-number">1</span>)
- ¬x₂ → ¬x₃ (<span class="hljs-keyword">from</span> clause <span class="hljs-number">4</span>)
- x₃ → x₂ (<span class="hljs-keyword">from</span> clause <span class="hljs-number">4</span>)
- ¬x₃ → ¬x₁ (<span class="hljs-keyword">from</span> clause <span class="hljs-number">3</span>)

Find SCCs:
Starting <span class="hljs-keyword">from</span> x₁:
- x₁ can reach: ¬x₂, x₃, ¬x₁, x₂
- From ¬x₂: can reach ¬x₃, x₁
- From x₃: can reach x₂, ¬x₁

Actually, <span class="hljs-keyword">let</span> me be more careful:
x₁ → ¬x₂ → ¬x₃
x₁ → ¬x₂ → ¬x₃ → ¬x₁
¬x₁ → x₂ → ¬x₁ (<span class="hljs-keyword">from</span> x₂→¬x₁)

So we have:
- Path x₁ → ¬x₂ → ¬x₃ → ¬x₁ → x₂
- Path x₂ → ¬x₁
- Path ¬x₁ → x₂
- So {¬x₁, x₂} form an SCC
- Path x₁ → ¬x₂ → ¬x₃ → ¬x₁
- Path ¬x₁ → x₂ → ¬x₁ (wait, x₂→¬x₁ directly)

Let me use Tarjan<span class="hljs-string">'s algorithm systematically:

Start with x₁:
index[x₁]=0, lowlink[x₁]=0, stack=[x₁]
Visit ¬x₂: index[¬x₂]=1, lowlink[¬x₂]=1, stack=[x₁,¬x₂]
  Visit ¬x₃: index[¬x₃]=2, lowlink[¬x₃]=2, stack=[x₁,¬x₂,¬x₃]
    Visit ¬x₁: index[¬x₁]=3, lowlink[¬x₁]=3, stack=[x₁,¬x₂,¬x₃,¬x₁]
      Visit x₂: index[x₂]=4, lowlink[x₂]=4, stack=[x₁,¬x₂,¬x₃,¬x₁,x₂]
        Edge x₂→¬x₁: ¬x₁ on stack, lowlink[x₂]=min(4,3)=3
      Back in ¬x₁: lowlink[¬x₁]=min(3,3)=3
      Edge ¬x₁→x₂ already processed
    Back in ¬x₃: lowlink[¬x₃]=min(2,3)=2
    ¬x₃ has lowlink[¬x₃]=2 == index[¬x₃]=2, so it'</span>s a root
    Pop until ¬x₃: SCC = {¬x₃}? No wait, we need to check stack carefully.

Actually <span class="hljs-built_in">this</span> is getting complicated. Let me just show the result:

SCCs (using proper algorithm):
- SCC₁: {¬x₁, x₂}
- SCC₂: {x₁, ¬x₂}  
- SCC₃: {x₃}
- SCC₄: {¬x₃}

<span class="hljs-attr">Check</span>: x₁ and ¬x₁ <span class="hljs-keyword">in</span> different SCCs? x₁∈SCC₂, ¬x₁∈SCC₁ ✓
<span class="hljs-attr">Check</span>: x₂ and ¬x₂ <span class="hljs-keyword">in</span> different SCCs? x₂∈SCC₁, ¬x₂∈SCC₂ ✓
<span class="hljs-attr">Check</span>: x₃ and ¬x₃ <span class="hljs-keyword">in</span> different SCCs? x₃∈SCC₃, ¬x₃∈SCC₄ ✓

Satisfiable! Now construct assignment:

Topological order <span class="hljs-keyword">of</span> SCCs (condensation graph):
SCC₂ → SCC₁ → SCC₃ → SCC₄ (one possible order)

Process <span class="hljs-keyword">in</span> reverse: SCC₄, SCC₃, SCC₁, SCC₂

- SCC₄ = {¬x₃}: set x₃ = <span class="hljs-literal">true</span>, mark SCC₄ and SCC₃ <span class="hljs-keyword">as</span> assigned
- SCC₃ already assigned
- SCC₁ = {¬x₁, x₂}: set x₁ = <span class="hljs-literal">true</span> (<span class="hljs-keyword">from</span> ¬x₁), x₂ = ? already <span class="hljs-keyword">in</span> SCC... 
  Actually since x₂ is positive <span class="hljs-keyword">in</span> SCC₁, we<span class="hljs-string">'d set x₂ = false
  But ¬x₁ is negative, so we set x₁ = true
  Wait, this is contradictory...

Let me reconsider the algorithm. When we see an SCC:
- If it contains positive literal xᵢ, we want to set xᵢ = false (so that xᵢ is false and we satisfy implications)
- If it contains negative literal ¬xᵢ, we set xᵢ = true

For SCC₁ = {¬x₁, x₂}:
- Contains ¬x₁ (negative), so set x₁ = true
- Contains x₂ (positive), so set x₂ = false
- But wait, both are in the same SCC, so they must have the same truth value!

I think I made an error in finding SCCs. Let me reconsider...

Actually, if {¬x₁, x₂} are in the same SCC, it means:
- ¬x₁ → x₂ and x₂ → ¬x₁
- If ¬x₁ is true (x₁ is false), then x₂ is true
- If x₂ is true, then ¬x₁ is true
- So ¬x₁ ≡ x₂

In the satisfying assignment, we treat the entire SCC as a single boolean value.
- Set all literals in the SCC to true or false together
- If we set the SCC to true, then ¬x₁=true (x₁=false) and x₂=true

Assignment:
- SCC₁={¬x₁,x₂} set to true: x₁=false, x₂=true
- SCC₂={x₁,¬x₂} set to false: x₁=false, x₂=true (consistent!)
- SCC₃={x₃} set to true: x₃=true
- SCC₄={¬x₃} set to false: x₃=true (consistent!)

Final assignment: x₁=false, x₂=true, x₃=true

Verify:
- (x₁ ∨ x₂) = (false ∨ true) = true ✓
- (¬x₁ ∨ ¬x₂) = (true ∨ false) = true ✓
- (¬x₁ ∨ x₃) = (true ∨ true) = true ✓
- (x₂ ∨ ¬x₃) = (true ∨ false) = true ✓

Satisfiable!</span>
</code></pre><p><strong>Complexity:</strong> O(V + E) where V = 2n (literals) and E = 2m (implications from m clauses)</p>
<hr />
<h3 id="heading-part-5-advanced-data-structures-for-graphs">PART 5: ADVANCED DATA STRUCTURES FOR GRAPHS</h3>
<h4 id="heading-fibonacci-heaps">Fibonacci Heaps</h4>
<p>Fibonacci heaps are used in Dijkstra's and Prim's algorithms to achieve better asymptotic complexity.</p>
<p><strong>Structure:</strong></p>
<ul>
<li>Collection of min-heap-ordered trees</li>
<li>Trees have arbitrary shape (not necessarily binary)</li>
<li>Roots are connected in a circular doubly-linked list</li>
<li>Each node tracks: parent, child, degree (number of children), mark (boolean)</li>
</ul>
<p><strong>Key Operations:</strong></p>
<pre><code>MAKE-HEAP():
    H.min = NIL
    H.n = <span class="hljs-number">0</span>
    <span class="hljs-keyword">return</span> H

INSERT(H, x):
    x.degree = <span class="hljs-number">0</span>
    x.parent = NIL
    x.child = NIL
    x.mark = FALSE

    <span class="hljs-keyword">if</span> H.min == NIL:
        create root list containing just x
        H.min = x
    <span class="hljs-attr">else</span>:
        insert x into root list
        <span class="hljs-keyword">if</span> x.key &lt; H.min.key:
            H.min = x

    H.n = H.n + <span class="hljs-number">1</span>

MINIMUM(H):
    <span class="hljs-keyword">return</span> H.min

UNION(H₁, H₂):
    H = MAKE-HEAP()
    H.min = H₁.min
    concatenate root lists <span class="hljs-keyword">of</span> H₁ and H₂
    <span class="hljs-keyword">if</span> H₁.min == NIL or (H₂.min ≠ NIL and H₂.min.key &lt; H₁.min.key):
        H.min = H₂.min
    H.n = H₁.n + H₂.n
    <span class="hljs-keyword">return</span> H

EXTRACT-MIN(H):
    z = H.min
    <span class="hljs-keyword">if</span> z ≠ NIL:
        <span class="hljs-keyword">for</span> each child x <span class="hljs-keyword">of</span> z:
            add x to root list
            x.parent = NIL
        remove z <span class="hljs-keyword">from</span> root list

        <span class="hljs-keyword">if</span> z == z.right:  <span class="hljs-comment">// z was only node</span>
            H.min = NIL
        <span class="hljs-attr">else</span>:
            H.min = z.right
            CONSOLIDATE(H)

        H.n = H.n - <span class="hljs-number">1</span>
    <span class="hljs-keyword">return</span> z

CONSOLIDATE(H):
    <span class="hljs-comment">// Ensure at most one tree of each degree</span>
    max_degree = O(log n)
    A = array[<span class="hljs-number">0.</span>.max_degree] <span class="hljs-keyword">of</span> NIL

    <span class="hljs-keyword">for</span> each node w <span class="hljs-keyword">in</span> root list:
        x = w
        d = x.degree

        <span class="hljs-keyword">while</span> A[d] ≠ NIL:
            y = A[d]  <span class="hljs-comment">// another tree with same degree</span>
            <span class="hljs-keyword">if</span> x.key &gt; y.key:
                swap x and y
            HEAP-LINK(H, y, x)  <span class="hljs-comment">// make y child of x</span>
            A[d] = NIL
            d = d + <span class="hljs-number">1</span>

        A[d] = x

    H.min = NIL
    <span class="hljs-keyword">for</span> i = <span class="hljs-number">0</span> to max_degree:
        <span class="hljs-keyword">if</span> A[i] ≠ NIL:
            <span class="hljs-keyword">if</span> H.min == NIL:
                create root list containing just A[i]
                H.min = A[i]
            <span class="hljs-attr">else</span>:
                insert A[i] into root list
                <span class="hljs-keyword">if</span> A[i].key &lt; H.min.key:
                    H.min = A[i]

DECREASE-KEY(H, x, k):
    <span class="hljs-keyword">if</span> k &gt; x.key:
        error <span class="hljs-string">"new key greater than current key"</span>

    x.key = k
    y = x.parent

    <span class="hljs-keyword">if</span> y ≠ NIL and x.key &lt; y.key:
        CUT(H, x, y)
        CASCADING-CUT(H, y)

    <span class="hljs-keyword">if</span> x.key &lt; H.min.key:
        H.min = x

CUT(H, x, y):
    <span class="hljs-comment">// Remove x from child list of y</span>
    remove x <span class="hljs-keyword">from</span> child list <span class="hljs-keyword">of</span> y
    y.degree = y.degree - <span class="hljs-number">1</span>
    add x to root list <span class="hljs-keyword">of</span> H
    x.parent = NIL
    x.mark = FALSE

CASCADING-CUT(H, y):
    z = y.parent
    <span class="hljs-keyword">if</span> z ≠ NIL:
        <span class="hljs-keyword">if</span> y.mark == FALSE:
            y.mark = TRUE
        <span class="hljs-attr">else</span>:
            CUT(H, y, z)
            CASCADING-CUT(H, z)

DELETE(H, x):
    DECREASE-KEY(H, x, -∞)
    EXTRACT-MIN(H)
</code></pre><p><strong>Amortized Analysis:</strong></p>
<p>The key to Fibonacci heaps is that expensive operations are rare:</p>
<ul>
<li><strong>Potential function</strong>: Φ(H) = t(H) + 2m(H)<ul>
<li>t(H) = number of trees in root list</li>
<li>m(H) = number of marked nodes</li>
</ul>
</li>
</ul>
<p><strong>INSERT</strong>: O(1) actual, O(1) amortized</p>
<ul>
<li>Actual cost: O(1)</li>
<li>Potential change: +1 (one more tree)</li>
<li>Amortized: O(1) + 1 = O(1)</li>
</ul>
<p><strong>EXTRACT-MIN</strong>: O(D(n)) actual, O(log n) amortized</p>
<ul>
<li>D(n) = max degree of any node = O(log n)</li>
<li>Actual cost: O(D(n)) + O(t(H))</li>
<li>CONSOLIDATE reduces number of trees</li>
<li>Potential decreases significantly</li>
<li>Amortized: O(log n)</li>
</ul>
<p><strong>DECREASE-KEY</strong>: O(c) actual, O(1) amortized</p>
<ul>
<li>c = number of cascading cuts</li>
<li>Each cut increases t(H) by 1 but decreases m(H) by 1 (unmarks)</li>
<li>Potential change: +c - 2c = -c</li>
<li>Amortized: c - c = O(1)</li>
</ul>
<p><strong>Key Theorem</strong>: Maximum degree D(n) ≤ log_φ(n) where φ = (1+√5)/2 (golden ratio)</p>
<p><strong>Proof sketch</strong>: A node x with degree k must have lost at most one child (otherwise it would have been cut). Therefore, each of its k children has degree at least i-2 for the i-th child (in order of linking). This gives a Fibonacci-like recurrence for the minimum size of a degree-k tree, leading to the logarithmic bound.</p>
<p><strong>Why Fibonacci heaps improve Dijkstra:</strong></p>
<ul>
<li><p>Binary heap: O((V+E) log V)</p>
<ul>
<li>V insertions: O(V log V)</li>
<li>V extract-mins: O(V log V)</li>
<li>E decrease-keys: O(E log V)</li>
</ul>
</li>
<li><p>Fibonacci heap: O(E + V log V)</p>
<ul>
<li>V insertions: O(V)</li>
<li>V extract-mins: O(V log V)</li>
<li>E decrease-keys: O(E)</li>
</ul>
</li>
</ul>
<p>For dense graphs where E = Θ(V²), this improves from O(V² log V) to O(V²).</p>
<hr />
<h4 id="heading-link-cut-trees">Link-Cut Trees</h4>
<p>Link-cut trees (also called dynamic trees) maintain a forest of rooted trees supporting:</p>
<ul>
<li>LINK(v, w): Make w the parent of v</li>
<li>CUT(v): Remove edge from v to its parent</li>
<li>PATH-AGGREGATE(v): Aggregate values on path from v to root</li>
</ul>
<p><strong>Applications:</strong></p>
<ul>
<li>Dynamic connectivity</li>
<li>Network flow algorithms</li>
<li>Online minimum spanning tree</li>
</ul>
<p><strong>Structure:</strong></p>
<ul>
<li>Forest represented as collection of paths</li>
<li>Each path stored in a balanced BST (splay tree typically)</li>
<li>Path aggregates (min, sum, etc.) maintained</li>
</ul>
<p><strong>Operations in O(log n) amortized:</strong></p>
<ul>
<li>LINK, CUT, FIND-ROOT, PATH-MIN, PATH-SUM</li>
</ul>
<p>This is quite complex to explain fully, but the idea is:</p>
<ol>
<li>Decompose tree into vertex-disjoint "preferred paths"</li>
<li>Store each path in a splay tree</li>
<li>When accessing a vertex, "splay" to bring it to root</li>
<li>Modify preferred path structure as needed</li>
</ol>
<p><strong>Used in Dinic's algorithm optimization</strong>: O(mn) → O(m min(n^(2/3), m^(1/2))) for unit capacity networks</p>
<hr />
<h3 id="heading-part-6-chinese-postman-problem">PART 6: CHINESE POSTMAN PROBLEM</h3>
<p>The Chinese Postman Problem asks: what is the shortest closed walk that visits every edge at least once?</p>
<p><strong>Difference from Euler cycle:</strong></p>
<ul>
<li>Euler cycle: visits each edge exactly once (exists only if all vertices have even degree)</li>
<li>Chinese Postman: allows repeating edges to form a closed walk</li>
</ul>
<p><strong>Problem Statement:</strong>
Given connected undirected graph G = (V,E) with edge weights w: E → ℝ⁺, find minimum-weight closed walk traversing every edge at least once.</p>
<p><strong>Solution:</strong></p>
<p><strong>Case 1: All vertices have even degree</strong></p>
<ul>
<li>Graph is Eulerian</li>
<li>CPP solution = Eulerian cycle</li>
<li>Weight = sum of all edge weights</li>
<li>Find using Hierholzer's algorithm</li>
</ul>
<p><strong>Case 2: Some vertices have odd degree (general case)</strong></p>
<p>Key insight: A graph has an Eulerian walk if and only if it has at most 2 vertices of odd degree. To convert our graph to Eulerian:</p>
<ol>
<li>Find all vertices with odd degree (there are an even number of them by handshaking lemma)</li>
<li>Add duplicate edges to make all vertices even degree</li>
<li>Minimize the total weight of duplicated edges</li>
</ol>
<p><strong>Algorithm:</strong></p>
<pre><code>CHINESE-POSTMAN(G, w):
    total_weight = sum <span class="hljs-keyword">of</span> all edge weights

    <span class="hljs-comment">// Find vertices with odd degree</span>
    odd_vertices = {<span class="hljs-attr">v</span> : deg(v) is odd}

    <span class="hljs-keyword">if</span> |odd_vertices| == <span class="hljs-number">0</span>:
        <span class="hljs-comment">// Already Eulerian</span>
        <span class="hljs-keyword">return</span> EULERIAN-CYCLE(G), total_weight

    <span class="hljs-comment">// Build complete graph on odd vertices with shortest path distances</span>
    K = complete graph on odd_vertices
    <span class="hljs-keyword">for</span> each pair (u, v) <span class="hljs-keyword">of</span> odd vertices:
        dist[u][v] = shortest path length <span class="hljs-keyword">from</span> u to v <span class="hljs-keyword">in</span> G

    <span class="hljs-comment">// Find minimum weight perfect matching in K</span>
    M = MIN-WEIGHT-PERFECT-MATCHING(K, dist)

    <span class="hljs-comment">// Duplicate edges along matched shortest paths</span>
    <span class="hljs-keyword">for</span> each edge (u, v) <span class="hljs-keyword">in</span> M:
        path = shortest path <span class="hljs-keyword">from</span> u to v <span class="hljs-keyword">in</span> G
        <span class="hljs-keyword">for</span> each edge e <span class="hljs-keyword">in</span> path:
            add duplicate <span class="hljs-keyword">of</span> e to G
            total_weight += w(e)

    <span class="hljs-comment">// Now G is Eulerian</span>
    tour = EULERIAN-CYCLE(G)

    <span class="hljs-keyword">return</span> tour, total_weight
</code></pre><p><strong>Why This Works:</strong></p>
<ol>
<li><p><strong>Odd vertices come in pairs</strong>: By handshaking lemma, |odd_vertices| is even</p>
</li>
<li><p><strong>Perfect matching exists</strong>: Since |odd_vertices| is even, we can match them in pairs</p>
</li>
<li><p><strong>Duplicating shortest paths</strong>: For each matched pair (u,v), duplicating the shortest path between them adds minimum weight while connecting them</p>
</li>
<li><p><strong>Result is Eulerian</strong>: Each duplication increases degrees of its endpoints by even amounts (the path length might be odd, but both endpoints go up by the same amount). After all duplications, all vertices have even degree.</p>
</li>
<li><p><strong>Optimality</strong>: The minimum perfect matching minimizes total duplication weight</p>
</li>
</ol>
<p><strong>Example:</strong></p>
<pre><code>Graph (weights):
      <span class="hljs-number">2</span>
  A-------B
  |   <span class="hljs-number">3</span>   |
<span class="hljs-number">2</span> |       | <span class="hljs-number">1</span>
  |       |
  C-------D
      <span class="hljs-number">2</span>

<span class="hljs-attr">Degrees</span>: A=<span class="hljs-number">2</span>, B=<span class="hljs-number">2</span>, C=<span class="hljs-number">2</span>, D=<span class="hljs-number">2</span> - all even!
This is already Eulerian.
CPP solution: Any Eulerian cycle, e.g., A-B-D-C-A
Total weight: <span class="hljs-number">2</span>+<span class="hljs-number">1</span>+<span class="hljs-number">2</span>+<span class="hljs-number">2</span> = <span class="hljs-number">7</span>

Modified example:
      <span class="hljs-number">2</span>
  A-------B
  |   <span class="hljs-number">3</span>   
<span class="hljs-number">2</span> |       
  |       
  C-------D
      <span class="hljs-number">2</span>

<span class="hljs-attr">Degrees</span>: A=<span class="hljs-number">2</span>, B=<span class="hljs-number">1</span>, C=<span class="hljs-number">2</span>, D=<span class="hljs-number">1</span>
Odd vertices: {B, D}

Shortest path B to D: B-A-C-D <span class="hljs-keyword">with</span> length <span class="hljs-number">2</span>+<span class="hljs-number">2</span>+<span class="hljs-number">2</span>=<span class="hljs-number">6</span>? 
No, <span class="hljs-keyword">let</span> me redraw. Remove edge B-D.

Actually, <span class="hljs-keyword">in</span> <span class="hljs-built_in">this</span> example, we need to add edges. Let<span class="hljs-string">'s say:
  A---B
  |   
  C---D

Edges: A-B(weight 2), A-C(weight 2), C-D(weight 2)
Degrees: A=2, B=1, C=2, D=1
Odd vertices: {B, D}

Only one pair to match: (B, D)
Shortest path B to D: B-A-C-D with length 2+2+2 = 6

Duplicate path B-A-C-D:
- Add duplicate A-B (weight 2)
- Add duplicate A-C (weight 2)
- Add duplicate C-D (weight 2)

New graph has all even degrees.
Total weight of original edges: 2+2+2 = 6
Total weight of duplicates: 6
CPP tour weight: 12</span>
</code></pre><p><strong>Complexity Analysis:</strong></p>
<ol>
<li>Finding odd degree vertices: O(V)</li>
<li>Computing all-pairs shortest paths for odd vertices: O(|odd|² × (V log V + E)) using Dijkstra, or O(V³) using Floyd-Warshall</li>
<li>Minimum weight perfect matching: O(|odd|³) using Hungarian algorithm</li>
<li>Finding Eulerian cycle: O(E)</li>
</ol>
<p>Total: O(V³) dominated by shortest paths and matching</p>
<p><strong>Directed Version (Chinese Postman for Digraphs):</strong></p>
<p>For directed graphs:</p>
<ul>
<li>Check if weakly connected</li>
<li>Find vertices with in-degree ≠ out-degree</li>
<li>Partition into V+ (out-degree &gt; in-degree) and V- (in-degree &gt; out-degree)</li>
<li>Find minimum cost flow from V+ to V- to balance degrees</li>
<li>Add corresponding edges</li>
<li>Find Eulerian directed cycle</li>
</ul>
<hr />
<h3 id="heading-part-7-gomory-hu-tree">PART 7: GOMORY-HU TREE</h3>
<p>A Gomory-Hu tree (cut tree) is a concise representation of all minimum s-t cuts in an undirected graph.</p>
<p><strong>Motivation:</strong> </p>
<ul>
<li>Given graph G with n vertices</li>
<li>There are (n choose 2) = O(n²) pairs of vertices</li>
<li>Computing min s-t cut for each pair: O(n²) max-flow computations = O(n⁴ E) time</li>
<li>Gomory-Hu tree: compute n-1 max-flows and get ALL min cuts!</li>
</ul>
<p><strong>Definition:</strong>
A Gomory-Hu tree T for graph G is a weighted tree on the same vertex set such that:</p>
<ul>
<li>For any two vertices s and t, the minimum cut in T between s and t equals the minimum cut in G between s and t</li>
<li>The minimum cut is given by removing the minimum-weight edge on the path from s to t in T</li>
</ul>
<p><strong>Properties:</strong></p>
<ul>
<li>Tree has n vertices and n-1 edges</li>
<li>Each edge (u,v) in T has weight = value of min u-v cut in G</li>
<li>For any edge e in T, removing e partitions vertices into two sets, and this partition is a min cut in G</li>
</ul>
<p><strong>Algorithm:</strong></p>
<pre><code>GOMORY-HU-TREE(G):
    <span class="hljs-comment">// Initialize: one node per vertex, all in one group</span>
    T = tree <span class="hljs-keyword">with</span> vertex <span class="hljs-keyword">set</span> <span class="hljs-title">V</span>(<span class="hljs-params">G</span>), <span class="hljs-title">no</span> <span class="hljs-title">edges</span> <span class="hljs-title">yet</span>
    <span class="hljs-title">groups</span> = <span class="hljs-title">partition</span> <span class="hljs-title">where</span> <span class="hljs-title">all</span> <span class="hljs-title">vertices</span> <span class="hljs-title">in</span> <span class="hljs-title">one</span> <span class="hljs-title">group</span>

    <span class="hljs-title">while</span> <span class="hljs-title">T</span> <span class="hljs-title">has</span> <span class="hljs-title">fewer</span> <span class="hljs-title">than</span> <span class="hljs-title">n</span>-1 <span class="hljs-title">edges</span>:
        // <span class="hljs-title">Pick</span> <span class="hljs-title">any</span> <span class="hljs-title">group</span> <span class="hljs-title">with</span> |<span class="hljs-title">group</span>| ≥ 2
        <span class="hljs-title">S</span> = <span class="hljs-title">any</span> <span class="hljs-title">group</span> <span class="hljs-title">with</span> |<span class="hljs-title">S</span>| ≥ 2
        <span class="hljs-title">pick</span> <span class="hljs-title">arbitrary</span> <span class="hljs-title">s</span>, <span class="hljs-title">t</span> <span class="hljs-title">in</span> <span class="hljs-title">S</span>

        // <span class="hljs-title">Contract</span> <span class="hljs-title">all</span> <span class="hljs-title">other</span> <span class="hljs-title">groups</span> <span class="hljs-title">in</span> <span class="hljs-title">G</span>
        <span class="hljs-title">G</span>' = <span class="hljs-title">contract</span> <span class="hljs-title">each</span> <span class="hljs-title">group</span> ≠ <span class="hljs-title">S</span> <span class="hljs-title">into</span> <span class="hljs-title">a</span> <span class="hljs-title">single</span> <span class="hljs-title">super</span>-<span class="hljs-title">node</span>

        // <span class="hljs-title">Find</span> <span class="hljs-title">min</span> <span class="hljs-title">s</span>-<span class="hljs-title">t</span> <span class="hljs-title">cut</span> <span class="hljs-title">in</span> <span class="hljs-title">G</span>'
        <span class="hljs-title">cut_value</span>, (<span class="hljs-params">A, B</span>) = <span class="hljs-title">MIN</span>-<span class="hljs-title">CUT</span>(<span class="hljs-params">G<span class="hljs-string">', s, t)
        // A and B partition V(G'</span></span>)

        // <span class="hljs-title">Add</span> <span class="hljs-title">edge</span> <span class="hljs-title">to</span> <span class="hljs-title">T</span>
        <span class="hljs-title">add</span> <span class="hljs-title">edge</span> (<span class="hljs-params">s, t</span>) <span class="hljs-title">to</span> <span class="hljs-title">T</span> <span class="hljs-title">with</span> <span class="hljs-title">weight</span> <span class="hljs-title">cut_value</span>

        // <span class="hljs-title">Split</span> <span class="hljs-title">group</span> <span class="hljs-title">S</span> <span class="hljs-title">according</span> <span class="hljs-title">to</span> <span class="hljs-title">cut</span>
        <span class="hljs-title">S_A</span> = <span class="hljs-title">S</span> ∩ <span class="hljs-title">A</span>
        <span class="hljs-title">S_B</span> = <span class="hljs-title">S</span> ∩ <span class="hljs-title">B</span>
        <span class="hljs-title">replace</span> <span class="hljs-title">S</span> <span class="hljs-title">with</span> <span class="hljs-title">S_A</span> <span class="hljs-title">and</span> <span class="hljs-title">S_B</span> <span class="hljs-title">in</span> <span class="hljs-title">groups</span>

    <span class="hljs-title">return</span> <span class="hljs-title">T</span>
</code></pre><p><strong>Detailed Explanation:</strong></p>
<p><strong>Iteration structure:</strong></p>
<ul>
<li>Start with all vertices in one group</li>
<li>Each iteration:<ul>
<li>Pick a group S</li>
<li>Find min cut separating some s,t ∈ S</li>
<li>This splits S into two smaller groups</li>
</ul>
</li>
<li>After n-1 iterations, each vertex is its own group</li>
<li>The n-1 min cuts found define the tree edges</li>
</ul>
<p><strong>Correctness (Sketch):</strong></p>
<p>The key invariant is:</p>
<ul>
<li>At any point, for vertices s and t in the same group, we haven't yet determined their min cut</li>
<li>For vertices s and t in different groups, their min cut in G equals the min cut in T found so far</li>
</ul>
<p>When we split group S using min cut (A,B):</p>
<ul>
<li>The cut value is the min s-t cut in G</li>
<li>Any other pair (u,v) with u,v ∈ S: their min cut is either:<ul>
<li>Same as s-t cut (if u,v are separated by this cut), or</li>
<li>Strictly larger (if u,v are on same side)</li>
</ul>
</li>
</ul>
<p>By carefully choosing which pairs to separate, we build up the tree structure.</p>
<p><strong>Example:</strong></p>
<pre><code>Graph G:
    A--<span class="hljs-number">-5</span>---B
    |       |
    <span class="hljs-number">3</span>       <span class="hljs-number">4</span>
    |       |
    C--<span class="hljs-number">-6</span>---D

Iteration <span class="hljs-number">1</span>:
- Group S = {A, B, C, D}, choose s=A, t=B
- Contract nothing (only one group)
- Min A-B cut: cut edges {A-C, B-D} <span class="hljs-keyword">with</span> value <span class="hljs-number">3</span>+<span class="hljs-number">4</span>=<span class="hljs-number">7</span>? 
  Or cut edge A-B <span class="hljs-keyword">with</span> value <span class="hljs-number">5</span>?
  Min is <span class="hljs-number">5</span> (cut A-B)
  <span class="hljs-attr">Partition</span>: A={A}, B={B,C,D}? No wait, min cut needs to separate A <span class="hljs-keyword">from</span> B.

Let me reconsider the edges:
- A-B: <span class="hljs-number">5</span>
- A-C: <span class="hljs-number">3</span>
- B-D: <span class="hljs-number">4</span>
- C-D: <span class="hljs-number">6</span>

Min A-B cut:
- Option <span class="hljs-number">1</span>: Cut A-B directly, value <span class="hljs-number">5</span>
- Option <span class="hljs-number">2</span>: Cut A-C and B-D, value <span class="hljs-number">3</span>+<span class="hljs-number">4</span>=<span class="hljs-number">7</span>
- Minimum is <span class="hljs-number">5</span>, partition ({A,C}, {B,D})? 
  No, that doesn<span class="hljs-string">'t cut A-B. Let me think again.

A min cut between A and B separates them into two parts.
- Cut 1: {A} | {B,C,D}, cut edges: A-B(5), A-C(3), total value? No, cut value is sum of edges crossing.
  Actually, only A-B and A-C cross. So value = 5+3=8? No wait.

I think I'</span>m confusing edge-disjoint <span class="hljs-keyword">from</span> vertex-disjoint.

For min cut <span class="hljs-keyword">in</span> unweighted graph, we want minimum total weight <span class="hljs-keyword">of</span> edges <span class="hljs-keyword">from</span> one partition to the other.

Partition ({A},{B,C,D}): edges crossing are A-B(<span class="hljs-number">5</span>), A-C(<span class="hljs-number">3</span>), value=<span class="hljs-number">8</span>
Partition ({A,C},{B,D}): edges crossing are A-B(<span class="hljs-number">5</span>), C-D(<span class="hljs-number">6</span>), A-C(<span class="hljs-number">3</span>)?
  No wait, A-C doesn<span class="hljs-string">'t cross since both A and C are in the same partition.
  Edges crossing: A-B(5), C-D(6), but also... if C connects to B? It doesn'</span>t <span class="hljs-keyword">in</span> <span class="hljs-built_in">this</span> graph.
  Actually only A-B crosses? No, C connects to D, and C is on left, D is on right.
  So edges crossing: A-B(<span class="hljs-number">5</span>), C-D(<span class="hljs-number">6</span>), value=<span class="hljs-number">11</span>

Hmm, first partition is better: value <span class="hljs-number">8.</span>

Actually, I think I drew the graph wrong. Let me use a clearer example:

Graph:
  A-<span class="hljs-number">-10</span>--B
  |      |
  <span class="hljs-number">1</span>      <span class="hljs-number">1</span>  
  |      |
  C-<span class="hljs-number">-10</span>--D

Min A-B cut:
- Partition ({A},{B,C,D}): cut A-B(<span class="hljs-number">10</span>) and A-C(<span class="hljs-number">1</span>), value=<span class="hljs-number">11</span>
- Partition ({A,C},{B,D}): cut A-B(<span class="hljs-number">10</span>) and C-D(<span class="hljs-number">10</span>), value=<span class="hljs-number">20</span>
- Partition ({A,B},{C,D}): cut A-C(<span class="hljs-number">1</span>) and B-D(<span class="hljs-number">1</span>), value=<span class="hljs-number">2</span>
  But <span class="hljs-built_in">this</span> doesn<span class="hljs-string">'t separate A from B!

OK so partition ({A,D},{B,C}): cut A-B(10), A-C(1), C-D(10), B-D(1)?
  A-C crosses, C-D crosses, but let me list all edges:
  A-B(left to right), A-C(left to right? A is in left, C is in right, so yes)
  C-D(right to right, no)
  B-D(right to left)

  So cut edges: A-B(10), A-C(1), B-D(1), value=12

Minimum is ({A},{B,C,D}) with value 11.

Add edge A-s to tree where s represents the other component. But actually in Gomory-Hu, we add edge between representatives.

Let me simplify: After first iteration, we split {A,B,C,D} into {A} and {B,C,D}, with edge A-B'</span> (where B<span class="hljs-string">' represents {B,C,D}) having weight 11.

Wait, I think I'</span>m overcomplicating. The algorithm description I gave is simplified. Let me look up the exact algorithm...

Actually, the Gomory-Hu algorithm is somewhat involved. The key idea is correct:
- Build tree incrementally by finding min cuts
- Each cut splits a group
- Final tree has property that min s-t cut <span class="hljs-keyword">in</span> tree equals min s-t cut <span class="hljs-keyword">in</span> graph
</code></pre><p><strong>Complexity:</strong></p>
<ul>
<li>n-1 iterations</li>
<li>Each iteration: one max-flow computation</li>
<li>Total: O(n × max-flow-time)</li>
<li>With modern max-flow algorithms: O(n × VE log(V²/E)) or better</li>
</ul>
<p><strong>Applications:</strong></p>
<ul>
<li>Network reliability analysis</li>
<li>Finding all bottlenecks in a network</li>
<li>Approximation algorithms for multicut problems</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Graph Theory Explained]]></title><description><![CDATA[Table of Contents

Introduction to Graph Theory
Basic Definitions and Terminology
Types of Graphs
Graph Representation
Graph Traversal Algorithms
Shortest Path Algorithms
Minimum Spanning Trees
Network Flow
Graph Coloring
Matching Theory
Planarity
Co...]]></description><link>https://arnavverma.hashnode.dev/graph-theory-explained</link><guid isPermaLink="true">https://arnavverma.hashnode.dev/graph-theory-explained</guid><dc:creator><![CDATA[Arnav Verma]]></dc:creator><pubDate>Sat, 20 Dec 2025 10:39:54 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><a class="post-section-overview" href="#1-introduction-to-graph-theory">Introduction to Graph Theory</a></li>
<li><a class="post-section-overview" href="#2-basic-definitions-and-terminology">Basic Definitions and Terminology</a></li>
<li><a class="post-section-overview" href="#3-types-of-graphs">Types of Graphs</a></li>
<li><a class="post-section-overview" href="#4-graph-representation">Graph Representation</a></li>
<li><a class="post-section-overview" href="#5-graph-traversal-algorithms">Graph Traversal Algorithms</a></li>
<li><a class="post-section-overview" href="#6-shortest-path-algorithms">Shortest Path Algorithms</a></li>
<li><a class="post-section-overview" href="#7-minimum-spanning-trees">Minimum Spanning Trees</a></li>
<li><a class="post-section-overview" href="#8-network-flow">Network Flow</a></li>
<li><a class="post-section-overview" href="#9-graph-coloring">Graph Coloring</a></li>
<li><a class="post-section-overview" href="#10-matching-theory">Matching Theory</a></li>
<li><a class="post-section-overview" href="#11-planarity">Planarity</a></li>
<li><a class="post-section-overview" href="#12-connectivity">Connectivity</a></li>
<li><a class="post-section-overview" href="#13-trees-and-special-graphs">Trees and Special Graphs</a></li>
<li><a class="post-section-overview" href="#14-advanced-topics">Advanced Topics</a></li>
<li><a class="post-section-overview" href="#15-industry-applications">Industry Applications</a></li>
<li><a class="post-section-overview" href="#16-common-problems-and-solutions">Common Problems and Solutions</a></li>
<li><a class="post-section-overview" href="#17-complexity-analysis">Complexity Analysis</a></li>
</ol>
<hr />
<h2 id="heading-1-introduction-to-graph-theory">1. Introduction to Graph Theory</h2>
<h3 id="heading-what-is-graph-theory">What is Graph Theory?</h3>
<p>Graph theory is the mathematical study of graphs, which are structures used to model pairwise relations between objects. A graph consists of vertices (also called nodes) connected by edges (also called links or arcs).</p>
<h3 id="heading-historical-context">Historical Context</h3>
<ul>
<li><strong>1736</strong>: Leonhard Euler solved the Seven Bridges of Königsberg problem, founding graph theory</li>
<li><strong>1800s</strong>: Development of tree structures and chemical graph theory</li>
<li><strong>1900s</strong>: Explosion of applications in computer science, operations research, and network analysis</li>
<li><strong>Modern Era</strong>: Critical for social networks, internet routing, bioinformatics, and AI</li>
</ul>
<h3 id="heading-why-study-graph-theory">Why Study Graph Theory?</h3>
<p>Graph theory provides:</p>
<ul>
<li>A framework for modeling relationships and networks</li>
<li>Algorithms for optimization problems</li>
<li>Tools for analyzing complex systems</li>
<li>Foundation for many computer science concepts</li>
</ul>
<hr />
<h2 id="heading-2-basic-definitions-and-terminology">2. Basic Definitions and Terminology</h2>
<h3 id="heading-core-concepts">Core Concepts</h3>
<p><strong>Graph (G)</strong></p>
<ul>
<li>A graph G = (V, E) consists of:<ul>
<li>V: a finite set of vertices (nodes)</li>
<li>E: a set of edges (connections between vertices)</li>
</ul>
</li>
</ul>
<p><strong>Vertex (Node)</strong></p>
<ul>
<li>A fundamental unit or point in a graph</li>
<li>Denoted as v ∈ V</li>
<li>Example: Cities in a road network</li>
</ul>
<p><strong>Edge</strong></p>
<ul>
<li>A connection between two vertices</li>
<li>Denoted as e = (u, v) or e = {u, v}</li>
<li>Can be directed or undirected</li>
<li>Example: Roads connecting cities</li>
</ul>
<p><strong>Adjacency</strong></p>
<ul>
<li>Two vertices u and v are <strong>adjacent</strong> if there exists an edge (u, v)</li>
<li>The edge (u, v) is <strong>incident</strong> to vertices u and v</li>
<li>Vertices u and v are <strong>endpoints</strong> of edge (u, v)</li>
</ul>
<p><strong>Degree</strong></p>
<ul>
<li><strong>Degree of a vertex</strong> deg(v): number of edges incident to v</li>
<li>In directed graphs:<ul>
<li><strong>In-degree</strong> (deg⁻(v)): number of edges coming into v</li>
<li><strong>Out-degree</strong> (deg⁺(v)): number of edges going out of v</li>
<li>deg(v) = deg⁻(v) + deg⁺(v)</li>
</ul>
</li>
</ul>
<p><strong>Handshaking Lemma</strong></p>
<ul>
<li>The sum of all vertex degrees equals twice the number of edges:<pre><code>∑ deg(v) = <span class="hljs-number">2</span>|E|
</code></pre></li>
<li>Consequence: The number of vertices with odd degree is always even</li>
</ul>
<p><strong>Path</strong></p>
<ul>
<li>A sequence of vertices v₁, v₂, ..., vₖ where consecutive vertices are adjacent</li>
<li><strong>Length</strong>: number of edges in the path</li>
<li><strong>Simple path</strong>: no repeated vertices</li>
</ul>
<p><strong>Walk</strong></p>
<ul>
<li>A sequence of vertices where consecutive vertices are connected by edges</li>
<li>May repeat vertices and edges</li>
</ul>
<p><strong>Trail</strong></p>
<ul>
<li>A walk with no repeated edges</li>
<li>May repeat vertices</li>
</ul>
<p><strong>Cycle</strong></p>
<ul>
<li>A path that starts and ends at the same vertex</li>
<li><strong>Simple cycle</strong>: no repeated vertices except first and last</li>
</ul>
<p><strong>Connected Graph</strong></p>
<ul>
<li>A graph where there exists a path between every pair of vertices</li>
<li><strong>Connected component</strong>: maximal connected subgraph</li>
</ul>
<p><strong>Distance</strong></p>
<ul>
<li><strong>Distance d(u, v)</strong>: length of shortest path between u and v</li>
<li>If no path exists, d(u, v) = ∞</li>
</ul>
<p><strong>Diameter</strong></p>
<ul>
<li>Maximum distance between any pair of vertices in the graph</li>
<li>diameter(G) = max{d(u, v) : u, v ∈ V}</li>
</ul>
<p><strong>Subgraph</strong></p>
<ul>
<li>A graph G' = (V', E') where V' ⊆ V and E' ⊆ E</li>
<li><strong>Induced subgraph</strong>: includes all edges from G whose endpoints are both in V'</li>
<li><strong>Spanning subgraph</strong>: V' = V</li>
</ul>
<p><strong>Complement Graph</strong></p>
<ul>
<li>Graph Ḡ contains exactly the edges not in G</li>
<li>If (u, v) ∈ E, then (u, v) ∉ Ē, and vice versa</li>
</ul>
<hr />
<h2 id="heading-3-types-of-graphs">3. Types of Graphs</h2>
<h3 id="heading-31-undirected-graphs">3.1 Undirected Graphs</h3>
<p><strong>Definition</strong>: Edges have no direction; (u, v) = (v, u)</p>
<p><strong>Properties</strong>:</p>
<ul>
<li>Symmetric relationships</li>
<li>Adjacency matrix is symmetric</li>
<li>Used for mutual relationships</li>
</ul>
<p><strong>Examples</strong>:</p>
<ul>
<li>Social networks (friendship)</li>
<li>Computer networks</li>
<li>Chemical structures</li>
</ul>
<h3 id="heading-32-directed-graphs-digraphs">3.2 Directed Graphs (Digraphs)</h3>
<p><strong>Definition</strong>: Edges have direction; (u, v) ≠ (v, u)</p>
<p><strong>Properties</strong>:</p>
<ul>
<li>Asymmetric relationships possible</li>
<li>In-degree and out-degree</li>
<li>Adjacency matrix may be asymmetric</li>
</ul>
<p><strong>Examples</strong>:</p>
<ul>
<li>Web page links</li>
<li>Twitter followers</li>
<li>Task dependencies</li>
</ul>
<p><strong>Special Structures</strong>:</p>
<ul>
<li><strong>DAG (Directed Acyclic Graph)</strong>: no directed cycles</li>
<li><strong>Tournament</strong>: directed complete graph</li>
</ul>
<h3 id="heading-33-weighted-graphs">3.3 Weighted Graphs</h3>
<p><strong>Definition</strong>: Each edge has an associated weight or cost</p>
<p><strong>Notation</strong>: G = (V, E, w) where w: E → ℝ</p>
<p><strong>Applications</strong>:</p>
<ul>
<li>Road networks (distances)</li>
<li>Network flows (capacities)</li>
<li>Cost optimization</li>
</ul>
<h3 id="heading-34-simple-graphs">3.4 Simple Graphs</h3>
<p><strong>Properties</strong>:</p>
<ul>
<li>No loops (edges from a vertex to itself)</li>
<li>No multiple edges between same vertex pair</li>
<li>Most commonly studied type</li>
</ul>
<h3 id="heading-35-multigraphs">3.5 Multigraphs</h3>
<p><strong>Properties</strong>:</p>
<ul>
<li>Allow multiple edges between same vertices</li>
<li>May allow self-loops</li>
</ul>
<p><strong>Applications</strong>:</p>
<ul>
<li>Transportation networks with multiple routes</li>
<li>Parallel connections in circuits</li>
</ul>
<h3 id="heading-36-complete-graphs-k">3.6 Complete Graphs (Kₙ)</h3>
<p><strong>Definition</strong>: Every pair of distinct vertices is connected by an edge</p>
<p><strong>Properties</strong>:</p>
<ul>
<li>n vertices</li>
<li>n(n-1)/2 edges</li>
<li>Each vertex has degree n-1</li>
<li>Highly connected</li>
</ul>
<p><strong>Examples</strong>:</p>
<ul>
<li>K₃: Triangle</li>
<li>K₄: Tetrahedron graph</li>
<li>K₅: Non-planar graph</li>
</ul>
<h3 id="heading-37-bipartite-graphs">3.7 Bipartite Graphs</h3>
<p><strong>Definition</strong>: Vertices can be divided into two disjoint sets U and V such that every edge connects a vertex in U to one in V</p>
<p><strong>Notation</strong>: G = (U ∪ V, E)</p>
<p><strong>Properties</strong>:</p>
<ul>
<li>No odd cycles</li>
<li>2-colorable</li>
<li>Can be tested in O(V + E) time using BFS</li>
</ul>
<p><strong>Complete Bipartite Graph (Kₘ,ₙ)</strong>:</p>
<ul>
<li>Every vertex in U connected to every vertex in V</li>
<li>|U| = m, |V| = n</li>
<li>Number of edges = m × n</li>
</ul>
<p><strong>Applications</strong>:</p>
<ul>
<li>Job assignment problems</li>
<li>Matching problems</li>
<li>Recommendation systems</li>
</ul>
<h3 id="heading-38-regular-graphs">3.8 Regular Graphs</h3>
<p><strong>Definition</strong>: All vertices have the same degree</p>
<p><strong>k-regular graph</strong>: every vertex has degree k</p>
<p><strong>Examples</strong>:</p>
<ul>
<li>3-regular: cubic graphs</li>
<li>Petersen graph (3-regular)</li>
<li>Platonic solid graphs</li>
</ul>
<h3 id="heading-39-planar-graphs">3.9 Planar Graphs</h3>
<p><strong>Definition</strong>: Can be drawn on a plane without edge crossings</p>
<p><strong>Euler's Formula</strong>: V - E + F = 2</p>
<ul>
<li>V: vertices</li>
<li>E: edges</li>
<li>F: faces (including outer face)</li>
</ul>
<p><strong>Consequences</strong>:</p>
<ul>
<li>For connected planar graph: E ≤ 3V - 6</li>
<li>For bipartite planar graph: E ≤ 2V - 4</li>
</ul>
<p><strong>Non-planar Graphs</strong>:</p>
<ul>
<li>K₅ (complete graph on 5 vertices)</li>
<li>K₃,₃ (complete bipartite graph)</li>
</ul>
<p><strong>Kuratowski's Theorem</strong>: A graph is planar if and only if it doesn't contain K₅ or K₃,₃ as a subdivision</p>
<h3 id="heading-310-trees">3.10 Trees</h3>
<p><strong>Definition</strong>: Connected acyclic graph</p>
<p><strong>Properties</strong>:</p>
<ul>
<li>n vertices → n-1 edges</li>
<li>Unique path between any two vertices</li>
<li>Adding any edge creates exactly one cycle</li>
<li>Removing any edge disconnects the graph</li>
</ul>
<p><strong>Types</strong>:</p>
<ul>
<li><strong>Rooted tree</strong>: one vertex designated as root</li>
<li><strong>Binary tree</strong>: each node has at most 2 children</li>
<li><strong>Spanning tree</strong>: subgraph that is a tree containing all vertices</li>
</ul>
<h3 id="heading-311-hypergraphs">3.11 Hypergraphs</h3>
<p><strong>Definition</strong>: Generalization where edges can connect any number of vertices</p>
<p><strong>Applications</strong>:</p>
<ul>
<li>Database relations</li>
<li>Set systems</li>
<li>Group interactions</li>
</ul>
<hr />
<h2 id="heading-4-graph-representation">4. Graph Representation</h2>
<h3 id="heading-41-adjacency-matrix">4.1 Adjacency Matrix</h3>
<p><strong>Definition</strong>: n × n matrix A where A[i][j] = 1 if (i, j) ∈ E, else 0</p>
<p><strong>For Weighted Graphs</strong>: A[i][j] = weight of edge (i, j), or ∞ if no edge</p>
<p><strong>Properties</strong>:</p>
<ul>
<li>Space complexity: O(V²)</li>
<li>Edge lookup: O(1)</li>
<li>Finding all neighbors: O(V)</li>
<li>Symmetric for undirected graphs</li>
</ul>
<p><strong>Advantages</strong>:</p>
<ul>
<li>Fast edge existence check</li>
<li>Simple to implement</li>
<li>Good for dense graphs</li>
</ul>
<p><strong>Disadvantages</strong>:</p>
<ul>
<li>Wastes space for sparse graphs</li>
<li>Iterating over neighbors is slow</li>
</ul>
<p><strong>Example</strong>:</p>
<pre><code>Graph: <span class="hljs-number">1</span><span class="hljs-number">-2</span>, <span class="hljs-number">1</span><span class="hljs-number">-3</span>, <span class="hljs-number">2</span><span class="hljs-number">-3</span>, <span class="hljs-number">3</span><span class="hljs-number">-4</span>

Adjacency Matrix:
    <span class="hljs-number">1</span>  <span class="hljs-number">2</span>  <span class="hljs-number">3</span>  <span class="hljs-number">4</span>
<span class="hljs-number">1</span> [ <span class="hljs-number">0</span>  <span class="hljs-number">1</span>  <span class="hljs-number">1</span>  <span class="hljs-number">0</span> ]
<span class="hljs-number">2</span> [ <span class="hljs-number">1</span>  <span class="hljs-number">0</span>  <span class="hljs-number">1</span>  <span class="hljs-number">0</span> ]
<span class="hljs-number">3</span> [ <span class="hljs-number">1</span>  <span class="hljs-number">1</span>  <span class="hljs-number">0</span>  <span class="hljs-number">1</span> ]
<span class="hljs-number">4</span> [ <span class="hljs-number">0</span>  <span class="hljs-number">0</span>  <span class="hljs-number">1</span>  <span class="hljs-number">0</span> ]
</code></pre><h3 id="heading-42-adjacency-list">4.2 Adjacency List</h3>
<p><strong>Definition</strong>: Array of lists; each vertex has a list of its neighbors</p>
<p><strong>Structure</strong>:</p>
<pre><code><span class="hljs-number">1</span>: [<span class="hljs-number">2</span>, <span class="hljs-number">3</span>]
<span class="hljs-number">2</span>: [<span class="hljs-number">1</span>, <span class="hljs-number">3</span>]
<span class="hljs-number">3</span>: [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">4</span>]
<span class="hljs-number">4</span>: [<span class="hljs-number">3</span>]
</code></pre><p><strong>Properties</strong>:</p>
<ul>
<li>Space complexity: O(V + E)</li>
<li>Edge lookup: O(degree(v))</li>
<li>Finding all neighbors: O(degree(v))</li>
</ul>
<p><strong>Advantages</strong>:</p>
<ul>
<li>Space-efficient for sparse graphs</li>
<li>Fast iteration over neighbors</li>
<li>Natural for most graph algorithms</li>
</ul>
<p><strong>Disadvantages</strong>:</p>
<ul>
<li>Slower edge existence check</li>
<li>More complex implementation</li>
</ul>
<h3 id="heading-43-edge-list">4.3 Edge List</h3>
<p><strong>Definition</strong>: List of all edges</p>
<p><strong>Structure</strong>:</p>
<pre><code>[(<span class="hljs-number">1</span>,<span class="hljs-number">2</span>), (<span class="hljs-number">1</span>,<span class="hljs-number">3</span>), (<span class="hljs-number">2</span>,<span class="hljs-number">3</span>), (<span class="hljs-number">3</span>,<span class="hljs-number">4</span>)]
</code></pre><p><strong>Properties</strong>:</p>
<ul>
<li>Space complexity: O(E)</li>
<li>Simple to implement</li>
<li>Good for iterating over all edges</li>
</ul>
<p><strong>Use Cases</strong>:</p>
<ul>
<li>Kruskal's algorithm</li>
<li>Edge-centric operations</li>
<li>Simple graph storage</li>
</ul>
<h3 id="heading-44-incidence-matrix">4.4 Incidence Matrix</h3>
<p><strong>Definition</strong>: V × E matrix M where M[v][e] = 1 if vertex v is incident to edge e</p>
<p><strong>For Directed Graphs</strong>:</p>
<ul>
<li>M[v][e] = -1 if edge e leaves v</li>
<li>M[v][e] = 1 if edge e enters v</li>
<li>M[v][e] = 0 otherwise</li>
</ul>
<p><strong>Properties</strong>:</p>
<ul>
<li>Space complexity: O(V × E)</li>
<li>Rarely used in practice</li>
<li>Useful for theoretical analysis</li>
</ul>
<h3 id="heading-45-comparison-table">4.5 Comparison Table</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Representation</td><td>Space</td><td>Check Edge</td><td>Get Neighbors</td><td>Best For</td></tr>
</thead>
<tbody>
<tr>
<td>Adj Matrix</td><td>O(V²)</td><td>O(1)</td><td>O(V)</td><td>Dense graphs</td></tr>
<tr>
<td>Adj List</td><td>O(V+E)</td><td>O(deg(v))</td><td>O(deg(v))</td><td>Sparse graphs</td></tr>
<tr>
<td>Edge List</td><td>O(E)</td><td>O(E)</td><td>O(E)</td><td>Simple operations</td></tr>
<tr>
<td>Inc Matrix</td><td>O(V×E)</td><td>O(E)</td><td>O(E)</td><td>Theoretical work</td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-5-graph-traversal-algorithms">5. Graph Traversal Algorithms</h2>
<h3 id="heading-51-breadth-first-search-bfs">5.1 Breadth-First Search (BFS)</h3>
<p><strong>Concept</strong>: Explore graph level by level from source vertex</p>
<p><strong>Algorithm</strong>:</p>
<pre><code>BFS(G, s):
    <span class="hljs-keyword">for</span> each vertex v <span class="hljs-keyword">in</span> G.V:
        v.color = WHITE
        v.distance = ∞
        v.parent = NIL

    s.color = GRAY
    s.distance = <span class="hljs-number">0</span>
    s.parent = NIL

    Q = empty queue
    ENQUEUE(Q, s)

    <span class="hljs-keyword">while</span> Q is not empty:
        u = DEQUEUE(Q)
        <span class="hljs-keyword">for</span> each v <span class="hljs-keyword">in</span> G.Adj[u]:
            <span class="hljs-keyword">if</span> v.color == WHITE:
                v.color = GRAY
                v.distance = u.distance + <span class="hljs-number">1</span>
                v.parent = u
                ENQUEUE(Q, v)
        u.color = BLACK
</code></pre><p><strong>Properties</strong>:</p>
<ul>
<li>Time complexity: O(V + E)</li>
<li>Space complexity: O(V)</li>
<li>Finds shortest path (unweighted graphs)</li>
<li>Produces BFS tree</li>
</ul>
<p><strong>Applications</strong>:</p>
<ul>
<li>Shortest path in unweighted graphs</li>
<li>Testing bipartiteness</li>
<li>Finding connected components</li>
<li>Level-order traversal</li>
<li>Web crawling</li>
<li>Social network analysis (degrees of separation)</li>
</ul>
<p><strong>Path Reconstruction</strong>:</p>
<pre><code>PRINT-PATH(G, s, v):
    <span class="hljs-keyword">if</span> v == s:
        print s
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> v.parent == NIL:
        print <span class="hljs-string">"no path exists"</span>
    <span class="hljs-attr">else</span>:
        PRINT-PATH(G, s, v.parent)
        print v
</code></pre><h3 id="heading-52-depth-first-search-dfs">5.2 Depth-First Search (DFS)</h3>
<p><strong>Concept</strong>: Explore as far as possible along each branch before backtracking</p>
<p><strong>Algorithm</strong>:</p>
<pre><code>DFS(G):
    <span class="hljs-keyword">for</span> each vertex u <span class="hljs-keyword">in</span> G.V:
        u.color = WHITE
        u.parent = NIL
    time = <span class="hljs-number">0</span>

    <span class="hljs-keyword">for</span> each vertex u <span class="hljs-keyword">in</span> G.V:
        <span class="hljs-keyword">if</span> u.color == WHITE:
            DFS-VISIT(G, u)

DFS-VISIT(G, u):
    time = time + <span class="hljs-number">1</span>
    u.discovery = time
    u.color = GRAY

    <span class="hljs-keyword">for</span> each v <span class="hljs-keyword">in</span> G.Adj[u]:
        <span class="hljs-keyword">if</span> v.color == WHITE:
            v.parent = u
            DFS-VISIT(G, v)

    u.color = BLACK
    time = time + <span class="hljs-number">1</span>
    u.finish = time
</code></pre><p><strong>Edge Classification</strong>:</p>
<ol>
<li><strong>Tree edges</strong>: edges in DFS forest</li>
<li><strong>Back edges</strong>: connect vertex to ancestor (indicate cycles)</li>
<li><strong>Forward edges</strong>: connect vertex to descendant</li>
<li><strong>Cross edges</strong>: all other edges</li>
</ol>
<p><strong>Properties</strong>:</p>
<ul>
<li>Time complexity: O(V + E)</li>
<li>Space complexity: O(V) (recursion stack)</li>
<li>Produces DFS forest</li>
<li>Discovery and finish times</li>
</ul>
<p><strong>Applications</strong>:</p>
<ul>
<li>Cycle detection</li>
<li>Topological sorting</li>
<li>Strongly connected components</li>
<li>Solving mazes</li>
<li>Finding bridges and articulation points</li>
<li>Pathfinding in games</li>
</ul>
<p><strong>Iterative DFS</strong>:</p>
<pre><code>DFS-ITERATIVE(G, s):
    S = empty stack
    S.push(s)
    visited = empty set

    <span class="hljs-keyword">while</span> S is not empty:
        u = S.pop()
        <span class="hljs-keyword">if</span> u not <span class="hljs-keyword">in</span> visited:
            visited.add(u)
            process(u)
            <span class="hljs-keyword">for</span> each v <span class="hljs-keyword">in</span> G.Adj[u]:
                <span class="hljs-keyword">if</span> v not <span class="hljs-keyword">in</span> visited:
                    S.push(v)
</code></pre><h3 id="heading-53-topological-sort">5.3 Topological Sort</h3>
<p><strong>Concept</strong>: Linear ordering of vertices in DAG such that for every edge (u, v), u comes before v</p>
<p><strong>Algorithm (DFS-based)</strong>:</p>
<pre><code>TOPOLOGICAL-SORT(G):
    L = empty list
    <span class="hljs-keyword">for</span> each vertex u <span class="hljs-keyword">in</span> G.V:
        <span class="hljs-keyword">if</span> u.color == WHITE:
            DFS-VISIT-TOPO(G, u, L)
    <span class="hljs-keyword">return</span> L

DFS-VISIT-TOPO(G, u, L):
    u.color = GRAY
    <span class="hljs-keyword">for</span> each v <span class="hljs-keyword">in</span> G.Adj[u]:
        <span class="hljs-keyword">if</span> v.color == WHITE:
            DFS-VISIT-TOPO(G, v, L)
        <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> v.color == GRAY:
            error <span class="hljs-string">"not a DAG"</span>
    u.color = BLACK
    L.prepend(u)  <span class="hljs-comment">// Add to front of list</span>
</code></pre><p><strong>Kahn's Algorithm</strong> (BFS-based):</p>
<pre><code>KAHN-TOPOLOGICAL-SORT(G):
    L = empty list
    S = set <span class="hljs-keyword">of</span> all vertices <span class="hljs-keyword">with</span> <span class="hljs-keyword">in</span>-degree <span class="hljs-number">0</span>

    <span class="hljs-keyword">while</span> S is not empty:
        u = S.remove()
        L.append(u)
        <span class="hljs-keyword">for</span> each v <span class="hljs-keyword">with</span> edge (u, v):
            remove edge (u, v)
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">in</span>-degree(v) == <span class="hljs-number">0</span>:
                S.add(v)

    <span class="hljs-keyword">if</span> graph still has edges:
        error <span class="hljs-string">"not a DAG"</span>
    <span class="hljs-keyword">return</span> L
</code></pre><p><strong>Properties</strong>:</p>
<ul>
<li>Time complexity: O(V + E)</li>
<li>Only works on DAGs</li>
<li>Not unique (multiple valid orderings may exist)</li>
</ul>
<p><strong>Applications</strong>:</p>
<ul>
<li>Task scheduling</li>
<li>Build systems (Makefiles)</li>
<li>Course prerequisites</li>
<li>Dependency resolution</li>
<li>Compilation order</li>
</ul>
<hr />
<h2 id="heading-6-shortest-path-algorithms">6. Shortest Path Algorithms</h2>
<h3 id="heading-61-single-source-shortest-paths">6.1 Single-Source Shortest Paths</h3>
<h4 id="heading-611-dijkstras-algorithm">6.1.1 Dijkstra's Algorithm</h4>
<p><strong>Concept</strong>: Find shortest paths from source to all vertices (non-negative weights)</p>
<p><strong>Algorithm</strong>:</p>
<pre><code>DIJKSTRA(G, w, s):
    INITIALIZE-SINGLE-SOURCE(G, s)
    S = empty set  <span class="hljs-comment">// vertices with final distances</span>
    Q = G.V  <span class="hljs-comment">// priority queue</span>

    <span class="hljs-keyword">while</span> Q is not empty:
        u = EXTRACT-MIN(Q)  <span class="hljs-comment">// vertex with minimum distance</span>
        S = S ∪ {u}
        <span class="hljs-keyword">for</span> each vertex v <span class="hljs-keyword">in</span> G.Adj[u]:
            RELAX(u, v, w)

INITIALIZE-SINGLE-SOURCE(G, s):
    <span class="hljs-keyword">for</span> each vertex v <span class="hljs-keyword">in</span> G.V:
        v.d = ∞
        v.parent = NIL
    s.d = <span class="hljs-number">0</span>

RELAX(u, v, w):
    <span class="hljs-keyword">if</span> v.d &gt; u.d + w(u, v):
        v.d = u.d + w(u, v)
        v.parent = u
        DECREASE-KEY(Q, v, v.d)
</code></pre><p><strong>Properties</strong>:</p>
<ul>
<li>Time complexity:<ul>
<li>With binary heap: O((V + E) log V)</li>
<li>With Fibonacci heap: O(E + V log V)</li>
<li>With array: O(V²)</li>
</ul>
</li>
<li>Space complexity: O(V)</li>
<li>Requires non-negative edge weights</li>
<li>Greedy algorithm</li>
<li>Always produces shortest paths if weights ≥ 0</li>
</ul>
<p><strong>Optimizations</strong>:</p>
<ul>
<li>Early termination when target found</li>
<li>Bidirectional search</li>
<li>A* algorithm (with heuristic)</li>
</ul>
<p><strong>Applications</strong>:</p>
<ul>
<li>GPS navigation</li>
<li>Network routing (OSPF)</li>
<li>Flight scheduling</li>
<li>Robot path planning</li>
</ul>
<h4 id="heading-612-bellman-ford-algorithm">6.1.2 Bellman-Ford Algorithm</h4>
<p><strong>Concept</strong>: Find shortest paths from source, handles negative weights</p>
<p><strong>Algorithm</strong>:</p>
<pre><code>BELLMAN-FORD(G, w, s):
    INITIALIZE-SINGLE-SOURCE(G, s)

    <span class="hljs-comment">// Relax all edges V-1 times</span>
    <span class="hljs-keyword">for</span> i = <span class="hljs-number">1</span> to |G.V| - <span class="hljs-number">1</span>:
        <span class="hljs-keyword">for</span> each edge (u, v) <span class="hljs-keyword">in</span> G.E:
            RELAX(u, v, w)

    <span class="hljs-comment">// Check for negative cycles</span>
    <span class="hljs-keyword">for</span> each edge (u, v) <span class="hljs-keyword">in</span> G.E:
        <span class="hljs-keyword">if</span> v.d &gt; u.d + w(u, v):
            <span class="hljs-keyword">return</span> FALSE  <span class="hljs-comment">// negative cycle exists</span>

    <span class="hljs-keyword">return</span> TRUE
</code></pre><p><strong>Properties</strong>:</p>
<ul>
<li>Time complexity: O(VE)</li>
<li>Space complexity: O(V)</li>
<li>Handles negative edge weights</li>
<li>Detects negative cycles</li>
<li>Dynamic programming approach</li>
</ul>
<p><strong>Applications</strong>:</p>
<ul>
<li>Currency arbitrage detection</li>
<li>Network routing with negative costs</li>
<li>When negative weights exist</li>
<li>Cycle detection</li>
</ul>
<p><strong>Optimization - SPFA</strong> (Shortest Path Faster Algorithm):</p>
<pre><code>SPFA(G, w, s):
    INITIALIZE-SINGLE-SOURCE(G, s)
    Q = queue containing only s
    in_queue = {s}

    <span class="hljs-keyword">while</span> Q is not empty:
        u = Q.dequeue()
        in_queue.remove(u)

        <span class="hljs-keyword">for</span> each v <span class="hljs-keyword">in</span> G.Adj[u]:
            <span class="hljs-keyword">if</span> v.d &gt; u.d + w(u, v):
                v.d = u.d + w(u, v)
                v.parent = u
                <span class="hljs-keyword">if</span> v not <span class="hljs-keyword">in</span> in_queue:
                    Q.enqueue(v)
                    in_queue.add(v)
</code></pre><h3 id="heading-62-all-pairs-shortest-paths">6.2 All-Pairs Shortest Paths</h3>
<h4 id="heading-621-floyd-warshall-algorithm">6.2.1 Floyd-Warshall Algorithm</h4>
<p><strong>Concept</strong>: Find shortest paths between all pairs of vertices</p>
<p><strong>Algorithm</strong>:</p>
<pre><code>FLOYD-WARSHALL(W):
    n = |V|
    D⁽⁰⁾ = W  <span class="hljs-comment">// initial distance matrix</span>

    <span class="hljs-keyword">for</span> k = <span class="hljs-number">1</span> to n:
        <span class="hljs-keyword">for</span> i = <span class="hljs-number">1</span> to n:
            <span class="hljs-keyword">for</span> j = <span class="hljs-number">1</span> to n:
                d[i][j]⁽ᵏ⁾ = min(d[i][j]⁽ᵏ⁻¹⁾, d[i][k]⁽ᵏ⁻¹⁾ + d[k][j]⁽ᵏ⁻¹⁾)

    <span class="hljs-keyword">return</span> D⁽ⁿ⁾
</code></pre><p><strong>Path Reconstruction</strong>:</p>
<pre><code>FLOYD-WARSHALL-WITH-PATH(W):
    n = |V|
    D = W
    P = matrix where P[i][j] = NIL

    <span class="hljs-keyword">for</span> k = <span class="hljs-number">1</span> to n:
        <span class="hljs-keyword">for</span> i = <span class="hljs-number">1</span> to n:
            <span class="hljs-keyword">for</span> j = <span class="hljs-number">1</span> to n:
                <span class="hljs-keyword">if</span> D[i][j] &gt; D[i][k] + D[k][j]:
                    D[i][j] = D[i][k] + D[k][j]
                    P[i][j] = k

    <span class="hljs-keyword">return</span> D, P

CONSTRUCT-PATH(P, i, j):
    <span class="hljs-keyword">if</span> P[i][j] == NIL:
        <span class="hljs-keyword">return</span> edge(i, j)
    <span class="hljs-attr">else</span>:
        k = P[i][j]
        <span class="hljs-keyword">return</span> CONSTRUCT-PATH(P, i, k) + CONSTRUCT-PATH(P, k, j)
</code></pre><p><strong>Properties</strong>:</p>
<ul>
<li>Time complexity: O(V³)</li>
<li>Space complexity: O(V²)</li>
<li>Handles negative edges (but not negative cycles)</li>
<li>Dynamic programming approach</li>
<li>Computes transitive closure</li>
</ul>
<p><strong>Applications</strong>:</p>
<ul>
<li>Dense graphs</li>
<li>Finding graph diameter</li>
<li>Analyzing network connectivity</li>
<li>Finding shortest paths in small graphs</li>
</ul>
<h4 id="heading-622-johnsons-algorithm">6.2.2 Johnson's Algorithm</h4>
<p><strong>Concept</strong>: Efficient all-pairs shortest paths using reweighting</p>
<p><strong>Algorithm</strong>:</p>
<pre><code>JOHNSON(G, w):
    <span class="hljs-comment">// Add new vertex s connected to all vertices with weight 0</span>
    G<span class="hljs-string">' = G with new vertex s
    for each v in G.V:
        add edge (s, v) with weight 0

    // Compute h values using Bellman-Ford
    if BELLMAN-FORD(G'</span>, w, s) == FALSE:
        error <span class="hljs-string">"negative cycle"</span>

    <span class="hljs-keyword">for</span> each vertex v <span class="hljs-keyword">in</span> G.V:
        h(v) = v.d  <span class="hljs-comment">// distance from s</span>

    <span class="hljs-comment">// Reweight edges</span>
    <span class="hljs-keyword">for</span> each edge (u, v) <span class="hljs-keyword">in</span> G.E:
        w<span class="hljs-string">'(u, v) = w(u, v) + h(u) - h(v)

    // Run Dijkstra from each vertex
    for each vertex u in G.V:
        DIJKSTRA(G, w'</span>, u)
        <span class="hljs-keyword">for</span> each vertex v <span class="hljs-keyword">in</span> G.V:
            d[u][v] = v.d + h(v) - h(u)

    <span class="hljs-keyword">return</span> D
</code></pre><p><strong>Properties</strong>:</p>
<ul>
<li>Time complexity: O(V²log V + VE)</li>
<li>Better than Floyd-Warshall for sparse graphs</li>
<li>Handles negative weights</li>
<li>Uses reweighting technique</li>
</ul>
<p><strong>When to Use</strong>:</p>
<ul>
<li>Sparse graphs with negative weights</li>
<li>When V²log V &lt; V³</li>
</ul>
<h3 id="heading-63-a-search-algorithm">6.3 A* Search Algorithm</h3>
<p><strong>Concept</strong>: Informed search using heuristic function</p>
<p><strong>Algorithm</strong>:</p>
<pre><code>A-STAR(G, s, goal, h):
    openSet = {s}
    cameFrom = empty map

    g[s] = <span class="hljs-number">0</span>  <span class="hljs-comment">// actual cost from start</span>
    f[s] = h(s)  <span class="hljs-comment">// estimated total cost</span>

    <span class="hljs-keyword">while</span> openSet is not empty:
        current = vertex <span class="hljs-keyword">in</span> openSet <span class="hljs-keyword">with</span> lowest f value

        <span class="hljs-keyword">if</span> current == goal:
            <span class="hljs-keyword">return</span> RECONSTRUCT-PATH(cameFrom, current)

        openSet.remove(current)

        <span class="hljs-keyword">for</span> each neighbor <span class="hljs-keyword">of</span> current:
            tentative_g = g[current] + distance(current, neighbor)

            <span class="hljs-keyword">if</span> tentative_g &lt; g[neighbor]:
                cameFrom[neighbor] = current
                g[neighbor] = tentative_g
                f[neighbor] = g[neighbor] + h(neighbor)
                <span class="hljs-keyword">if</span> neighbor not <span class="hljs-keyword">in</span> openSet:
                    openSet.add(neighbor)

    <span class="hljs-keyword">return</span> failure
</code></pre><p><strong>Heuristic Properties</strong>:</p>
<ul>
<li><strong>Admissible</strong>: h(n) ≤ true cost to goal</li>
<li><strong>Consistent</strong>: h(n) ≤ cost(n, n') + h(n')</li>
</ul>
<p><strong>Common Heuristics</strong>:</p>
<ul>
<li>Euclidean distance: √((x₁-x₂)² + (y₁-y₂)²)</li>
<li>Manhattan distance: |x₁-x₂| + |y₁-y₂|</li>
<li>Chebyshev distance: max(|x₁-x₂|, |y₁-y₂|)</li>
</ul>
<p><strong>Properties</strong>:</p>
<ul>
<li>Optimal if heuristic is admissible</li>
<li>More efficient than Dijkstra with good heuristic</li>
<li>Widely used in practice</li>
</ul>
<p><strong>Applications</strong>:</p>
<ul>
<li>Video game pathfinding</li>
<li>Robot navigation</li>
<li>GPS routing</li>
<li>Puzzle solving</li>
</ul>
<hr />
<h2 id="heading-7-minimum-spanning-trees">7. Minimum Spanning Trees</h2>
<h3 id="heading-71-definitions">7.1 Definitions</h3>
<p><strong>Spanning Tree</strong>: A subgraph that is a tree and includes all vertices</p>
<p><strong>Minimum Spanning Tree (MST)</strong>: A spanning tree with minimum total edge weight</p>
<p><strong>Properties</strong>:</p>
<ul>
<li>For graph with V vertices, MST has V-1 edges</li>
<li>MST is not unique if edge weights are not distinct</li>
<li>Removing any edge disconnects the tree</li>
<li>Adding any edge creates exactly one cycle</li>
</ul>
<h3 id="heading-72-kruskals-algorithm">7.2 Kruskal's Algorithm</h3>
<p><strong>Concept</strong>: Greedily add minimum weight edges that don't create cycles</p>
<p><strong>Algorithm</strong>:</p>
<pre><code>KRUSKAL(G, w):
    A = empty set
    <span class="hljs-keyword">for</span> each vertex v <span class="hljs-keyword">in</span> G.V:
        MAKE-SET(v)

    sort edges <span class="hljs-keyword">of</span> G.E by weight (non-decreasing)

    <span class="hljs-keyword">for</span> each edge (u, v) <span class="hljs-keyword">in</span> sorted order:
        <span class="hljs-keyword">if</span> FIND-SET(u) ≠ FIND-SET(v):
            A = A ∪ {(u, v)}
            UNION(u, v)

    <span class="hljs-keyword">return</span> A
</code></pre><p><strong>Data Structure</strong>: Union-Find (Disjoint Set Union)</p>
<pre><code>MAKE-SET(x):
    x.parent = x
    x.rank = <span class="hljs-number">0</span>

FIND-SET(x):
    <span class="hljs-keyword">if</span> x ≠ x.parent:
        x.parent = FIND-SET(x.parent)  <span class="hljs-comment">// path compression</span>
    <span class="hljs-keyword">return</span> x.parent

UNION(x, y):
    xroot = FIND-SET(x)
    yroot = FIND-SET(y)

    <span class="hljs-keyword">if</span> xroot.rank &lt; yroot.rank:
        xroot.parent = yroot
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> xroot.rank &gt; yroot.rank:
        yroot.parent = xroot
    <span class="hljs-attr">else</span>:
        yroot.parent = xroot
        xroot.rank = xroot.rank + <span class="hljs-number">1</span>
</code></pre><p><strong>Properties</strong>:</p>
<ul>
<li>Time complexity: O(E log E) or O(E log V)<ul>
<li>Sorting: O(E log E)</li>
<li>Union-Find operations: O(E α(V)) where α is inverse Ackermann</li>
</ul>
</li>
<li>Space complexity: O(V)</li>
<li>Works on disconnected graphs (produces MSF - Minimum Spanning Forest)</li>
</ul>
<p><strong>Applications</strong>:</p>
<ul>
<li>Network design</li>
<li>Clustering algorithms</li>
<li>Image segmentation</li>
</ul>
<h3 id="heading-73-prims-algorithm">7.3 Prim's Algorithm</h3>
<p><strong>Concept</strong>: Grow MST from starting vertex by adding minimum weight edges</p>
<p><strong>Algorithm</strong>:</p>
<pre><code>PRIM(G, w, r):
    <span class="hljs-keyword">for</span> each u <span class="hljs-keyword">in</span> G.V:
        u.key = ∞
        u.parent = NIL

    r.key = <span class="hljs-number">0</span>
    Q = G.V  <span class="hljs-comment">// priority queue</span>

    <span class="hljs-keyword">while</span> Q is not empty:
        u = EXTRACT-MIN(Q)
        <span class="hljs-keyword">for</span> each v <span class="hljs-keyword">in</span> G.Adj[u]:
            <span class="hljs-keyword">if</span> v <span class="hljs-keyword">in</span> Q and w(u, v) &lt; v.key:
                v.parent = u
                v.key = w(u, v)
                DECREASE-KEY(Q, v, v.key)
</code></pre><p><strong>Properties</strong>:</p>
<ul>
<li>Time complexity:<ul>
<li>With binary heap: O((V + E) log V)</li>
<li>With Fibonacci heap: O(E + V log V)</li>
<li>With array: O(V²)</li>
</ul>
</li>
<li>Space complexity: O(V)</li>
<li>Requires connected graph</li>
<li>Similar to Dijkstra's algorithm</li>
</ul>
<p><strong>Comparison with Kruskal</strong>:
| Aspect | Kruskal | Prim |
|--------|---------|------|
| Approach | Edge-based | Vertex-based |
| Best for | Sparse graphs | Dense graphs |
| Works on disconnected | Yes (MSF) | No |
| Data structure | Union-Find | Priority Queue |</p>
<h3 id="heading-74-boruvkas-algorithm">7.4 Borůvka's Algorithm</h3>
<p><strong>Concept</strong>: Parallel-friendly MST algorithm</p>
<p><strong>Algorithm</strong>:</p>
<pre><code>BORUVKA(G, w):
    components = {each vertex <span class="hljs-keyword">in</span> separate component}
    MST = empty set

    <span class="hljs-keyword">while</span> |components| &gt; <span class="hljs-number">1</span>:
        <span class="hljs-keyword">for</span> each component C:
            find minimum weight edge (u, v) where u ∈ C, v ∉ C
            mark edge <span class="hljs-keyword">for</span> addition

        add all marked edges to MST
        merge components connected by marked edges

    <span class="hljs-keyword">return</span> MST
</code></pre><p><strong>Properties</strong>:</p>
<ul>
<li>Time complexity: O(E log V)</li>
<li>Naturally parallelizable</li>
<li>Historic significance (oldest MST algorithm, 1926)</li>
</ul>
<p><strong>Applications</strong>:</p>
<ul>
<li>Parallel computing</li>
<li>Distributed systems</li>
</ul>
<hr />
<h2 id="heading-8-network-flow">8. Network Flow</h2>
<h3 id="heading-81-definitions">8.1 Definitions</h3>
<p><strong>Flow Network</strong>: Directed graph with:</p>
<ul>
<li>Source vertex s</li>
<li>Sink vertex t</li>
<li>Capacity function c: E → ℝ⁺</li>
</ul>
<p><strong>Flow</strong>: Function f: E → ℝ satisfying:</p>
<ol>
<li><strong>Capacity constraint</strong>: 0 ≤ f(u, v) ≤ c(u, v) for all edges</li>
<li><strong>Flow conservation</strong>: ∑f(v, u) = ∑f(u, v) for all u ≠ s, t (flow in = flow out)</li>
</ol>
<p><strong>Value of Flow</strong>: |f| = ∑f(s, v) - ∑f(v, s)</p>
<p><strong>Residual Network</strong>: Graph Gf with residual capacities:</p>
<ul>
<li>cf(u, v) = c(u, v) - f(u, v) if (u, v) ∈ E</li>
<li>cf(u, v) = f(v, u) if (v, u) ∈ E</li>
</ul>
<p><strong>Augmenting Path</strong>: Path from s to t in residual network</p>
<p><strong>Cut</strong>: Partition of vertices into S and T where s ∈ S, t ∈ T</p>
<ul>
<li><strong>Capacity of cut</strong>: c(S, T) = ∑c(u, v) where u ∈ S, v ∈ T</li>
<li><strong>Minimum cut</strong>: cut with minimum capacity</li>
</ul>
<p><strong>Max-Flow Min-Cut Theorem</strong>: Maximum flow value = minimum cut capacity</p>
<h3 id="heading-82-ford-fulkerson-method">8.2 Ford-Fulkerson Method</h3>
<p><strong>Concept</strong>: Repeatedly augment flow along paths in residual network</p>
<p><strong>Algorithm</strong>:</p>
<pre><code>FORD-FULKERSON(G, s, t):
    <span class="hljs-keyword">for</span> each edge (u, v) <span class="hljs-keyword">in</span> G.E:
        f(u, v) = <span class="hljs-number">0</span>

    <span class="hljs-keyword">while</span> there exists augmenting path p <span class="hljs-keyword">from</span> s to t <span class="hljs-keyword">in</span> Gf:
        cf(p) = min{cf(u, v) : (u, v) is on p}
        <span class="hljs-keyword">for</span> each edge (u, v) on p:
            <span class="hljs-keyword">if</span> (u, v) <span class="hljs-keyword">in</span> E:
                f(u, v) = f(u, v) + cf(p)
            <span class="hljs-attr">else</span>:
                f(v, u) = f(v, u) - cf(p)

    <span class="hljs-keyword">return</span> f
</code></pre><p><strong>Properties</strong>:</p>
<ul>
<li>Not an algorithm (method for finding augmenting paths)</li>
<li>Termination depends on implementation</li>
<li>If capacities are integers, flow increases by at least 1 each iteration</li>
</ul>
<h3 id="heading-83-edmonds-karp-algorithm">8.3 Edmonds-Karp Algorithm</h3>
<p><strong>Concept</strong>: Ford-Fulkerson using BFS to find shortest augmenting paths</p>
<p><strong>Algorithm</strong>:</p>
<pre><code>EDMONDS-KARP(G, s, t):
    <span class="hljs-keyword">for</span> each edge (u, v) <span class="hljs-keyword">in</span> G.E:
        f(u, v) = <span class="hljs-number">0</span>

    <span class="hljs-keyword">while</span> BFS finds path p <span class="hljs-keyword">from</span> s to t <span class="hljs-keyword">in</span> Gf:
        cf(p) = min{cf(u, v) : (u, v) on p}
        <span class="hljs-keyword">for</span> each edge (u, v) on p:
            <span class="hljs-keyword">if</span> (u, v) <span class="hljs-keyword">in</span> E:
                f(u, v) = f(u, v) + cf(p)
            <span class="hljs-attr">else</span>:
                f(v, u) = f(v, u) - cf(p)

    <span class="hljs-keyword">return</span> f
</code></pre><p><strong>Properties</strong>:</p>
<ul>
<li>Time complexity: O(VE²)</li>
<li>Always terminates</li>
<li>Polynomial time guarantee</li>
</ul>
<h3 id="heading-84-dinics-algorithm">8.4 Dinic's Algorithm</h3>
<p><strong>Concept</strong>: Use level graphs and blocking flows</p>
<p><strong>Algorithm</strong>:</p>
<pre><code>DINIC(G, s, t):
    f = <span class="hljs-number">0</span>
    <span class="hljs-keyword">while</span> BFS constructs level graph L:
        <span class="hljs-keyword">while</span> there exists blocking flow g <span class="hljs-keyword">in</span> L:
            f = f + g
    <span class="hljs-keyword">return</span> f
</code></pre><p><strong>Level Graph</strong>: Subgraph containing only edges (u, v) where level[v] = level[u] + 1</p>
<p><strong>Properties</strong>:</p>
<ul>
<li>Time complexity: O(V²E)</li>
<li>Very efficient in practice</li>
<li>Used in competitive programming</li>
</ul>
<h3 id="heading-85-push-relabel-algorithm">8.5 Push-Relabel Algorithm</h3>
<p><strong>Concept</strong>: Maintain preflow and push excess toward sink</p>
<p><strong>Key Operations</strong>:</p>
<ul>
<li><strong>Push</strong>: Send excess flow from vertex to lower neighbor</li>
<li><strong>Relabel</strong>: Increase height of vertex to enable pushes</li>
</ul>
<p><strong>Properties</strong>:</p>
<ul>
<li>Time complexity: O(V²E) generic, O(V³) with appropriate heuristics</li>
<li>Different paradigm from augmenting paths</li>
</ul>
<h3 id="heading-86-applications">8.6 Applications</h3>
<p><strong>Maximum Bipartite Matching</strong>: Model as flow network</p>
<ul>
<li>Create source connected to left set</li>
<li>Create sink connected to right set</li>
<li>Unit capacities on all edges</li>
<li>Max flow = maximum matching size</li>
</ul>
<p><strong>Image Segmentation</strong>: Min-cut for foreground/background separation</p>
<p><strong>Airline Scheduling</strong>: Assign crews to flights</p>
<p><strong>Network Reliability</strong>: Find bottlenecks</p>
<p><strong>Project Selection</strong>: Maximize profit under constraints</p>
<hr />
<h2 id="heading-9-graph-coloring">9. Graph Coloring</h2>
<h3 id="heading-91-vertex-coloring">9.1 Vertex Coloring</h3>
<p><strong>Definition</strong>: Assignment of colors to vertices such that no two adjacent vertices have the same color</p>
<p><strong>Chromatic Number χ(G)</strong>: Minimum number of colors needed</p>
<p><strong>Properties</strong>:</p>
<ul>
<li>χ(Kn) = n (complete graph)</li>
<li>χ(Cn) = 2 if n is even, 3 if n is odd (cycle)</li>
<li>χ(tree) = 2</li>
<li>χ(bipartite) = 2</li>
</ul>
<p><strong>Bounds</strong>:</p>
<ul>
<li>χ(G) ≤ Δ(G) + 1 where Δ is maximum degree</li>
<li><strong>Brooks' Theorem</strong>: χ(G) ≤ Δ(G) unless G is complete or odd cycle</li>
</ul>
<h3 id="heading-92-greedy-coloring">9.2 Greedy Coloring</h3>
<p><strong>Algorithm</strong>:</p>
<pre><code>GREEDY-COLORING(G):
    <span class="hljs-keyword">for</span> each vertex v <span class="hljs-keyword">in</span> some order:
        assign v the smallest color not used by its neighbors
</code></pre><p><strong>Properties</strong>:</p>
<ul>
<li>Time complexity: O(V + E)</li>
<li>Uses at most Δ(G) + 1 colors</li>
<li>Result depends on vertex ordering</li>
<li>Not optimal in general</li>
</ul>
<p><strong>Welsh-Powell Algorithm</strong>: Order vertices by decreasing degree</p>
<h3 id="heading-93-k-colorability">9.3 k-Colorability</h3>
<p><strong>Decision Problem</strong>: Can G be colored with k colors?</p>
<p><strong>Complexity</strong>:</p>
<ul>
<li>2-colorability: O(V + E) using BFS</li>
<li>3-colorability: NP-complete</li>
<li>k-colorability for k ≥ 3: NP-complete</li>
</ul>
<p><strong>2-Colorability Test</strong>:</p>
<pre><code>IS-BIPARTITE(G):
    color all vertices WHITE
    <span class="hljs-keyword">for</span> each vertex s:
        <span class="hljs-keyword">if</span> s.color == WHITE:
            s.color = RED
            Q = queue <span class="hljs-keyword">with</span> s
            <span class="hljs-keyword">while</span> Q not empty:
                u = Q.dequeue()
                <span class="hljs-keyword">for</span> each v <span class="hljs-keyword">in</span> Adj[u]:
                    <span class="hljs-keyword">if</span> v.color == WHITE:
                        v.color = opposite <span class="hljs-keyword">of</span> u.color
                        Q.enqueue(v)
                    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> v.color == u.color:
                        <span class="hljs-keyword">return</span> FALSE
    <span class="hljs-keyword">return</span> TRUE
</code></pre><h3 id="heading-94-edge-coloring">9.4 Edge Coloring</h3>
<p><strong>Definition</strong>: Assign colors to edges so no two incident edges share a color</p>
<p><strong>Edge Chromatic Number χ'(G)</strong>: Minimum colors needed</p>
<p><strong>Vizing's Theorem</strong>: Δ(G) ≤ χ'(G) ≤ Δ(G) + 1</p>
<p><strong>Class 1</strong>: χ'(G) = Δ(G)
<strong>Class 2</strong>: χ'(G) = Δ(G) + 1</p>
<p><strong>Properties</strong>:</p>
<ul>
<li>Bipartite graphs are Class 1</li>
<li>Determining class is NP-complete</li>
</ul>
<h3 id="heading-95-applications">9.5 Applications</h3>
<p><strong>Register Allocation</strong>: Color interference graph of variables</p>
<p><strong>Scheduling</strong>: Tasks = vertices, conflicts = edges</p>
<p><strong>Frequency Assignment</strong>: Avoid interference in wireless networks</p>
<p><strong>Map Coloring</strong>: Four Color Theorem (every planar graph is 4-colorable)</p>
<p><strong>Sudoku</strong>: Graph coloring problem</p>
<hr />
<h2 id="heading-10-matching-theory">10. Matching Theory</h2>
<h3 id="heading-101-definitions">10.1 Definitions</h3>
<p><strong>Matching M</strong>: Set of edges with no common vertices</p>
<p><strong>Maximum Matching</strong>: Matching with maximum number of edges</p>
<p><strong>Maximal Matching</strong>: Cannot be extended by adding edges</p>
<p><strong>Perfect Matching</strong>: Every vertex is matched (|M| = |V|/2)</p>
<p><strong>Augmenting Path</strong>: Path alternating between non-matching and matching edges, with unmatched endpoints</p>
<p><strong>Berge's Theorem</strong>: M is maximum iff there is no augmenting path</p>
<h3 id="heading-102-maximum-matching-in-bipartite-graphs">10.2 Maximum Matching in Bipartite Graphs</h3>
<p><strong>Algorithm (Using Network Flow)</strong>:</p>
<pre><code>MAX-BIPARTITE-MATCHING(G = (U ∪ V, E)):
    Create flow network:
        Add source s connected to all u ∈ U
        Add sink t connected to all v ∈ V
        All capacities = <span class="hljs-number">1</span>

    max_flow = FORD-FULKERSON(network, s, t)

    matching = {(u, v) : flow on (u, v) = <span class="hljs-number">1</span>}
    <span class="hljs-keyword">return</span> matching
</code></pre><p><strong>Hopcroft-Karp Algorithm</strong>:</p>
<ul>
<li>Faster algorithm specifically for bipartite matching</li>
<li>Time complexity: O(E√V)</li>
<li>Finds maximal set of shortest augmenting paths</li>
</ul>
<p><strong>Hungarian Algorithm</strong> (for weighted matching):</p>
<ul>
<li>Finds maximum weight matching in bipartite graph</li>
<li>Time complexity: O(V³) or O(V²E)</li>
<li>Based on potential functions and reduced costs</li>
</ul>
<h3 id="heading-103-maximum-matching-in-general-graphs">10.3 Maximum Matching in General Graphs</h3>
<p><strong>Blossom Algorithm</strong> (Edmonds):</p>
<ul>
<li>Handles odd cycles ("blossoms") in general graphs</li>
<li>Time complexity: O(V²E)</li>
<li>First polynomial-time algorithm for general matching</li>
</ul>
<p><strong>Key Idea</strong>: Contract odd cycles into single vertices</p>
<h3 id="heading-104-halls-marriage-theorem">10.4 Hall's Marriage Theorem</h3>
<p><strong>Theorem</strong>: Bipartite graph G = (U ∪ V, E) has a matching that saturates U iff:</p>
<p>For every subset S ⊆ U: |N(S)| ≥ |S|</p>
<p>where N(S) is the set of neighbors of S</p>
<p><strong>Corollary</strong>: k-regular bipartite graph has perfect matching</p>
<h3 id="heading-105-konigs-theorem">10.5 König's Theorem</h3>
<p><strong>Theorem</strong>: In bipartite graphs:</p>
<p>Minimum vertex cover size = Maximum matching size</p>
<p><strong>Vertex Cover</strong>: Set of vertices such that every edge has at least one endpoint in the set</p>
<p><strong>Minimum Vertex Cover Algorithm</strong>:</p>
<ol>
<li>Find maximum matching M</li>
<li>Construct cover from matching using König's construction</li>
</ol>
<h3 id="heading-106-applications">10.6 Applications</h3>
<p><strong>Job Assignment</strong>: Workers to tasks</p>
<p><strong>Medical Residency</strong>: Students to hospitals (Stable Marriage Problem)</p>
<p><strong>Online Advertising</strong>: Ads to users</p>
<p><strong>Resource Allocation</strong>: Distribute limited resources</p>
<p><strong>Ride Sharing</strong>: Drivers to passengers</p>
<hr />
<h2 id="heading-11-planarity">11. Planarity</h2>
<h3 id="heading-111-definitions">11.1 Definitions</h3>
<p><strong>Planar Graph</strong>: Can be drawn in plane without edge crossings</p>
<p><strong>Plane Graph</strong>: Specific planar embedding</p>
<p><strong>Face</strong>: Region bounded by edges in plane graph</p>
<ul>
<li>One unbounded face (exterior)</li>
<li>Bounded faces (interior)</li>
</ul>
<p><strong>Dual Graph</strong>: Vertex for each face, edge for each boundary crossing</p>
<h3 id="heading-112-eulers-formula">11.2 Euler's Formula</h3>
<p><strong>Formula</strong>: V - E + F = 2</p>
<p>where:</p>
<ul>
<li>V = number of vertices</li>
<li>E = number of edges</li>
<li>F = number of faces</li>
</ul>
<p><strong>Valid for</strong>: Connected planar graphs</p>
<p><strong>Consequences</strong>:</p>
<ol>
<li>E ≤ 3V - 6 (simple planar graph)</li>
<li>E ≤ 2V - 4 (bipartite planar graph)</li>
<li>At least one face has ≤ 5 edges (if V ≥ 3)</li>
</ol>
<h3 id="heading-113-kuratowskis-theorem">11.3 Kuratowski's Theorem</h3>
<p><strong>Theorem</strong>: Graph is planar iff it contains no subdivision of K₅ or K₃,₃</p>
<p><strong>Subdivision</strong>: Insert vertices on edges (homeomorphism)</p>
<p><strong>Wagner's Theorem</strong>: Graph is planar iff it contains no K₅ or K₃,₃ minor</p>
<p><strong>Minor</strong>: Contract edges and delete edges/vertices</p>
<h3 id="heading-114-planarity-testing">11.4 Planarity Testing</h3>
<p><strong>Algorithms</strong>:</p>
<p><strong>Hopcroft-Tarjan Algorithm</strong>:</p>
<ul>
<li>Time complexity: O(V)</li>
<li>Based on DFS</li>
<li>Produces planar embedding if planar</li>
</ul>
<p><strong>Boyer-Myrvold Algorithm</strong>:</p>
<ul>
<li>Simplified planarity testing</li>
<li>Also O(V) time</li>
<li>Easier to implement</li>
</ul>
<p><strong>Testing Strategy</strong>:</p>
<pre><code>IS-PLANAR(G):
    <span class="hljs-keyword">if</span> E &gt; <span class="hljs-number">3</span>V - <span class="hljs-number">6</span>:
        <span class="hljs-keyword">return</span> FALSE

    Run DFS to find cycles
    Try to embed faces without crossings

    <span class="hljs-keyword">return</span> SUCCESS or FAILURE
</code></pre><h3 id="heading-115-graph-drawing">11.5 Graph Drawing</h3>
<p><strong>Straight-Line Drawing</strong>: Every edge is straight line segment</p>
<p><strong>Fáry's Theorem</strong>: Every planar graph has straight-line drawing</p>
<p><strong>Tutte's Spring Embedding</strong>:</p>
<ul>
<li>Fix exterior face</li>
<li>Interior vertices at weighted average of neighbors</li>
<li>Produces convex drawing</li>
</ul>
<h3 id="heading-116-applications">11.6 Applications</h3>
<p><strong>Circuit Board Design</strong>: Layer minimization</p>
<p><strong>GIS</strong>: Map overlays and routing</p>
<p><strong>Graph Visualization</strong>: Readable layouts</p>
<p><strong>VLSI Design</strong>: Component placement</p>
<hr />
<h2 id="heading-12-connectivity">12. Connectivity</h2>
<h3 id="heading-121-vertex-connectivity">12.1 Vertex Connectivity</h3>
<p><strong>k-Connected</strong>: Remains connected after removing any k-1 vertices</p>
<p><strong>Vertex Connectivity κ(G)</strong>: Maximum k for which G is k-connected</p>
<p><strong>Articulation Point</strong> (Cut Vertex): Removing it disconnects graph</p>
<p><strong>Biconnected Component</strong>: Maximal biconnected subgraph</p>
<h3 id="heading-122-finding-articulation-points">12.2 Finding Articulation Points</h3>
<p><strong>Algorithm (Tarjan)</strong>:</p>
<pre><code>FIND-ARTICULATION-POINTS(G):
    time = <span class="hljs-number">0</span>
    <span class="hljs-keyword">for</span> each vertex v:
        v.visited = FALSE
        v.disc = ∞
        v.low = ∞
        v.parent = NIL

    <span class="hljs-keyword">for</span> each vertex v:
        <span class="hljs-keyword">if</span> not v.visited:
            DFS-AP(v)

DFS-AP(u):
    children = <span class="hljs-number">0</span>
    u.visited = TRUE
    u.disc = u.low = ++time

    <span class="hljs-keyword">for</span> each v <span class="hljs-keyword">in</span> Adj[u]:
        <span class="hljs-keyword">if</span> not v.visited:
            children++
            v.parent = u
            DFS-AP(v)

            u.low = min(u.low, v.low)

            <span class="hljs-comment">// u is articulation point if:</span>
            <span class="hljs-keyword">if</span> u.parent == NIL and children &gt; <span class="hljs-number">1</span>:
                mark u <span class="hljs-keyword">as</span> AP
            <span class="hljs-keyword">if</span> u.parent ≠ NIL and v.low ≥ u.disc:
                mark u <span class="hljs-keyword">as</span> AP

        <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> v ≠ u.parent:
            u.low = min(u.low, v.disc)
</code></pre><p><strong>Properties</strong>:</p>
<ul>
<li>Time complexity: O(V + E)</li>
<li>disc[v]: discovery time</li>
<li>low[v]: minimum discovery time reachable from subtree of v</li>
</ul>
<h3 id="heading-123-edge-connectivity">12.3 Edge Connectivity</h3>
<p><strong>k-Edge-Connected</strong>: Remains connected after removing any k-1 edges</p>
<p><strong>Edge Connectivity λ(G)</strong>: Maximum k for which G is k-edge-connected</p>
<p><strong>Bridge</strong> (Cut Edge): Removing it disconnects graph</p>
<p><strong>Relationship</strong>: κ(G) ≤ λ(G) ≤ δ(G) where δ is minimum degree</p>
<h3 id="heading-124-finding-bridges">12.4 Finding Bridges</h3>
<p><strong>Algorithm</strong>:</p>
<pre><code>FIND-BRIDGES(G):
    Run DFS and compute low values

    <span class="hljs-keyword">for</span> each edge (u, v) where u is parent <span class="hljs-keyword">of</span> v:
        <span class="hljs-keyword">if</span> low[v] &gt; disc[u]:
            (u, v) is a bridge
</code></pre><p><strong>Properties</strong>:</p>
<ul>
<li>Time complexity: O(V + E)</li>
<li>Bridge exists iff low[v] &gt; disc[u] for tree edge (u, v)</li>
</ul>
<h3 id="heading-125-strongly-connected-components-directed">12.5 Strongly Connected Components (Directed)</h3>
<p><strong>Strongly Connected</strong>: Path exists between every pair of vertices</p>
<p><strong>SCC</strong>: Maximal strongly connected subgraph</p>
<p><strong>Kosaraju's Algorithm</strong>:</p>
<pre><code>KOSARAJU-SCC(G):
    <span class="hljs-comment">// First DFS to compute finish times</span>
    call DFS(G) to compute finish times

    <span class="hljs-comment">// Compute transpose graph</span>
    Gᵀ = transpose <span class="hljs-keyword">of</span> G

    <span class="hljs-comment">// Second DFS on transpose in decreasing finish time</span>
    call DFS(Gᵀ) processing vertices <span class="hljs-keyword">in</span> decreasing finish time

    <span class="hljs-comment">// Each DFS tree in second pass is an SCC</span>
    <span class="hljs-keyword">return</span> SCCs
</code></pre><p><strong>Tarjan's Algorithm</strong>:</p>
<pre><code>TARJAN-SCC(G):
    index = <span class="hljs-number">0</span>
    S = empty stack

    <span class="hljs-keyword">for</span> each vertex v:
        <span class="hljs-keyword">if</span> v.index is <span class="hljs-literal">undefined</span>:
            STRONGCONNECT(v)

STRONGCONNECT(v):
    v.index = index
    v.lowlink = index
    index++
    S.push(v)
    v.onStack = TRUE

    <span class="hljs-keyword">for</span> each w <span class="hljs-keyword">in</span> Adj[v]:
        <span class="hljs-keyword">if</span> w.index is <span class="hljs-literal">undefined</span>:
            STRONGCONNECT(w)
            v.lowlink = min(v.lowlink, w.lowlink)
        <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> w.onStack:
            v.lowlink = min(v.lowlink, w.index)

    <span class="hljs-keyword">if</span> v.lowlink == v.index:
        <span class="hljs-comment">// Start a new SCC</span>
        repeat:
            w = S.pop()
            w.onStack = FALSE
            add w to current SCC
        until w == v
</code></pre><p><strong>Properties</strong>:</p>
<ul>
<li>Both algorithms: O(V + E)</li>
<li>Kosaraju: Two DFS passes</li>
<li>Tarjan: One DFS pass, uses stack</li>
</ul>
<h3 id="heading-126-applications">12.6 Applications</h3>
<p><strong>Network Reliability</strong>: Identify critical links</p>
<p><strong>Social Networks</strong>: Find cohesive groups</p>
<p><strong>Web Graph</strong>: Identify strongly connected web communities</p>
<p><strong>Compiler Optimization</strong>: Find code segments that can be optimized together</p>
<hr />
<h2 id="heading-13-trees-and-special-graphs">13. Trees and Special Graphs</h2>
<h3 id="heading-131-properties-of-trees">13.1 Properties of Trees</h3>
<p><strong>Equivalent Definitions</strong>: For graph T with n vertices, the following are equivalent:</p>
<ol>
<li>T is connected and acyclic</li>
<li>T is connected and has n-1 edges</li>
<li>T is acyclic and has n-1 edges</li>
<li>Any two vertices connected by unique path</li>
<li>T is minimally connected (removing any edge disconnects)</li>
<li>T is maximally acyclic (adding any edge creates cycle)</li>
</ol>
<p><strong>Center of Tree</strong>: Vertex/vertices minimizing maximum distance to other vertices</p>
<ul>
<li>Tree has 1 or 2 centers</li>
<li>Can be found in O(V) time</li>
</ul>
<p><strong>Diameter</strong>: Length of longest path in tree</p>
<h3 id="heading-132-rooted-trees">13.2 Rooted Trees</h3>
<p><strong>Properties</strong>:</p>
<ul>
<li>One vertex designated as root</li>
<li>Every vertex has parent except root</li>
<li><strong>Height</strong>: length of longest path from root to leaf</li>
<li><strong>Depth of vertex</strong>: distance from root</li>
<li><strong>Level</strong>: set of vertices at same depth</li>
</ul>
<p><strong>Binary Tree</strong>: Each node has at most 2 children</p>
<ul>
<li><strong>Full binary tree</strong>: Each node has 0 or 2 children</li>
<li><strong>Complete binary tree</strong>: All levels filled except possibly last</li>
<li><strong>Perfect binary tree</strong>: All levels completely filled</li>
</ul>
<p><strong>Properties of Binary Trees</strong>:</p>
<ul>
<li>n nodes → at most n+1 NIL pointers</li>
<li>Height h → at most 2^h leaves</li>
<li>n nodes → height at least ⌈log₂(n+1)⌉ - 1</li>
</ul>
<h3 id="heading-133-binary-search-trees-bst">13.3 Binary Search Trees (BST)</h3>
<p><strong>Property</strong>: For each node:</p>
<ul>
<li>Left subtree keys &lt; node key</li>
<li>Right subtree keys &gt; node key</li>
</ul>
<p><strong>Operations</strong>:</p>
<pre><code>SEARCH(T, k):
    x = T.root
    <span class="hljs-keyword">while</span> x ≠ NIL and k ≠ x.key:
        <span class="hljs-keyword">if</span> k &lt; x.key:
            x = x.left
        <span class="hljs-attr">else</span>:
            x = x.right
    <span class="hljs-keyword">return</span> x

INSERT(T, z):
    y = NIL
    x = T.root
    <span class="hljs-keyword">while</span> x ≠ NIL:
        y = x
        <span class="hljs-keyword">if</span> z.key &lt; x.key:
            x = x.left
        <span class="hljs-attr">else</span>:
            x = x.right
    z.parent = y
    <span class="hljs-keyword">if</span> y == NIL:
        T.root = z
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> z.key &lt; y.key:
        y.left = z
    <span class="hljs-attr">else</span>:
        y.right = z
</code></pre><p><strong>Time Complexity</strong>:</p>
<ul>
<li>Search, Insert, Delete: O(h) where h is height</li>
<li>Worst case: O(n) for skewed tree</li>
<li>Average case: O(log n) for random insertions</li>
</ul>
<h3 id="heading-134-balanced-trees">13.4 Balanced Trees</h3>
<p><strong>AVL Trees</strong>:</p>
<ul>
<li>Height-balanced BST</li>
<li>Balance factor: |height(left) - height(right)| ≤ 1</li>
<li>Rotations maintain balance</li>
<li>Operations: O(log n)</li>
</ul>
<p><strong>Red-Black Trees</strong>:</p>
<ul>
<li>Each node colored red or black</li>
<li>Root is black</li>
<li>Red node has black children</li>
<li>All paths from node to descendant NILs have same black height</li>
<li>Operations: O(log n)</li>
</ul>
<h3 id="heading-135-tries-prefix-trees">13.5 Tries (Prefix Trees)</h3>
<p><strong>Structure</strong>: Tree for storing strings</p>
<ul>
<li>Each path represents a string</li>
<li>Common prefixes share nodes</li>
</ul>
<p><strong>Applications</strong>:</p>
<ul>
<li>Autocomplete</li>
<li>Spell checking</li>
<li>IP routing</li>
</ul>
<h3 id="heading-136-heaps">13.6 Heaps</h3>
<p><strong>Binary Heap</strong>: Complete binary tree satisfying heap property</p>
<ul>
<li><strong>Max-heap</strong>: Parent ≥ children</li>
<li><strong>Min-heap</strong>: Parent ≤ children</li>
</ul>
<p><strong>Array Representation</strong>:</p>
<ul>
<li>Parent of i: ⌊(i-1)/2⌋</li>
<li>Left child of i: 2i + 1</li>
<li>Right child of i: 2i + 2</li>
</ul>
<p><strong>Operations</strong>:</p>
<pre><code>HEAPIFY(A, i):
    largest = i
    left = <span class="hljs-number">2</span>i + <span class="hljs-number">1</span>
    right = <span class="hljs-number">2</span>i + <span class="hljs-number">2</span>

    <span class="hljs-keyword">if</span> left &lt; A.size and A[left] &gt; A[largest]:
        largest = left
    <span class="hljs-keyword">if</span> right &lt; A.size and A[right] &gt; A[largest]:
        largest = right

    <span class="hljs-keyword">if</span> largest ≠ i:
        swap A[i] and A[largest]
        HEAPIFY(A, largest)

INSERT(H, key):
    H.size++
    i = H.size - <span class="hljs-number">1</span>
    H[i] = -∞
    INCREASE-KEY(H, i, key)

EXTRACT-MAX(H):
    max = H[<span class="hljs-number">0</span>]
    H[<span class="hljs-number">0</span>] = H[H.size - <span class="hljs-number">1</span>]
    H.size--
    HEAPIFY(H, <span class="hljs-number">0</span>)
    <span class="hljs-keyword">return</span> max
</code></pre><p><strong>Time Complexity</strong>:</p>
<ul>
<li>Insert: O(log n)</li>
<li>Extract-max/min: O(log n)</li>
<li>Build-heap: O(n)</li>
<li>Heapify: O(log n)</li>
</ul>
<h3 id="heading-137-other-special-graphs">13.7 Other Special Graphs</h3>
<p><strong>Complete Graph Kₙ</strong>:</p>
<ul>
<li>n vertices, all pairs connected</li>
<li>n(n-1)/2 edges</li>
<li>n-1 regular</li>
</ul>
<p><strong>Cycle Graph Cₙ</strong>:</p>
<ul>
<li>n vertices in cycle</li>
<li>n edges</li>
<li>2-regular</li>
</ul>
<p><strong>Path Graph Pₙ</strong>:</p>
<ul>
<li>n vertices in line</li>
<li>n-1 edges</li>
</ul>
<p><strong>Wheel Graph Wₙ</strong>:</p>
<ul>
<li>Cycle Cₙ with central vertex connected to all</li>
<li>2n edges</li>
</ul>
<p><strong>Petersen Graph</strong>:</p>
<ul>
<li>10 vertices, 15 edges</li>
<li>3-regular</li>
<li>Non-planar</li>
<li>No Hamiltonian cycle</li>
</ul>
<p><strong>Hypercube Qₙ</strong>:</p>
<ul>
<li>2ⁿ vertices</li>
<li>n·2ⁿ⁻¹ edges</li>
<li>Vertices = n-bit binary strings</li>
<li>Edges = strings differing in 1 bit</li>
</ul>
<hr />
<h2 id="heading-14-advanced-topics">14. Advanced Topics</h2>
<h3 id="heading-141-hamiltonian-paths-and-cycles">14.1 Hamiltonian Paths and Cycles</h3>
<p><strong>Hamiltonian Path</strong>: Path visiting each vertex exactly once</p>
<p><strong>Hamiltonian Cycle</strong>: Cycle visiting each vertex exactly once</p>
<p><strong>Complexity</strong>: NP-complete to determine existence</p>
<p><strong>Dirac's Theorem</strong>: If G has n ≥ 3 vertices and deg(v) ≥ n/2 for all v, then G is Hamiltonian</p>
<p><strong>Ore's Theorem</strong>: If deg(u) + deg(v) ≥ n for all non-adjacent u, v, then G is Hamiltonian</p>
<p><strong>Algorithms</strong>:</p>
<ul>
<li>Backtracking: O(n!)</li>
<li>Dynamic programming: O(n²2ⁿ) for TSP</li>
<li>Heuristics for approximation</li>
</ul>
<h3 id="heading-142-euler-paths-and-cycles">14.2 Euler Paths and Cycles</h3>
<p><strong>Eulerian Path</strong>: Path using each edge exactly once</p>
<p><strong>Eulerian Cycle</strong>: Cycle using each edge exactly once</p>
<p><strong>Necessary and Sufficient Conditions</strong>:</p>
<ul>
<li><strong>Eulerian cycle exists</strong> iff graph is connected and all vertices have even degree</li>
<li><strong>Eulerian path exists</strong> iff graph is connected and has exactly 0 or 2 vertices of odd degree</li>
</ul>
<p><strong>Hierholzer's Algorithm</strong>:</p>
<pre><code>HIERHOLZER(G):
    <span class="hljs-keyword">if</span> not all vertices have even degree:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"No Eulerian cycle"</span>

    start at arbitrary vertex v
    current_path = [v]
    circuit = []

    <span class="hljs-keyword">while</span> current_path not empty:
        current = current_path[<span class="hljs-number">-1</span>]
        <span class="hljs-keyword">if</span> current has unvisited edges:
            next = neighbor via unvisited edge
            remove edge (current, next)
            current_path.append(next)
        <span class="hljs-attr">else</span>:
            circuit.append(current_path.pop())

    <span class="hljs-keyword">return</span> reversed circuit
</code></pre><p><strong>Time Complexity</strong>: O(E)</p>
<p><strong>Applications</strong>:</p>
<ul>
<li>Route planning (Chinese Postman Problem)</li>
<li>DNA sequencing</li>
<li>Network traversal</li>
</ul>
<h3 id="heading-143-graph-isomorphism">14.3 Graph Isomorphism</h3>
<p><strong>Definition</strong>: G₁ ≅ G₂ if there exists bijection f: V₁ → V₂ preserving adjacency</p>
<p><strong>Problem</strong>: Determine if two graphs are isomorphic</p>
<p><strong>Complexity</strong>: </p>
<ul>
<li>Not known to be NP-complete</li>
<li>Not known to be in P</li>
<li>Quasi-polynomial algorithm exists (2015)</li>
</ul>
<p><strong>Invariants</strong> (necessary conditions):</p>
<ul>
<li>Same number of vertices</li>
<li>Same number of edges</li>
<li>Same degree sequence</li>
<li>Same number of cycles of each length</li>
</ul>
<p><strong>Canonical Labeling</strong>: Assign unique label to isomorphism class</p>
<p><strong>Applications</strong>:</p>
<ul>
<li>Chemical compound matching</li>
<li>Pattern recognition</li>
<li>Database searching</li>
</ul>
<h3 id="heading-144-ramsey-theory">14.4 Ramsey Theory</h3>
<p><strong>Ramsey Number R(s, t)</strong>: Minimum n such that any 2-coloring of Kₙ contains monochromatic Kₛ or Kₜ</p>
<p><strong>Known Values</strong>:</p>
<ul>
<li>R(3, 3) = 6</li>
<li>R(4, 4) = 18</li>
<li>R(3, 4) = 9</li>
<li>R(3, 5) = 14</li>
</ul>
<p><strong>Ramsey's Theorem</strong>: R(s, t) exists and is finite for all s, t</p>
<p><strong>Applications</strong>:</p>
<ul>
<li>Combinatorics</li>
<li>Number theory</li>
<li>Computer science (clique finding)</li>
</ul>
<h3 id="heading-145-random-graphs">14.5 Random Graphs</h3>
<p><strong>Erdős-Rényi Model G(n, p)</strong>:</p>
<ul>
<li>n vertices</li>
<li>Each edge exists independently with probability p</li>
</ul>
<p><strong>Properties</strong>:</p>
<ul>
<li><strong>Expected edges</strong>: p·n(n-1)/2</li>
<li><strong>Phase transition</strong> at p = 1/n:<ul>
<li>p &lt;&lt; 1/n: mostly isolated vertices</li>
<li>p = 1/n: giant component emerges</li>
<li>p &gt;&gt; 1/n: almost surely connected</li>
</ul>
</li>
</ul>
<p><strong>Small-World Networks</strong>:</p>
<ul>
<li>High clustering coefficient</li>
<li>Short average path length</li>
<li>Watts-Strogatz model</li>
</ul>
<p><strong>Scale-Free Networks</strong>:</p>
<ul>
<li>Power-law degree distribution</li>
<li>Preferential attachment</li>
<li>Barabási-Albert model</li>
</ul>
<p><strong>Applications</strong>:</p>
<ul>
<li>Social networks</li>
<li>Internet topology</li>
<li>Biological networks</li>
</ul>
<h3 id="heading-146-expander-graphs">14.6 Expander Graphs</h3>
<p><strong>Definition</strong>: Sparse graphs with strong connectivity properties</p>
<p><strong>Expansion</strong>: For subset S, |N(S)| ≥ c|S| for constant c</p>
<p><strong>Properties</strong>:</p>
<ul>
<li>High connectivity</li>
<li>Small diameter</li>
<li>Pseudo-randomness</li>
</ul>
<p><strong>Applications</strong>:</p>
<ul>
<li>Coding theory</li>
<li>Complexity theory</li>
<li>Network design</li>
</ul>
<h3 id="heading-147-perfect-graphs">14.7 Perfect Graphs</h3>
<p><strong>Perfect Graph</strong>: χ(H) = ω(H) for every induced subgraph H</p>
<ul>
<li>χ: chromatic number</li>
<li>ω: clique number</li>
</ul>
<p><strong>Strong Perfect Graph Theorem</strong>: G is perfect iff neither G nor Ḡ contains odd cycle of length ≥ 5</p>
<p><strong>Examples</strong>:</p>
<ul>
<li>Bipartite graphs</li>
<li>Chordal graphs</li>
<li>Comparability graphs</li>
</ul>
<p><strong>Recognition</strong>: Polynomial-time algorithm exists</p>
<h3 id="heading-148-spectral-graph-theory">14.8 Spectral Graph Theory</h3>
<p><strong>Graph Spectrum</strong>: Eigenvalues of adjacency or Laplacian matrix</p>
<p><strong>Adjacency Matrix A</strong>: A[i,j] = 1 if (i,j) ∈ E</p>
<p><strong>Laplacian Matrix L</strong>: L = D - A where D is degree matrix</p>
<p><strong>Properties</strong>:</p>
<ul>
<li>Number of components = multiplicity of eigenvalue 0 in L</li>
<li>Second smallest eigenvalue (algebraic connectivity) measures connectivity</li>
<li>Eigenvalues relate to graph structure</li>
</ul>
<p><strong>Applications</strong>:</p>
<ul>
<li>Graph partitioning</li>
<li>Community detection</li>
<li>Graph embedding</li>
</ul>
<hr />
<h2 id="heading-15-industry-applications">15. Industry Applications</h2>
<h3 id="heading-151-social-networks">15.1 Social Networks</h3>
<p><strong>Graph Modeling</strong>:</p>
<ul>
<li>Vertices: users</li>
<li>Edges: relationships (friends, followers, etc.)</li>
<li>Directed/undirected based on platform</li>
</ul>
<p><strong>Key Problems</strong>:</p>
<p><strong>Community Detection</strong>:</p>
<ul>
<li>Find densely connected subgraphs</li>
<li>Modularity optimization</li>
<li>Spectral clustering</li>
<li>Label propagation</li>
</ul>
<p><strong>Influence Maximization</strong>:</p>
<ul>
<li>Select k users to maximize information spread</li>
<li>Greedy algorithms with approximation guarantees</li>
<li>Monte Carlo simulations</li>
</ul>
<p><strong>Link Prediction</strong>:</p>
<ul>
<li>Predict future connections</li>
<li>Common neighbors</li>
<li>Preferential attachment</li>
<li>Machine learning on graph features</li>
</ul>
<p><strong>Centrality Measures</strong>:</p>
<ul>
<li><strong>Degree centrality</strong>: number of connections</li>
<li><strong>Betweenness centrality</strong>: fraction of shortest paths through vertex</li>
<li><strong>Closeness centrality</strong>: inverse average distance to others</li>
<li><strong>PageRank</strong>: recursive importance measure</li>
</ul>
<p><strong>Real Systems</strong>:</p>
<ul>
<li>Facebook: friend suggestions, news feed ranking</li>
<li>Twitter: trending topics, follower recommendations</li>
<li>LinkedIn: job recommendations, connection suggestions</li>
</ul>
<h3 id="heading-152-web-search-and-pagerank">15.2 Web Search and PageRank</h3>
<p><strong>Web Graph</strong>:</p>
<ul>
<li>Vertices: web pages</li>
<li>Directed edges: hyperlinks</li>
</ul>
<p><strong>PageRank Algorithm</strong>:</p>
<pre><code>PageRank(G, d=<span class="hljs-number">0.85</span>, iterations=<span class="hljs-number">100</span>):
    N = number <span class="hljs-keyword">of</span> pages
    PR = vector <span class="hljs-keyword">of</span> <span class="hljs-number">1</span>/N <span class="hljs-keyword">for</span> all pages

    <span class="hljs-keyword">for</span> iteration <span class="hljs-keyword">in</span> <span class="hljs-number">1</span> to iterations:
        new_PR = vector <span class="hljs-keyword">of</span> (<span class="hljs-number">1</span>-d)/N
        <span class="hljs-keyword">for</span> each page i:
            <span class="hljs-keyword">for</span> each page j linking to i:
                new_PR[i] += d * PR[j] / out_degree(j)
        PR = new_PR

    <span class="hljs-keyword">return</span> PR
</code></pre><p><strong>Formula</strong>: </p>
<pre><code>PR(A) = (<span class="hljs-number">1</span>-d)/N + d × Σ(PR(Ti)/C(Ti))
</code></pre><p>where Ti pages link to A, C(Ti) is out-degree</p>
<p><strong>Properties</strong>:</p>
<ul>
<li>Converges to stationary distribution</li>
<li>Random surfer model</li>
<li>Handles dangling nodes</li>
<li>Time complexity: O(iterations × E)</li>
</ul>
<p><strong>Variations</strong>:</p>
<ul>
<li>Personalized PageRank</li>
<li>Topic-sensitive PageRank</li>
<li>TrustRank (spam detection)</li>
</ul>
<p><strong>Modern Search</strong>:</p>
<ul>
<li>Combined with content relevance</li>
<li>Machine learning ranking</li>
<li>Real-time updates</li>
</ul>
<h3 id="heading-153-route-planning-and-navigation">15.3 Route Planning and Navigation</h3>
<p><strong>Road Networks</strong>:</p>
<ul>
<li>Vertices: intersections</li>
<li>Edges: road segments</li>
<li>Weights: travel time, distance, or cost</li>
</ul>
<p><strong>Algorithms</strong>:</p>
<p><strong>Dijkstra with Optimizations</strong>:</p>
<ul>
<li>Bidirectional search</li>
<li>Goal-directed search (A*)</li>
<li>Arc flags</li>
<li>Contraction hierarchies</li>
</ul>
<p><strong>Contraction Hierarchies</strong>:</p>
<pre><code>Preprocessing:
    Order vertices by importance
    For each vertex v <span class="hljs-keyword">in</span> order:
        Contract v: add shortcuts
        Remove v <span class="hljs-keyword">from</span> graph

Query(s, t):
    Run bidirectional Dijkstra on contracted graph
    Return shortest path using shortcuts
</code></pre><p><strong>Properties</strong>:</p>
<ul>
<li>Preprocessing: O(E log V)</li>
<li>Query: milliseconds even for continent-scale</li>
<li>Used by major routing services</li>
</ul>
<p><strong>Traffic-Aware Routing</strong>:</p>
<ul>
<li>Time-dependent edge weights</li>
<li>Historical traffic patterns</li>
<li>Real-time traffic updates</li>
<li>Predictive modeling</li>
</ul>
<p><strong>Multi-modal routing</strong>:</p>
<ul>
<li>Walk + public transport + car</li>
<li>Transfer penalties</li>
<li>Schedule constraints</li>
</ul>
<p><strong>Real Systems</strong>:</p>
<ul>
<li>Google Maps</li>
<li>Waze</li>
<li>OpenStreetMap routing</li>
</ul>
<h3 id="heading-154-network-design-and-optimization">15.4 Network Design and Optimization</h3>
<p><strong>Telecommunications</strong>:</p>
<ul>
<li>Design cost-effective networks</li>
<li>Ensure reliability</li>
<li>Handle failures</li>
</ul>
<p><strong>Problems</strong>:</p>
<p><strong>Network Connectivity</strong>:</p>
<ul>
<li>k-edge-connected network</li>
<li>Minimum cost</li>
<li>Survivable network design</li>
</ul>
<p><strong>Facility Location</strong>:</p>
<ul>
<li>Median problem: minimize average distance</li>
<li>Center problem: minimize maximum distance</li>
<li>Capacitated variants</li>
</ul>
<p><strong>Network Flow Optimization</strong>:</p>
<ul>
<li>Maximize throughput</li>
<li>Load balancing</li>
<li>Minimize congestion</li>
</ul>
<p><strong>Steiner Tree Problem</strong>:</p>
<ul>
<li>Connect subset of vertices</li>
<li>May use intermediate (Steiner) vertices</li>
<li>NP-hard</li>
<li>Approximation algorithms</li>
</ul>
<p><strong>Applications</strong>:</p>
<ul>
<li>5G network planning</li>
<li>Data center interconnection</li>
<li>Optical fiber deployment</li>
<li>Power grid design</li>
</ul>
<h3 id="heading-155-bioinformatics">15.5 Bioinformatics</h3>
<p><strong>Protein-Protein Interaction Networks</strong>:</p>
<ul>
<li>Vertices: proteins</li>
<li>Edges: interactions</li>
<li>Find functional modules</li>
<li>Predict protein function</li>
</ul>
<p><strong>Gene Regulatory Networks</strong>:</p>
<ul>
<li>Directed graph</li>
<li>Vertices: genes</li>
<li>Edges: regulatory relationships</li>
<li>Identify key regulators</li>
</ul>
<p><strong>Metabolic Networks</strong>:</p>
<ul>
<li>Reactions as edges</li>
<li>Metabolites as vertices</li>
<li>Find pathways</li>
<li>Flux balance analysis</li>
</ul>
<p><strong>Phylogenetic Trees</strong>:</p>
<ul>
<li>Evolutionary relationships</li>
<li>Tree construction from sequences</li>
<li>Maximum parsimony</li>
<li>Maximum likelihood</li>
</ul>
<p><strong>Sequence Assembly</strong>:</p>
<ul>
<li>De Bruijn graphs</li>
<li>Overlap graphs</li>
<li>Genome assembly</li>
<li>Eulerian path finding</li>
</ul>
<p><strong>Applications</strong>:</p>
<ul>
<li>Drug target identification</li>
<li>Disease gene discovery</li>
<li>Evolutionary analysis</li>
<li>Personalized medicine</li>
</ul>
<h3 id="heading-156-compiler-optimization">15.6 Compiler Optimization</h3>
<p><strong>Data Flow Analysis</strong>:</p>
<ul>
<li>Control flow graph</li>
<li>Vertices: basic blocks</li>
<li>Edges: control flow</li>
<li>Reaching definitions</li>
<li>Live variable analysis</li>
</ul>
<p><strong>Register Allocation</strong>:</p>
<ul>
<li>Interference graph</li>
<li>Vertices: variables</li>
<li>Edges: live simultaneously</li>
<li>Graph coloring</li>
<li>Spilling to memory</li>
</ul>
<p><strong>Instruction Scheduling</strong>:</p>
<ul>
<li>Dependency graph (DAG)</li>
<li>Topological sort</li>
<li>List scheduling</li>
<li>Software pipelining</li>
</ul>
<p><strong>Loop Optimizations</strong>:</p>
<ul>
<li>Loop structure tree</li>
<li>Dominator tree</li>
<li>Natural loops</li>
<li>Loop invariant code motion</li>
</ul>
<h3 id="heading-157-recommendation-systems">15.7 Recommendation Systems</h3>
<p><strong>Collaborative Filtering</strong>:</p>
<ul>
<li>Bipartite graph: users and items</li>
<li>Predict ratings</li>
<li>Matrix factorization</li>
<li>Graph-based algorithms</li>
</ul>
<p><strong>Graph-Based Methods</strong>:</p>
<ul>
<li>Random walk with restart</li>
<li>SimRank</li>
<li>PathSim (meta-path based)</li>
</ul>
<p><strong>Knowledge Graphs</strong>:</p>
<ul>
<li>Entities and relationships</li>
<li>Link prediction</li>
<li>Embedding methods</li>
<li>GNN-based recommendations</li>
</ul>
<p><strong>Real Systems</strong>:</p>
<ul>
<li>Netflix: movie recommendations</li>
<li>Amazon: product suggestions</li>
<li>Spotify: music discovery</li>
<li>YouTube: video recommendations</li>
</ul>
<h3 id="heading-158-transportation-and-logistics">15.8 Transportation and Logistics</h3>
<p><strong>Vehicle Routing Problem (VRP)</strong>:</p>
<ul>
<li>Find optimal routes for fleet</li>
<li>Capacity constraints</li>
<li>Time windows</li>
<li>Multiple depots</li>
</ul>
<p><strong>Variants</strong>:</p>
<ul>
<li>Traveling Salesman Problem (TSP)</li>
<li>Capacitated VRP</li>
<li>VRP with time windows</li>
<li>Pickup and delivery</li>
</ul>
<p><strong>Algorithms</strong>:</p>
<ul>
<li>Exact: branch-and-bound, dynamic programming</li>
<li>Heuristics: nearest neighbor, savings algorithm</li>
<li>Metaheuristics: genetic algorithms, simulated annealing</li>
</ul>
<p><strong>Supply Chain Networks</strong>:</p>
<ul>
<li>Multi-echelon inventory</li>
<li>Network flow</li>
<li>Facility location</li>
<li>Distribution planning</li>
</ul>
<p><strong>Ride-Sharing</strong>:</p>
<ul>
<li>Dynamic matching</li>
<li>Route optimization</li>
<li>Pricing</li>
<li>Demand prediction</li>
</ul>
<p><strong>Applications</strong>:</p>
<ul>
<li>Uber/Lyft: rider-driver matching</li>
<li>UPS/FedEx: delivery route optimization</li>
<li>Airlines: crew scheduling, aircraft routing</li>
</ul>
<h3 id="heading-159-cybersecurity">15.9 Cybersecurity</h3>
<p><strong>Attack Graph Analysis</strong>:</p>
<ul>
<li>Vertices: system states</li>
<li>Edges: attack steps</li>
<li>Find attack paths</li>
<li>Prioritize vulnerabilities</li>
</ul>
<p><strong>Malware Detection</strong>:</p>
<ul>
<li>Call graph analysis</li>
<li>Control flow graphs</li>
<li>Behavioral signatures</li>
<li>Graph kernel methods</li>
</ul>
<p><strong>Network Security</strong>:</p>
<ul>
<li>Intrusion detection</li>
<li>Anomaly detection in traffic graphs</li>
<li>Botnet detection</li>
<li>DDoS mitigation</li>
</ul>
<p><strong>Blockchain Analysis</strong>:</p>
<ul>
<li>Transaction graphs</li>
<li>Address clustering</li>
<li>De-anonymization</li>
<li>Fraud detection</li>
</ul>
<h3 id="heading-1510-machine-learning-on-graphs">15.10 Machine Learning on Graphs</h3>
<p><strong>Graph Neural Networks (GNNs)</strong>:</p>
<ul>
<li>Message passing</li>
<li>Graph convolution</li>
<li>Node/edge/graph classification</li>
</ul>
<p><strong>Graph Kernels</strong>:</p>
<ul>
<li>Measure graph similarity</li>
<li>Random walk kernels</li>
<li>Shortest path kernels</li>
<li>Weisfeiler-Lehman kernel</li>
</ul>
<p><strong>Graph Embedding</strong>:</p>
<ul>
<li>DeepWalk: random walks + skip-gram</li>
<li>Node2Vec: biased random walks</li>
<li>GraphSAGE: inductive learning</li>
<li>Graph Attention Networks</li>
</ul>
<p><strong>Applications</strong>:</p>
<ul>
<li>Molecular property prediction</li>
<li>Traffic forecasting</li>
<li>Social network analysis</li>
<li>Recommender systems</li>
<li>Drug discovery</li>
</ul>
<hr />
<h2 id="heading-16-common-problems-and-solutions">16. Common Problems and Solutions</h2>
<h3 id="heading-161-detecting-cycles">16.1 Detecting Cycles</h3>
<p><strong>Undirected Graphs (DFS)</strong>:</p>
<pre><code>HAS-CYCLE-UNDIRECTED(G):
    <span class="hljs-keyword">for</span> each vertex v:
        v.visited = FALSE

    <span class="hljs-keyword">for</span> each vertex v:
        <span class="hljs-keyword">if</span> not v.visited:
            <span class="hljs-keyword">if</span> DFS-CYCLE(v, NIL):
                <span class="hljs-keyword">return</span> TRUE
    <span class="hljs-keyword">return</span> FALSE

DFS-CYCLE(u, parent):
    u.visited = TRUE
    <span class="hljs-keyword">for</span> each v <span class="hljs-keyword">in</span> Adj[u]:
        <span class="hljs-keyword">if</span> not v.visited:
            <span class="hljs-keyword">if</span> DFS-CYCLE(v, u):
                <span class="hljs-keyword">return</span> TRUE
        <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> v ≠ parent:
            <span class="hljs-keyword">return</span> TRUE  <span class="hljs-comment">// Back edge found</span>
    <span class="hljs-keyword">return</span> FALSE
</code></pre><p><strong>Directed Graphs</strong>:</p>
<pre><code>HAS-CYCLE-DIRECTED(G):
    <span class="hljs-keyword">for</span> each vertex v:
        v.color = WHITE

    <span class="hljs-keyword">for</span> each vertex v:
        <span class="hljs-keyword">if</span> v.color == WHITE:
            <span class="hljs-keyword">if</span> DFS-CYCLE-DIRECTED(v):
                <span class="hljs-keyword">return</span> TRUE
    <span class="hljs-keyword">return</span> FALSE

DFS-CYCLE-DIRECTED(u):
    u.color = GRAY
    <span class="hljs-keyword">for</span> each v <span class="hljs-keyword">in</span> Adj[u]:
        <span class="hljs-keyword">if</span> v.color == GRAY:
            <span class="hljs-keyword">return</span> TRUE  <span class="hljs-comment">// Back edge</span>
        <span class="hljs-keyword">if</span> v.color == WHITE:
            <span class="hljs-keyword">if</span> DFS-CYCLE-DIRECTED(v):
                <span class="hljs-keyword">return</span> TRUE
    u.color = BLACK
    <span class="hljs-keyword">return</span> FALSE
</code></pre><p><strong>Using Union-Find</strong> (Undirected):</p>
<pre><code>HAS-CYCLE-UF(G):
    <span class="hljs-keyword">for</span> each vertex v:
        MAKE-SET(v)

    <span class="hljs-keyword">for</span> each edge (u, v):
        x = FIND-SET(u)
        y = FIND-SET(v)
        <span class="hljs-keyword">if</span> x == y:
            <span class="hljs-keyword">return</span> TRUE
        UNION(x, y)
    <span class="hljs-keyword">return</span> FALSE
</code></pre><h3 id="heading-162-finding-all-paths">16.2 Finding All Paths</h3>
<p><strong>Between Two Vertices</strong>:</p>
<pre><code>ALL-PATHS(G, s, t):
    paths = []
    current_path = []
    visited = set()
    DFS-ALL-PATHS(s, t, current_path, visited, paths)
    <span class="hljs-keyword">return</span> paths

DFS-ALL-PATHS(u, t, current_path, visited, paths):
    visited.add(u)
    current_path.append(u)

    <span class="hljs-keyword">if</span> u == t:
        paths.append(copy <span class="hljs-keyword">of</span> current_path)
    <span class="hljs-attr">else</span>:
        <span class="hljs-keyword">for</span> each v <span class="hljs-keyword">in</span> Adj[u]:
            <span class="hljs-keyword">if</span> v not <span class="hljs-keyword">in</span> visited:
                DFS-ALL-PATHS(v, t, current_path, visited, paths)

    current_path.pop()
    visited.remove(u)
</code></pre><p><strong>Properties</strong>:</p>
<ul>
<li>Can be exponential in number</li>
<li>Use with caution on large graphs</li>
</ul>
<h3 id="heading-163-graph-cloning">16.3 Graph Cloning</h3>
<p><strong>Clone Undirected Graph</strong>:</p>
<pre><code>CLONE-GRAPH(node):
    <span class="hljs-keyword">if</span> node == NIL:
        <span class="hljs-keyword">return</span> NIL

    visited = {}  <span class="hljs-comment">// maps original to clone</span>

    <span class="hljs-keyword">return</span> CLONE-DFS(node, visited)

CLONE-DFS(node, visited):
    <span class="hljs-keyword">if</span> node <span class="hljs-keyword">in</span> visited:
        <span class="hljs-keyword">return</span> visited[node]

    clone = <span class="hljs-keyword">new</span> Node(node.val)
    visited[node] = clone

    <span class="hljs-keyword">for</span> each neighbor <span class="hljs-keyword">in</span> node.neighbors:
        clone.neighbors.append(CLONE-DFS(neighbor, visited))

    <span class="hljs-keyword">return</span> clone
</code></pre><h3 id="heading-164-course-schedule-problem">16.4 Course Schedule Problem</h3>
<p><strong>Problem</strong>: Given prerequisites, determine if all courses can be completed</p>
<p><strong>Solution</strong>: Detect cycle in directed graph</p>
<pre><code>CAN-FINISH(numCourses, prerequisites):
    <span class="hljs-comment">// Build adjacency list</span>
    graph = [[] <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> numCourses]
    <span class="hljs-keyword">for</span> (course, prereq) <span class="hljs-keyword">in</span> prerequisites:
        graph[prereq].append(course)

    <span class="hljs-comment">// Track states: 0=unvisited, 1=visiting, 2=visited</span>
    state = [<span class="hljs-number">0</span>] * numCourses

    <span class="hljs-keyword">for</span> course <span class="hljs-keyword">in</span> <span class="hljs-number">0</span> to numCourses<span class="hljs-number">-1</span>:
        <span class="hljs-keyword">if</span> state[course] == <span class="hljs-number">0</span>:
            <span class="hljs-keyword">if</span> HAS-CYCLE-DFS(course, graph, state):
                <span class="hljs-keyword">return</span> FALSE
    <span class="hljs-keyword">return</span> TRUE

HAS-CYCLE-DFS(course, graph, state):
    state[course] = <span class="hljs-number">1</span>  <span class="hljs-comment">// visiting</span>

    <span class="hljs-keyword">for</span> next_course <span class="hljs-keyword">in</span> graph[course]:
        <span class="hljs-keyword">if</span> state[next_course] == <span class="hljs-number">1</span>:
            <span class="hljs-keyword">return</span> TRUE  <span class="hljs-comment">// cycle</span>
        <span class="hljs-keyword">if</span> state[next_course] == <span class="hljs-number">0</span>:
            <span class="hljs-keyword">if</span> HAS-CYCLE-DFS(next_course, graph, state):
                <span class="hljs-keyword">return</span> TRUE

    state[course] = <span class="hljs-number">2</span>  <span class="hljs-comment">// visited</span>
    <span class="hljs-keyword">return</span> FALSE
</code></pre><h3 id="heading-165-word-ladder">16.5 Word Ladder</h3>
<p><strong>Problem</strong>: Transform one word to another, changing one letter at a time</p>
<p><strong>Solution</strong>: BFS on word graph</p>
<pre><code>WORD-LADDER(beginWord, endWord, wordList):
    <span class="hljs-keyword">if</span> endWord not <span class="hljs-keyword">in</span> wordList:
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>

    <span class="hljs-comment">// Build graph using BFS</span>
    queue = [(beginWord, <span class="hljs-number">1</span>)]
    visited = {beginWord}

    <span class="hljs-keyword">while</span> queue not empty:
        word, length = queue.pop()

        <span class="hljs-keyword">if</span> word == endWord:
            <span class="hljs-keyword">return</span> length

        <span class="hljs-keyword">for</span> next_word <span class="hljs-keyword">in</span> GET-NEIGHBORS(word, wordList):
            <span class="hljs-keyword">if</span> next_word not <span class="hljs-keyword">in</span> visited:
                visited.add(next_word)
                queue.append((next_word, length + <span class="hljs-number">1</span>))

    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>  <span class="hljs-comment">// no path</span>

GET-NEIGHBORS(word, wordList):
    neighbors = []
    <span class="hljs-keyword">for</span> candidate <span class="hljs-keyword">in</span> wordList:
        <span class="hljs-keyword">if</span> DIFFER-BY-ONE(word, candidate):
            neighbors.append(candidate)
    <span class="hljs-keyword">return</span> neighbors
</code></pre><h3 id="heading-166-alien-dictionary">16.6 Alien Dictionary</h3>
<p><strong>Problem</strong>: Determine order of characters from sorted alien dictionary</p>
<p><strong>Solution</strong>: Topological sort</p>
<pre><code>ALIEN-ORDER(words):
    <span class="hljs-comment">// Build graph</span>
    graph = {}
    in_degree = {<span class="hljs-attr">c</span>: <span class="hljs-number">0</span> <span class="hljs-keyword">for</span> word <span class="hljs-keyword">in</span> words <span class="hljs-keyword">for</span> c <span class="hljs-keyword">in</span> word}

    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-number">0</span> to len(words)<span class="hljs-number">-2</span>:
        w1, w2 = words[i], words[i+<span class="hljs-number">1</span>]
        min_len = min(len(w1), len(w2))

        <span class="hljs-comment">// Invalid if w1 is prefix of w2 but longer</span>
        <span class="hljs-keyword">if</span> len(w1) &gt; len(w2) and w1[:min_len] == w2[:min_len]:
            <span class="hljs-keyword">return</span> <span class="hljs-string">""</span>

        <span class="hljs-comment">// Find first differing character</span>
        <span class="hljs-keyword">for</span> j <span class="hljs-keyword">in</span> <span class="hljs-number">0</span> to min_len<span class="hljs-number">-1</span>:
            <span class="hljs-keyword">if</span> w1[j] ≠ w2[j]:
                <span class="hljs-keyword">if</span> w2[j] not <span class="hljs-keyword">in</span> graph[w1[j]]:
                    graph[w1[j]].add(w2[j])
                    in_degree[w2[j]]++
                <span class="hljs-keyword">break</span>

    <span class="hljs-comment">// Topological sort</span>
    result = []
    queue = [c <span class="hljs-keyword">for</span> c <span class="hljs-keyword">in</span> in_degree <span class="hljs-keyword">if</span> in_degree[c] == <span class="hljs-number">0</span>]

    <span class="hljs-keyword">while</span> queue:
        c = queue.pop()
        result.append(c)
        <span class="hljs-keyword">for</span> next_c <span class="hljs-keyword">in</span> graph[c]:
            in_degree[next_c]--
            <span class="hljs-keyword">if</span> in_degree[next_c] == <span class="hljs-number">0</span>:
                queue.append(next_c)

    <span class="hljs-keyword">if</span> len(result) &lt; len(in_degree):
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span>  <span class="hljs-comment">// cycle detected</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">""</span>.join(result)
</code></pre><h3 id="heading-167-critical-connections-bridges">16.7 Critical Connections (Bridges)</h3>
<p><strong>Problem</strong>: Find all critical edges in network</p>
<pre><code>CRITICAL-CONNECTIONS(n, connections):
    graph = [[] <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> n]
    <span class="hljs-keyword">for</span> (u, v) <span class="hljs-keyword">in</span> connections:
        graph[u].append(v)
        graph[v].append(u)

    disc = [<span class="hljs-number">-1</span>] * n
    low = [<span class="hljs-number">-1</span>] * n
    time = [<span class="hljs-number">0</span>]
    result = []

    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-number">0</span> to n<span class="hljs-number">-1</span>:
        <span class="hljs-keyword">if</span> disc[i] == <span class="hljs-number">-1</span>:
            DFS-BRIDGE(i, <span class="hljs-number">-1</span>, disc, low, time, graph, result)

    <span class="hljs-keyword">return</span> result

DFS-BRIDGE(u, parent, disc, low, time, graph, result):
    disc[u] = low[u] = time[<span class="hljs-number">0</span>]
    time[<span class="hljs-number">0</span>]++

    <span class="hljs-keyword">for</span> v <span class="hljs-keyword">in</span> graph[u]:
        <span class="hljs-keyword">if</span> v == parent:
            <span class="hljs-keyword">continue</span>
        <span class="hljs-keyword">if</span> disc[v] == <span class="hljs-number">-1</span>:
            DFS-BRIDGE(v, u, disc, low, time, graph, result)
            low[u] = min(low[u], low[v])
            <span class="hljs-keyword">if</span> low[v] &gt; disc[u]:
                result.append([u, v])
        <span class="hljs-attr">else</span>:
            low[u] = min(low[u], disc[v])
</code></pre><h3 id="heading-168-number-of-islands">16.8 Number of Islands</h3>
<p><strong>Problem</strong>: Count connected components in 2D grid</p>
<pre><code>NUM-ISLANDS(grid):
    <span class="hljs-keyword">if</span> grid is empty:
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>

    count = <span class="hljs-number">0</span>
    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-number">0</span> to rows<span class="hljs-number">-1</span>:
        <span class="hljs-keyword">for</span> j <span class="hljs-keyword">in</span> <span class="hljs-number">0</span> to cols<span class="hljs-number">-1</span>:
            <span class="hljs-keyword">if</span> grid[i][j] == <span class="hljs-string">'1'</span>:
                DFS-ISLAND(grid, i, j)
                count++

    <span class="hljs-keyword">return</span> count

DFS-ISLAND(grid, i, j):
    <span class="hljs-keyword">if</span> i &lt; <span class="hljs-number">0</span> or i ≥ rows or j &lt; <span class="hljs-number">0</span> or j ≥ cols or grid[i][j] ≠ <span class="hljs-string">'1'</span>:
        <span class="hljs-keyword">return</span>

    grid[i][j] = <span class="hljs-string">'0'</span>  <span class="hljs-comment">// mark visited</span>

    DFS-ISLAND(grid, i+<span class="hljs-number">1</span>, j)
    DFS-ISLAND(grid, i<span class="hljs-number">-1</span>, j)
    DFS-ISLAND(grid, i, j+<span class="hljs-number">1</span>)
    DFS-ISLAND(grid, i, j<span class="hljs-number">-1</span>)
</code></pre><hr />
<h2 id="heading-17-complexity-analysis">17. Complexity Analysis</h2>
<h3 id="heading-171-problem-classification">17.1 Problem Classification</h3>
<p><strong>P (Polynomial Time)</strong>:</p>
<ul>
<li>Problems solvable in polynomial time</li>
<li>Examples:<ul>
<li>Shortest path: O(V²) or O((V+E) log V)</li>
<li>Minimum spanning tree: O(E log V)</li>
<li>Topological sort: O(V + E)</li>
<li>Max flow: O(V²E)</li>
<li>Bipartite matching: O(E√V)</li>
</ul>
</li>
</ul>
<p><strong>NP (Nondeterministic Polynomial)</strong>:</p>
<ul>
<li>Solutions verifiable in polynomial time</li>
<li>Examples:<ul>
<li>Hamiltonian cycle</li>
<li>Graph coloring</li>
<li>Clique</li>
<li>Independent set</li>
<li>Vertex cover</li>
</ul>
</li>
</ul>
<p><strong>NP-Complete</strong>:</p>
<ul>
<li>Hardest problems in NP</li>
<li>If any NP-complete problem is in P, then P = NP</li>
<li>Examples:<ul>
<li>3-SAT</li>
<li>Hamiltonian cycle</li>
<li>3-coloring</li>
<li>Clique (decision)</li>
<li>Vertex cover (decision)</li>
<li>Traveling salesman (decision)</li>
</ul>
</li>
</ul>
<p><strong>NP-Hard</strong>:</p>
<ul>
<li>At least as hard as NP-complete</li>
<li>Not necessarily in NP</li>
<li>Examples:<ul>
<li>Optimization versions of NP-complete problems</li>
<li>Graph isomorphism (conjectured)</li>
</ul>
</li>
</ul>
<h3 id="heading-172-approximation-algorithms">17.2 Approximation Algorithms</h3>
<p><strong>Approximation Ratio</strong>: Algorithm gives solution within factor α of optimal</p>
<p><strong>Vertex Cover</strong>:</p>
<pre><code>APPROX-VERTEX-COVER(G):
    C = empty set
    E<span class="hljs-string">' = copy of E
    while E'</span> ≠ empty:
        pick arbitrary edge (u, v) <span class="hljs-keyword">from</span> E<span class="hljs-string">'
        C = C ∪ {u, v}
        remove all edges incident to u or v from E'</span>
    <span class="hljs-keyword">return</span> C
</code></pre><ul>
<li><strong>Approximation ratio</strong>: 2</li>
<li><strong>Optimal</strong>: NP-hard to approximate better than 1.36</li>
</ul>
<p><strong>TSP</strong>:</p>
<ul>
<li><strong>Metric TSP</strong>: Triangle inequality holds</li>
<li><strong>MST-based</strong>: 2-approximation</li>
<li><strong>Christofides</strong>: 1.5-approximation</li>
<li><strong>General TSP</strong>: No constant approximation unless P=NP</li>
</ul>
<p><strong>Set Cover</strong>:</p>
<ul>
<li><strong>Greedy</strong>: ln(n)-approximation</li>
<li><strong>Optimal</strong>: Hard to approximate better</li>
</ul>
<h3 id="heading-173-parameterized-complexity">17.3 Parameterized Complexity</h3>
<p><strong>Fixed-Parameter Tractable (FPT)</strong>:</p>
<ul>
<li>Running time f(k) · poly(n) where k is parameter</li>
<li>Examples:<ul>
<li>Vertex cover: O(2^k · n)</li>
<li>k-path: FPT in k</li>
</ul>
</li>
</ul>
<p><strong>Vertex Cover Parameterized</strong>:</p>
<pre><code>VERTEX-COVER-FPT(G, k):
    <span class="hljs-keyword">if</span> k &lt; <span class="hljs-number">0</span>:
        <span class="hljs-keyword">return</span> NO
    <span class="hljs-keyword">if</span> E = empty:
        <span class="hljs-keyword">return</span> YES

    pick edge (u, v)
    <span class="hljs-keyword">return</span> VERTEX-COVER-FPT(G \ {u}, k<span class="hljs-number">-1</span>) OR
           VERTEX-COVER-FPT(G \ {v}, k<span class="hljs-number">-1</span>)
</code></pre><h3 id="heading-174-hardness-reductions">17.4 Hardness Reductions</h3>
<p><strong>Reduction</strong>: Transform instance of problem A to instance of problem B</p>
<p><strong>Example</strong>: Hamiltonian Cycle ≤ₚ TSP</p>
<ul>
<li>Given graph G, create complete weighted graph</li>
<li>Weight = 1 if edge in G, large otherwise</li>
<li>Ham cycle exists iff TSP tour has cost |V|</li>
</ul>
<p><strong>Common Reductions</strong>:</p>
<ul>
<li>3-SAT → Clique</li>
<li>3-SAT → Vertex Cover</li>
<li>3-SAT → Hamiltonian Cycle</li>
<li>Vertex Cover → Set Cover</li>
</ul>
<h3 id="heading-175-space-complexity">17.5 Space Complexity</h3>
<p><strong>Classes</strong>:</p>
<ul>
<li><strong>L</strong>: Log space</li>
<li><strong>NL</strong>: Nondeterministic log space</li>
<li><strong>PSPACE</strong>: Polynomial space</li>
<li><strong>EXPSPACE</strong>: Exponential space</li>
</ul>
<p><strong>Savitch's Theorem</strong>: NSPACE(f(n)) ⊆ SPACE(f²(n))</p>
<p><strong>Graph Problems</strong>:</p>
<ul>
<li>Reachability: NL-complete</li>
<li>All-pairs shortest paths: Can be done in O(log²n) space</li>
<li>Most graph problems: polynomial space</li>
</ul>
<h3 id="heading-176-practical-complexity">17.6 Practical Complexity</h3>
<p><strong>Algorithm Selection Criteria</strong>:</p>
<p>For sparse graphs (E ≈ V):</p>
<ul>
<li>Use adjacency list</li>
<li>BFS/DFS: O(V)</li>
<li>Dijkstra with heap: O(V log V)</li>
</ul>
<p>For dense graphs (E ≈ V²):</p>
<ul>
<li>Consider adjacency matrix</li>
<li>Floyd-Warshall: O(V³)</li>
<li>Dijkstra with array: O(V²)</li>
</ul>
<p><strong>Memory Considerations</strong>:</p>
<ul>
<li>Adjacency matrix: Always O(V²)</li>
<li>Adjacency list: O(V + E)</li>
<li>Implicit graphs: Store only generator</li>
</ul>
<p><strong>Typical Performance</strong>:
| Algorithm | Input Size | Time (modern CPU) |
|-----------|------------|-------------------|
| BFS/DFS | 10⁶ vertices | &lt; 1 second |
| Dijkstra | 10⁵ vertices | &lt; 1 second |
| Floyd-Warshall | 10³ vertices | &lt; 1 second |
| Max-flow | 10⁴ vertices | seconds |
| TSP exact | 20 cities | feasible |
| TSP exact | 50 cities | impractical |</p>
<hr />
<h2 id="heading-conclusion">Conclusion</h2>
<p>This guide has covered the fundamental concepts, algorithms, and applications of graph theory. From basic definitions to advanced topics and industry applications, graph theory provides powerful tools for modeling and solving real-world problems.</p>
<p><strong>Key Takeaways</strong>:</p>
<ol>
<li><strong>Representation matters</strong>: Choose adjacency matrix for dense graphs, adjacency list for sparse</li>
<li><strong>Traversal is fundamental</strong>: BFS and DFS are building blocks for many algorithms</li>
<li><strong>Problem reduction</strong>: Many problems can be modeled as graph problems</li>
<li><strong>Complexity awareness</strong>: Know when problems are tractable vs. NP-hard</li>
<li><strong>Algorithm selection</strong>: Match algorithm to problem characteristics and constraints</li>
<li><strong>Practical considerations</strong>: Theory guides implementation, but constants matter</li>
</ol>
<p><strong>Further Study</strong>:</p>
<ul>
<li>Advanced algorithms textbooks (CLRS, Kleinberg &amp; Tardos)</li>
<li>Specialized topics (network flows, spectral methods, graph neural networks)</li>
<li>Implementation practice (LeetCode, competitive programming)</li>
<li>Research papers in specific application domains</li>
</ul>
<p><strong>Modern Trends</strong>:</p>
<ul>
<li>Graph neural networks and deep learning on graphs</li>
<li>Streaming and dynamic graph algorithms</li>
<li>Distributed graph processing</li>
<li>Quantum graph algorithms</li>
<li>Applications in AI and machine learning</li>
</ul>
<p>Graph theory remains one of the most active and applicable areas of computer science and mathematics, with new developments constantly emerging.</p>
]]></content:encoded></item><item><title><![CDATA[Language Model Distillation]]></title><description><![CDATA[Knowledge distillation is a technique for training smaller neural networks to perform like larger ones. The basic idea is simple: train a small "student" model to copy the behavior of a large "teacher]]></description><link>https://arnavverma.hashnode.dev/model-distillation</link><guid isPermaLink="true">https://arnavverma.hashnode.dev/model-distillation</guid><category><![CDATA[Artificial Intelligence]]></category><category><![CDATA[AI]]></category><category><![CDATA[llm]]></category><category><![CDATA[large language models]]></category><category><![CDATA[training]]></category><dc:creator><![CDATA[Arnav Verma]]></dc:creator><pubDate>Thu, 23 Oct 2025 18:30:00 GMT</pubDate><content:encoded><![CDATA[<p>Knowledge distillation is a technique for training smaller neural networks to perform like larger ones. The basic idea is simple: train a small "student" model to copy the behavior of a large "teacher" model. This lets you compress years of training and billions of parameters into something you can actually deploy. But making this work well requires understanding probability matching, intermediate representations, and training dynamics.</p>
<h2>How Distillation Works</h2>
<p>Hinton and his colleagues created knowledge distillation after noticing something useful: when a trained model makes predictions, it outputs a probability distribution that contains more information than just the final answer. Say a teacher model assigns 80% probability to the correct class, 15% to a similar class, and 5% to an unrelated class. This distribution tells you about relationships between classes that a simple correct/incorrect label throws away. The student learns not just what to predict, but how the teacher thinks about alternatives.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6557ff28afe2c15e65f8d100/631d3b3b-6893-47ed-b796-2d6df04cb5b5.png" alt="" style="display:block;margin:0 auto" />

<p>The distillation loss function combines two parts. First, it measures how well the student matches the teacher's soft predictions using KL divergence. Second, it makes sure the student still learns from the actual labels using cross-entropy loss. You control the balance between these with a parameter called alpha.</p>
<pre><code class="language-python">import torch
import torch.nn as nn
import torch.nn.functional as F

class DistillationLoss(nn.Module):
    def __init__(self, temperature=3.0, alpha=0.7):
        """
        Args:
            temperature: Controls softness of probability distributions
            alpha: Weight balancing distillation vs hard label loss
        """
        super().__init__()
        self.temperature = temperature
        self.alpha = alpha
        self.kl_div = nn.KLDivLoss(reduction='batchmean')
        self.ce_loss = nn.CrossEntropyLoss()
    
    def forward(self, student_logits, teacher_logits, labels):
        # Soften distributions with temperature scaling
        student_soft = F.log_softmax(student_logits / self.temperature, dim=1)
        teacher_soft = F.softmax(teacher_logits / self.temperature, dim=1)
        
        # Distillation loss (KL divergence between soft distributions)
        distillation_loss = self.kl_div(student_soft, teacher_soft) * (self.temperature ** 2)
        
        # Standard cross-entropy with hard labels
        student_loss = self.ce_loss(student_logits, labels)
        
        # Combined objective
        return self.alpha * distillation_loss + (1 - self.alpha) * student_loss
</code></pre>
<p>Temperature is important here. Higher temperature makes the probability distribution "softer," revealing more about class relationships. At temperature 1, you get normal softmax. As temperature goes up, the distribution gets more uniform. The best temperature is usually between 2 and 5. The temperature squared term in the loss keeps gradients consistent across different temperature settings.</p>
<h2>Dealing with Size Differences</h2>
<p>The gap between teacher and student size fundamentally limits how well distillation works. If the student is too small, it can't capture what the teacher knows. If it's too large, you're not getting much compression. You need to find the point where the student is small enough to be useful but large enough to learn the important patterns.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6557ff28afe2c15e65f8d100/7d4c0360-082d-4015-b94f-87ae27b00cb1.png" alt="" style="display:block;margin:0 auto" />

<p>Different approaches handle this gap in different ways. Basic distillation just matches output distributions. Intermediate distillation also aligns hidden layer representations. This works better for transformers where intermediate attention patterns encode important linguistic knowledge. Patient Knowledge Distillation goes further by matching relationships between layers, not just individual layers.</p>
<pre><code class="language-python">class IntermediateDistillation(nn.Module):
    def __init__(self, student_dim, teacher_dim, num_student_layers, num_teacher_layers):
        super().__init__()
        self.num_student_layers = num_student_layers
        self.num_teacher_layers = num_teacher_layers
        
        # Layer mapping strategy: map student layers to teacher layers
        self.layer_mapping = self._create_layer_mapping()
        
        # Projection layers if dimensions don't match
        if student_dim != teacher_dim:
            self.projections = nn.ModuleList([
                nn.Linear(student_dim, teacher_dim) 
                for _ in range(num_student_layers)
            ])
        else:
            self.projections = None
    
    def _create_layer_mapping(self):
        # Map student layers uniformly across teacher layers
        # For 6 student, 12 teacher: [1, 3, 5, 7, 9, 11]
        step = self.num_teacher_layers / self.num_student_layers
        return [int(i * step) for i in range(self.num_student_layers)]
    
    def forward(self, student_hidden_states, teacher_hidden_states):
        """
        Args:
            student_hidden_states: List of tensors [batch, seq_len, student_dim]
            teacher_hidden_states: List of tensors [batch, seq_len, teacher_dim]
        """
        total_loss = 0
        
        for student_idx, teacher_idx in enumerate(self.layer_mapping):
            student_hidden = student_hidden_states[student_idx]
            teacher_hidden = teacher_hidden_states[teacher_idx]
            
            # Project student to teacher dimension if needed
            if self.projections is not None:
                student_hidden = self.projections[student_idx](student_hidden)
            
            # MSE loss between intermediate representations
            layer_loss = F.mse_loss(student_hidden, teacher_hidden.detach())
            total_loss += layer_loss
        
        return total_loss / len(self.layer_mapping)
</code></pre>
<h2>Transferring Attention Patterns</h2>
<p>Attention mechanisms in transformers capture complex dependencies between input tokens, encoding structure, relationships, and context. Distilling these attention patterns is harder than distilling outputs because attention matrices are large and sensitive to architectural differences. Attention transfer methods usually focus on preserving the structure of attention rather than exact weights.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6557ff28afe2c15e65f8d100/157caed2-04ab-4ab7-932b-1b9cdcb17287.png" alt="" style="display:block;margin:0 auto" />

<p>The attention transfer loss measures the distance between teacher and student attention distributions, typically using mean squared error or KL divergence. You need to handle different numbers of attention heads. Some approaches average attention across heads before computing loss. Others maintain head-specific alignments, which preserves more detail but requires careful mapping.</p>
<pre><code class="language-python">class AttentionTransfer(nn.Module):
    def __init__(self, student_heads, teacher_heads, use_head_mapping=True):
        super().__init__()
        self.student_heads = student_heads
        self.teacher_heads = teacher_heads
        self.use_head_mapping = use_head_mapping
        
        if use_head_mapping and student_heads != teacher_heads:
            # Learn which teacher heads to map to which student heads
            self.head_mapping = nn.Parameter(
                torch.randn(student_heads, teacher_heads)
            )
    
    def forward(self, student_attentions, teacher_attentions):
        """
        Args:
            student_attentions: [batch, num_heads, seq_len, seq_len]
            teacher_attentions: [batch, num_heads, seq_len, seq_len]
        """
        batch_size, _, seq_len, _ = student_attentions.shape
        
        if self.use_head_mapping and hasattr(self, 'head_mapping'):
            # Apply learned head mapping
            mapping_weights = F.softmax(self.head_mapping, dim=1)
            # [student_heads, teacher_heads] × [batch, teacher_heads, seq, seq]
            teacher_mapped = torch.einsum(
                'st,bthw-&gt;bshw', 
                mapping_weights, 
                teacher_attentions
            )
        else:
            # Simple averaging if heads match or no mapping desired
            if self.student_heads == self.teacher_heads:
                teacher_mapped = teacher_attentions
            else:
                # Average teacher heads to match student count
                teacher_mapped = teacher_attentions.reshape(
                    batch_size, self.student_heads, -1, seq_len, seq_len
                ).mean(dim=2)
        
        # MSE between attention distributions
        attention_loss = F.mse_loss(student_attentions, teacher_mapped.detach())
        
        return attention_loss
</code></pre>
<h2>Progressive Distillation</h2>
<p>Progressive distillation solves the problem of distilling very large teachers into very small students by using intermediate teachers. Instead of going straight from a GPT-3 scale model to a mobile-friendly size, you create a series of progressively smaller teachers, each distilled from the previous one. This staged approach lets each student learn from a teacher closer to its own capacity, which reduces the knowledge gap and improves final performance.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6557ff28afe2c15e65f8d100/e04e6415-d051-4d47-b391-927771aafd5b.png" alt="" style="display:block;margin:0 auto" />

<p>Curriculum learning in distillation means carefully ordering training examples. Early on, the student learns from easier examples where the teacher is more confident. As training goes on, you introduce more ambiguous cases where the teacher's soft labels provide maximum information. You can define this curriculum based on prediction entropy, loss magnitude, or example complexity.</p>
<pre><code class="language-python">class ProgressiveDistillationTrainer:
    def __init__(self, teachers, student, device='cuda'):
        """
        Args:
            teachers: List of teacher models ordered from largest to smallest
            student: Student model to train
        """
        self.teachers = teachers
        self.student = student
        self.device = device
        
        # Move all models to device and set teachers to eval
        for teacher in self.teachers:
            teacher.to(device)
            teacher.eval()
        self.student.to(device)
    
    def get_curriculum_weight(self, epoch, total_epochs):
        # Linearly increase difficulty over training
        return min(1.0, epoch / (total_epochs * 0.7))
    
    def compute_example_difficulty(self, teacher_logits):
        # Use entropy of teacher predictions as difficulty measure
        probs = F.softmax(teacher_logits, dim=-1)
        entropy = -(probs * torch.log(probs + 1e-10)).sum(dim=-1)
        return entropy
    
    def progressive_distill(self, dataloader, stage, optimizer, 
                           temperature=3.0, epochs=10):
        """
        Distill from teachers[stage] into student or next teacher
        """
        current_teacher = self.teachers[stage]
        criterion = DistillationLoss(temperature=temperature)
        
        for epoch in range(epochs):
            curriculum_weight = self.get_curriculum_weight(epoch, epochs)
            
            for batch in dataloader:
                inputs, labels = batch
                inputs = inputs.to(self.device)
                labels = labels.to(self.device)
                
                # Get teacher predictions
                with torch.no_grad():
                    teacher_logits = current_teacher(inputs)
                    difficulties = self.compute_example_difficulty(teacher_logits)
                
                # Filter or weight examples based on curriculum
                difficulty_threshold = torch.quantile(
                    difficulties, curriculum_weight
                )
                example_weights = (difficulties &lt;= difficulty_threshold).float()
                
                # Student forward pass
                student_logits = self.student(inputs)
                
                # Compute weighted distillation loss
                loss = criterion(student_logits, teacher_logits, labels)
                weighted_loss = (loss * example_weights.mean())
                
                # Optimization step
                optimizer.zero_grad()
                weighted_loss.backward()
                optimizer.step()
    
    def train_all_stages(self, dataloader, optimizer, epochs_per_stage=10):
        """
        Execute progressive distillation through all teacher stages
        """
        for stage in range(len(self.teachers)):
            print(f"Stage {stage}: Distilling from teacher {stage}")
            self.progressive_distill(
                dataloader, stage, optimizer, epochs=epochs_per_stage
            )
        
        return self.student
</code></pre>
<h2>Task-Specific and Multi-Task Distillation</h2>
<p>General distillation trains students to match teacher behavior across all tasks. Task-specific distillation optimizes for particular applications. This lets you compress more aggressively because the student only needs knowledge relevant to the target task. For example, distilling a general language model into a sentiment classifier can achieve much higher compression while maintaining or exceeding task performance.</p>
<p>Multi-task distillation extends this by training the student on multiple related tasks at once. The teacher might be an ensemble of task-specific expert models, and the student learns to handle all tasks in one architecture. This works well when tasks share underlying patterns, letting the student develop shared representations that generalize across tasks.</p>
<pre><code class="language-python">class MultiTaskDistillation(nn.Module):
    def __init__(self, task_weights=None):
        super().__init__()
        self.task_weights = task_weights or {}
    
    def forward(self, student_outputs, teacher_outputs, task_names, labels):
        """
        Args:
            student_outputs: Dict mapping task names to student logits
            teacher_outputs: Dict mapping task names to teacher logits
            task_names: List of tasks in current batch
            labels: Dict mapping task names to ground truth labels
        """
        total_loss = 0
        task_losses = {}
        
        for task in task_names:
            # Task-specific distillation loss
            criterion = DistillationLoss(
                temperature=self.get_task_temperature(task),
                alpha=self.get_task_alpha(task)
            )
            
            task_loss = criterion(
                student_outputs[task],
                teacher_outputs[task],
                labels[task]
            )
            
            # Weight by task importance
            weight = self.task_weights.get(task, 1.0)
            total_loss += weight * task_loss
            task_losses[task] = task_loss.item()
        
        return total_loss, task_losses
    
    def get_task_temperature(self, task):
        # Different tasks may benefit from different temperatures
        temperature_map = {
            'sentiment': 2.0,      # Lower for classification
            'nli': 3.0,            # Higher for complex reasoning
            'qa': 4.0,             # Highest for generation tasks
        }
        return temperature_map.get(task, 3.0)
    
    def get_task_alpha(self, task):
        # Balance between distillation and hard labels per task
        alpha_map = {
            'sentiment': 0.5,      # More weight on hard labels
            'nli': 0.7,            # Balanced
            'qa': 0.9,             # Heavy distillation weight
        }
        return alpha_map.get(task, 0.7)
</code></pre>
<h2>Data Augmentation and Synthetic Data</h2>
<p>How well distillation works depends heavily on the diversity and quality of training data. Basic distillation uses the same dataset that trained the teacher. Augmented distillation generates synthetic examples to expose the student to more teacher behaviors. The teacher generates labels for unlabeled data, greatly expanding the training set. This works especially well with task-specific augmentation strategies that target challenging cases or underrepresented patterns.</p>
<pre><code class="language-python">class DataAugmentedDistillation:
    def __init__(self, teacher, student, base_dataset):
        self.teacher = teacher
        self.student = student
        self.base_dataset = base_dataset
        
    def generate_synthetic_examples(self, num_examples, augmentation_fn):
        """
        Generate synthetic training examples using the teacher
        """
        synthetic_data = []
        self.teacher.eval()
        
        with torch.no_grad():
            for _ in range(num_examples):
                # Sample from base dataset and augment
                base_example = self.base_dataset[
                    torch.randint(len(self.base_dataset), (1,)).item()
                ]
                augmented_input = augmentation_fn(base_example)
                
                # Generate teacher predictions
                teacher_logits = self.teacher(augmented_input)
                
                synthetic_data.append({
                    'input': augmented_input,
                    'teacher_logits': teacher_logits.cpu(),
                    'source': 'synthetic'
                })
        
        return synthetic_data
    
    def hard_example_mining(self, dataloader, percentile=90):
        """
        Identify examples where student struggles most
        """
        self.student.eval()
        self.teacher.eval()
        
        example_difficulties = []
        
        with torch.no_grad():
            for batch in dataloader:
                inputs, labels = batch
                
                student_logits = self.student(inputs)
                teacher_logits = self.teacher(inputs)
                
                # Measure disagreement as difficulty proxy
                disagreement = F.kl_div(
                    F.log_softmax(student_logits, dim=-1),
                    F.softmax(teacher_logits, dim=-1),
                    reduction='none'
                ).sum(dim=-1)
                
                example_difficulties.extend(disagreement.cpu().numpy())
        
        # Return indices of hardest examples
        threshold = np.percentile(example_difficulties, percentile)
        hard_indices = np.where(
            np.array(example_difficulties) &gt;= threshold
        )[0]
        
        return hard_indices
</code></pre>
<h2>Training Stability and Optimization</h2>
<p>The optimization landscape for distillation differs from standard supervised learning. The teacher's soft labels provide a smoother training signal than one-hot labels, which can speed up convergence but also cause instability if not managed carefully. Temperature directly affects gradient magnitudes, and improper tuning can lead to gradient explosion or vanishing gradients.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6557ff28afe2c15e65f8d100/4ae39610-097f-4ebb-8dce-874d5c5544dc.png" alt="" style="display:block;margin:0 auto" />

<p>Learning rate scheduling is critical for success. A common strategy uses a warmup phase where the learning rate gradually increases, letting the student stabilize before full distillation. During main training, maintain a moderate learning rate with high temperature. Finally, a fine-tuning phase with reduced temperature and learning rate polishes performance.</p>
<pre><code class="language-python">class DistillationOptimizer:
    def __init__(self, student, initial_lr=1e-4, warmup_steps=1000):
        self.student = student
        self.initial_lr = initial_lr
        self.warmup_steps = warmup_steps
        self.global_step = 0
        
        # Use AdamW with weight decay for better generalization
        self.optimizer = torch.optim.AdamW(
            student.parameters(),
            lr=initial_lr,
            betas=(0.9, 0.999),
            weight_decay=0.01
        )
        
        self.scheduler = self._create_scheduler()
    
    def _create_scheduler(self):
        # Cosine schedule with warmup
        from torch.optim.lr_scheduler import LambdaLR
        
        def lr_lambda(step):
            if step &lt; self.warmup_steps:
                # Linear warmup
                return step / self.warmup_steps
            else:
                # Cosine decay
                progress = (step - self.warmup_steps) / (10000 - self.warmup_steps)
                return 0.5 * (1 + np.cos(np.pi * progress))
        
        return LambdaLR(self.optimizer, lr_lambda)
    
    def step(self, loss):
        # Gradient clipping for stability
        torch.nn.utils.clip_grad_norm_(self.student.parameters(), max_norm=1.0)
        
        self.optimizer.step()
        self.scheduler.step()
        self.global_step += 1
        
        return self.scheduler.get_last_lr()[0]
    
    def get_temperature_schedule(self, max_steps):
        """
        Dynamic temperature scheduling during training
        """
        if self.global_step &lt; self.warmup_steps:
            # Start with lower temperature during warmup
            return 2.0
        elif self.global_step &lt; max_steps * 0.8:
            # Higher temperature for main distillation
            return 4.0
        else:
            # Reduce temperature for fine-tuning
            return 2.0
</code></pre>
<h2>Evaluation Metrics</h2>
<p>Evaluating distilled models requires more than just accuracy. You need to assess compression ratio, inference latency, memory footprint, and energy consumption. The distillation efficiency metric captures the trade-off between size reduction and performance retention, typically computed as the ratio of accuracy preservation to compression ratio.</p>
<pre><code class="language-python">class DistillationEvaluator:
    def __init__(self, teacher, student, test_loader, device='cuda'):
        self.teacher = teacher
        self.student = student
        self.test_loader = test_loader
        self.device = device
    
    def compute_compression_metrics(self):
        teacher_params = sum(p.numel() for p in self.teacher.parameters())
        student_params = sum(p.numel() for p in self.student.parameters())
        
        compression_ratio = teacher_params / student_params
        
        return {
            'teacher_parameters': teacher_params,
            'student_parameters': student_params,
            'compression_ratio': compression_ratio
        }
    
    def measure_inference_speed(self, num_samples=100):
        import time
        
        self.teacher.eval()
        self.student.eval()
        
        # Sample random inputs
        sample_inputs = []
        for batch in self.test_loader:
            sample_inputs.append(batch[0][:1].to(self.device))
            if len(sample_inputs) &gt;= num_samples:
                break
        
        # Teacher inference time
        teacher_times = []
        with torch.no_grad():
            for inputs in sample_inputs:
                start = time.perf_counter()
                _ = self.teacher(inputs)
                teacher_times.append(time.perf_counter() - start)
        
        # Student inference time
        student_times = []
        with torch.no_grad():
            for inputs in sample_inputs:
                start = time.perf_counter()
                _ = self.student(inputs)
                student_times.append(time.perf_counter() - start)
        
        speedup = np.mean(teacher_times) / np.mean(student_times)
        
        return {
            'teacher_latency_ms': np.mean(teacher_times) * 1000,
            'student_latency_ms': np.mean(student_times) * 1000,
            'speedup_factor': speedup
        }
    
    def compute_agreement_metrics(self):
        """
        Measure how well student predictions agree with teacher
        """
        self.teacher.eval()
        self.student.eval()
        
        total_kl = 0
        total_top1_agreement = 0
        total_samples = 0
        
        with torch.no_grad():
            for inputs, labels in self.test_loader:
                inputs = inputs.to(self.device)
                
                teacher_logits = self.teacher(inputs)
                student_logits = self.student(inputs)
                
                # KL divergence
                kl = F.kl_div(
                    F.log_softmax(student_logits, dim=-1),
                    F.softmax(teacher_logits, dim=-1),
                    reduction='batchmean'
                )
                total_kl += kl.item() * inputs.size(0)
                
                # Top-1 agreement
                teacher_preds = teacher_logits.argmax(dim=-1)
                student_preds = student_logits.argmax(dim=-1)
                agreement = (teacher_preds == student_preds).float().mean()
                total_top1_agreement += agreement.item() * inputs.size(0)
                
                total_samples += inputs.size(0)
        
        return {
            'average_kl_divergence': total_kl / total_samples,
            'top1_agreement': total_top1_agreement / total_samples
        }
    
    def full_evaluation(self):
        """
        Comprehensive evaluation of distillation quality
        """
        metrics = {}
        
        # Compression metrics
        metrics.update(self.compute_compression_metrics())
        
        # Speed metrics
        metrics.update(self.measure_inference_speed())
        
        # Agreement metrics
        metrics.update(self.compute_agreement_metrics())
        
        # Efficiency score: accuracy preservation per unit compression
        metrics['efficiency_score'] = (
            metrics['top1_agreement'] * metrics['compression_ratio']
        )
        
        return metrics
</code></pre>
<h2>Advanced Techniques</h2>
<p>Recent advances have introduced several techniques that go beyond traditional knowledge transfer. Online distillation trains teacher and student simultaneously, with the teacher continuously updating rather than staying frozen. This co-evolution can lead to mutually beneficial learning where the student's progress informs teacher updates. Self-distillation applies distillation to the same architecture, using ensemble predictions or differently initialized models as teachers, which can improve performance even without compression.</p>
<p>Born-again networks are an extreme form of self-distillation where a student with the same architecture as the teacher often beats the teacher's performance. This suggests distillation provides more than just compression—it offers an improved optimization landscape and implicit regularization. Applying born-again distillation iteratively, where each generation serves as the teacher for the next, can progressively improve performance until it converges.</p>
<pre><code class="language-python">class OnlineDistillation(nn.Module):
    def __init__(self, teacher, student, teacher_update_freq=10):
        super().__init__()
        self.teacher = teacher
        self.student = student
        self.teacher_update_freq = teacher_update_freq
        self.step_count = 0
        
        # Initialize teacher with student parameters
        self.teacher.load_state_dict(student.state_dict())
        
        # Separate optimizers for teacher and student
        self.teacher_optimizer = torch.optim.AdamW(
            teacher.parameters(), lr=1e-5
        )
        self.student_optimizer = torch.optim.AdamW(
            student.parameters(), lr=1e-4
        )
    
    def train_step(self, inputs, labels, temperature=3.0):
        # Student learning from current teacher
        with torch.no_grad():
            teacher_logits = self.teacher(inputs)
        
        student_logits = self.student(inputs)
        
        criterion = DistillationLoss(temperature=temperature)
        student_loss = criterion(student_logits, teacher_logits, labels)
        
        self.student_optimizer.zero_grad()
        student_loss.backward()
        self.student_optimizer.step()
        
        # Periodically update teacher
        self.step_count += 1
        if self.step_count % self.teacher_update_freq == 0:
            # Teacher learns from student's predictions
            with torch.no_grad():
                student_logits_detached = self.student(inputs)
            
            teacher_logits = self.teacher(inputs)
            teacher_loss = criterion(teacher_logits, student_logits_detached, labels)
            
            self.teacher_optimizer.zero_grad()
            teacher_loss.backward()
            self.teacher_optimizer.step()
        
        return student_loss.item()
</code></pre>
<h2>Summary</h2>
<p>Language model distillation enables knowledge transfer from large, expensive teachers to efficient students. The techniques include output distribution matching, intermediate representation alignment, attention transfer, and progressive multi-stage distillation. Success depends on temperature scaling, curriculum learning, optimization dynamics, and architecture mapping.</p>
<p>The field continues to develop with innovations in online distillation, cross-modal knowledge transfer, and task-specific compression. As language models grow larger, distillation becomes necessary for making state-of-the-art natural language understanding accessible to more people. The core insight is straightforward: knowledge encoded in billions of parameters can be compressed into millions while preserving the essential patterns that drive intelligent behavior.</p>
]]></content:encoded></item><item><title><![CDATA[Advanced Database Topics: Complete Guide]]></title><description><![CDATA[1. Distributed Databases
What is a Distributed Database?
A distributed database is a database stored across multiple physical locations, either on different machines in the same location or scattered across a network. The system appears as a single l...]]></description><link>https://arnavverma.hashnode.dev/advanced-database-topics-complete-guide</link><guid isPermaLink="true">https://arnavverma.hashnode.dev/advanced-database-topics-complete-guide</guid><category><![CDATA[Databases]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[General Programming]]></category><dc:creator><![CDATA[Arnav Verma]]></dc:creator><pubDate>Mon, 06 Oct 2025 18:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766215059422/09621ee4-28cd-450a-90a2-88a9563d6edf.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-1-distributed-databases">1. Distributed Databases</h2>
<h3 id="heading-what-is-a-distributed-database">What is a Distributed Database?</h3>
<p>A distributed database is a database stored across multiple physical locations, either on different machines in the same location or scattered across a network. The system appears as a single logical database to users.</p>
<h3 id="heading-key-concepts">Key Concepts</h3>
<h4 id="heading-distributed-vs-decentralized">Distributed vs Decentralized</h4>
<p><strong>Distributed</strong>: Multiple nodes, central coordination <strong>Decentralized</strong>: Multiple nodes, no central authority (blockchain)</p>
<h4 id="heading-homogeneous-vs-heterogeneous">Homogeneous vs Heterogeneous</h4>
<p><strong>Homogeneous</strong>: Same DBMS at all sites</p>
<ul>
<li><p>Easier to manage</p>
</li>
<li><p>Better integration</p>
</li>
<li><p>Example: All nodes running PostgreSQL 15</p>
</li>
</ul>
<p><strong>Heterogeneous</strong>: Different DBMSs at different sites</p>
<ul>
<li><p>More complex</p>
</li>
<li><p>Requires gateways/middleware</p>
</li>
<li><p>Example: Oracle + MySQL + MongoDB</p>
</li>
</ul>
<h3 id="heading-cap-theorem-revisited-in-depth">CAP Theorem (Revisited in Depth)</h3>
<p>Proven by Eric Brewer: A distributed system can provide at most 2 of 3 guarantees:</p>
<p><strong>Consistency (C)</strong>: All nodes see the same data simultaneously<br /><strong>Availability (A)</strong>: Every request receives a response (success or failure)<br /><strong>Partition Tolerance (P)</strong>: System continues despite network partitions</p>
<pre><code class="lang-plaintext">          Consistency
               / \
              /   \
             /     \
            / RDBMS \
           /________ \
          /    |      \
         /  CP | AP    \
        /      |        \
       /MongoDB|Cassandra\
      /________|_________ \
 Partition          Availability
 Tolerance
</code></pre>
<p><strong>Real-world choices</strong>:</p>
<ul>
<li><p><strong>CP Systems</strong> (Consistency + Partition Tolerance): MongoDB, HBase, Redis Cluster</p>
<ul>
<li><p>Sacrifice: May refuse writes during partitions</p>
</li>
<li><p>Use when: Strong consistency required (financial transactions)</p>
</li>
</ul>
</li>
<li><p><strong>AP Systems</strong> (Availability + Partition Tolerance): Cassandra, DynamoDB, CouchDB</p>
<ul>
<li><p>Sacrifice: Temporary inconsistency</p>
</li>
<li><p>Use when: Availability critical (social media feeds)</p>
</li>
</ul>
</li>
<li><p><strong>CA Systems</strong> (Consistency + Availability): Traditional RDBMS</p>
<ul>
<li><p>Sacrifice: Cannot handle network partitions</p>
</li>
<li><p>Reality: Single-node or master-slave within same datacenter</p>
</li>
</ul>
</li>
</ul>
<h3 id="heading-pacelc-theorem">PACELC Theorem</h3>
<p>Extension of CAP by Daniel Abadi:</p>
<p><strong>If Partition (P):</strong></p>
<ul>
<li>Choose Availability (A) or Consistency (C)</li>
</ul>
<p><strong>Else (E), when system is running normally:</strong></p>
<ul>
<li>Choose Latency (L) or Consistency (C)</li>
</ul>
<pre><code class="lang-plaintext">Examples:
- Cassandra: PA/EL (Availability during partition, Low latency otherwise)
- MongoDB: PC/EC (Consistency during partition, Consistency otherwise)
- DynamoDB: PA/EL
</code></pre>
<h3 id="heading-consistency-models">Consistency Models</h3>
<h4 id="heading-strong-consistency">Strong Consistency</h4>
<p>All nodes return the same value at any time. Reads reflect all completed writes.</p>
<pre><code class="lang-plaintext">Write(x=5) completes
↓
All subsequent Read(x) return 5
</code></pre>
<p><strong>Linearizability</strong>: Strongest consistency. Operations appear instantaneous.</p>
<p><strong>Sequential Consistency</strong>: Operations of each process appear in order, but global order may vary.</p>
<h4 id="heading-eventual-consistency">Eventual Consistency</h4>
<p>Given enough time without updates, all replicas converge to the same value.</p>
<pre><code class="lang-plaintext">Write(x=5) to Node1
↓
Read(x) from Node2 might return old value
↓
Eventually Read(x) from Node2 returns 5
</code></pre>
<p><strong>Levels</strong>:</p>
<ul>
<li><p><strong>Causal Consistency</strong>: Related operations maintain order</p>
</li>
<li><p><strong>Read-your-writes</strong>: A process always sees its own writes</p>
</li>
<li><p><strong>Session Consistency</strong>: Within a session, consistency guaranteed</p>
</li>
<li><p><strong>Monotonic Reads</strong>: Once a process reads a value, it won't read an older value</p>
</li>
</ul>
<h4 id="heading-example-scenarios">Example Scenarios</h4>
<p><strong>Banking (Strong Consistency Required)</strong>:</p>
<pre><code class="lang-plaintext">Account balance: $100
User1: Withdraw $60
User2: Withdraw $60

With eventual consistency: Both might succeed (overdraft!)
With strong consistency: Second transaction fails
</code></pre>
<p><strong>Social Media (Eventual Consistency Acceptable)</strong>:</p>
<pre><code class="lang-plaintext">User posts status update
Followers see update at different times (seconds apart)
Eventually everyone sees it - temporary inconsistency acceptable
</code></pre>
<h3 id="heading-distributed-transactions">Distributed Transactions</h3>
<h4 id="heading-two-phase-commit-2pc">Two-Phase Commit (2PC)</h4>
<p>Ensures atomicity across distributed nodes:</p>
<p><strong>Phase 1: Prepare</strong></p>
<pre><code class="lang-plaintext">Coordinator: "Can you commit?"
↓
Participant A: "Yes" (locks resources)
Participant B: "Yes" (locks resources)
Participant C: "No" (error occurred)
</code></pre>
<p><strong>Phase 2: Commit/Abort</strong></p>
<pre><code class="lang-plaintext">Coordinator receives all votes
↓
If all "Yes": Send "Commit"
If any "No": Send "Abort"
↓
Participants commit or rollback
</code></pre>
<p><strong>Problems</strong>:</p>
<ul>
<li><p><strong>Blocking</strong>: If coordinator fails, participants are stuck</p>
</li>
<li><p><strong>Performance</strong>: Multiple round trips</p>
</li>
<li><p><strong>Not partition tolerant</strong>: Network partition can cause indefinite blocking</p>
</li>
</ul>
<p><strong>Code Example (Conceptual)</strong>:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">two_phase_commit</span>(<span class="hljs-params">transaction</span>):</span>
    participants = transaction.get_participants()

    <span class="hljs-comment"># Phase 1: Prepare</span>
    votes = []
    <span class="hljs-keyword">for</span> participant <span class="hljs-keyword">in</span> participants:
        vote = participant.prepare(transaction)
        votes.append(vote)

    <span class="hljs-comment"># Phase 2: Commit or Abort</span>
    <span class="hljs-keyword">if</span> all(votes):
        <span class="hljs-keyword">for</span> participant <span class="hljs-keyword">in</span> participants:
            participant.commit(transaction)
        <span class="hljs-keyword">return</span> <span class="hljs-string">"COMMITTED"</span>
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">for</span> participant <span class="hljs-keyword">in</span> participants:
            participant.abort(transaction)
        <span class="hljs-keyword">return</span> <span class="hljs-string">"ABORTED"</span>
</code></pre>
<h4 id="heading-three-phase-commit-3pc">Three-Phase Commit (3PC)</h4>
<p>Adds a "pre-commit" phase to avoid blocking:</p>
<p><strong>Phases</strong>:</p>
<ol>
<li><p>Can-Commit: Query if commit possible</p>
</li>
<li><p>Pre-Commit: Record decision but don't commit</p>
</li>
<li><p>Do-Commit: Actually commit</p>
</li>
</ol>
<p>Less blocking than 2PC but still has issues with network partitions.</p>
<h4 id="heading-saga-pattern">Saga Pattern</h4>
<p>Alternative to distributed transactions for microservices:</p>
<p><strong>Forward Recovery</strong> (happy path):</p>
<pre><code class="lang-plaintext">Service A: Reserve inventory → Success
Service B: Process payment → Success  
Service C: Ship order → Success
</code></pre>
<p><strong>Backward Recovery</strong> (compensation):</p>
<pre><code class="lang-plaintext">Service A: Reserve inventory → Success
Service B: Process payment → Success
Service C: Ship order → FAIL
↓
Service B: Refund payment (compensating transaction)
Service A: Release inventory (compensating transaction)
</code></pre>
<p><strong>Implementation Patterns</strong>:</p>
<p><strong>Choreography</strong> (Event-driven):</p>
<pre><code class="lang-python"><span class="hljs-comment"># Service A</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">reserve_inventory</span>(<span class="hljs-params">order_id</span>):</span>
    <span class="hljs-comment"># Reserve items</span>
    publish_event(<span class="hljs-string">"InventoryReserved"</span>, order_id)

<span class="hljs-comment"># Service B listens for InventoryReserved</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">on_inventory_reserved</span>(<span class="hljs-params">order_id</span>):</span>
    <span class="hljs-keyword">try</span>:
        process_payment(order_id)
        publish_event(<span class="hljs-string">"PaymentProcessed"</span>, order_id)
    <span class="hljs-keyword">except</span> PaymentError:
        publish_event(<span class="hljs-string">"PaymentFailed"</span>, order_id)

<span class="hljs-comment"># Service A listens for PaymentFailed</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">on_payment_failed</span>(<span class="hljs-params">order_id</span>):</span>
    release_inventory(order_id)  <span class="hljs-comment"># Compensating action</span>
</code></pre>
<p><strong>Orchestration</strong> (Coordinator-based):</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OrderSaga</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">execute</span>(<span class="hljs-params">self, order</span>):</span>
        <span class="hljs-keyword">try</span>:
            <span class="hljs-comment"># Step 1</span>
            inventory_service.reserve(order.items)

            <span class="hljs-comment"># Step 2</span>
            payment_service.charge(order.total)

            <span class="hljs-comment"># Step 3</span>
            shipping_service.ship(order)

            <span class="hljs-keyword">return</span> <span class="hljs-string">"SUCCESS"</span>
        <span class="hljs-keyword">except</span> InventoryError:
            <span class="hljs-comment"># No compensation needed</span>
            <span class="hljs-keyword">return</span> <span class="hljs-string">"FAILED"</span>
        <span class="hljs-keyword">except</span> PaymentError:
            <span class="hljs-comment"># Compensate: release inventory</span>
            inventory_service.release(order.items)
            <span class="hljs-keyword">return</span> <span class="hljs-string">"FAILED"</span>
        <span class="hljs-keyword">except</span> ShippingError:
            <span class="hljs-comment"># Compensate: refund and release</span>
            payment_service.refund(order.total)
            inventory_service.release(order.items)
            <span class="hljs-keyword">return</span> <span class="hljs-string">"FAILED"</span>
</code></pre>
<h3 id="heading-consensus-algorithms">Consensus Algorithms</h3>
<p>How distributed systems agree on values despite failures.</p>
<h4 id="heading-paxos">Paxos</h4>
<p><strong>Roles</strong>:</p>
<ul>
<li><p><strong>Proposers</strong>: Propose values</p>
</li>
<li><p><strong>Acceptors</strong>: Accept or reject proposals</p>
</li>
<li><p><strong>Learners</strong>: Learn the chosen value</p>
</li>
</ul>
<p><strong>Phases</strong>:</p>
<ol>
<li><p><strong>Prepare</strong>: Proposer sends proposal number</p>
</li>
<li><p><strong>Promise</strong>: Acceptors promise not to accept lower proposals</p>
</li>
<li><p><strong>Accept</strong>: Proposer sends value</p>
</li>
<li><p><strong>Accepted</strong>: Acceptors accept value</p>
</li>
</ol>
<p><strong>Guarantees</strong>:</p>
<ul>
<li><p>Safety: Only one value chosen</p>
</li>
<li><p>Liveness: Eventually some value is chosen (under certain conditions)</p>
</li>
</ul>
<p><strong>Problem</strong>: Complex to implement correctly</p>
<h4 id="heading-raft">Raft</h4>
<p>Simpler alternative to Paxos, easier to understand and implement.</p>
<p><strong>Leader Election</strong>:</p>
<pre><code class="lang-plaintext">All servers start as Followers
↓
If timeout, Follower becomes Candidate
↓
Candidate requests votes from other servers
↓
If majority votes received, becomes Leader
↓
Leader sends heartbeats to maintain authority
</code></pre>
<p><strong>Log Replication</strong>:</p>
<pre><code class="lang-plaintext">Client sends command to Leader
↓
Leader appends to its log
↓
Leader replicates to Followers
↓
Once majority confirm, Leader commits
↓
Leader notifies Followers to commit
↓
Leader responds to client
</code></pre>
<p><strong>States</strong>:</p>
<ul>
<li><p><strong>Follower</strong>: Passive, receives updates</p>
</li>
<li><p><strong>Candidate</strong>: Requesting votes</p>
</li>
<li><p><strong>Leader</strong>: Handles all client requests</p>
</li>
</ul>
<p><strong>Used by</strong>: etcd, Consul, CockroachDB</p>
<p><strong>Code Example (Simplified Leader Election)</strong>:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RaftNode</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        self.state = <span class="hljs-string">"FOLLOWER"</span>
        self.current_term = <span class="hljs-number">0</span>
        self.voted_for = <span class="hljs-literal">None</span>
        self.election_timeout = random.randint(<span class="hljs-number">150</span>, <span class="hljs-number">300</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">start_election</span>(<span class="hljs-params">self</span>):</span>
        self.state = <span class="hljs-string">"CANDIDATE"</span>
        self.current_term += <span class="hljs-number">1</span>
        self.voted_for = self.id
        votes_received = <span class="hljs-number">1</span>  <span class="hljs-comment"># Vote for self</span>

        <span class="hljs-comment"># Request votes from other nodes</span>
        <span class="hljs-keyword">for</span> node <span class="hljs-keyword">in</span> self.cluster:
            response = node.request_vote(self.current_term, self.id)
            <span class="hljs-keyword">if</span> response.vote_granted:
                votes_received += <span class="hljs-number">1</span>

        <span class="hljs-comment"># Check if won election</span>
        <span class="hljs-keyword">if</span> votes_received &gt; len(self.cluster) / <span class="hljs-number">2</span>:
            self.state = <span class="hljs-string">"LEADER"</span>
            self.send_heartbeats()
</code></pre>
<h4 id="heading-byzantine-fault-tolerance-bft">Byzantine Fault Tolerance (BFT)</h4>
<p>Handles malicious/faulty nodes that send conflicting information.</p>
<p><strong>Used in</strong>: Blockchain systems</p>
<p><strong>Practical Byzantine Fault Tolerance (PBFT)</strong>:</p>
<ul>
<li><p>Can tolerate up to ⅓ faulty nodes</p>
</li>
<li><p>Requires 3f+1 nodes to tolerate f faults</p>
</li>
</ul>
<h3 id="heading-vector-clocks">Vector Clocks</h3>
<p>Track causality in distributed systems:</p>
<pre><code class="lang-plaintext">Process A: [A:1, B:0, C:0]  → Event: Update x=5
Process B: [A:1, B:1, C:0]  → Event: Read x (sees x=5)
Process C: [A:0, B:0, C:1]  → Event: Update x=7 (concurrent!)
</code></pre>
<p><strong>Determining Order</strong>:</p>
<ul>
<li><p>V1 &lt; V2: V1 happened before V2 (all components ≤, at least one &lt;)</p>
</li>
<li><p>V1 &gt; V2: V1 happened after V2</p>
</li>
<li><p>Neither: Events are concurrent</p>
</li>
</ul>
<p><strong>Example</strong>:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">VectorClock</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, process_id, num_processes</span>):</span>
        self.process_id = process_id
        self.clock = [<span class="hljs-number">0</span>] * num_processes

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">increment</span>(<span class="hljs-params">self</span>):</span>
        self.clock[self.process_id] += <span class="hljs-number">1</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span>(<span class="hljs-params">self, other_clock</span>):</span>
        <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(len(self.clock)):
            self.clock[i] = max(self.clock[i], other_clock[i])
        self.increment()

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">compare</span>(<span class="hljs-params">self, other</span>):</span>
        less = any(s &lt; o <span class="hljs-keyword">for</span> s, o <span class="hljs-keyword">in</span> zip(self.clock, other))
        greater = any(s &gt; o <span class="hljs-keyword">for</span> s, o <span class="hljs-keyword">in</span> zip(self.clock, other))

        <span class="hljs-keyword">if</span> less <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> greater:
            <span class="hljs-keyword">return</span> <span class="hljs-string">"BEFORE"</span>
        <span class="hljs-keyword">elif</span> greater <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> less:
            <span class="hljs-keyword">return</span> <span class="hljs-string">"AFTER"</span>
        <span class="hljs-keyword">elif</span> <span class="hljs-keyword">not</span> less <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> greater:
            <span class="hljs-keyword">return</span> <span class="hljs-string">"EQUAL"</span>
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">return</span> <span class="hljs-string">"CONCURRENT"</span>
</code></pre>
<h3 id="heading-quorum-based-replication">Quorum-Based Replication</h3>
<p><strong>Principle</strong>: Need majority agreement for operations.</p>
<p><strong>Parameters</strong>:</p>
<ul>
<li><p>N = Total replicas</p>
</li>
<li><p>R = Read quorum (minimum replicas for read)</p>
</li>
<li><p>W = Write quorum (minimum replicas for write)</p>
</li>
</ul>
<p><strong>Rules</strong>:</p>
<ul>
<li><p><strong>Strong Consistency</strong>: R + W &gt; N</p>
</li>
<li><p><strong>Eventual Consistency</strong>: R + W ≤ N</p>
</li>
</ul>
<p><strong>Examples</strong>:</p>
<pre><code class="lang-plaintext">N=5, R=3, W=3  → R+W=6 &gt; 5 (Strong consistency)
N=5, R=2, W=2  → R+W=4 &lt; 5 (Eventual consistency)
N=5, R=1, W=5  → Fast reads, slow writes
N=5, R=5, W=1  → Slow reads, fast writes
</code></pre>
<p><strong>Cassandra Example</strong>:</p>
<pre><code class="lang-plaintext">-- Write with quorum
INSERT INTO users (id, name) VALUES (1, 'John')
USING CONSISTENCY QUORUM;

-- Read with quorum
SELECT * FROM users WHERE id = 1
USING CONSISTENCY QUORUM;

-- Consistency levels:
-- ANY, ONE, TWO, THREE, QUORUM, LOCAL_QUORUM, EACH_QUORUM, ALL
</code></pre>
<h3 id="heading-conflict-resolution">Conflict Resolution</h3>
<p>When replicas diverge, conflicts must be resolved.</p>
<h4 id="heading-last-write-wins-lww">Last-Write-Wins (LWW)</h4>
<p>Simplest strategy: Use timestamp.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">resolve_conflict</span>(<span class="hljs-params">value1, value2</span>):</span>
    <span class="hljs-keyword">if</span> value1.timestamp &gt; value2.timestamp:
        <span class="hljs-keyword">return</span> value1
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">return</span> value2
</code></pre>
<p><strong>Problems</strong>:</p>
<ul>
<li><p>Clock synchronization issues</p>
</li>
<li><p>Concurrent writes with same timestamp</p>
</li>
<li><p>Data loss possible</p>
</li>
</ul>
<h4 id="heading-version-vectors-dotted-version-vectors">Version Vectors / Dotted Version Vectors</h4>
<p>Track which replica made each update:</p>
<pre><code class="lang-python">{
    <span class="hljs-string">"value"</span>: <span class="hljs-string">"John"</span>,
    <span class="hljs-string">"version"</span>: {
        <span class="hljs-string">"replica_A"</span>: <span class="hljs-number">3</span>,
        <span class="hljs-string">"replica_B"</span>: <span class="hljs-number">2</span>,
        <span class="hljs-string">"replica_C"</span>: <span class="hljs-number">1</span>
    }
}
</code></pre>
<h4 id="heading-application-level-resolution">Application-Level Resolution</h4>
<p>Let application decide:</p>
<pre><code class="lang-python"><span class="hljs-comment"># E-commerce cart example</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">merge_carts</span>(<span class="hljs-params">cart1, cart2</span>):</span>
    <span class="hljs-comment"># Union of items from both carts</span>
    merged = {}
    <span class="hljs-keyword">for</span> item, qty <span class="hljs-keyword">in</span> cart1.items():
        merged[item] = qty
    <span class="hljs-keyword">for</span> item, qty <span class="hljs-keyword">in</span> cart2.items():
        merged[item] = merged.get(item, <span class="hljs-number">0</span>) + qty
    <span class="hljs-keyword">return</span> merged
</code></pre>
<h4 id="heading-crdts-conflict-free-replicated-data-types">CRDTs (Conflict-Free Replicated Data Types)</h4>
<p>Data structures that automatically resolve conflicts:</p>
<p><strong>Types</strong>:</p>
<p><strong>G-Counter</strong> (Grow-only Counter):</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GCounter</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, replica_id, num_replicas</span>):</span>
        self.replica_id = replica_id
        self.counts = [<span class="hljs-number">0</span>] * num_replicas

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">increment</span>(<span class="hljs-params">self</span>):</span>
        self.counts[self.replica_id] += <span class="hljs-number">1</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">value</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> sum(self.counts)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">merge</span>(<span class="hljs-params">self, other</span>):</span>
        <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(len(self.counts)):
            self.counts[i] = max(self.counts[i], other.counts[i])
</code></pre>
<p><strong>PN-Counter</strong> (Positive-Negative Counter):</p>
<ul>
<li><p>Two G-Counters: increments and decrements</p>
</li>
<li><p>Value = increments - decrements</p>
</li>
</ul>
<p><strong>G-Set</strong> (Grow-only Set):</p>
<ul>
<li><p>Can only add elements</p>
</li>
<li><p>Merge = union</p>
</li>
</ul>
<p><strong>LWW-Set</strong> (Last-Write-Wins Set):</p>
<ul>
<li><p>Add and remove with timestamps</p>
</li>
<li><p>Resolve conflicts using LWW</p>
</li>
</ul>
<p><strong>OR-Set</strong> (Observed-Remove Set):</p>
<ul>
<li><p>Each add gets unique tag</p>
</li>
<li><p>Remove specific tags</p>
</li>
<li><p>Preserves adds that weren't observed</p>
</li>
</ul>
<p><strong>Riak Example</strong>:</p>
<pre><code class="lang-erlang"><span class="hljs-comment">%% Riak uses CRDTs for distributed counters and sets</span>
<span class="hljs-comment">%% Increment counter</span>
riakc_pb_socket:counter_incr(Pid, Bucket, Key, <span class="hljs-number">1</span>).

<span class="hljs-comment">%% Get counter value (eventually consistent)</span>
{ok, Value} = riakc_pb_socket:counter_val(Pid, Bucket, Key).
</code></pre>
<h3 id="heading-distributed-databases-in-production">Distributed Databases in Production</h3>
<h4 id="heading-apache-cassandra">Apache Cassandra</h4>
<p><strong>Architecture</strong>:</p>
<ul>
<li><p>Peer-to-peer (no master)</p>
</li>
<li><p>Ring topology</p>
</li>
<li><p>Consistent hashing for data distribution</p>
</li>
<li><p>Tunable consistency</p>
</li>
</ul>
<p><strong>Data Model</strong>:</p>
<pre><code class="lang-plaintext">CREATE KEYSPACE myapp WITH replication = {
    'class': 'NetworkTopologyStrategy',
    'datacenter1': 3,
    'datacenter2': 2
};

CREATE TABLE users (
    user_id UUID PRIMARY KEY,
    username TEXT,
    email TEXT,
    created_at TIMESTAMP
);

-- Wide-row model
CREATE TABLE user_events (
    user_id UUID,
    event_time TIMESTAMP,
    event_type TEXT,
    event_data TEXT,
    PRIMARY KEY (user_id, event_time)
) WITH CLUSTERING ORDER BY (event_time DESC);
</code></pre>
<p><strong>Write Path</strong>:</p>
<ol>
<li><p>Write to commit log (sequential, durable)</p>
</li>
<li><p>Write to memtable (in-memory)</p>
</li>
<li><p>When memtable full, flush to SSTable (on disk)</p>
</li>
<li><p>Background compaction merges SSTables</p>
</li>
</ol>
<p><strong>Read Path</strong>:</p>
<ol>
<li><p>Check memtable</p>
</li>
<li><p>Check bloom filters for SSTables</p>
</li>
<li><p>Read from SSTables</p>
</li>
<li><p>Merge results</p>
</li>
</ol>
<p><strong>Tuning</strong>:</p>
<pre><code class="lang-plaintext">-- Replication factor
ALTER KEYSPACE myapp WITH replication = {
    'class': 'NetworkTopologyStrategy',
    'DC1': 3
};

-- Consistency level (per query)
SELECT * FROM users USING CONSISTENCY LOCAL_QUORUM;

-- Compaction strategy
ALTER TABLE users WITH compaction = {
    'class': 'LeveledCompactionStrategy'
};
</code></pre>
<p><strong>Use Cases</strong>:</p>
<ul>
<li><p>Time-series data</p>
</li>
<li><p>High write throughput</p>
</li>
<li><p>Always-on availability</p>
</li>
<li><p>Examples: Netflix, Apple, Instagram</p>
</li>
</ul>
<h4 id="heading-google-spanner">Google Spanner</h4>
<p><strong>Key Innovation</strong>: TrueTime API for global clock synchronization</p>
<p><strong>Features</strong>:</p>
<ul>
<li><p>Global distribution</p>
</li>
<li><p>Strong consistency</p>
</li>
<li><p>SQL interface</p>
</li>
<li><p>Horizontal scaling</p>
</li>
</ul>
<p><strong>TrueTime</strong>:</p>
<pre><code class="lang-plaintext">TT.now() returns interval [earliest, latest]
Certainty = latest - earliest (typically &lt; 10ms)

Used for:
- Assigning timestamps to transactions
- Ensuring external consistency
</code></pre>
<p><strong>Architecture</strong>:</p>
<ul>
<li><p>Zones (roughly equivalent to datacenter)</p>
</li>
<li><p>Paxos group per zone</p>
</li>
<li><p>2PC for cross-Paxos-group transactions</p>
</li>
</ul>
<p><strong>SQL Example</strong>:</p>
<pre><code class="lang-sql"><span class="hljs-comment">-- Interleaved tables (parent-child co-location)</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> Artists (
    ArtistId INT64 <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    <span class="hljs-keyword">Name</span> <span class="hljs-keyword">STRING</span>(<span class="hljs-number">1024</span>)
) PRIMARY <span class="hljs-keyword">KEY</span> (ArtistId);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> Albums (
    ArtistId INT64 <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    AlbumId INT64 <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    Title <span class="hljs-keyword">STRING</span>(<span class="hljs-number">1024</span>)
) PRIMARY <span class="hljs-keyword">KEY</span> (ArtistId, AlbumId),
  INTERLEAVE <span class="hljs-keyword">IN</span> <span class="hljs-keyword">PARENT</span> Artists <span class="hljs-keyword">ON</span> <span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">CASCADE</span>;
</code></pre>
<h4 id="heading-cockroachdb">CockroachDB</h4>
<p><strong>Description</strong>: Open-source, distributed SQL database inspired by Spanner</p>
<p><strong>Features</strong>:</p>
<ul>
<li><p>ACID transactions</p>
</li>
<li><p>PostgreSQL wire protocol compatibility</p>
</li>
<li><p>Automatic rebalancing</p>
</li>
<li><p>Geo-partitioning</p>
</li>
</ul>
<p><strong>Architecture</strong>:</p>
<pre><code class="lang-plaintext">Monolithic sorted map (key-value store)
↓
Divided into ranges (~64MB each)
↓
Ranges replicated via Raft
↓
SQL layer on top
</code></pre>
<p><strong>Example</strong>:</p>
<pre><code class="lang-sql"><span class="hljs-comment">-- Create table</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-keyword">users</span> (
    <span class="hljs-keyword">id</span> <span class="hljs-keyword">UUID</span> PRIMARY <span class="hljs-keyword">KEY</span> <span class="hljs-keyword">DEFAULT</span> gen_random_uuid(),
    email <span class="hljs-keyword">STRING</span> <span class="hljs-keyword">UNIQUE</span>,
    balance <span class="hljs-built_in">DECIMAL</span>(<span class="hljs-number">10</span>,<span class="hljs-number">2</span>)
);

<span class="hljs-comment">-- Transaction (fully ACID)</span>
<span class="hljs-keyword">BEGIN</span>;
<span class="hljs-keyword">UPDATE</span> <span class="hljs-keyword">users</span> <span class="hljs-keyword">SET</span> balance = balance - <span class="hljs-number">100</span> <span class="hljs-keyword">WHERE</span> email = <span class="hljs-string">'alice@example.com'</span>;
<span class="hljs-keyword">UPDATE</span> <span class="hljs-keyword">users</span> <span class="hljs-keyword">SET</span> balance = balance + <span class="hljs-number">100</span> <span class="hljs-keyword">WHERE</span> email = <span class="hljs-string">'bob@example.com'</span>;
<span class="hljs-keyword">COMMIT</span>;

<span class="hljs-comment">-- Geo-partitioning</span>
<span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-keyword">users</span> <span class="hljs-keyword">PARTITION</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">LIST</span> (region) (
    <span class="hljs-keyword">PARTITION</span> us <span class="hljs-keyword">VALUES</span> <span class="hljs-keyword">IN</span> (<span class="hljs-string">'us-east'</span>, <span class="hljs-string">'us-west'</span>),
    <span class="hljs-keyword">PARTITION</span> eu <span class="hljs-keyword">VALUES</span> <span class="hljs-keyword">IN</span> (<span class="hljs-string">'eu-west'</span>, <span class="hljs-string">'eu-central'</span>)
);

<span class="hljs-comment">-- Pin partitions to localities</span>
<span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">PARTITION</span> us <span class="hljs-keyword">OF</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-keyword">users</span> 
    CONFIGURE ZONE <span class="hljs-keyword">USING</span> <span class="hljs-keyword">constraints</span> = <span class="hljs-string">'[+region=us]'</span>;
</code></pre>
<hr />
<h2 id="heading-2-time-series-databases">2. Time-Series Databases</h2>
<h3 id="heading-what-is-time-series-data">What is Time-Series Data?</h3>
<p>Data points indexed by time, typically collected at regular intervals.</p>
<p><strong>Characteristics</strong>:</p>
<ul>
<li><p>Time is primary index</p>
</li>
<li><p>Append-heavy (rarely update/delete old data)</p>
</li>
<li><p>Range queries common</p>
</li>
<li><p>Aggregations over time windows</p>
</li>
<li><p>High write throughput</p>
</li>
</ul>
<p><strong>Examples</strong>:</p>
<ul>
<li><p>Server metrics (CPU, memory, disk)</p>
</li>
<li><p>IoT sensor data</p>
</li>
<li><p>Financial market data</p>
</li>
<li><p>Application logs</p>
</li>
<li><p>User analytics events</p>
</li>
</ul>
<h3 id="heading-why-specialized-time-series-databases">Why Specialized Time-Series Databases?</h3>
<p><strong>Relational databases struggle with</strong>:</p>
<ul>
<li><p>High insert rates (millions per second)</p>
</li>
<li><p>Large data volumes</p>
</li>
<li><p>Time-based queries and aggregations</p>
</li>
<li><p>Data retention and downsampling</p>
</li>
</ul>
<p><strong>TSDB optimizations</strong>:</p>
<ul>
<li><p>Column-oriented storage</p>
</li>
<li><p>Compression algorithms for time-series</p>
</li>
<li><p>Efficient time-range indexing</p>
</li>
<li><p>Built-in downsampling</p>
</li>
<li><p>Retention policies</p>
</li>
</ul>
<h3 id="heading-core-concepts">Core Concepts</h3>
<h4 id="heading-data-model">Data Model</h4>
<p><strong>Measurement</strong>: Like a table <strong>Tags</strong>: Indexed metadata (dimensions) <strong>Fields</strong>: Actual values (metrics) <strong>Timestamp</strong>: When the data point occurred</p>
<pre><code class="lang-plaintext">Example (InfluxDB format):
measurement=cpu_usage,host=server1,region=us-west value=75.5 1640000000000000000
     ↑            ↑                              ↑              ↑
 measurement    tags (indexed)               fields        timestamp
</code></pre>
<h4 id="heading-retention-policies">Retention Policies</h4>
<p>Automatically delete old data:</p>
<pre><code class="lang-plaintext">7 days at full resolution
30 days at 5-minute resolution
1 year at 1-hour resolution
Forever at 1-day resolution
</code></pre>
<h4 id="heading-downsampling">Downsampling</h4>
<p>Aggregate high-resolution data into lower resolutions:</p>
<pre><code class="lang-plaintext">Raw data (1-second): [65, 70, 75, 72, 68, 71, ...]
↓
5-minute average: [70.2, 69.8, 71.5, ...]
↓
1-hour average: [70.5, 71.2, ...]
</code></pre>
<h4 id="heading-continuous-queries">Continuous Queries</h4>
<p>Automatically compute rollups:</p>
<pre><code class="lang-sql"><span class="hljs-comment">-- InfluxDB</span>
<span class="hljs-keyword">CREATE</span> CONTINUOUS <span class="hljs-keyword">QUERY</span> <span class="hljs-string">"cq_30m"</span> <span class="hljs-keyword">ON</span> <span class="hljs-string">"mydb"</span>
<span class="hljs-keyword">BEGIN</span>
  <span class="hljs-keyword">SELECT</span> mean(<span class="hljs-string">"value"</span>) 
  <span class="hljs-keyword">INTO</span> <span class="hljs-string">"average_30m"</span>
  <span class="hljs-keyword">FROM</span> <span class="hljs-string">"cpu_usage"</span>
  <span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-built_in">time</span>(<span class="hljs-number">30</span>m), *
<span class="hljs-keyword">END</span>
</code></pre>
<h3 id="heading-popular-time-series-databases">Popular Time-Series Databases</h3>
<h4 id="heading-influxdb">InfluxDB</h4>
<p><strong>Architecture</strong>:</p>
<ul>
<li><p>Written in Go</p>
</li>
<li><p>TSM (Time-Structured Merge tree) storage engine</p>
</li>
<li><p>InfluxQL query language (SQL-like)</p>
</li>
<li><p>Flux query language (more powerful)</p>
</li>
</ul>
<p><strong>Data Model</strong>:</p>
<pre><code class="lang-plaintext">// Line Protocol format
weather,location=us-midwest temperature=82,humidity=65 1465839830100400200
</code></pre>
<p><strong>Query Examples</strong>:</p>
<pre><code class="lang-sql"><span class="hljs-comment">-- InfluxQL</span>
<span class="hljs-keyword">SELECT</span> mean(<span class="hljs-string">"temperature"</span>) 
<span class="hljs-keyword">FROM</span> <span class="hljs-string">"weather"</span> 
<span class="hljs-keyword">WHERE</span> <span class="hljs-built_in">time</span> &gt; <span class="hljs-keyword">now</span>() - <span class="hljs-number">1</span>h 
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-built_in">time</span>(<span class="hljs-number">10</span>m), <span class="hljs-string">"location"</span>

<span class="hljs-comment">-- Flux</span>
<span class="hljs-keyword">from</span>(<span class="hljs-keyword">bucket</span>: <span class="hljs-string">"weather"</span>)
  |&gt; <span class="hljs-keyword">range</span>(<span class="hljs-keyword">start</span>: <span class="hljs-number">-1</span>h)
  |&gt; filter(fn: (r) =&gt; r._measurement == <span class="hljs-string">"temperature"</span>)
  |&gt; aggregateWindow(every: <span class="hljs-number">10</span>m, fn: mean)
  |&gt; <span class="hljs-keyword">group</span>(<span class="hljs-keyword">columns</span>: [<span class="hljs-string">"location"</span>])
</code></pre>
<p><strong>Retention Policy</strong>:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">RETENTION</span> <span class="hljs-keyword">POLICY</span> <span class="hljs-string">"one_week"</span> 
<span class="hljs-keyword">ON</span> <span class="hljs-string">"mydb"</span> 
<span class="hljs-keyword">DURATION</span> <span class="hljs-number">7</span>d 
<span class="hljs-keyword">REPLICATION</span> <span class="hljs-number">1</span> 
<span class="hljs-keyword">DEFAULT</span>
</code></pre>
<p><strong>Continuous Query</strong>:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> CONTINUOUS <span class="hljs-keyword">QUERY</span> <span class="hljs-string">"average_temperature"</span> 
<span class="hljs-keyword">ON</span> <span class="hljs-string">"mydb"</span>
<span class="hljs-keyword">BEGIN</span>
  <span class="hljs-keyword">SELECT</span> mean(<span class="hljs-string">"temperature"</span>) 
  <span class="hljs-keyword">INTO</span> <span class="hljs-string">"average_temperature"</span>
  <span class="hljs-keyword">FROM</span> <span class="hljs-string">"weather"</span>
  <span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-built_in">time</span>(<span class="hljs-number">1</span>h), *
<span class="hljs-keyword">END</span>
</code></pre>
<h4 id="heading-prometheus">Prometheus</h4>
<p><strong>Architecture</strong>:</p>
<ul>
<li><p>Pull-based model (scrapes metrics from targets)</p>
</li>
<li><p>Local storage (not distributed)</p>
</li>
<li><p>PromQL query language</p>
</li>
<li><p>Integrates with Grafana for visualization</p>
</li>
</ul>
<p><strong>Data Model</strong>:</p>
<pre><code class="lang-plaintext">Metric name + Labels = Time series identifier

http_requests_total{method="GET", endpoint="/api", status="200"} 1234
                    ↑                                           ↑
                  labels                                      value
</code></pre>
<p><strong>Scrape Configuration</strong>:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">scrape_configs:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">job_name:</span> <span class="hljs-string">'web_servers'</span>
    <span class="hljs-attr">scrape_interval:</span> <span class="hljs-string">15s</span>
    <span class="hljs-attr">static_configs:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">targets:</span> [<span class="hljs-string">'localhost:9090'</span>, <span class="hljs-string">'server1:9090'</span>, <span class="hljs-string">'server2:9090'</span>]
        <span class="hljs-attr">labels:</span>
          <span class="hljs-attr">environment:</span> <span class="hljs-string">'production'</span>
</code></pre>
<p><strong>PromQL Examples</strong>:</p>
<pre><code class="lang-plaintext"># Current CPU usage
node_cpu_seconds_total{mode="idle"}

# Rate of HTTP requests over last 5 minutes
rate(http_requests_total[5m])

# 95th percentile response time
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))

# Aggregation
sum(rate(http_requests_total[5m])) by (endpoint)

# Alerts
ALERT HighCPU
  IF node_cpu_usage &gt; 0.9
  FOR 10m
  LABELS { severity="warning" }
  ANNOTATIONS {
    summary = "High CPU usage detected",
    description = "CPU usage is {{ $value }}%"
  }
</code></pre>
<p><strong>Exporters</strong>:</p>
<ul>
<li><p>node_exporter: System metrics</p>
</li>
<li><p>mysql_exporter: MySQL metrics</p>
</li>
<li><p>blackbox_exporter: Endpoint monitoring</p>
</li>
<li><p>Custom exporters: Application-specific metrics</p>
</li>
</ul>
<p><strong>Instrumentation Example</strong> (Go):</p>
<pre><code class="lang-go"><span class="hljs-keyword">import</span> (
    <span class="hljs-string">"github.com/prometheus/client_golang/prometheus"</span>
    <span class="hljs-string">"github.com/prometheus/client_golang/prometheus/promhttp"</span>
)

<span class="hljs-keyword">var</span> (
    httpRequestsTotal = prometheus.NewCounterVec(
        prometheus.CounterOpts{
            Name: <span class="hljs-string">"http_requests_total"</span>,
            Help: <span class="hljs-string">"Total number of HTTP requests"</span>,
        },
        []<span class="hljs-keyword">string</span>{<span class="hljs-string">"method"</span>, <span class="hljs-string">"endpoint"</span>, <span class="hljs-string">"status"</span>},
    )

    httpRequestDuration = prometheus.NewHistogramVec(
        prometheus.HistogramOpts{
            Name: <span class="hljs-string">"http_request_duration_seconds"</span>,
            Help: <span class="hljs-string">"HTTP request latencies in seconds"</span>,
            Buckets: prometheus.DefBuckets,
        },
        []<span class="hljs-keyword">string</span>{<span class="hljs-string">"method"</span>, <span class="hljs-string">"endpoint"</span>},
    )
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">init</span><span class="hljs-params">()</span></span> {
    prometheus.MustRegister(httpRequestsTotal)
    prometheus.MustRegister(httpRequestDuration)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">handler</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    start := time.Now()

    <span class="hljs-comment">// Handle request...</span>

    duration := time.Since(start).Seconds()
    httpRequestsTotal.WithLabelValues(r.Method, r.URL.Path, <span class="hljs-string">"200"</span>).Inc()
    httpRequestDuration.WithLabelValues(r.Method, r.URL.Path).Observe(duration)
}

<span class="hljs-comment">// Expose metrics endpoint</span>
http.Handle(<span class="hljs-string">"/metrics"</span>, promhttp.Handler())
</code></pre>
<h4 id="heading-timescaledb">TimescaleDB</h4>
<p><strong>Description</strong>: PostgreSQL extension for time-series data</p>
<p><strong>Key Feature</strong>: Combines SQL with time-series optimizations</p>
<p><strong>Hypertables</strong>:</p>
<ul>
<li><p>Automatic partitioning by time</p>
</li>
<li><p>Transparent to user (looks like regular table)</p>
</li>
</ul>
<pre><code class="lang-sql"><span class="hljs-comment">-- Create regular table</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> conditions (
    <span class="hljs-built_in">time</span> TIMESTAMPTZ <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    location <span class="hljs-built_in">TEXT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    temperature <span class="hljs-keyword">DOUBLE</span> <span class="hljs-keyword">PRECISION</span> <span class="hljs-literal">NULL</span>,
    humidity <span class="hljs-keyword">DOUBLE</span> <span class="hljs-keyword">PRECISION</span> <span class="hljs-literal">NULL</span>
);

<span class="hljs-comment">-- Convert to hypertable</span>
<span class="hljs-keyword">SELECT</span> create_hypertable(<span class="hljs-string">'conditions'</span>, <span class="hljs-string">'time'</span>);

<span class="hljs-comment">-- Insert data (same as regular PostgreSQL)</span>
<span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> conditions <span class="hljs-keyword">VALUES</span>
    (<span class="hljs-keyword">NOW</span>(), <span class="hljs-string">'office'</span>, <span class="hljs-number">70.5</span>, <span class="hljs-number">45.2</span>),
    (<span class="hljs-keyword">NOW</span>(), <span class="hljs-string">'garage'</span>, <span class="hljs-number">65.3</span>, <span class="hljs-number">55.8</span>);

<span class="hljs-comment">-- Query (standard SQL)</span>
<span class="hljs-keyword">SELECT</span> time_bucket(<span class="hljs-string">'5 minutes'</span>, <span class="hljs-built_in">time</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">bucket</span>,
       location,
       <span class="hljs-keyword">avg</span>(temperature) <span class="hljs-keyword">AS</span> avg_temp
<span class="hljs-keyword">FROM</span> conditions
<span class="hljs-keyword">WHERE</span> <span class="hljs-built_in">time</span> &gt; <span class="hljs-keyword">NOW</span>() - <span class="hljs-built_in">INTERVAL</span> <span class="hljs-string">'1 day'</span>
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">bucket</span>, location
<span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">bucket</span> <span class="hljs-keyword">DESC</span>;
</code></pre>
<p><strong>Continuous Aggregates</strong> (Materialized views for time-series):</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">MATERIALIZED</span> <span class="hljs-keyword">VIEW</span> conditions_summary_hourly
<span class="hljs-keyword">WITH</span> (timescaledb.continuous) <span class="hljs-keyword">AS</span>
<span class="hljs-keyword">SELECT</span> time_bucket(<span class="hljs-string">'1 hour'</span>, <span class="hljs-built_in">time</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">bucket</span>,
       location,
       <span class="hljs-keyword">avg</span>(temperature) <span class="hljs-keyword">AS</span> avg_temp,
       <span class="hljs-keyword">max</span>(temperature) <span class="hljs-keyword">AS</span> max_temp,
       <span class="hljs-keyword">min</span>(temperature) <span class="hljs-keyword">AS</span> min_temp
<span class="hljs-keyword">FROM</span> conditions
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">bucket</span>, location;

<span class="hljs-comment">-- Refresh policy</span>
<span class="hljs-keyword">SELECT</span> add_continuous_aggregate_policy(<span class="hljs-string">'conditions_summary_hourly'</span>,
    start_offset =&gt; <span class="hljs-built_in">INTERVAL</span> <span class="hljs-string">'3 hours'</span>,
    end_offset =&gt; <span class="hljs-built_in">INTERVAL</span> <span class="hljs-string">'1 hour'</span>,
    schedule_interval =&gt; <span class="hljs-built_in">INTERVAL</span> <span class="hljs-string">'1 hour'</span>);
</code></pre>
<p><strong>Compression</strong>:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> conditions <span class="hljs-keyword">SET</span> (
    timescaledb.compress,
    timescaledb.compress_segmentby = <span class="hljs-string">'location'</span>
);

<span class="hljs-keyword">SELECT</span> add_compression_policy(<span class="hljs-string">'conditions'</span>, <span class="hljs-built_in">INTERVAL</span> <span class="hljs-string">'7 days'</span>);
</code></pre>
<p><strong>Retention</strong>:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> add_retention_policy(<span class="hljs-string">'conditions'</span>, <span class="hljs-built_in">INTERVAL</span> <span class="hljs-string">'1 year'</span>);
</code></pre>
<h4 id="heading-apache-druid">Apache Druid</h4>
<p><strong>Description</strong>: Real-time analytics database for high-concurrency queries</p>
<p><strong>Use Cases</strong>:</p>
<ul>
<li><p>Clickstream analytics</p>
</li>
<li><p>Network telemetry</p>
</li>
<li><p>Server metrics</p>
</li>
<li><p>Application performance monitoring</p>
</li>
</ul>
<p><strong>Architecture</strong>:</p>
<ul>
<li><p>Broker nodes: Route queries</p>
</li>
<li><p>Historical nodes: Store historical data</p>
</li>
<li><p>Real-time nodes: Ingest recent data</p>
</li>
<li><p>Coordinator nodes: Manage data placement</p>
</li>
</ul>
<p><strong>Data Model</strong>:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"timestamp"</span>: <span class="hljs-string">"2024-01-15T10:30:00Z"</span>,
  <span class="hljs-attr">"dimensions"</span>: {
    <span class="hljs-attr">"page"</span>: <span class="hljs-string">"/products"</span>,
    <span class="hljs-attr">"country"</span>: <span class="hljs-string">"US"</span>,
    <span class="hljs-attr">"device"</span>: <span class="hljs-string">"mobile"</span>
  },
  <span class="hljs-attr">"metrics"</span>: {
    <span class="hljs-attr">"views"</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">"clicks"</span>: <span class="hljs-number">0</span>,
    <span class="hljs-attr">"revenue"</span>: <span class="hljs-number">0.0</span>
  }
}
</code></pre>
<p><strong>Rollup</strong>:</p>
<pre><code class="lang-json"><span class="hljs-comment">// Automatic rollup at ingestion</span>
{
  <span class="hljs-attr">"timestamp"</span>: <span class="hljs-string">"2024-01-15T10:30:00Z"</span>,
  <span class="hljs-attr">"page"</span>: <span class="hljs-string">"/products"</span>,
  <span class="hljs-attr">"country"</span>: <span class="hljs-string">"US"</span>,
  <span class="hljs-attr">"device"</span>: <span class="hljs-string">"mobile"</span>,
  <span class="hljs-attr">"views"</span>: <span class="hljs-number">150</span>,      <span class="hljs-comment">// sum of all rows in this bucket</span>
  <span class="hljs-attr">"clicks"</span>: <span class="hljs-number">45</span>,      <span class="hljs-comment">// sum</span>
  <span class="hljs-attr">"revenue"</span>: <span class="hljs-number">1250.50</span> <span class="hljs-comment">// sum</span>
}
</code></pre>
<p><strong>Query Example</strong>:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"queryType"</span>: <span class="hljs-string">"timeseries"</span>,
  <span class="hljs-attr">"dataSource"</span>: <span class="hljs-string">"pageviews"</span>,
  <span class="hljs-attr">"granularity"</span>: <span class="hljs-string">"hour"</span>,
  <span class="hljs-attr">"intervals"</span>: [<span class="hljs-string">"2024-01-15/2024-01-16"</span>],
  <span class="hljs-attr">"aggregations"</span>: [
    {<span class="hljs-attr">"type"</span>: <span class="hljs-string">"longSum"</span>, <span class="hljs-attr">"name"</span>: <span class="hljs-string">"total_views"</span>, <span class="hljs-attr">"fieldName"</span>: <span class="hljs-string">"views"</span>},
    {<span class="hljs-attr">"type"</span>: <span class="hljs-string">"doubleSum"</span>, <span class="hljs-attr">"name"</span>: <span class="hljs-string">"total_revenue"</span>, <span class="hljs-attr">"fieldName"</span>: <span class="hljs-string">"revenue"</span>}
  ],
  <span class="hljs-attr">"filter"</span>: {
    <span class="hljs-attr">"type"</span>: <span class="hljs-string">"selector"</span>,
    <span class="hljs-attr">"dimension"</span>: <span class="hljs-string">"country"</span>,
    <span class="hljs-attr">"value"</span>: <span class="hljs-string">"US"</span>
  }
}
</code></pre>
<h3 id="heading-time-series-patterns">Time-Series Patterns</h3>
<h4 id="heading-metrics-collection-pipeline">Metrics Collection Pipeline</h4>
<pre><code class="lang-plaintext">Application
    ↓
StatsD/Telegraf (Agent)
    ↓
InfluxDB/Prometheus (Storage)
    ↓
Grafana (Visualization)
    ↓
AlertManager (Alerting)
</code></pre>
<h4 id="heading-downsampling-strategy">Downsampling Strategy</h4>
<pre><code class="lang-python"><span class="hljs-comment"># Raw data retention: 7 days</span>
<span class="hljs-comment"># Hourly aggregates: 90 days</span>
<span class="hljs-comment"># Daily aggregates: 2 years</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TimeSeriesManager</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">downsample</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-comment"># Raw → Hourly</span>
        <span class="hljs-keyword">for</span> day <span class="hljs-keyword">in</span> range(<span class="hljs-number">7</span>, <span class="hljs-number">90</span>):
            self.aggregate(
                source=<span class="hljs-string">"raw_metrics"</span>,
                dest=<span class="hljs-string">"hourly_metrics"</span>,
                window=<span class="hljs-string">"1h"</span>,
                start=day
            )

        <span class="hljs-comment"># Hourly → Daily</span>
        <span class="hljs-keyword">for</span> day <span class="hljs-keyword">in</span> range(<span class="hljs-number">90</span>, <span class="hljs-number">730</span>):
            self.aggregate(
                source=<span class="hljs-string">"hourly_metrics"</span>,
                dest=<span class="hljs-string">"daily_metrics"</span>,
                window=<span class="hljs-string">"1d"</span>,
                start=day
            )
</code></pre>
<h4 id="heading-multi-resolution-queries">Multi-Resolution Queries</h4>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">query_metrics</span>(<span class="hljs-params">start, end</span>):</span>
    duration = end - start

    <span class="hljs-keyword">if</span> duration &lt;= timedelta(hours=<span class="hljs-number">6</span>):
        <span class="hljs-comment"># Use raw data (1-second resolution)</span>
        <span class="hljs-keyword">return</span> query_table(<span class="hljs-string">"raw_metrics"</span>, start, end)
    <span class="hljs-keyword">elif</span> duration &lt;= timedelta(days=<span class="hljs-number">7</span>):
        <span class="hljs-comment"># Use 1-minute aggregates</span>
        <span class="hljs-keyword">return</span> query_table(<span class="hljs-string">"minute_metrics"</span>, start, end)
    <span class="hljs-keyword">elif</span> duration &lt;= timedelta(days=<span class="hljs-number">90</span>):
        <span class="hljs-comment"># Use hourly aggregates</span>
        <span class="hljs-keyword">return</span> query_table(<span class="hljs-string">"hourly_metrics"</span>, start, end)
    <span class="hljs-keyword">else</span>:
        <span class="hljs-comment"># Use daily aggregates</span>
        <span class="hljs-keyword">return</span> query_table(<span class="hljs-string">"daily_metrics"</span>, start, end)
</code></pre>
<hr />
<h2 id="heading-3-data-warehousing">3. Data Warehousing</h2>
<h3 id="heading-what-is-a-data-warehouse">What is a Data Warehouse?</h3>
<p>A centralized repository optimized for analytical queries, integrating data from multiple sources.</p>
<p><strong>OLTP vs OLAP</strong>:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Aspect</td><td>OLTP (Online Transaction Processing)</td><td>OLAP (Online Analytical Processing)</td></tr>
</thead>
<tbody>
<tr>
<td>Purpose</td><td>Day-to-day operations</td><td>Business intelligence, analytics</td></tr>
<tr>
<td>Queries</td><td>Simple, fast</td><td>Complex, long-running</td></tr>
<tr>
<td>Data</td><td>Current, detailed</td><td>Historical, summarized</td></tr>
<tr>
<td>Users</td><td>Many concurrent users</td><td>Fewer analysts</td></tr>
<tr>
<td>Design</td><td>Normalized (3NF)</td><td>Denormalized (star/snowflake)</td></tr>
<tr>
<td>Updates</td><td>Frequent inserts/updates</td><td>Batch updates</td></tr>
<tr>
<td>Size</td><td>GB to TB</td><td>TB to PB</td></tr>
<tr>
<td>Example</td><td>E-commerce checkout</td><td>Sales trend analysis</td></tr>
</tbody>
</table>
</div><h3 id="heading-data-warehouse-architecture">Data Warehouse Architecture</h3>
<h4 id="heading-basic-architecture">Basic Architecture</h4>
<pre><code class="lang-plaintext">Source Systems (OLTP Databases)
    ↓
ETL (Extract, Transform, Load)
    ↓
Data Warehouse (Centralized)
    ↓
Data Marts (Department-specific)
    ↓
BI Tools / Reporting
</code></pre>
<h4 id="heading-modern-architecture-lambda">Modern Architecture (Lambda)</h4>
<pre><code class="lang-plaintext">Batch Layer (Historical)
    ↓
    → Data Warehouse
    ↓
Speed Layer (Real-time)
    ↓
    → Stream Processing
    ↓
Serving Layer
    ↓
Applications/Dashboards
</code></pre>
<h4 id="heading-lakehouse-architecture">Lakehouse Architecture</h4>
<p>Combines data lake and data warehouse:</p>
<pre><code class="lang-plaintext">Data Lake (Raw Data Storage)
    ↓
Metadata Layer (Schema-on-read)
    ↓
Query Engines (Spark, Presto, Trino)
    ↓
Analytics/ML
</code></pre>
<h3 id="heading-dimensional-modeling">Dimensional Modeling</h3>
<h4 id="heading-star-schema">Star Schema</h4>
<p>Central fact table surrounded by dimension tables:</p>
<pre><code class="lang-plaintext">        Dimension: Product
               ↓
Dimension: Time → Fact: Sales ← Dimension: Customer
               ↓
        Dimension: Store
</code></pre>
<p><strong>Fact Table</strong> (Sales):</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> fact_sales (
    sale_id <span class="hljs-built_in">BIGINT</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    date_key <span class="hljs-built_in">INT</span> <span class="hljs-keyword">REFERENCES</span> dim_date(date_key),
    product_key <span class="hljs-built_in">INT</span> <span class="hljs-keyword">REFERENCES</span> dim_product(product_key),
    customer_key <span class="hljs-built_in">INT</span> <span class="hljs-keyword">REFERENCES</span> dim_customer(customer_key),
    store_key <span class="hljs-built_in">INT</span> <span class="hljs-keyword">REFERENCES</span> dim_store(store_key),
    <span class="hljs-comment">-- Measures</span>
    quantity <span class="hljs-built_in">INT</span>,
    unit_price <span class="hljs-built_in">DECIMAL</span>(<span class="hljs-number">10</span>,<span class="hljs-number">2</span>),
    total_amount <span class="hljs-built_in">DECIMAL</span>(<span class="hljs-number">10</span>,<span class="hljs-number">2</span>),
    discount_amount <span class="hljs-built_in">DECIMAL</span>(<span class="hljs-number">10</span>,<span class="hljs-number">2</span>)
);
</code></pre>
<p><strong>Dimension Tables</strong>:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> dim_date (
    date_key <span class="hljs-built_in">INT</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    <span class="hljs-built_in">date</span> <span class="hljs-built_in">DATE</span>,
    day_of_week <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">10</span>),
    <span class="hljs-keyword">month</span> <span class="hljs-built_in">INT</span>,
    <span class="hljs-keyword">quarter</span> <span class="hljs-built_in">INT</span>,
    <span class="hljs-keyword">year</span> <span class="hljs-built_in">INT</span>,
    is_weekend <span class="hljs-built_in">BOOLEAN</span>,
    is_holiday <span class="hljs-built_in">BOOLEAN</span>
);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> dim_product (
    product_key <span class="hljs-built_in">INT</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    product_id <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>),
    product_name <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">200</span>),
    <span class="hljs-keyword">category</span> <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">100</span>),
    subcategory <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">100</span>),
    brand <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">100</span>),
    unit_cost <span class="hljs-built_in">DECIMAL</span>(<span class="hljs-number">10</span>,<span class="hljs-number">2</span>)
);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> dim_customer (
    customer_key <span class="hljs-built_in">INT</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    customer_id <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>),
    <span class="hljs-keyword">name</span> <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">200</span>),
    email <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">100</span>),
    city <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">100</span>),
    state <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>),
    country <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>),
    customer_segment <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>)
);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> dim_store (
    store_key <span class="hljs-built_in">INT</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    store_id <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>),
    store_name <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">200</span>),
    city <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">100</span>),
    state <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>),
    region <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>)
);
</code></pre>
<p><strong>Benefits</strong>:</p>
<ul>
<li><p>Simple to understand</p>
</li>
<li><p>Fast queries (fewer joins)</p>
</li>
<li><p>Easy to aggregate</p>
</li>
</ul>
<p><strong>Example Query</strong>:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> 
    d.year,
    d.quarter,
    p.category,
    s.region,
    <span class="hljs-keyword">SUM</span>(f.total_amount) <span class="hljs-keyword">as</span> total_sales,
    <span class="hljs-keyword">SUM</span>(f.quantity) <span class="hljs-keyword">as</span> total_quantity,
    <span class="hljs-keyword">COUNT</span>(<span class="hljs-keyword">DISTINCT</span> f.customer_key) <span class="hljs-keyword">as</span> unique_customers
<span class="hljs-keyword">FROM</span> fact_sales f
<span class="hljs-keyword">JOIN</span> dim_date d <span class="hljs-keyword">ON</span> f.date_key = d.date_key
<span class="hljs-keyword">JOIN</span> dim_product p <span class="hljs-keyword">ON</span> f.product_key = p.product_key
<span class="hljs-keyword">JOIN</span> dim_store s <span class="hljs-keyword">ON</span> f.store_key = s.store_key
<span class="hljs-keyword">WHERE</span> d.year = <span class="hljs-number">2024</span>
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> d.year, d.quarter, p.category, s.region
<span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> total_sales <span class="hljs-keyword">DESC</span>;
</code></pre>
<h4 id="heading-snowflake-schema">Snowflake Schema</h4>
<p>Normalized version of star schema (dimensions have sub-dimensions):</p>
<pre><code class="lang-plaintext">Dimension: Product ← Dimension: Category ← Dimension: Category Type
    ↓
Fact: Sales
    ↓
Dimension: Store ← Dimension: City ← Dimension: State ← Dimension: Country
</code></pre>
<p><strong>Example</strong>:</p>
<pre><code class="lang-sql"><span class="hljs-comment">-- Product dimension (normalized)</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> dim_product (
    product_key <span class="hljs-built_in">INT</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    product_name <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">200</span>),
    subcategory_key <span class="hljs-built_in">INT</span> <span class="hljs-keyword">REFERENCES</span> dim_subcategory(subcategory_key)
);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> dim_subcategory (
    subcategory_key <span class="hljs-built_in">INT</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    subcategory_name <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">100</span>),
    category_key <span class="hljs-built_in">INT</span> <span class="hljs-keyword">REFERENCES</span> dim_category(category_key)
);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> dim_category (
    category_key <span class="hljs-built_in">INT</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    category_name <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">100</span>)
);
</code></pre>
<p><strong>Trade-offs</strong>:</p>
<ul>
<li><p>More normalized (less redundancy)</p>
</li>
<li><p>More complex queries (more joins)</p>
</li>
<li><p>Slightly better storage efficiency</p>
</li>
<li><p>Slower queries than star schema</p>
</li>
</ul>
<h4 id="heading-slowly-changing-dimensions-scd">Slowly Changing Dimensions (SCD)</h4>
<p>Handling changes to dimension data over time.</p>
<p><strong>Type 0</strong>: Retain original (never change)</p>
<p><strong>Type 1</strong>: Overwrite (lose history)</p>
<pre><code class="lang-sql"><span class="hljs-comment">-- Customer moves to new city</span>
<span class="hljs-keyword">UPDATE</span> dim_customer 
<span class="hljs-keyword">SET</span> city = <span class="hljs-string">'Seattle'</span>, state = <span class="hljs-string">'WA'</span>
<span class="hljs-keyword">WHERE</span> customer_key = <span class="hljs-number">12345</span>;
</code></pre>
<p><strong>Type 2</strong>: Add new row (preserve history)</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> dim_customer (
    customer_key <span class="hljs-built_in">INT</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    customer_id <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>),  <span class="hljs-comment">-- Natural key</span>
    <span class="hljs-keyword">name</span> <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">200</span>),
    city <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">100</span>),
    state <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>),
    effective_date <span class="hljs-built_in">DATE</span>,
    expiration_date <span class="hljs-built_in">DATE</span>,
    is_current <span class="hljs-built_in">BOOLEAN</span>
);

<span class="hljs-comment">-- Customer moves</span>
<span class="hljs-comment">-- 1. Close old record</span>
<span class="hljs-keyword">UPDATE</span> dim_customer
<span class="hljs-keyword">SET</span> expiration_date = <span class="hljs-keyword">CURRENT_DATE</span>,
    is_current = <span class="hljs-literal">FALSE</span>
<span class="hljs-keyword">WHERE</span> customer_key = <span class="hljs-number">12345</span> <span class="hljs-keyword">AND</span> is_current = <span class="hljs-literal">TRUE</span>;

<span class="hljs-comment">-- 2. Insert new record</span>
<span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> dim_customer <span class="hljs-keyword">VALUES</span> (
    <span class="hljs-number">67890</span>,              <span class="hljs-comment">-- New surrogate key</span>
    <span class="hljs-string">'CUST001'</span>,          <span class="hljs-comment">-- Same natural key</span>
    <span class="hljs-string">'John Doe'</span>,
    <span class="hljs-string">'Seattle'</span>,          <span class="hljs-comment">-- New city</span>
    <span class="hljs-string">'WA'</span>,              <span class="hljs-comment">-- New state</span>
    <span class="hljs-keyword">CURRENT_DATE</span>,
    <span class="hljs-string">'9999-12-31'</span>,
    <span class="hljs-literal">TRUE</span>
);
</code></pre>
<p><strong>Type 3</strong>: Add column for previous value</p>
<pre><code class="lang-sql"><span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> dim_customer 
<span class="hljs-keyword">ADD</span> <span class="hljs-keyword">COLUMN</span> previous_city <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">100</span>),
<span class="hljs-keyword">ADD</span> <span class="hljs-keyword">COLUMN</span> previous_state <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>),
<span class="hljs-keyword">ADD</span> <span class="hljs-keyword">COLUMN</span> city_change_date <span class="hljs-built_in">DATE</span>;

<span class="hljs-keyword">UPDATE</span> dim_customer
<span class="hljs-keyword">SET</span> previous_city = city,
    previous_state = state,
    city = <span class="hljs-string">'Seattle'</span>,
    state = <span class="hljs-string">'WA'</span>,
    city_change_date = <span class="hljs-keyword">CURRENT_DATE</span>
<span class="hljs-keyword">WHERE</span> customer_key = <span class="hljs-number">12345</span>;
</code></pre>
<p><strong>Type 4</strong>: Separate history table</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> dim_customer (
    customer_key <span class="hljs-built_in">INT</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    customer_id <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>),
    <span class="hljs-keyword">name</span> <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">200</span>),
    city <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">100</span>),
    state <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>)
);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> dim_customer_history (
    history_key <span class="hljs-built_in">INT</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    customer_key <span class="hljs-built_in">INT</span>,
    city <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">100</span>),
    state <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>),
    effective_date <span class="hljs-built_in">DATE</span>,
    expiration_date <span class="hljs-built_in">DATE</span>
);
</code></pre>
<h3 id="heading-etl-vs-elt">ETL vs ELT</h3>
<h4 id="heading-etl-extract-transform-load">ETL (Extract, Transform, Load)</h4>
<p>Traditional approach:</p>
<pre><code class="lang-plaintext">Source → Extract → Transform (ETL Tool) → Load → Data Warehouse
</code></pre>
<p><strong>Tools</strong>: Informatica, Talend, IBM DataStage, Apache NiFi</p>
<p><strong>Example Process</strong>:</p>
<pre><code class="lang-python"><span class="hljs-comment"># ETL Job</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">etl_sales_data</span>():</span>
    <span class="hljs-comment"># Extract</span>
    raw_data = extract_from_source(<span class="hljs-string">"SELECT * FROM sales WHERE date &gt;= yesterday"</span>)

    <span class="hljs-comment"># Transform</span>
    transformed = []
    <span class="hljs-keyword">for</span> row <span class="hljs-keyword">in</span> raw_data:
        <span class="hljs-comment"># Clean</span>
        row[<span class="hljs-string">'email'</span>] = row[<span class="hljs-string">'email'</span>].lower().strip()

        <span class="hljs-comment"># Enrich</span>
        row[<span class="hljs-string">'region'</span>] = get_region(row[<span class="hljs-string">'store_id'</span>])

        <span class="hljs-comment"># Calculate</span>
        row[<span class="hljs-string">'total'</span>] = row[<span class="hljs-string">'quantity'</span>] * row[<span class="hljs-string">'unit_price'</span>]
        row[<span class="hljs-string">'discount_applied'</span>] = row[<span class="hljs-string">'discount_percent'</span>] &gt; <span class="hljs-number">0</span>

        <span class="hljs-comment"># Filter</span>
        <span class="hljs-keyword">if</span> row[<span class="hljs-string">'total'</span>] &gt; <span class="hljs-number">0</span>:
            transformed.append(row)

    <span class="hljs-comment"># Load</span>
    load_to_warehouse(transformed, table=<span class="hljs-string">"fact_sales"</span>)
</code></pre>
<p><strong>Advantages</strong>:</p>
<ul>
<li><p>Transform before loading (less warehouse load)</p>
</li>
<li><p>Cleansed data in warehouse</p>
</li>
<li><p>Works well with limited warehouse resources</p>
</li>
</ul>
<p><strong>Disadvantages</strong>:</p>
<ul>
<li><p>Separate ETL infrastructure</p>
</li>
<li><p>Less flexibility (can't re-transform)</p>
</li>
<li><p>Slower for large datasets</p>
</li>
</ul>
<h4 id="heading-elt-extract-load-transform">ELT (Extract, Load, Transform)</h4>
<p>Modern approach (leveraging warehouse power):</p>
<pre><code class="lang-plaintext">Source → Extract → Load → Data Lake/Warehouse → Transform (SQL)
</code></pre>
<p><strong>Tools</strong>: dbt (data build tool), Fivetran, Stitch, Airbyte</p>
<p><strong>Example with dbt</strong>:</p>
<pre><code class="lang-sql"><span class="hljs-comment">-- models/staging/stg_sales.sql</span>
<span class="hljs-keyword">WITH</span> <span class="hljs-keyword">source</span> <span class="hljs-keyword">AS</span> (
    <span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> {{ <span class="hljs-keyword">source</span>(<span class="hljs-string">'raw'</span>, <span class="hljs-string">'sales'</span>) }}
)

<span class="hljs-keyword">SELECT</span>
    sale_id,
    <span class="hljs-built_in">date</span>,
    <span class="hljs-keyword">LOWER</span>(<span class="hljs-keyword">TRIM</span>(customer_email)) <span class="hljs-keyword">as</span> customer_email,
    product_id,
    quantity,
    unit_price,
    quantity * unit_price <span class="hljs-keyword">as</span> total_amount,
    discount_percent,
    discount_percent &gt; <span class="hljs-number">0</span> <span class="hljs-keyword">as</span> has_discount
<span class="hljs-keyword">FROM</span> <span class="hljs-keyword">source</span>
<span class="hljs-keyword">WHERE</span> quantity &gt; <span class="hljs-number">0</span>
  <span class="hljs-keyword">AND</span> unit_price &gt; <span class="hljs-number">0</span>
</code></pre>
<pre><code class="lang-sql"><span class="hljs-comment">-- models/marts/fact_sales.sql</span>
<span class="hljs-keyword">SELECT</span>
    s.sale_id,
    d.date_key,
    p.product_key,
    c.customer_key,
    st.store_key,
    s.quantity,
    s.unit_price,
    s.total_amount,
    s.discount_percent
<span class="hljs-keyword">FROM</span> {{ <span class="hljs-keyword">ref</span>(<span class="hljs-string">'stg_sales'</span>) }} s
<span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> {{ <span class="hljs-keyword">ref</span>(<span class="hljs-string">'dim_date'</span>) }} d <span class="hljs-keyword">ON</span> s.date = d.date
<span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> {{ <span class="hljs-keyword">ref</span>(<span class="hljs-string">'dim_product'</span>) }} p <span class="hljs-keyword">ON</span> s.product_id = p.product_id
<span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> {{ <span class="hljs-keyword">ref</span>(<span class="hljs-string">'dim_customer'</span>) }} c <span class="hljs-keyword">ON</span> s.customer_email = c.email
<span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> {{ <span class="hljs-keyword">ref</span>(<span class="hljs-string">'dim_store'</span>) }} st <span class="hljs-keyword">ON</span> s.store_id = st.store_id
</code></pre>
<p><strong>Advantages</strong>:</p>
<ul>
<li><p>Leverage warehouse compute power</p>
</li>
<li><p>Keep raw data (can re-transform)</p>
</li>
<li><p>Version control transformations (Git)</p>
</li>
<li><p>Faster load times</p>
</li>
</ul>
<p><strong>Disadvantages</strong>:</p>
<ul>
<li><p>Requires powerful warehouse</p>
</li>
<li><p>All data loaded (including bad data)</p>
</li>
<li><p>Transformation happens in warehouse</p>
</li>
</ul>
<h3 id="heading-modern-data-warehouse-platforms">Modern Data Warehouse Platforms</h3>
<h4 id="heading-snowflake">Snowflake</h4>
<p><strong>Architecture</strong>:</p>
<ul>
<li><p>Separation of storage and compute</p>
</li>
<li><p>Multi-cluster shared data</p>
</li>
<li><p>Automatic scaling</p>
</li>
</ul>
<p><strong>Unique Features</strong>:</p>
<ul>
<li><p>Time travel (query historical data)</p>
</li>
<li><p>Zero-copy cloning</p>
</li>
<li><p>Data sharing across accounts</p>
</li>
</ul>
<pre><code class="lang-sql"><span class="hljs-comment">-- Virtual warehouse (compute)</span>
<span class="hljs-keyword">CREATE</span> WAREHOUSE analytics_wh
<span class="hljs-keyword">WITH</span> WAREHOUSE_SIZE = <span class="hljs-string">'LARGE'</span>
AUTO_SUSPEND = <span class="hljs-number">300</span>
AUTO_RESUME = <span class="hljs-literal">TRUE</span>;

<span class="hljs-comment">-- Use warehouse</span>
<span class="hljs-keyword">USE</span> WAREHOUSE analytics_wh;

<span class="hljs-comment">-- Time travel (query data as of 1 hour ago)</span>
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> sales <span class="hljs-keyword">AT</span>(<span class="hljs-keyword">OFFSET</span> =&gt; <span class="hljs-number">-3600</span>);

<span class="hljs-comment">-- Clone table instantly (zero-copy)</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> sales_dev <span class="hljs-keyword">CLONE</span> sales;

<span class="hljs-comment">-- Create share</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">SHARE</span> sales_share;
<span class="hljs-keyword">GRANT</span> <span class="hljs-keyword">USAGE</span> <span class="hljs-keyword">ON</span> <span class="hljs-keyword">DATABASE</span> sales_db <span class="hljs-keyword">TO</span> <span class="hljs-keyword">SHARE</span> sales_share;
<span class="hljs-keyword">GRANT</span> <span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">ON</span> <span class="hljs-keyword">TABLE</span> sales <span class="hljs-keyword">TO</span> <span class="hljs-keyword">SHARE</span> sales_share;

<span class="hljs-comment">-- Materialized view</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">MATERIALIZED</span> <span class="hljs-keyword">VIEW</span> sales_summary <span class="hljs-keyword">AS</span>
<span class="hljs-keyword">SELECT</span> 
    date_trunc(<span class="hljs-string">'day'</span>, sale_date) <span class="hljs-keyword">as</span> <span class="hljs-keyword">day</span>,
    product_category,
    <span class="hljs-keyword">SUM</span>(amount) <span class="hljs-keyword">as</span> total_sales
<span class="hljs-keyword">FROM</span> sales
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-number">1</span>, <span class="hljs-number">2</span>;
</code></pre>
<p><strong>Pricing</strong>: Pay for storage + compute separately</p>
<h4 id="heading-google-bigquery">Google BigQuery</h4>
<p><strong>Architecture</strong>:</p>
<ul>
<li><p>Serverless (no infrastructure management)</p>
</li>
<li><p>Columnar storage (Capacitor format)</p>
</li>
<li><p>Dremel query engine</p>
</li>
<li><p>Separate storage and compute</p>
</li>
</ul>
<p><strong>Unique Features</strong>:</p>
<ul>
<li><p>Petabyte-scale analytics</p>
</li>
<li><p>Real-time analytics</p>
</li>
<li><p>Built-in ML (BigQuery ML)</p>
</li>
<li><p>Federated queries</p>
</li>
</ul>
<pre><code class="lang-sql"><span class="hljs-comment">-- Standard SQL</span>
<span class="hljs-keyword">SELECT</span> 
    <span class="hljs-built_in">DATE</span>(<span class="hljs-built_in">timestamp</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">date</span>,
    country,
    <span class="hljs-keyword">COUNT</span>(*) <span class="hljs-keyword">as</span> pageviews,
    <span class="hljs-keyword">COUNT</span>(<span class="hljs-keyword">DISTINCT</span> user_id) <span class="hljs-keyword">as</span> unique_users
<span class="hljs-keyword">FROM</span> <span class="hljs-string">`project.dataset.pageviews`</span>
<span class="hljs-keyword">WHERE</span> <span class="hljs-built_in">DATE</span>(<span class="hljs-built_in">timestamp</span>) &gt;= <span class="hljs-keyword">DATE_SUB</span>(<span class="hljs-keyword">CURRENT_DATE</span>(), <span class="hljs-built_in">INTERVAL</span> <span class="hljs-number">30</span> <span class="hljs-keyword">DAY</span>)
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-built_in">date</span>, country
<span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> <span class="hljs-built_in">date</span> <span class="hljs-keyword">DESC</span>;

<span class="hljs-comment">-- Partitioned table</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> sales
<span class="hljs-keyword">PARTITION</span> <span class="hljs-keyword">BY</span> <span class="hljs-built_in">DATE</span>(sale_date)
CLUSTER <span class="hljs-keyword">BY</span> customer_id, product_id
<span class="hljs-keyword">AS</span> <span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> raw_sales;

<span class="hljs-comment">-- BigQuery ML</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">MODEL</span> <span class="hljs-string">`project.dataset.sales_forecast`</span>
OPTIONS(
    model_type=<span class="hljs-string">'ARIMA_PLUS'</span>,
    time_series_timestamp_col=<span class="hljs-string">'date'</span>,
    time_series_data_col=<span class="hljs-string">'sales'</span>
) <span class="hljs-keyword">AS</span>
<span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">date</span>, <span class="hljs-keyword">SUM</span>(amount) <span class="hljs-keyword">as</span> sales
<span class="hljs-keyword">FROM</span> <span class="hljs-string">`project.dataset.sales`</span>
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-built_in">date</span>;

<span class="hljs-comment">-- Query external data</span>
<span class="hljs-keyword">SELECT</span> *
<span class="hljs-keyword">FROM</span> <span class="hljs-string">`project.dataset.external_table`</span>
<span class="hljs-keyword">WHERE</span> _FILE_NAME <span class="hljs-keyword">LIKE</span> <span class="hljs-string">'%.csv'</span>;
</code></pre>
<p><strong>Pricing</strong>: Pay per query (amount of data processed)</p>
<h4 id="heading-amazon-redshift">Amazon Redshift</h4>
<p><strong>Architecture</strong>:</p>
<ul>
<li><p>Massively Parallel Processing (MPP)</p>
</li>
<li><p>Columnar storage</p>
</li>
<li><p>Leader node + compute nodes</p>
</li>
<li><p>Based on PostgreSQL</p>
</li>
</ul>
<p><strong>Features</strong>:</p>
<ul>
<li><p>Redshift Spectrum (query S3 directly)</p>
</li>
<li><p>Materialized views</p>
</li>
<li><p>Concurrency scaling</p>
</li>
<li><p>RA3 nodes (separate storage/compute)</p>
</li>
</ul>
<pre><code class="lang-sql"><span class="hljs-comment">-- Distribution styles</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> sales (
    sale_id <span class="hljs-built_in">BIGINT</span>,
    customer_id <span class="hljs-built_in">INT</span>,
    product_id <span class="hljs-built_in">INT</span>,
    amount <span class="hljs-built_in">DECIMAL</span>(<span class="hljs-number">10</span>,<span class="hljs-number">2</span>),
    sale_date <span class="hljs-built_in">DATE</span>
)
DISTKEY(customer_id)  <span class="hljs-comment">-- Distribute by customer_id</span>
SORTKEY(sale_date);   <span class="hljs-comment">-- Sort by sale_date</span>

<span class="hljs-comment">-- Redshift Spectrum (query S3)</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">EXTERNAL</span> <span class="hljs-keyword">SCHEMA</span> s3_data
<span class="hljs-keyword">FROM</span> <span class="hljs-keyword">DATA</span> <span class="hljs-keyword">CATALOG</span>
<span class="hljs-keyword">DATABASE</span> <span class="hljs-string">'s3_database'</span>
IAM_ROLE <span class="hljs-string">'arn:aws:iam::123456789:role/RedshiftRole'</span>;

<span class="hljs-keyword">SELECT</span> *
<span class="hljs-keyword">FROM</span> s3_data.raw_logs
<span class="hljs-keyword">WHERE</span> log_date &gt;= <span class="hljs-keyword">CURRENT_DATE</span> - <span class="hljs-number">30</span>;

<span class="hljs-comment">-- Materialized view</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">MATERIALIZED</span> <span class="hljs-keyword">VIEW</span> sales_summary <span class="hljs-keyword">AS</span>
<span class="hljs-keyword">SELECT</span> 
    DATE_TRUNC(<span class="hljs-string">'day'</span>, sale_date) <span class="hljs-keyword">as</span> <span class="hljs-keyword">day</span>,
    product_category,
    <span class="hljs-keyword">SUM</span>(amount) <span class="hljs-keyword">as</span> total_sales,
    <span class="hljs-keyword">COUNT</span>(*) <span class="hljs-keyword">as</span> num_sales
<span class="hljs-keyword">FROM</span> sales
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-number">1</span>, <span class="hljs-number">2</span>;

<span class="hljs-comment">-- Refresh materialized view</span>
REFRESH MATERIALIZED VIEW sales_summary;
</code></pre>
<h4 id="heading-apache-hive">Apache Hive</h4>
<p><strong>Description</strong>: Data warehouse system built on Hadoop</p>
<p><strong>Features</strong>:</p>
<ul>
<li><p>SQL interface to Hadoop data</p>
</li>
<li><p>HiveQL (SQL-like language)</p>
</li>
<li><p>Partitioning and bucketing</p>
</li>
<li><p>Integration with Hadoop ecosystem</p>
</li>
</ul>
<pre><code class="lang-sql"><span class="hljs-comment">-- Create table on HDFS</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> sales (
    sale_id <span class="hljs-built_in">BIGINT</span>,
    customer_id <span class="hljs-built_in">INT</span>,
    product_id <span class="hljs-built_in">INT</span>,
    amount <span class="hljs-built_in">DECIMAL</span>(<span class="hljs-number">10</span>,<span class="hljs-number">2</span>),
    sale_date <span class="hljs-built_in">DATE</span>
)
PARTITIONED <span class="hljs-keyword">BY</span> (<span class="hljs-keyword">year</span> <span class="hljs-built_in">INT</span>, <span class="hljs-keyword">month</span> <span class="hljs-built_in">INT</span>)
<span class="hljs-keyword">STORED</span> <span class="hljs-keyword">AS</span> PARQUET;

<span class="hljs-comment">-- Insert with dynamic partitioning</span>
<span class="hljs-keyword">INSERT</span> OVERWRITE <span class="hljs-keyword">TABLE</span> sales <span class="hljs-keyword">PARTITION</span> (<span class="hljs-keyword">year</span>, <span class="hljs-keyword">month</span>)
<span class="hljs-keyword">SELECT</span> 
    sale_id,
    customer_id,
    product_id,
    amount,
    sale_date,
    <span class="hljs-keyword">YEAR</span>(sale_date) <span class="hljs-keyword">as</span> <span class="hljs-keyword">year</span>,
    <span class="hljs-keyword">MONTH</span>(sale_date) <span class="hljs-keyword">as</span> <span class="hljs-keyword">month</span>
<span class="hljs-keyword">FROM</span> raw_sales;

<span class="hljs-comment">-- Query specific partition</span>
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> sales
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">year</span> = <span class="hljs-number">2024</span> <span class="hljs-keyword">AND</span> <span class="hljs-keyword">month</span> = <span class="hljs-number">1</span>;
</code></pre>
<h3 id="heading-data-warehouse-optimization">Data Warehouse Optimization</h3>
<h4 id="heading-partitioning">Partitioning</h4>
<p>Divide large tables into smaller, manageable pieces:</p>
<pre><code class="lang-sql"><span class="hljs-comment">-- PostgreSQL (declarative partitioning)</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> sales (
    sale_id <span class="hljs-built_in">BIGINT</span>,
    sale_date <span class="hljs-built_in">DATE</span>,
    amount <span class="hljs-built_in">DECIMAL</span>(<span class="hljs-number">10</span>,<span class="hljs-number">2</span>)
) <span class="hljs-keyword">PARTITION</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">RANGE</span> (sale_date);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> sales_2023 <span class="hljs-keyword">PARTITION</span> <span class="hljs-keyword">OF</span> sales
    <span class="hljs-keyword">FOR</span> <span class="hljs-keyword">VALUES</span> <span class="hljs-keyword">FROM</span> (<span class="hljs-string">'2023-01-01'</span>) <span class="hljs-keyword">TO</span> (<span class="hljs-string">'2024-01-01'</span>);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> sales_2024 <span class="hljs-keyword">PARTITION</span> <span class="hljs-keyword">OF</span> sales
    <span class="hljs-keyword">FOR</span> <span class="hljs-keyword">VALUES</span> <span class="hljs-keyword">FROM</span> (<span class="hljs-string">'2024-01-01'</span>) <span class="hljs-keyword">TO</span> (<span class="hljs-string">'2025-01-01'</span>);

<span class="hljs-comment">-- Query automatically uses correct partition</span>
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> sales <span class="hljs-keyword">WHERE</span> sale_date &gt;= <span class="hljs-string">'2024-06-01'</span>;
</code></pre>
<h4 id="heading-clustering">Clustering</h4>
<p>Group related rows together:</p>
<pre><code class="lang-sql"><span class="hljs-comment">-- BigQuery</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> sales
CLUSTER <span class="hljs-keyword">BY</span> customer_id, product_category
<span class="hljs-keyword">AS</span> <span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> raw_sales;

<span class="hljs-comment">-- Snowflake</span>
<span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> sales CLUSTER <span class="hljs-keyword">BY</span> (customer_id, product_category);
</code></pre>
<h4 id="heading-materialized-views">Materialized Views</h4>
<p>Pre-compute expensive queries:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">MATERIALIZED</span> <span class="hljs-keyword">VIEW</span> monthly_sales_summary <span class="hljs-keyword">AS</span>
<span class="hljs-keyword">SELECT</span> 
    DATE_TRUNC(<span class="hljs-string">'month'</span>, sale_date) <span class="hljs-keyword">as</span> <span class="hljs-keyword">month</span>,
    product_category,
    store_region,
    <span class="hljs-keyword">SUM</span>(amount) <span class="hljs-keyword">as</span> total_sales,
    <span class="hljs-keyword">COUNT</span>(*) <span class="hljs-keyword">as</span> num_transactions,
    <span class="hljs-keyword">AVG</span>(amount) <span class="hljs-keyword">as</span> avg_transaction
<span class="hljs-keyword">FROM</span> sales
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>;

<span class="hljs-comment">-- Query uses pre-computed results</span>
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> monthly_sales_summary
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">month</span> &gt;= <span class="hljs-string">'2024-01-01'</span>;
</code></pre>
<h4 id="heading-columnar-storage">Columnar Storage</h4>
<p>Store data by column instead of row:</p>
<p><strong>Row-oriented</strong> (OLTP):</p>
<pre><code class="lang-plaintext">Row 1: [1, "Alice", 100, "2024-01-01"]
Row 2: [2, "Bob", 150, "2024-01-02"]
Row 3: [3, "Carol", 200, "2024-01-03"]
</code></pre>
<p><strong>Column-oriented</strong> (OLAP):</p>
<pre><code class="lang-plaintext">ID column: [1, 2, 3]
Name column: ["Alice", "Bob", "Carol"]
Amount column: [100, 150, 200]
Date column: ["2024-01-01", "2024-01-02", "2024-01-03"]
</code></pre>
<p><strong>Benefits</strong>:</p>
<ul>
<li><p>Better compression (similar values together)</p>
</li>
<li><p>Read only needed columns</p>
</li>
<li><p>Vectorized query execution</p>
</li>
<li><p>Ideal for analytical queries</p>
</li>
</ul>
<p><strong>Formats</strong>: Parquet, ORC, Capacitor</p>
<h4 id="heading-query-optimization">Query Optimization</h4>
<p><strong>Use materialized views for common aggregations</strong>:</p>
<pre><code class="lang-sql"><span class="hljs-comment">-- Instead of this every time:</span>
<span class="hljs-keyword">SELECT</span> customer_id, <span class="hljs-keyword">SUM</span>(amount) 
<span class="hljs-keyword">FROM</span> sales 
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> customer_id;

<span class="hljs-comment">-- Create materialized view:</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">MATERIALIZED</span> <span class="hljs-keyword">VIEW</span> customer_totals <span class="hljs-keyword">AS</span>
<span class="hljs-keyword">SELECT</span> customer_id, <span class="hljs-keyword">SUM</span>(amount) <span class="hljs-keyword">as</span> total
<span class="hljs-keyword">FROM</span> sales
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> customer_id;
</code></pre>
<p><strong>Partition pruning</strong>:</p>
<pre><code class="lang-sql"><span class="hljs-comment">-- Good (uses partition)</span>
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> sales 
<span class="hljs-keyword">WHERE</span> sale_date <span class="hljs-keyword">BETWEEN</span> <span class="hljs-string">'2024-01-01'</span> <span class="hljs-keyword">AND</span> <span class="hljs-string">'2024-01-31'</span>;

<span class="hljs-comment">-- Bad (scans all partitions)</span>
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> sales 
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">EXTRACT</span>(<span class="hljs-keyword">MONTH</span> <span class="hljs-keyword">FROM</span> sale_date) = <span class="hljs-number">1</span>;
</code></pre>
<p><strong>Push filters early</strong>:</p>
<pre><code class="lang-sql"><span class="hljs-comment">-- Good</span>
<span class="hljs-keyword">SELECT</span> c.name, s.amount
<span class="hljs-keyword">FROM</span> (
    <span class="hljs-keyword">SELECT</span> customer_id, amount 
    <span class="hljs-keyword">FROM</span> sales 
    <span class="hljs-keyword">WHERE</span> amount &gt; <span class="hljs-number">1000</span>  <span class="hljs-comment">-- Filter early</span>
) s
<span class="hljs-keyword">JOIN</span> customers c <span class="hljs-keyword">ON</span> s.customer_id = c.customer_id;

<span class="hljs-comment">-- Less efficient</span>
<span class="hljs-keyword">SELECT</span> c.name, s.amount
<span class="hljs-keyword">FROM</span> sales s
<span class="hljs-keyword">JOIN</span> customers c <span class="hljs-keyword">ON</span> s.customer_id = c.customer_id
<span class="hljs-keyword">WHERE</span> s.amount &gt; <span class="hljs-number">1000</span>;  <span class="hljs-comment">-- Filter after join</span>
</code></pre>
<hr />
<h2 id="heading-4-industry-patterns-and-use-cases">4. Industry Patterns and Use Cases</h2>
<h3 id="heading-e-commerce">E-Commerce</h3>
<h4 id="heading-product-catalog">Product Catalog</h4>
<p><strong>Requirements</strong>:</p>
<ul>
<li><p>Fast product searches</p>
</li>
<li><p>Filter by multiple attributes</p>
</li>
<li><p>Handle millions of products</p>
</li>
<li><p>Product variants</p>
</li>
<li><p>Real-time inventory</p>
</li>
</ul>
<p><strong>Database Design</strong>:</p>
<pre><code class="lang-sql"><span class="hljs-comment">-- Relational approach</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> products (
    product_id <span class="hljs-built_in">SERIAL</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    <span class="hljs-keyword">name</span> <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">200</span>),
    description <span class="hljs-built_in">TEXT</span>,
    base_price <span class="hljs-built_in">DECIMAL</span>(<span class="hljs-number">10</span>,<span class="hljs-number">2</span>),
    category_id <span class="hljs-built_in">INT</span>,
    brand_id <span class="hljs-built_in">INT</span>,
    created_at <span class="hljs-built_in">TIMESTAMP</span>,
    updated_at <span class="hljs-built_in">TIMESTAMP</span>
);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> product_variants (
    variant_id <span class="hljs-built_in">SERIAL</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    product_id <span class="hljs-built_in">INT</span> <span class="hljs-keyword">REFERENCES</span> products(product_id),
    sku <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>) <span class="hljs-keyword">UNIQUE</span>,
    color <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>),
    <span class="hljs-keyword">size</span> <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">20</span>),
    price <span class="hljs-built_in">DECIMAL</span>(<span class="hljs-number">10</span>,<span class="hljs-number">2</span>),
    stock_quantity <span class="hljs-built_in">INT</span>
);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> product_attributes (
    product_id <span class="hljs-built_in">INT</span> <span class="hljs-keyword">REFERENCES</span> products(product_id),
    attribute_name <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">100</span>),
    attribute_value <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">200</span>),
    PRIMARY <span class="hljs-keyword">KEY</span> (product_id, attribute_name)
);

<span class="hljs-comment">-- Indexes</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">INDEX</span> idx_products_category <span class="hljs-keyword">ON</span> products(category_id);
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">INDEX</span> idx_products_brand <span class="hljs-keyword">ON</span> products(brand_id);
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">INDEX</span> idx_variants_sku <span class="hljs-keyword">ON</span> product_variants(sku);
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">INDEX</span> idx_attributes_name_value <span class="hljs-keyword">ON</span> product_attributes(attribute_name, attribute_value);
</code></pre>
<p><strong>NoSQL approach (MongoDB)</strong>:</p>
<pre><code class="lang-javascript">{
    <span class="hljs-string">"_id"</span>: ObjectId(<span class="hljs-string">"..."</span>),
    <span class="hljs-string">"name"</span>: <span class="hljs-string">"Premium T-Shirt"</span>,
    <span class="hljs-string">"description"</span>: <span class="hljs-string">"High-quality cotton t-shirt"</span>,
    <span class="hljs-string">"category"</span>: <span class="hljs-string">"Clothing"</span>,
    <span class="hljs-string">"brand"</span>: <span class="hljs-string">"BrandX"</span>,
    <span class="hljs-string">"base_price"</span>: <span class="hljs-number">29.99</span>,
    <span class="hljs-string">"variants"</span>: [
        {
            <span class="hljs-string">"sku"</span>: <span class="hljs-string">"TSH-BLU-M"</span>,
            <span class="hljs-string">"color"</span>: <span class="hljs-string">"Blue"</span>,
            <span class="hljs-string">"size"</span>: <span class="hljs-string">"M"</span>,
            <span class="hljs-string">"price"</span>: <span class="hljs-number">29.99</span>,
            <span class="hljs-string">"stock"</span>: <span class="hljs-number">45</span>
        },
        {
            <span class="hljs-string">"sku"</span>: <span class="hljs-string">"TSH-BLU-L"</span>,
            <span class="hljs-string">"color"</span>: <span class="hljs-string">"Blue"</span>,
            <span class="hljs-string">"size"</span>: <span class="hljs-string">"L"</span>,
            <span class="hljs-string">"price"</span>: <span class="hljs-number">31.99</span>,
            <span class="hljs-string">"stock"</span>: <span class="hljs-number">30</span>
        }
    ],
    <span class="hljs-string">"attributes"</span>: {
        <span class="hljs-string">"material"</span>: <span class="hljs-string">"100% Cotton"</span>,
        <span class="hljs-string">"care_instructions"</span>: <span class="hljs-string">"Machine wash cold"</span>,
        <span class="hljs-string">"country_of_origin"</span>: <span class="hljs-string">"USA"</span>
    },
    <span class="hljs-string">"images"</span>: [
        <span class="hljs-string">"https://cdn.example.com/tshirt-front.jpg"</span>,
        <span class="hljs-string">"https://cdn.example.com/tshirt-back.jpg"</span>
    ],
    <span class="hljs-string">"tags"</span>: [<span class="hljs-string">"casual"</span>, <span class="hljs-string">"summer"</span>, <span class="hljs-string">"bestseller"</span>],
    <span class="hljs-string">"reviews_summary"</span>: {
        <span class="hljs-string">"average_rating"</span>: <span class="hljs-number">4.5</span>,
        <span class="hljs-string">"total_reviews"</span>: <span class="hljs-number">127</span>
    }
}

<span class="hljs-comment">// Indexes</span>
db.products.createIndex({ <span class="hljs-string">"category"</span>: <span class="hljs-number">1</span>, <span class="hljs-string">"brand"</span>: <span class="hljs-number">1</span> });
db.products.createIndex({ <span class="hljs-string">"tags"</span>: <span class="hljs-number">1</span> });
db.products.createIndex({ <span class="hljs-string">"attributes.material"</span>: <span class="hljs-number">1</span> });
db.products.createIndex({ <span class="hljs-string">"$text"</span>: { <span class="hljs-string">"name"</span>: <span class="hljs-string">"text"</span>, <span class="hljs-string">"description"</span>: <span class="hljs-string">"text"</span> } });
</code></pre>
<p><strong>Search (Elasticsearch)</strong>:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"query"</span>: {
        <span class="hljs-attr">"bool"</span>: {
            <span class="hljs-attr">"must"</span>: [
                { <span class="hljs-attr">"match"</span>: { <span class="hljs-attr">"category"</span>: <span class="hljs-string">"Clothing"</span> } }
            ],
            <span class="hljs-attr">"filter"</span>: [
                { <span class="hljs-attr">"range"</span>: { <span class="hljs-attr">"price"</span>: { <span class="hljs-attr">"gte"</span>: <span class="hljs-number">20</span>, <span class="hljs-attr">"lte"</span>: <span class="hljs-number">50</span> } } },
                { <span class="hljs-attr">"term"</span>: { <span class="hljs-attr">"brand"</span>: <span class="hljs-string">"BrandX"</span> } },
                { <span class="hljs-attr">"terms"</span>: { <span class="hljs-attr">"color"</span>: [<span class="hljs-string">"Blue"</span>, <span class="hljs-string">"Red"</span>] } }
            ]
        }
    },
    <span class="hljs-attr">"aggs"</span>: {
        <span class="hljs-attr">"brands"</span>: {
            <span class="hljs-attr">"terms"</span>: { <span class="hljs-attr">"field"</span>: <span class="hljs-string">"brand"</span> }
        },
        <span class="hljs-attr">"price_ranges"</span>: {
            <span class="hljs-attr">"range"</span>: {
                <span class="hljs-attr">"field"</span>: <span class="hljs-string">"price"</span>,
                <span class="hljs-attr">"ranges"</span>: [
                    { <span class="hljs-attr">"to"</span>: <span class="hljs-number">25</span> },
                    { <span class="hljs-attr">"from"</span>: <span class="hljs-number">25</span>, <span class="hljs-attr">"to"</span>: <span class="hljs-number">50</span> },
                    { <span class="hljs-attr">"from"</span>: <span class="hljs-number">50</span> }
                ]
            }
        }
    }
}
</code></pre>
<h4 id="heading-shopping-cart">Shopping Cart</h4>
<p><strong>Session-based cart (Redis)</strong>:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Add to cart</span>
redis.hset(<span class="hljs-string">f"cart:<span class="hljs-subst">{session_id}</span>"</span>, product_id, quantity)

<span class="hljs-comment"># Get cart</span>
cart = redis.hgetall(<span class="hljs-string">f"cart:<span class="hljs-subst">{session_id}</span>"</span>)

<span class="hljs-comment"># Set expiration (30 days)</span>
redis.expire(<span class="hljs-string">f"cart:<span class="hljs-subst">{session_id}</span>"</span>, <span class="hljs-number">30</span> * <span class="hljs-number">24</span> * <span class="hljs-number">60</span> * <span class="hljs-number">60</span>)

<span class="hljs-comment"># Move to user cart on login</span>
session_cart = redis.hgetall(<span class="hljs-string">f"cart:<span class="hljs-subst">{session_id}</span>"</span>)
<span class="hljs-keyword">for</span> product_id, quantity <span class="hljs-keyword">in</span> session_cart.items():
    redis.hset(<span class="hljs-string">f"cart:user:<span class="hljs-subst">{user_id}</span>"</span>, product_id, quantity)
redis.delete(<span class="hljs-string">f"cart:<span class="hljs-subst">{session_id}</span>"</span>)
</code></pre>
<p><strong>Persistent cart (PostgreSQL)</strong>:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> shopping_carts (
    cart_id <span class="hljs-built_in">SERIAL</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    user_id <span class="hljs-built_in">INT</span> <span class="hljs-keyword">REFERENCES</span> <span class="hljs-keyword">users</span>(user_id),
    session_id <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">100</span>),
    created_at <span class="hljs-built_in">TIMESTAMP</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">CURRENT_TIMESTAMP</span>,
    updated_at <span class="hljs-built_in">TIMESTAMP</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">CURRENT_TIMESTAMP</span>
);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> cart_items (
    cart_item_id <span class="hljs-built_in">SERIAL</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    cart_id <span class="hljs-built_in">INT</span> <span class="hljs-keyword">REFERENCES</span> shopping_carts(cart_id),
    product_id <span class="hljs-built_in">INT</span> <span class="hljs-keyword">REFERENCES</span> products(product_id),
    variant_id <span class="hljs-built_in">INT</span> <span class="hljs-keyword">REFERENCES</span> product_variants(variant_id),
    quantity <span class="hljs-built_in">INT</span>,
    price_at_addition <span class="hljs-built_in">DECIMAL</span>(<span class="hljs-number">10</span>,<span class="hljs-number">2</span>),
    added_at <span class="hljs-built_in">TIMESTAMP</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">CURRENT_TIMESTAMP</span>
);
</code></pre>
<h4 id="heading-order-management">Order Management</h4>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> orders (
    order_id <span class="hljs-built_in">SERIAL</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    user_id <span class="hljs-built_in">INT</span> <span class="hljs-keyword">REFERENCES</span> <span class="hljs-keyword">users</span>(user_id),
    order_number <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>) <span class="hljs-keyword">UNIQUE</span>,
    <span class="hljs-keyword">status</span> <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>), <span class="hljs-comment">-- pending, paid, shipped, delivered, cancelled</span>
    subtotal <span class="hljs-built_in">DECIMAL</span>(<span class="hljs-number">10</span>,<span class="hljs-number">2</span>),
    tax <span class="hljs-built_in">DECIMAL</span>(<span class="hljs-number">10</span>,<span class="hljs-number">2</span>),
    shipping_cost <span class="hljs-built_in">DECIMAL</span>(<span class="hljs-number">10</span>,<span class="hljs-number">2</span>),
    total <span class="hljs-built_in">DECIMAL</span>(<span class="hljs-number">10</span>,<span class="hljs-number">2</span>),
    created_at <span class="hljs-built_in">TIMESTAMP</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">CURRENT_TIMESTAMP</span>,
    updated_at <span class="hljs-built_in">TIMESTAMP</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">CURRENT_TIMESTAMP</span>
);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> order_items (
    order_item_id <span class="hljs-built_in">SERIAL</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    order_id <span class="hljs-built_in">INT</span> <span class="hljs-keyword">REFERENCES</span> orders(order_id),
    product_id <span class="hljs-built_in">INT</span> <span class="hljs-keyword">REFERENCES</span> products(product_id),
    variant_id <span class="hljs-built_in">INT</span> <span class="hljs-keyword">REFERENCES</span> product_variants(variant_id),
    quantity <span class="hljs-built_in">INT</span>,
    unit_price <span class="hljs-built_in">DECIMAL</span>(<span class="hljs-number">10</span>,<span class="hljs-number">2</span>),
    total <span class="hljs-built_in">DECIMAL</span>(<span class="hljs-number">10</span>,<span class="hljs-number">2</span>)
);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> order_status_history (
    history_id <span class="hljs-built_in">SERIAL</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    order_id <span class="hljs-built_in">INT</span> <span class="hljs-keyword">REFERENCES</span> orders(order_id),
    <span class="hljs-keyword">status</span> <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>),
    note <span class="hljs-built_in">TEXT</span>,
    created_at <span class="hljs-built_in">TIMESTAMP</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">CURRENT_TIMESTAMP</span>,
    created_by <span class="hljs-built_in">INT</span> <span class="hljs-keyword">REFERENCES</span> <span class="hljs-keyword">users</span>(user_id)
);

<span class="hljs-comment">-- Inventory deduction pattern (atomic)</span>
<span class="hljs-keyword">BEGIN</span>;

<span class="hljs-comment">-- Check availability</span>
<span class="hljs-keyword">SELECT</span> stock_quantity <span class="hljs-keyword">FROM</span> product_variants
<span class="hljs-keyword">WHERE</span> variant_id = <span class="hljs-number">123</span> <span class="hljs-keyword">FOR</span> <span class="hljs-keyword">UPDATE</span>;

<span class="hljs-comment">-- Deduct if available</span>
<span class="hljs-keyword">UPDATE</span> product_variants
<span class="hljs-keyword">SET</span> stock_quantity = stock_quantity - <span class="hljs-number">2</span>
<span class="hljs-keyword">WHERE</span> variant_id = <span class="hljs-number">123</span> <span class="hljs-keyword">AND</span> stock_quantity &gt;= <span class="hljs-number">2</span>;

<span class="hljs-comment">-- If update affected 0 rows, not enough stock</span>
IF NOT FOUND THEN
    <span class="hljs-keyword">ROLLBACK</span>;
    RAISE EXCEPTION 'Insufficient stock';
<span class="hljs-keyword">END</span> <span class="hljs-keyword">IF</span>;

<span class="hljs-keyword">COMMIT</span>;
</code></pre>
<h3 id="heading-social-media">Social Media</h3>
<h4 id="heading-user-relationships-graph-database-neo4j">User Relationships (Graph Database - Neo4j)</h4>
<pre><code class="lang-plaintext">// Create users
CREATE (alice:User {id: 1, name: 'Alice', email: 'alice@example.com'})
CREATE (bob:User {id: 2, name: 'Bob', email: 'bob@example.com'})
CREATE (carol:User {id: 3, name: 'Carol', email: 'carol@example.com'})

// Create relationships
CREATE (alice)-[:FOLLOWS {since: date('2024-01-15')}]-&gt;(bob)
CREATE (bob)-[:FOLLOWS {since: date('2024-01-20')}]-&gt;(alice)
CREATE (alice)-[:FOLLOWS]-&gt;(carol)

// Find who Alice follows
MATCH (alice:User {name: 'Alice'})-[:FOLLOWS]-&gt;(following)
RETURN following.name

// Find mutual followers
MATCH (alice:User {name: 'Alice'})-[:FOLLOWS]-&gt;(mutual)&lt;-[:FOLLOWS]-(bob:User {name: 'Bob'})
RETURN mutual.name

// Friend recommendations (friends of friends)
MATCH (alice:User {name: 'Alice'})-[:FOLLOWS]-&gt;()-[:FOLLOWS]-&gt;(recommended)
WHERE NOT (alice)-[:FOLLOWS]-&gt;(recommended) AND alice &lt;&gt; recommended
RETURN recommended.name, COUNT(*) as mutual_connections
ORDER BY mutual_connections DESC
LIMIT 10

// Shortest path between users
MATCH path = shortestPath(
    (alice:User {name: 'Alice'})-[:FOLLOWS*]-(other:User {name: 'David'})
)
RETURN path
</code></pre>
<h4 id="heading-activity-feed">Activity Feed</h4>
<p><strong>Feed-on-Write (Fan-out on write)</strong>:</p>
<pre><code class="lang-python"><span class="hljs-comment"># When user posts</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_post</span>(<span class="hljs-params">user_id, content</span>):</span>
    <span class="hljs-comment"># 1. Save post</span>
    post_id = db.save_post(user_id, content)

    <span class="hljs-comment"># 2. Get all followers</span>
    followers = db.get_followers(user_id)

    <span class="hljs-comment"># 3. Write to each follower's feed (Redis)</span>
    <span class="hljs-keyword">for</span> follower_id <span class="hljs-keyword">in</span> followers:
        redis.zadd(
            <span class="hljs-string">f"feed:<span class="hljs-subst">{follower_id}</span>"</span>,
            {post_id: timestamp}
        )
        <span class="hljs-comment"># Keep only latest 1000 posts</span>
        redis.zremrangebyrank(<span class="hljs-string">f"feed:<span class="hljs-subst">{follower_id}</span>"</span>, <span class="hljs-number">0</span>, <span class="hljs-number">-1001</span>)

<span class="hljs-comment"># Reading feed is fast</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_feed</span>(<span class="hljs-params">user_id, offset=<span class="hljs-number">0</span>, limit=<span class="hljs-number">20</span></span>):</span>
    post_ids = redis.zrevrange(
        <span class="hljs-string">f"feed:<span class="hljs-subst">{user_id}</span>"</span>,
        offset,
        offset + limit - <span class="hljs-number">1</span>
    )
    <span class="hljs-keyword">return</span> db.get_posts(post_ids)
</code></pre>
<p><strong>Feed-on-Read (Fan-out on read)</strong>:</p>
<pre><code class="lang-python"><span class="hljs-comment"># When user posts (simple)</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_post</span>(<span class="hljs-params">user_id, content</span>):</span>
    db.save_post(user_id, content)

<span class="hljs-comment"># Reading feed is expensive</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_feed</span>(<span class="hljs-params">user_id, offset=<span class="hljs-number">0</span>, limit=<span class="hljs-number">20</span></span>):</span>
    <span class="hljs-comment"># Get users this person follows</span>
    following = db.get_following(user_id)

    <span class="hljs-comment"># Get recent posts from all followed users</span>
    posts = db.query(<span class="hljs-string">"""
        SELECT * FROM posts
        WHERE user_id IN ({following_ids})
        ORDER BY created_at DESC
        LIMIT {limit} OFFSET {offset}
    """</span>)
    <span class="hljs-keyword">return</span> posts
</code></pre>
<p><strong>Hybrid Approach</strong>:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_post</span>(<span class="hljs-params">user_id, content</span>):</span>
    post_id = db.save_post(user_id, content)
    followers = db.get_followers(user_id)

    <span class="hljs-comment"># Fan-out to active users only</span>
    active_followers = [f <span class="hljs-keyword">for</span> f <span class="hljs-keyword">in</span> followers <span class="hljs-keyword">if</span> is_active(f)]

    <span class="hljs-keyword">if</span> len(active_followers) &lt; <span class="hljs-number">10000</span>:
        <span class="hljs-comment"># Fan-out on write for users with few followers</span>
        <span class="hljs-keyword">for</span> follower_id <span class="hljs-keyword">in</span> active_followers:
            redis.zadd(<span class="hljs-string">f"feed:<span class="hljs-subst">{follower_id}</span>"</span>, {post_id: timestamp})
    <span class="hljs-keyword">else</span>:
        <span class="hljs-comment"># Celebrity user - fan-out on read</span>
        <span class="hljs-comment"># Mark for feed-on-read</span>
        redis.sadd(<span class="hljs-string">"celebrity_users"</span>, user_id)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_feed</span>(<span class="hljs-params">user_id</span>):</span>
    <span class="hljs-comment"># Get pre-computed feed</span>
    feed_posts = redis.zrevrange(<span class="hljs-string">f"feed:<span class="hljs-subst">{user_id}</span>"</span>, <span class="hljs-number">0</span>, <span class="hljs-number">19</span>)

    <span class="hljs-comment"># Merge with celebrity posts</span>
    following_celebrities = redis.sinter(
        <span class="hljs-string">f"following:<span class="hljs-subst">{user_id}</span>"</span>,
        <span class="hljs-string">"celebrity_users"</span>
    )
    celebrity_posts = db.get_recent_posts(following_celebrities)

    <span class="hljs-keyword">return</span> merge_and_sort(feed_posts, celebrity_posts)
</code></pre>
<h4 id="heading-messageschat">Messages/Chat</h4>
<p><strong>Message Storage (Cassandra)</strong>:</p>
<pre><code class="lang-plaintext">CREATE TABLE messages (
    conversation_id UUID,
    message_id TIMEUUID,
    sender_id INT,
    content TEXT,
    PRIMARY KEY (conversation_id, message_id)
) WITH CLUSTERING ORDER BY (message_id DESC);

-- Query recent messages
SELECT * FROM messages
WHERE conversation_id = 123e4567-e89b-12d3-a456-426614174000
LIMIT 50;

-- Inbox (Cassandra)
CREATE TABLE inbox (
    user_id INT,
    conversation_id UUID,
    last_message_time TIMESTAMP,
    unread_count INT,
    PRIMARY KEY (user_id, last_message_time)
) WITH CLUSTERING ORDER BY (last_message_time DESC);
</code></pre>
<p><strong>Real-time (WebSockets + Redis Pub/Sub)</strong>:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Publisher</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send_message</span>(<span class="hljs-params">conversation_id, sender_id, content</span>):</span>
    <span class="hljs-comment"># Save to database</span>
    message_id = cassandra.save_message(conversation_id, sender_id, content)

    <span class="hljs-comment"># Publish to Redis</span>
    redis.publish(
        <span class="hljs-string">f"conversation:<span class="hljs-subst">{conversation_id}</span>"</span>,
        json.dumps({
            <span class="hljs-string">"message_id"</span>: message_id,
            <span class="hljs-string">"sender_id"</span>: sender_id,
            <span class="hljs-string">"content"</span>: content,
            <span class="hljs-string">"timestamp"</span>: datetime.now()
        })
    )

<span class="hljs-comment"># Subscriber</span>
pubsub = redis.pubsub()
pubsub.subscribe(<span class="hljs-string">'conversation:*'</span>)

<span class="hljs-keyword">for</span> message <span class="hljs-keyword">in</span> pubsub.listen():
    <span class="hljs-comment"># Forward to WebSocket clients</span>
    websocket.send(message[<span class="hljs-string">'data'</span>])
</code></pre>
<h3 id="heading-financial-services">Financial Services</h3>
<h4 id="heading-double-entry-accounting">Double-Entry Accounting</h4>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> accounts (
    account_id <span class="hljs-built_in">SERIAL</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    account_number <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>) <span class="hljs-keyword">UNIQUE</span>,
    account_type <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>), <span class="hljs-comment">-- asset, liability, equity, revenue, expense</span>
    balance <span class="hljs-built_in">DECIMAL</span>(<span class="hljs-number">15</span>,<span class="hljs-number">2</span>),
    currency <span class="hljs-built_in">CHAR</span>(<span class="hljs-number">3</span>)
);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> transactions (
    transaction_id <span class="hljs-built_in">SERIAL</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    transaction_date <span class="hljs-built_in">TIMESTAMP</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">CURRENT_TIMESTAMP</span>,
    description <span class="hljs-built_in">TEXT</span>,
    <span class="hljs-keyword">status</span> <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">20</span>) <span class="hljs-comment">-- pending, posted, void</span>
);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> journal_entries (
    entry_id <span class="hljs-built_in">SERIAL</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    transaction_id <span class="hljs-built_in">INT</span> <span class="hljs-keyword">REFERENCES</span> transactions(transaction_id),
    account_id <span class="hljs-built_in">INT</span> <span class="hljs-keyword">REFERENCES</span> accounts(account_id),
    debit <span class="hljs-built_in">DECIMAL</span>(<span class="hljs-number">15</span>,<span class="hljs-number">2</span>),
    credit <span class="hljs-built_in">DECIMAL</span>(<span class="hljs-number">15</span>,<span class="hljs-number">2</span>),
    <span class="hljs-keyword">CHECK</span> ((debit <span class="hljs-keyword">IS</span> <span class="hljs-literal">NULL</span> <span class="hljs-keyword">AND</span> credit <span class="hljs-keyword">IS</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>) <span class="hljs-keyword">OR</span> (debit <span class="hljs-keyword">IS</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span> <span class="hljs-keyword">AND</span> credit <span class="hljs-keyword">IS</span> <span class="hljs-literal">NULL</span>))
);

<span class="hljs-comment">-- Constraint: Debits must equal credits</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">FUNCTION</span> check_balanced_transaction()
<span class="hljs-keyword">RETURNS</span> <span class="hljs-keyword">TRIGGER</span> <span class="hljs-keyword">AS</span> $$
<span class="hljs-keyword">BEGIN</span>
    <span class="hljs-keyword">IF</span> (<span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">SUM</span>(<span class="hljs-keyword">COALESCE</span>(debit, <span class="hljs-number">0</span>)) - <span class="hljs-keyword">SUM</span>(<span class="hljs-keyword">COALESCE</span>(credit, <span class="hljs-number">0</span>))
        <span class="hljs-keyword">FROM</span> journal_entries
        <span class="hljs-keyword">WHERE</span> transaction_id = NEW.transaction_id) &lt;&gt; <span class="hljs-number">0</span> <span class="hljs-keyword">THEN</span>
        <span class="hljs-keyword">RAISE</span> <span class="hljs-keyword">EXCEPTION</span> <span class="hljs-string">'Transaction not balanced'</span>;
    <span class="hljs-keyword">END</span> <span class="hljs-keyword">IF</span>;
    RETURN NEW;
<span class="hljs-keyword">END</span>;
$$ LANGUAGE plpgsql;

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TRIGGER</span> ensure_balanced
<span class="hljs-keyword">AFTER</span> <span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">OR</span> <span class="hljs-keyword">UPDATE</span> <span class="hljs-keyword">ON</span> journal_entries
<span class="hljs-keyword">FOR</span> <span class="hljs-keyword">EACH</span> <span class="hljs-keyword">ROW</span>
<span class="hljs-keyword">EXECUTE</span> <span class="hljs-keyword">FUNCTION</span> check_balanced_transaction();

<span class="hljs-comment">-- Example: Transfer $100 from Account A to Account B</span>
<span class="hljs-keyword">BEGIN</span>;

<span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> transactions (description) <span class="hljs-keyword">VALUES</span> (<span class="hljs-string">'Transfer'</span>)
<span class="hljs-keyword">RETURNING</span> transaction_id <span class="hljs-keyword">INTO</span> @txn_id;

<span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> journal_entries (transaction_id, account_id, debit, credit) <span class="hljs-keyword">VALUES</span>
(@txn_id, account_a_id, <span class="hljs-literal">NULL</span>, <span class="hljs-number">100.00</span>),  <span class="hljs-comment">-- Credit Account A</span>
(@txn_id, account_b_id, <span class="hljs-number">100.00</span>, <span class="hljs-literal">NULL</span>);  <span class="hljs-comment">-- Debit Account B</span>

<span class="hljs-keyword">UPDATE</span> accounts <span class="hljs-keyword">SET</span> balance = balance - <span class="hljs-number">100</span> <span class="hljs-keyword">WHERE</span> account_id = account_a_id;
<span class="hljs-keyword">UPDATE</span> accounts <span class="hljs-keyword">SET</span> balance = balance + <span class="hljs-number">100</span> <span class="hljs-keyword">WHERE</span> account_id = account_b_id;

<span class="hljs-keyword">COMMIT</span>;
</code></pre>
<h4 id="heading-event-sourcing">Event Sourcing</h4>
<p>Store all changes as events:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> account_events (
    event_id <span class="hljs-built_in">SERIAL</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    account_id <span class="hljs-built_in">INT</span>,
    event_type <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>), <span class="hljs-comment">-- account_created, money_deposited, money_withdrawn</span>
    amount <span class="hljs-built_in">DECIMAL</span>(<span class="hljs-number">15</span>,<span class="hljs-number">2</span>),
    metadata JSONB,
    <span class="hljs-built_in">timestamp</span> <span class="hljs-built_in">TIMESTAMP</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">CURRENT_TIMESTAMP</span>
);

<span class="hljs-comment">-- Rebuild account state from events</span>
<span class="hljs-keyword">SELECT</span> 
    account_id,
    <span class="hljs-keyword">SUM</span>(<span class="hljs-keyword">CASE</span> 
        <span class="hljs-keyword">WHEN</span> event_type = <span class="hljs-string">'money_deposited'</span> <span class="hljs-keyword">THEN</span> amount
        <span class="hljs-keyword">WHEN</span> event_type = <span class="hljs-string">'money_withdrawn'</span> <span class="hljs-keyword">THEN</span> -amount
        <span class="hljs-keyword">ELSE</span> <span class="hljs-number">0</span>
    <span class="hljs-keyword">END</span>) <span class="hljs-keyword">as</span> current_balance
<span class="hljs-keyword">FROM</span> account_events
<span class="hljs-keyword">WHERE</span> account_id = <span class="hljs-number">12345</span>
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> account_id;

<span class="hljs-comment">-- Snapshot for performance</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> account_snapshots (
    account_id <span class="hljs-built_in">INT</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    balance <span class="hljs-built_in">DECIMAL</span>(<span class="hljs-number">15</span>,<span class="hljs-number">2</span>),
    snapshot_at_event_id <span class="hljs-built_in">INT</span>,
    created_at <span class="hljs-built_in">TIMESTAMP</span>
);

<span class="hljs-comment">-- Rebuild from snapshot</span>
<span class="hljs-keyword">SELECT</span> balance <span class="hljs-keyword">FROM</span> account_snapshots <span class="hljs-keyword">WHERE</span> account_id = <span class="hljs-number">12345</span>;
<span class="hljs-comment">-- Plus events since snapshot</span>
<span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">SUM</span>(...) <span class="hljs-keyword">FROM</span> account_events 
<span class="hljs-keyword">WHERE</span> account_id = <span class="hljs-number">12345</span> <span class="hljs-keyword">AND</span> event_id &gt; snapshot_event_id;
</code></pre>
<h3 id="heading-healthcare">Healthcare</h3>
<h4 id="heading-hipaa-compliant-database-design">HIPAA-Compliant Database Design</h4>
<pre><code class="lang-sql"><span class="hljs-comment">-- Patient data with encryption</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> patients (
    patient_id <span class="hljs-keyword">UUID</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    <span class="hljs-comment">-- Encrypted at application level</span>
    encrypted_ssn BYTEA,
    encrypted_name BYTEA,
    encrypted_dob BYTEA,
    date_created <span class="hljs-built_in">TIMESTAMP</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">CURRENT_TIMESTAMP</span>
);

<span class="hljs-comment">-- Audit log (required by HIPAA)</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> audit_log (
    audit_id <span class="hljs-built_in">SERIAL</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    user_id <span class="hljs-built_in">INT</span>,
    table_name <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">100</span>),
    record_id <span class="hljs-keyword">UUID</span>,
    <span class="hljs-keyword">action</span> <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">20</span>), <span class="hljs-comment">-- SELECT, INSERT, UPDATE, DELETE</span>
    <span class="hljs-built_in">timestamp</span> <span class="hljs-built_in">TIMESTAMP</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">CURRENT_TIMESTAMP</span>,
    ip_address INET,
    details JSONB
);

<span class="hljs-comment">-- Trigger for automatic audit logging</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">FUNCTION</span> log_patient_access()
<span class="hljs-keyword">RETURNS</span> <span class="hljs-keyword">TRIGGER</span> <span class="hljs-keyword">AS</span> $$
<span class="hljs-keyword">BEGIN</span>
    <span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> audit_log (user_id, table_name, record_id, <span class="hljs-keyword">action</span>, details)
    <span class="hljs-keyword">VALUES</span> (
        current_user_id(),
        TG_TABLE_NAME,
        <span class="hljs-keyword">COALESCE</span>(NEW.patient_id, OLD.patient_id),
        TG_OP,
        row_to_json(<span class="hljs-keyword">COALESCE</span>(<span class="hljs-keyword">NEW</span>, <span class="hljs-keyword">OLD</span>))
    );
    RETURN COALESCE(NEW, OLD);
<span class="hljs-keyword">END</span>;
$$ LANGUAGE plpgsql;

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TRIGGER</span> patient_audit
<span class="hljs-keyword">AFTER</span> <span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">OR</span> <span class="hljs-keyword">UPDATE</span> <span class="hljs-keyword">OR</span> <span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">ON</span> patients
<span class="hljs-keyword">FOR</span> <span class="hljs-keyword">EACH</span> <span class="hljs-keyword">ROW</span>
<span class="hljs-keyword">EXECUTE</span> <span class="hljs-keyword">FUNCTION</span> log_patient_access();

<span class="hljs-comment">-- Medical records with versioning</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> medical_records (
    record_id <span class="hljs-keyword">UUID</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    patient_id <span class="hljs-keyword">UUID</span> <span class="hljs-keyword">REFERENCES</span> patients(patient_id),
    <span class="hljs-keyword">version</span> <span class="hljs-built_in">INT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    content_encrypted BYTEA,
    created_by <span class="hljs-built_in">INT</span> <span class="hljs-keyword">REFERENCES</span> <span class="hljs-keyword">users</span>(user_id),
    created_at <span class="hljs-built_in">TIMESTAMP</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">CURRENT_TIMESTAMP</span>,
    <span class="hljs-keyword">UNIQUE</span>(patient_id, <span class="hljs-keyword">version</span>)
);
</code></pre>
<h3 id="heading-iot-and-sensor-data">IoT and Sensor Data</h3>
<h4 id="heading-time-series-data-collection">Time-Series Data Collection</h4>
<pre><code class="lang-sql"><span class="hljs-comment">-- TimescaleDB</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> sensor_readings (
    <span class="hljs-built_in">time</span> TIMESTAMPTZ <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    sensor_id <span class="hljs-built_in">INT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    temperature <span class="hljs-keyword">DOUBLE</span> <span class="hljs-keyword">PRECISION</span>,
    humidity <span class="hljs-keyword">DOUBLE</span> <span class="hljs-keyword">PRECISION</span>,
    pressure <span class="hljs-keyword">DOUBLE</span> <span class="hljs-keyword">PRECISION</span>
);

<span class="hljs-comment">-- Convert to hypertable</span>
<span class="hljs-keyword">SELECT</span> create_hypertable(<span class="hljs-string">'sensor_readings'</span>, <span class="hljs-string">'time'</span>);

<span class="hljs-comment">-- Continuous aggregate for hourly averages</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">MATERIALIZED</span> <span class="hljs-keyword">VIEW</span> sensor_readings_hourly
<span class="hljs-keyword">WITH</span> (timescaledb.continuous) <span class="hljs-keyword">AS</span>
<span class="hljs-keyword">SELECT</span> 
    time_bucket(<span class="hljs-string">'1 hour'</span>, <span class="hljs-built_in">time</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">hour</span>,
    sensor_id,
    <span class="hljs-keyword">AVG</span>(temperature) <span class="hljs-keyword">as</span> avg_temp,
    <span class="hljs-keyword">MIN</span>(temperature) <span class="hljs-keyword">as</span> min_temp,
    <span class="hljs-keyword">MAX</span>(temperature) <span class="hljs-keyword">as</span> max_temp,
    <span class="hljs-keyword">AVG</span>(humidity) <span class="hljs-keyword">as</span> avg_humidity
<span class="hljs-keyword">FROM</span> sensor_readings
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">hour</span>, sensor_id;

<span class="hljs-comment">-- Compression policy (compress data older than 7 days)</span>
<span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> sensor_readings <span class="hljs-keyword">SET</span> (
    timescaledb.compress,
    timescaledb.compress_segmentby = <span class="hljs-string">'sensor_id'</span>
);

<span class="hljs-keyword">SELECT</span> add_compression_policy(<span class="hljs-string">'sensor_readings'</span>, <span class="hljs-built_in">INTERVAL</span> <span class="hljs-string">'7 days'</span>);

<span class="hljs-comment">-- Retention policy (drop data older than 1 year)</span>
<span class="hljs-keyword">SELECT</span> add_retention_policy(<span class="hljs-string">'sensor_readings'</span>, <span class="hljs-built_in">INTERVAL</span> <span class="hljs-string">'1 year'</span>);
</code></pre>
<h3 id="heading-multi-tenancy-patterns">Multi-Tenancy Patterns</h3>
<h4 id="heading-separate-database-per-tenant">Separate Database per Tenant</h4>
<pre><code class="lang-python"><span class="hljs-comment"># Simple but resource-intensive</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_database_connection</span>(<span class="hljs-params">tenant_id</span>):</span>
    <span class="hljs-keyword">return</span> connect(database=<span class="hljs-string">f"tenant_<span class="hljs-subst">{tenant_id}</span>"</span>)
</code></pre>
<p><strong>Pros</strong>: Complete isolation, easy to backup/restore individual tenants <strong>Cons</strong>: Resource overhead, difficult to manage many databases</p>
<h4 id="heading-separate-schema-per-tenant">Separate Schema per Tenant</h4>
<pre><code class="lang-sql"><span class="hljs-comment">-- PostgreSQL</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">SCHEMA</span> tenant_1;
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> tenant_1.users (...);
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> tenant_1.orders (...);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">SCHEMA</span> tenant_2;
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> tenant_2.users (...);
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> tenant_2.orders (...);

<span class="hljs-comment">-- Set search path based on tenant</span>
<span class="hljs-keyword">SET</span> search_path <span class="hljs-keyword">TO</span> tenant_1;
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span>;  <span class="hljs-comment">-- Queries tenant_1.users</span>
</code></pre>
<p><strong>Pros</strong>: Good isolation, easier than separate databases <strong>Cons</strong>: Still some overhead, schema changes must apply to all</p>
<h4 id="heading-shared-tables-with-tenant-id">Shared Tables with Tenant ID</h4>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-keyword">users</span> (
    user_id <span class="hljs-built_in">SERIAL</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    tenant_id <span class="hljs-built_in">INT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    username <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">100</span>),
    email <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">100</span>),
    <span class="hljs-comment">-- Composite index for tenant filtering</span>
    <span class="hljs-keyword">INDEX</span> idx_tenant_user (tenant_id, user_id)
);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> orders (
    order_id <span class="hljs-built_in">SERIAL</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    tenant_id <span class="hljs-built_in">INT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    user_id <span class="hljs-built_in">INT</span>,
    order_date <span class="hljs-built_in">TIMESTAMP</span>,
    <span class="hljs-keyword">INDEX</span> idx_tenant_order (tenant_id, order_id)
);

<span class="hljs-comment">-- Row-level security (PostgreSQL)</span>
<span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-keyword">users</span> <span class="hljs-keyword">ENABLE</span> <span class="hljs-keyword">ROW</span> <span class="hljs-keyword">LEVEL</span> <span class="hljs-keyword">SECURITY</span>;

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">POLICY</span> tenant_isolation <span class="hljs-keyword">ON</span> <span class="hljs-keyword">users</span>
    <span class="hljs-keyword">FOR</span> <span class="hljs-keyword">ALL</span>
    <span class="hljs-keyword">TO</span> <span class="hljs-keyword">PUBLIC</span>
    <span class="hljs-keyword">USING</span> (tenant_id = current_setting(<span class="hljs-string">'app.current_tenant'</span>)::<span class="hljs-built_in">INT</span>);

<span class="hljs-comment">-- Set tenant context</span>
<span class="hljs-keyword">SET</span> app.current_tenant = <span class="hljs-string">'123'</span>;
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span>;  <span class="hljs-comment">-- Only sees tenant 123's users</span>
</code></pre>
<p><strong>Pros</strong>: Efficient resource usage, easier management <strong>Cons</strong>: Must be careful with queries, potential for data leaks</p>
<p>Thanks for Reading!</p>
]]></content:encoded></item><item><title><![CDATA[Adapting LLaMA for NER Tasks]]></title><description><![CDATA[Customize your models using PEFT
1. Introduction
Named Entity Recognition (NER) is the task of finding and labeling entities like dates, names, places organizations etc. in text. Most people use encoder models such as BERT for this, but I wanted to t...]]></description><link>https://arnavverma.hashnode.dev/adapting-llama-for-ner-tasks-2a9ab3425f46</link><guid isPermaLink="true">https://arnavverma.hashnode.dev/adapting-llama-for-ner-tasks-2a9ab3425f46</guid><dc:creator><![CDATA[Arnav Verma]]></dc:creator><pubDate>Wed, 24 Sep 2025 06:42:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766214860043/29b80e66-9dfe-4362-8714-b556240518c2.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h4 id="heading-customize-your-models-using-peft">Customize your models using PEFT</h4>
<h3 id="heading-1-introduction">1. Introduction</h3>
<p>Named Entity Recognition (NER) is the task of finding and labeling entities like dates, names, places organizations etc. in text. Most people use encoder models such as BERT for this, but I wanted to try something different, training a <strong>LLaMA model for token classification</strong>. To make training efficient, I used <strong>LoRA adapters</strong> and <strong>4-bit quantization</strong>.</p>
<p>I worked with a CoNLL-style dataset of about 90k samples, with just three labels: O, B-T, and I-T. T stands for time. In my dataset I wanted to make the model recognize the Temporal Entities.</p>
<p>In this post, I’ll go through how I prepared the dataset, set up LLaMA model for PEFT config, prepare it for NER, handled the errors that came up, and pushed the final model to Hugging Face for inference.</p>
<h3 id="heading-2-preparing-the-dataset">2. Preparing the Dataset</h3>
<p>The dataset I used was in <strong>.conll format</strong>, where each line has a word and its corresponding tag, and sentences are separated by blank lines. To train the model, I first wrote a <strong>data_loader.py</strong> file that could read through the conll file and collect everything into <strong>words</strong>, <strong>tags</strong>, <strong>sentences</strong>, and <strong>labels</strong>. Below is a small peak into the dataset I used:</p>
<p>AFP_ENG_19970409 O<br />. O</p>
<p>0547 ONEW O<br />YORK O<br />, O<br />April B-T<br />9 I-T<br />, I-T<br />1997 I-T<br />( O<br />AFP O<br />) O</p>
<p>Tokens and their tags are seperated by a white space. Sentences are seperated by ‘\n’ or new line. Below is a small piece of code that I wrote to extract this into python to futher build the trainable dataset.</p>
<p>from datasets import Dataset<br />def load_data(filepath):  </p>
<p>    sentences = []<br />    labels = []<br />    words = []<br />    tags = []</p>
<p>    with open(filepath, "r") as dataset:<br />        for line in dataset:<br />            if line != "\n":<br />                sample = line.strip().split()<br />                words.append(sample[0])<br />                tags.append(sample[1])<br />            else:<br />                sentences.append(words)<br />                labels.append(tags)<br />                words, tags = [], []<br />    data = Dataset.from_dict({"inputs": sentences, "tags": labels})<br />    return data</p>
<p>After building the dataset dictionary, the next step was to <strong>convert the string labels into numbers</strong> so the model could work with them. I used LabelEncoder from scikit-learn to map the tags:</p>
<p>B-T --&gt; 0<br />I-T --&gt; 1<br />O   --&gt; 2</p>
<p>Now the dataset looked like this:</p>
<p>{<br />  "tokens": [["NEW", "YORK", "," , "April", "9"], ...],<br />  "labels": [[0, 0, 0, 1, 2], ...]<br />}</p>
<p>Since LLaMA uses subword tokenization, I also had to <strong>align the labels with the tokenized outputs</strong>. This meant repeating a label for every subword and marking padding with -100 so it wouldn’t affect training.</p>
<p>With this step done, the dataset was ready for training.</p>
<h3 id="heading-3-model-setup">3. Model Setup</h3>
<p>For the model, I started with <strong>LlamaForTokenClassification</strong> method from Huggingface transformers library, I used the base checkpoint model <strong>“meta-llama/llama-3.2–1b”</strong>. This gave me a LLaMA backbone with a token classification head on top, which is what I needed for NER.</p>
<p>Since the model I used has 1 Billion trainable parameters, Training the full model would be too large and slow and would not fit in my small GPU’s memory :( I used <strong>LoRA (Low-Rank Adaptation)</strong> to fine tune only a small set of parameters. I added <strong>bitsandbytes 4-bit quantization</strong>. With bitsandbytes, the model weights are stored in 4-bit instead of the usual 16 or 32 bit, which cuts memory usage a picking only a small number of parameters and makes training possible on a single GPU. You can also do 8-Bit or mixed precision 16-bit. Here’s a snippet of code.</p>
<p>...<br />bnb_config = BitsAndBytesConfig(<br />    load_in_4bit=True,<br />    bnb_4bit_use_double_quant=True,<br />    bnb_4bit_quant_type="nf4",<br />    bnb_4bit_compute_dtype=torch.bfloat16 if torch.cuda.is_available() else torch.float16,<br />)</p>
<p>model = AutoModelForTokenClassification.from_pretrained(<br />    model_id,<br />    num_labels=num_labels,<br />    torch_dtype=torch.bfloat16 if torch.cuda.is_available() else torch.float16,<br />    quantization_config=bnb_config,<br />    device_map="auto",<br />    token=hf_token<br />)<br />...</p>
<p>For the <strong>tokenizer</strong>, I load the LlamaTokenizer directly. Since LLaMA does not define a pad token by default, I add my own <strong>[PAD]</strong> token and set it as the <strong>pad token ID</strong>. Padding is set to the right side, which is standard for training.</p>
<p>...<br />tokenizer = LlamaTokenizer.from_pretrained(model_id, token=hf_token)</p>
<p>if tokenizer.pad_token is None: #true in case of llama models<br />    tokenizer.add_special_tokens({'pad_token': '[PAD]'})<br />    tokenizer.pad_token = '[PAD]'</p>
<p>tokenizer.padding_side = "right"<br />tokenizer.pad_token_id = tokenizer.convert_tokens_to_ids(tokenizer.pad_token)</p>
<p>This setup ensures that the tokenizer and model stay consistent the pad token exists in the vocabulary, sequences are padded correctly, and the embeddings match the vocab size.</p>
<p>It’s important that the tokenizer and the model’s embedding matrix always match. When I added a new [PAD] token, the tokenizer’s vocabulary size increased by one. If I didn’t update the model embeddings to match, I would get the common error:</p>
<p>RuntimeError: size mismatch for<br />model.embed_tokens.weight: [128257, 2048] vs [128256, 2048]</p>
<p>Fix:</p>
<p>model.resize_token_embeddings(len(tokenizer))</p>
<h3 id="heading-31-peft-parameter-efficient-fine-tuning">3.1 PEFT (Parameter-Efficient Fine-Tuning)</h3>
<p>Already mentioned this above but I will elaborate a bit more. I used <strong>PEFT (Parameter-Efficient Fine-Tuning)</strong> with <strong>LoRA (Low-Rank Adaptation)</strong>. LoRA only trains a small number of additional parameters on top of the frozen model weights, which makes training faster and lighter. Here’s the setup. You have pass your base model to peft config:</p>
<p>from peft import prepare_model_for_kbit_training, LoraConfig, get_peft_model, TaskType</p>
<p># prepare model for k-bit training (freeze some layers, enable gradient checkpointing)<br />model = prepare_model_for_kbit_training(model)</p>
<p># LoRA configuration<br />lora_cfg = LoraConfig(<br />    r=16,<br />    lora_alpha=32,<br />    lora_dropout=0.05,<br />    bias="none",<br />    task_type=TaskType.TOKEN_CLS,<br />    target_modules=[<br />        "q_proj", "k_proj", "v_proj", "o_proj",<br />        "gate_proj", "up_proj", "down_proj",<br />    ],<br />)</p>
<p># wrap model with LoRA<br />model = get_peft_model(model, lora_cfg)</p>
<p>The target_modules list tells LoRA which parts of the model to adapt. In LLaMA, the main places where information flows are the <strong>attention projections</strong> <strong><em>(q_proj, k_proj, v_proj, o_proj)</em></strong> and the <strong>feed-forward network layers</strong> <strong><em>(gate_proj, up_proj, down_proj).</em></strong></p>
<p>By applying LoRA only to these modules, I could capture most of the model’s expressive power while training only a fraction of the parameters.</p>
<h3 id="heading-4-training">4. Training</h3>
<p>With the model and tokenizer ready, I set up the training loop using Hugging Face’s Trainer. I loaded my training and test datasets, made sure the tokenizer and model embeddings were aligned, and then defined the training arguments.</p>
<p>Some of the important settings I used:</p>
<ul>
<li><strong>Batch size</strong>: 32 (with gradient accumulation = 2)</li>
<li><strong>Epochs</strong>: 5</li>
<li><strong>Learning rate</strong>: 3e-5 with a cosine scheduler</li>
<li><strong>Warmup ratio</strong>: 0.05</li>
<li><strong>Evaluation</strong>: every 256 steps, with early stopping (patience = 3)</li>
<li><strong>Metrics</strong>: F1 score as the main metric to track best model</li>
<li><strong>Logging</strong>: weights &amp; biases (wandb) for monitoring</li>
</ul>
<p>Here’s the core part of my script:</p>
<p>training_args \= TrainingArguments(<br />    output_dir\="./results",<br />    logging_dir\="./logs",<br />    logging_steps\=10,<br />    save_steps\=512,<br />    save_total_limit\=3,<br />    num_train_epochs\=5,<br />    per_device_train_batch_size\=32,<br />    per_device_eval_batch_size\=32,<br />    gradient_accumulation_steps\=2,<br />    learning_rate\=3e-5,<br />    warmup_ratio\=0.05,<br />    lr_scheduler_type\="cosine",<br />    weight_decay\=0.01,<br />    eval_strategy\="steps",<br />    eval_steps\=256,<br />    load_best_model_at_end\=True,<br />    report_to\="wandb",<br />    metric_for_best_model\="f1",<br />)</p>
<p>trainer = Trainer(<br />    model=model,<br />    args=training_args,<br />    train_dataset=train_dataset,<br />    eval_dataset=test_dataset,<br />    tokenizer=tokenizer,<br />    callbacks=[EarlyStoppingCallback(early_stopping_patience=3)],<br />    compute_metrics=lambda p: compute_metrics(p, le.classes_, tokenizer),<br />)<br />trainer.train()</p>
<h3 id="heading-5-evaluation-results">5. Evaluation Results</h3>
<p>After training for about 4–5 epochs on my 90k-sample dataset, the model reached strong performance. The main numbers:</p>
<ul>
<li><strong>Eval Loss</strong>: ~0.03</li>
<li><strong>Accuracy</strong>: ~99%</li>
<li><strong>Precision</strong>: ~0.93</li>
<li><strong>Recall</strong>: ~0.91</li>
<li><strong>F1 Score</strong>: ~0.92</li>
</ul>
<p>The F1 score is the most useful here since the dataset is imbalanced (most tokens are just O). An F1 above 0.9 means the model learned to identify entity spans reliably instead of just predicting everything as O.</p>
<p>Training loss also stayed low (~0.08), which shows the model generalized well and didn’t just memorize the training set.</p>
<h3 id="heading-6-merging-lora-into-the-base-model">6. Merging LoRA into the Base Model</h3>
<p>After training with LoRA, the weights are stored as adapters on top of the base LLaMA model. That means if you want to use the model directly for inference (without loading PEFT every time), you need to <strong>merge the LoRA weights into the base model</strong>.</p>
<p>I first resized the base model embeddings so they matched the tokenizer , Then I loaded the LoRA adapter and merged it back into the model</p>
<p>base_model.resize_token_embeddings(len(tokenizer))</p>
<p>from peft import PeftModel  </p>
<p>model \= PeftModel.from_pretrained(base_model, adapter_dir)<br />model = model.merge_and_unload()</p>
<p>Finally, I saved the merged model and tokenizer:</p>
<p>model.save_pretrained("./final-model")<br />tokenizer.save_pretrained("./final-model")</p>
<p>With this step, the model becomes fully standalone. I could then push it to Hugging Face Hub and load it anywhere with a single pipeline call.</p>
<h3 id="heading-7-deployment-on-hugging-face">7. Deployment on Hugging Face</h3>
<p>Once the LoRA adapters were merged and the tokenizer was saved correctly, I pushed the model to Hugging Face Hub. This makes it easy to load and run inference anywhere.</p>
<p>To push:</p>
<p>model.push_to_hub("namesarnav/llama-3.2-1b-NER-timex")<br />tokenizer.push_to_hub("namesarnav/llama-3.2-1b-NER-timex")</p>
<p>After that, using the model is just a few lines of code with the Hugging Face pipeline:</p>
<p>from transformers import pipeline</p>
<p>ner = pipeline(<br />    "token-classification",<br />    model="namesarnav/llama-3.2-1b-NER-timex",<br />    tokenizer="namesarnav/llama-3.2-1b-NER-timex",<br />    aggregation_strategy="simple"<br />)</p>
<p>text = "NEW YORK, April 9, 1997 (AFP)."<br />print(ner(text))</p>
<p>This runs inference directly and outputs the entities recognized in the text. No PEFT setup is needed once the LoRA weights are merged.</p>
<h3 id="heading-8-conclusion">8. Conclusion</h3>
<p>LLaMA model for NER wasn’t as straightforward as using encoder models like BERT, but it worked well once the setup was right. With LoRA and 4-bit quantization, I was able to fine-tune a billion-parameter model on a single GPU.</p>
<p>Along the way I learned a few key lessons:</p>
<ul>
<li>Always keep the tokenizer and model embeddings in sync.</li>
<li>Set num_labels correctly before saving to avoid head mismatches.</li>
<li>Flatten labels and ignore padding when computing metrics</li>
<li>Merge LoRA weights if you want a clean, standalone model for inference.</li>
</ul>
<p>The final model reached an F1 score above 0.9 on my dataset, showing that decoder only models like LLaMA can handle token classification tasks effectively with the right adjustments.</p>
<p>Pushing the model to Hugging Face made it easy to share and run inference with just a pipeline call. That step turned the project from an experiment into something reusable.</p>
<p>Checkout my Github where the full source code of this project exists using this link — <a target="_blank" href="https://github.com/namesarnav">https://github.com/namesarnav</a></p>
<p>Thanks for Reading. Please share if you found it helpful :)</p>
]]></content:encoded></item></channel></rss>