<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Machine-Learning on Krishnatejaswi S</title>
    <link>https://KTS-o7.github.io/bear/tags/machine-learning/</link>
    <description>Recent content in Machine-Learning on Krishnatejaswi S</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <managingEditor>shentharkrishnatejaswi@gmail.com (Krishnatejaswi S)</managingEditor>
    <webMaster>shentharkrishnatejaswi@gmail.com (Krishnatejaswi S)</webMaster>
    <copyright>Krishnatejaswi S</copyright>
    <lastBuildDate>Sun, 03 May 2026 00:00:00 +0530</lastBuildDate>
    <atom:link href="https://KTS-o7.github.io/bear/tags/machine-learning/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>KV Cache and PagedAttention: Why vLLM Actually Works</title>
      <link>https://KTS-o7.github.io/bear/ml/kv_cache_paged_attention/</link>
      <pubDate>Sun, 03 May 2026 00:00:00 +0530</pubDate><author>shentharkrishnatejaswi@gmail.com (Krishnatejaswi S)</author>
      <guid>https://KTS-o7.github.io/bear/ml/kv_cache_paged_attention/</guid>
      <description>&lt;p&gt;LLM inference is split into two phases with completely different performance characteristics. The first phase processes all input tokens in parallel &amp;ndash; it&amp;rsquo;s compute-bound, and modern GPUs handle it well. The second phase generates one token at a time &amp;ndash; it&amp;rsquo;s memory-bandwidth-bound, and this is where naive serving falls apart.&lt;/p&gt;&#xA;&lt;p&gt;The key data structure driving both phases is the KV cache.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-the-kv-cache-is&#34;&gt;What the KV Cache Is&lt;/h2&gt;&#xA;&lt;p&gt;In the attention mechanism, each token attends to every previous token in the sequence. To compute attention for token N, you need the key and value vectors for tokens 1 through N-1. Recomputing those from scratch at each step would mean re-running the full model for all previous tokens every time you generate a new one &amp;ndash; O(N^2) compute per sequence.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>LLM inference is split into two phases with completely different performance characteristics. The first phase processes all input tokens in parallel &ndash; it&rsquo;s compute-bound, and modern GPUs handle it well. The second phase generates one token at a time &ndash; it&rsquo;s memory-bandwidth-bound, and this is where naive serving falls apart.</p>
<p>The key data structure driving both phases is the KV cache.</p>
<h2 id="what-the-kv-cache-is">What the KV Cache Is</h2>
<p>In the attention mechanism, each token attends to every previous token in the sequence. To compute attention for token N, you need the key and value vectors for tokens 1 through N-1. Recomputing those from scratch at each step would mean re-running the full model for all previous tokens every time you generate a new one &ndash; O(N^2) compute per sequence.</p>
<p>The KV cache avoids this. After the prefill phase, the key and value vectors for every input token are stored in GPU memory. During the decode phase, each new token appended to the sequence just needs to compute its own K and V vectors and append them to the cache. Attending to all previous tokens is then a lookup into the cached K and V tensors, not a recomputation.</p>
<p>The cost of this: GPU memory. For a 7B parameter model at FP16, the KV cache for a single sequence can be hundreds of megabytes depending on context length, number of layers, and number of attention heads. For larger models serving many concurrent users, the KV cache dominates memory usage.</p>
<h2 id="the-fragmentation-problem">The Fragmentation Problem</h2>
<p>Naive serving allocates KV cache memory statically: reserve a maximum-size block for each sequence upfront. This has the same problem as a badly written memory allocator.</p>
<p>Say you pre-allocate 4096 tokens per sequence and your GPU can hold 20 such blocks. A new request comes in and needs 10 blocks. 18 are free but they&rsquo;re fragmented across physical memory &ndash; no contiguous run of 10. Request rejected, GPU sitting at 60% capacity.</p>
<p>Even worse: you don&rsquo;t know the output length when a request arrives. If you allocate for the max context length immediately, most of that memory is wasted for short outputs. If you allocate conservatively, long outputs fail mid-generation.</p>
<h2 id="pagedattention">PagedAttention</h2>
<p>vLLM&rsquo;s solution, introduced in the 2023 paper, borrows the virtual memory abstraction from operating systems.</p>
<p>Physical GPU memory is divided into fixed-size blocks (pages), typically 16 tokens each. Each sequence gets a logical block table that maps logical block indices to physical blocks. The blocks for one sequence don&rsquo;t need to be contiguous in physical memory &ndash; the block table handles the translation.</p>





<pre tabindex="0"><code>Sequence A: logical [0,1,2] → physical [block_7, block_2, block_15]
Sequence B: logical [0,1]   → physical [block_3, block_11]</code></pre><p>Blocks are allocated one at a time as tokens are generated, not all upfront. A short output uses a few blocks and returns them when done. A long output gradually accumulates more. Fragmentation is eliminated because the allocation granularity matches the actual growth pattern of sequences.</p>
<p>The result: GPU memory utilization goes from 20-40% with naive block allocation to 70-90% with PagedAttention. More sequences fit in memory simultaneously, which means higher throughput.</p>
<h2 id="continuous-batching">Continuous Batching</h2>
<p>PagedAttention works with continuous batching to make the GPU utilization gains practical.</p>
<p>Traditional batching waits until a batch of N requests is assembled, runs inference for the full batch, returns all results. Any sequence that finishes early holds its slot until the full batch is done. Expensive for variable-length outputs.</p>
<p>Continuous batching (also called iteration-level scheduling) processes one decode step at a time across all in-flight sequences. After each step, finished sequences are removed and new requests are inserted into the freed slots. The batch is always full &ndash; there&rsquo;s no waiting.</p>
<p>Combined with PagedAttention, the memory freed by completed sequences is immediately available for new ones. The GPU is busy at every step, not idling through early completions.</p>
<h2 id="radixattention">RadixAttention</h2>
<p>SGLang introduced RadixAttention for workloads with shared prompt prefixes, common in agentic systems and structured generation.</p>
<p>Consider an agent that runs many LLM calls with the same system prompt and tool definitions &ndash; often 1000+ tokens. With standard PagedAttention, each request recomputes the KV vectors for those shared tokens during prefill. If you&rsquo;re making 100 calls with the same 1000-token preamble, that&rsquo;s 100x the prefill compute for tokens that never change.</p>
<p>RadixAttention maintains a radix tree of cached KV blocks keyed by token sequence prefix. When a new request arrives, it walks the tree to find the longest matching prefix, reuses those cached blocks, and only computes KV vectors for the novel suffix.</p>
<p>For agentic workloads where the same system prompt and tool list appear in every call, the first request pays the full prefill cost. Every subsequent request with the same prefix pays only for its unique portion. At the scale of thousands of agent steps, this is a substantial throughput difference.</p>
<p>The tradeoff: RadixAttention adds cache management overhead. The tree needs to be pruned when memory pressure is high. The eviction policy matters &ndash; evicting a heavily shared prefix that gets reused on the next request undoes the benefit. SGLang uses LRU eviction, which works well for stable prefixes but less well for highly dynamic ones.</p>
<h2 id="choosing-between-vllm-and-sglang">Choosing Between vLLM and SGLang</h2>
<p>The choice comes down to workload:</p>
<p><strong>vLLM</strong> is the default for general inference, chat APIs, and throughput benchmarks. PagedAttention and continuous batching are well-tested across a wide range of models and hardware.</p>
<p><strong>SGLang</strong> is better for agentic workflows, structured output (constrained decoding for JSON, function calling), and any case where many requests share large prompt prefixes. The RadixAttention benefit only materializes when your prefix reuse rate is high.</p>
<p>Both expose an OpenAI-compatible API and support the same quantization formats. Running both and profiling your actual traffic pattern is the right answer if you&rsquo;re unsure &ndash; the throughput difference on real workloads is usually decisive.</p>
<p>The KV cache is why LLM inference is memory-bound rather than compute-bound during decode. Understanding it makes the rest of the optimization landscape &ndash; quantization to shrink the model, PagedAttention to use memory efficiently, RadixAttention to reuse computation &ndash; follow naturally from first principles.</p>
]]></content:encoded>
    </item>
    <item>
      <title>LLM Inference Optimization: Stop Paying for Idle Silicon</title>
      <link>https://KTS-o7.github.io/bear/ml/llm_inference_optimization/</link>
      <pubDate>Sat, 02 May 2026 00:00:00 +0530</pubDate><author>shentharkrishnatejaswi@gmail.com (Krishnatejaswi S)</author>
      <guid>https://KTS-o7.github.io/bear/ml/llm_inference_optimization/</guid>
      <description>&lt;p&gt;GPU bills were doubling every quarter. Throughput wasn&amp;rsquo;t moving. I kept assuming the answer was a bigger GPU. It wasn&amp;rsquo;t.&lt;/p&gt;&#xA;&lt;p&gt;The problem was naive serving. Standard HuggingFace &lt;code&gt;pipeline()&lt;/code&gt; processes one request at a time, sequentially. The GPU sits idle between requests, waiting. You&amp;rsquo;re paying for hardware that&amp;rsquo;s doing nothing most of the time.&lt;/p&gt;&#xA;&lt;p&gt;The fix is a set of four techniques, each with a different ROI profile. Here&amp;rsquo;s what they are, how they work, and when to use each one.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>GPU bills were doubling every quarter. Throughput wasn&rsquo;t moving. I kept assuming the answer was a bigger GPU. It wasn&rsquo;t.</p>
<p>The problem was naive serving. Standard HuggingFace <code>pipeline()</code> processes one request at a time, sequentially. The GPU sits idle between requests, waiting. You&rsquo;re paying for hardware that&rsquo;s doing nothing most of the time.</p>
<p>The fix is a set of four techniques, each with a different ROI profile. Here&rsquo;s what they are, how they work, and when to use each one.</p>
<h2 id="quantization">Quantization</h2>
<p>This is the first thing to do because it&rsquo;s a one-time change with immediate impact. AWQ 4-bit quantization cuts VRAM usage by roughly 75%. FP8 cuts it by 50% but requires H100s.</p>
<p>That 75% reduction matters because it changes which GPU class you need. A model that required an A100 might now fit on an A6000. That&rsquo;s not a minor cost reduction &ndash; it&rsquo;s a different tier of hardware entirely.</p>
<p>The tooling is straightforward. Use <a href="https://github.com/casper-hansen/AutoAWQ">AutoAWQ</a> to quantize yourself, or just pull a pre-quantized checkpoint from HuggingFace. Most popular models have AWQ variants already uploaded.</p>
<h2 id="serving-engine">Serving Engine</h2>
<p>Once you&rsquo;re quantized, the next question is which serving engine to use. The two serious options are vLLM and SGLang, and they&rsquo;re not interchangeable.</p>
<p><strong>vLLM</strong> is the right choice for chat and general inference. Two mechanisms make the difference:</p>
<p>Continuous batching inserts new requests into the batch as slots open up rather than waiting for a full batch to complete. Naive serving sits at around 20% GPU utilization. vLLM pushes that to 60-85%.</p>
<p>PagedAttention treats VRAM like virtual memory for the KV cache. Instead of pre-allocating contiguous blocks, it pages memory on demand. More sequences can coexist in memory simultaneously, which means more throughput per dollar.</p>
<p>The key vLLM flags worth knowing in production:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">--max-num-seqs <span class="m">64</span>        <span class="c1"># tune for your context length + available VRAM</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">--disable-log-stats      <span class="c1"># remove logging overhead</span></span></span></code></pre></div><p><strong>SGLang</strong> is the right choice for agentic workflows and structured output. Its main advantage is RadixAttention &ndash; it automatically detects shared prompt prefixes and reuses their KV cache. If every request in your system starts with the same 1000-token system prompt and tool definitions, SGLang computes that prefix once and caches it. LMSYS benchmarks show higher throughput than vLLM on structured generation workloads.</p>
<p>The rule of thumb: SGLang for agents and JSON, vLLM for chat.</p>
<h2 id="speculative-decoding">Speculative Decoding</h2>
<p>This one is less obvious but gets you 2-3x speedup on long-form generation. The mechanism is a draft-then-verify loop:</p>
<ol>
<li>A small draft model (1-7B parameters) generates 3-12 candidate tokens per step</li>
<li>The large target model verifies all candidates in a single parallel forward pass</li>
<li>When the draft guesses correctly &ndash; which happens 70-90% of the time &ndash; you get multiple tokens for the cost of one forward pass</li>
</ol>
<p>The canonical pairing is Llama-3-8B-Instruct-AWQ as the draft model against Llama-3-70B-Instruct-AWQ as the target.</p>
<p>A full deployment looks like this:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kn">import</span> <span class="nn">runpod</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="n">runpod</span><span class="o">.</span><span class="n">api_key</span> <span class="o">=</span> <span class="s2">&#34;your_api_key&#34;</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="n">pod</span> <span class="o">=</span> <span class="n">runpod</span><span class="o">.</span><span class="n">create_pod</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="n">name</span><span class="o">=</span><span class="s2">&#34;llama3-70b-speculative&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="n">image_name</span><span class="o">=</span><span class="s2">&#34;vllm/vllm-openai:latest&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="n">gpu_type_id</span><span class="o">=</span><span class="s2">&#34;NVIDIA RTX A6000&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="n">gpu_count</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="n">docker_args</span><span class="o">=</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">        <span class="s2">&#34;--model TechxGenus/Meta-Llama-3-70B-Instruct-AWQ &#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">        <span class="s2">&#34;--quantization awq &#34;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">        <span class="s2">&#34;--tensor-parallel-size 2 &#34;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">        <span class="s2">&#34;--speculative-model TechxGenus/Meta-Llama-3-8B-Instruct-AWQ &#34;</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">        <span class="s2">&#34;--num-speculative-tokens 5 &#34;</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">        <span class="s2">&#34;--max-model-len 8192&#34;</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="p">)</span></span></span></code></pre></div><p>The critical thing to watch: draft model acceptance rate. Monitor it via Prometheus:</p>





<pre tabindex="0"><code>vllm:spec_decode_draft_acceptance_length</code></pre><p>If acceptance drops below 0.5 tokens per step, you have the wrong draft model. It adds overhead rather than saving it. The draft model must match the target architecture &ndash; vLLM won&rsquo;t warn you about this at startup.</p>
<h2 id="deployment-mode">Deployment Mode</h2>
<p>The last decision is serverless vs. pods, and it&rsquo;s purely a traffic pattern question.</p>
<p>Spiky or unpredictable traffic belongs on serverless &ndash; you pay only for actual inference time. Sustained, predictable load is cheaper on pods because you&rsquo;re not paying the serverless premium on every call.</p>
<p>Neither is universally better. The mistake is running pods for workloads that get 10 requests an hour or paying for serverless when you have a steady 200 RPS.</p>
<h2 id="why-decode-is-the-bottleneck">Why Decode is the Bottleneck</h2>
<p>LLM generation has two phases and they&rsquo;re bound by different things.</p>
<p>Prefill is compute-bound &ndash; processing all the input tokens in one parallel forward pass. Decode is memory-bound &ndash; generating one token at a time, loading the entire model&rsquo;s KV cache from VRAM on every single step.</p>
<p>This means VRAM bandwidth determines inter-token latency, not FLOPs. Two GPUs with similar compute specs can have very different inference latency:</p>
<table>
  <thead>
      <tr>
          <th>GPU</th>
          <th>Memory Bandwidth</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>H100 SXM5</td>
          <td>3.35 TB/s</td>
      </tr>
      <tr>
          <td>A6000</td>
          <td>768 GB/s</td>
      </tr>
  </tbody>
</table>
<p>That&rsquo;s a ~4x difference on long-form generation. If you&rsquo;re choosing hardware for an inference workload and only looking at FLOPs, you&rsquo;re optimizing the wrong thing.</p>
<p>The practical checklist once you understand this:</p>
<ul>
<li>Quantize with AWQ first (or FP8 on H100s)</li>
<li>Pick SGLang for agents and JSON, vLLM for chat</li>
<li>Enable speculative decoding for long-form endpoints, monitor acceptance rate</li>
<li>Wire Prometheus to <code>vllm:gpu_cache_usage_perc</code> to know when you&rsquo;re VRAM-constrained</li>
<li>Match deployment mode to your actual traffic shape</li>
</ul>
<p>The hardware question comes last, not first.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Eigenvalues of a Matrix</title>
      <link>https://KTS-o7.github.io/bear/ml/eigen_vals/</link>
      <pubDate>Thu, 08 Jan 2026 07:07:07 +0100</pubDate><author>shentharkrishnatejaswi@gmail.com (Krishnatejaswi S)</author>
      <guid>https://KTS-o7.github.io/bear/ml/eigen_vals/</guid>
      <description>&lt;h2 id=&#34;why-eigenvalues-matter&#34;&gt;Why Eigenvalues Matter&lt;/h2&gt;&#xA;&lt;p&gt;Eigenvalues describe the directions along which a linear transformation stretches or compresses space. Behind every PCA dimension, every stability analysis, and every spectral decomposition lies the same question: &lt;em&gt;What scalars \(\lambda\) satisfy \(A\mathbf{v} = \lambda \mathbf{v}\) for some non-zero vector \(\mathbf{v}\)?&lt;/em&gt; Here we clarify the polynomial road map to those scalars and show how it generalizes from 2×2 matrices to higher dimensions, including why principal minors appear in the coefficients.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="why-eigenvalues-matter">Why Eigenvalues Matter</h2>
<p>Eigenvalues describe the directions along which a linear transformation stretches or compresses space. Behind every PCA dimension, every stability analysis, and every spectral decomposition lies the same question: <em>What scalars \(\lambda\) satisfy \(A\mathbf{v} = \lambda \mathbf{v}\) for some non-zero vector \(\mathbf{v}\)?</em> Here we clarify the polynomial road map to those scalars and show how it generalizes from 2×2 matrices to higher dimensions, including why principal minors appear in the coefficients.</p>
<h2 id="the-characteristic-equation">The Characteristic Equation</h2>
<p>For any square matrix \(A\), the eigenvalues are the roots of the characteristic polynomial:</p>
\[
\chi(\lambda) = \det(A - \lambda I) = 0.
\]<h3 id="22-refresher">2×2 refresher</h3>
<p>When \(A = \begin{bmatrix} a & b \\ c & d \end{bmatrix}\), the determinant expands to</p>
\[
\chi(\lambda) = \lambda^2 - (a+d)\lambda + (ad - bc).
\]<p>Hence the trace \((a+d)\) appears as the \(\lambda\)-coefficient and the determinant \(ad-bc\) is the constant term. Solving the quadratic gives the eigenvalues.</p>
<h2 id="worked-examples--and">Worked Examples: \(n=3\) and \(n=4\)</h2>
<h3>\(3 × 3\)</h3>
<p>Let</p>
\[
A = \begin{bmatrix}
a & b & c \\
d & e & f \\
g & h & i
\end{bmatrix}.
\]<p>Then \(A - \lambda I\) becomes</p>
\[
\begin{bmatrix}
a - \lambda & b & c \\
d & e - \lambda & f \\
g & h & i - \lambda
\end{bmatrix},
\]<p>
For a 3×3 matrix \( A = \begin{bmatrix} a & b & c \\ d & e & f \\ g & h & i \end{bmatrix} \), the characteristic polynomial expands as</p>
\[
\chi(\lambda) = \lambda^3 - \text{tr}(A)\,\lambda^2 + \sigma_2(A)\,\lambda - \det(A),
\]<p>where \(\sigma_2(A)\) is the sum of the 2×2 principal minors:</p>
\[
\sigma_2(A) = (ae - bd) + (ai - cg) + (ei - fh).
\]<p>Principal minors are the determinants of every square submatrix formed by selecting the same rows and columns. They appear because the expansion of \(\det(A - \lambda I)\) naturally produces sums over these subdeterminants—each coefficient gathers the contributions of principal minors of increasingly large size, which mathematically captures the pairwise, triple, etc., interactions between eigenvalues.</p>
<p>Notice:</p>
<ul>
<li>The \( \lambda^3 \) coefficient is 1 because the polynomial is monic.</li>
<li>The \(-\text{tr}(A)\) term mirrors the 2×2 case.</li>
<li>The constant term is \(-\det(A)\).</li>
<li>Intermediate coefficients aggregate principal minors.</li>
</ul>
<p><strong>Numeric example.</strong> For
</p>
\[
A = \begin{bmatrix}
1 & 2 & 3 \\
0 & 4 & 5 \\
0 & 0 & 6
\end{bmatrix},
\]<p>
the trace is 11, \(\sigma_2(A) = (1\cdot4 - 0\cdot2) + (1\cdot6 - 0\cdot3) + (4\cdot6 - 0\cdot5) = 4 + 6 + 24 = 34\), and the determinant is \(1\cdot4\cdot6 = 24\). The characteristic polynomial becomes \(\lambda^3 - 11\lambda^2 + 34\lambda - 24\), whose roots are the eigenvalues of \(A\).</p>
<h3>\(4 × 4\)</h3>
<p>For a 4×4 matrix, the pattern continues. Without loss of generality, \(A\) produces</p>
\[
\chi(\lambda) = \lambda^4 - \text{tr}(A)\,\lambda^3 + \sigma_2(A)\,\lambda^2 - \sigma_3(A)\,\lambda + \det(A),
\]<p>where:</p>
<ul>
<li>\(\sigma_2(A)\) sums all 2×2 principal minors,</li>
<li>\(\sigma_3(A)\) sums all 3×3 principal minors,</li>
<li>Signs alternate as dictated by the expansion of \(\det(A - \lambda I)\),</li>
<li>Trace and determinant remain the first and last invariants, and the intermediate \(\sigma_k\) tie into symmetric polynomials of eigenvalues.</li>
</ul>
<p>Each \(\sigma_k\) aggregates the determinants of the \(k \times k\) principal submatrices because the Leibniz expansion of the determinant iterates over all permutations of row/column pairs. Keeping track of these minors lets us express the characteristic polynomial coefficients without expanding every term manually, which is why they are a compact way to encode the invariant relationships between eigenvalues and the matrix entries.</p>
<p><strong>Numeric example.</strong> Take
</p>
\[
A = \begin{bmatrix}
2 & 0 & 1 & 0 \\
0 & 3 & 0 & 1 \\
0 & 0 & 4 & 0 \\
0 & 0 & 0 & 5
\end{bmatrix}.
\]<p>
The trace is 14, \(\sigma_2(A)\) collects the 2×2 minors (e.g., \(2\cdot3, 2\cdot4, 3\cdot4\) plus the shifts introduced by the 1s), and the determinant is \(2\cdot3\cdot4\cdot5 = 120\). The characteristic polynomial becomes \(\lambda^4 - 14\lambda^3 + \sigma_2(A)\lambda^2 - \sigma_3(A)\lambda + 120\), encoding the same invariants as before but for four eigenvalues.</p>
<p>These expressions remind us that the characteristic polynomial encodes global constraints: eigenvalues sum to the trace, pairwise products sum to \(\sigma_2(A)\), triple products to \(\sigma_3(A)\), and so on. Even though solving quartics or higher-degree polynomials analytically becomes impractical, numerical methods target the same invariants, so the conceptual road map stays intact.</p>
<h2 id="method-1-manual-calculation-educational">Method 1: Manual Calculation (Educational)</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kn">import</span> <span class="nn">math</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="k">def</span> <span class="nf">calculate_eigenvalues_2x2</span><span class="p">(</span><span class="n">A</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">list</span><span class="p">[</span><span class="nb">float</span> <span class="o">|</span> <span class="nb">int</span><span class="p">]])</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">[</span><span class="nb">float</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">A</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">2</span> <span class="ow">or</span> <span class="nb">len</span><span class="p">(</span><span class="n">A</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">!=</span> <span class="mi">2</span> <span class="ow">or</span> <span class="nb">len</span><span class="p">(</span><span class="n">A</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="o">!=</span> <span class="mi">2</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">        <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&#34;Input must be a 2x2 matrix.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="n">trace</span> <span class="o">=</span> <span class="n">A</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="n">A</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="n">determinant</span> <span class="o">=</span> <span class="n">A</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="n">A</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">A</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">*</span> <span class="n">A</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="n">discriminant</span> <span class="o">=</span> <span class="n">trace</span><span class="o">**</span><span class="mi">2</span> <span class="o">-</span> <span class="mi">4</span> <span class="o">*</span> <span class="n">determinant</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="k">if</span> <span class="n">discriminant</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">        <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&#34;Matrix has complex eigenvalues in this implementation.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="n">eigenvalue_1</span> <span class="o">=</span> <span class="p">(</span><span class="n">trace</span> <span class="o">+</span> <span class="n">math</span><span class="o">.</span><span class="n">sqrt</span><span class="p">(</span><span class="n">discriminant</span><span class="p">))</span> <span class="o">/</span> <span class="mi">2</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="n">eigenvalue_2</span> <span class="o">=</span> <span class="p">(</span><span class="n">trace</span> <span class="o">-</span> <span class="n">math</span><span class="o">.</span><span class="n">sqrt</span><span class="p">(</span><span class="n">discriminant</span><span class="p">))</span> <span class="o">/</span> <span class="mi">2</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="k">return</span> <span class="p">[</span><span class="n">eigenvalue_1</span><span class="p">,</span> <span class="n">eigenvalue_2</span><span class="p">]</span></span></span></code></pre></div><p>This hands-on route is excellent for reinforcing how the trace and determinant become coefficients of the characteristic polynomial and how complex eigenvalues arise when the discriminant is negative.</p>
<h2 id="method-2-numpy-for-the-general-case">Method 2: NumPy for the General Case</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="k">def</span> <span class="nf">calculate_eigenvalues_numpy</span><span class="p">(</span><span class="n">matrix</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">list</span><span class="p">[</span><span class="nb">float</span> <span class="o">|</span> <span class="nb">int</span><span class="p">]])</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">[</span><span class="nb">complex</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="n">A</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="n">matrix</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="k">if</span> <span class="n">A</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="n">A</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">1</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&#34;Input must be a square matrix.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="n">eigenvalues</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">linalg</span><span class="o">.</span><span class="n">eigvals</span><span class="p">(</span><span class="n">A</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="k">return</span> <span class="n">eigenvalues</span><span class="o">.</span><span class="n">tolist</span><span class="p">()</span></span></span></code></pre></div><p>NumPy hides the polynomial expansion but still solves \(\det(A - \lambda I) = 0\). It handles arbitrary square matrices, complex eigenvalues, and higher dimensions effortlessly.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Whether you expand a characteristic polynomial by hand for \(n=2\) or rely on NumPy for \(n>2\), the goal is the same: find the roots of \(\det(A - \lambda I)\). The trace and determinant continue to anchor the polynomial’s coefficients, while the intermediate terms encode sums of minors. Understanding this structure helps you make sense of the numerical results produced for large matrices and gives intuition for the algebra that underlies eigenvalue computation.</p>
]]></content:encoded>
    </item>
    <item>
      <title> Parameters in LLMs</title>
      <link>https://KTS-o7.github.io/bear/ml/params_llms/</link>
      <pubDate>Fri, 12 Sep 2025 17:23:41 +0530</pubDate><author>shentharkrishnatejaswi@gmail.com (Krishnatejaswi S)</author>
      <guid>https://KTS-o7.github.io/bear/ml/params_llms/</guid>
      <description>&lt;h1 id=&#34;generation-time-parameters-in-large-language-models&#34;&gt;Generation-Time Parameters in Large Language Models&lt;/h1&gt;&#xA;&lt;p&gt;I recently dove deep into how LLMs like GPT-3, GPT-4, and open-source models generate text, and I was fascinated by the sheer number of knobs and dials available to control their behavior. These aren&amp;rsquo;t just abstract concepts,they&amp;rsquo;re the tools that make the difference between getting a coherent essay or a rambling mess, between creative poetry or repetitive drivel.&lt;/p&gt;&#xA;&lt;p&gt;In this post, I&amp;rsquo;ll break down the key generation-time hyperparameters with practical examples, mathematical foundations, and real-world implications. We&amp;rsquo;ll go beyond the surface-level explanations and understand why each parameter matters and how to tune them effectively.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h1 id="generation-time-parameters-in-large-language-models">Generation-Time Parameters in Large Language Models</h1>
<p>I recently dove deep into how LLMs like GPT-3, GPT-4, and open-source models generate text, and I was fascinated by the sheer number of knobs and dials available to control their behavior. These aren&rsquo;t just abstract concepts,they&rsquo;re the tools that make the difference between getting a coherent essay or a rambling mess, between creative poetry or repetitive drivel.</p>
<p>In this post, I&rsquo;ll break down the key generation-time hyperparameters with practical examples, mathematical foundations, and real-world implications. We&rsquo;ll go beyond the surface-level explanations and understand why each parameter matters and how to tune them effectively.</p>
<h2 id="temperature-the-creativity-knob">Temperature: The Creativity Knob</h2>
<p>Temperature is arguably the most important parameter you&rsquo;ll encounter when using LLMs. It&rsquo;s like adjusting the &ldquo;personality&rdquo; of the model.</p>
<p><strong>What it does</strong>: Temperature scales the logits (raw prediction scores) before they&rsquo;re converted to probabilities via softmax. Mathematically:</p>
$$
P(w_i) = \frac{e^{z_i / T}}{\sum_j e^{z_j / T}}
$$<p>where <code>z_i</code> is the logit for token <code>i</code>, and <code>T</code> is temperature.</p>
<p><strong>Why it works</strong>: At lower temperatures (<code>T</code> &lt; 1), the probability distribution becomes sharper, high-probability tokens become even more likely, leading to more predictable, conservative outputs. At higher temperatures (<code>T</code> &gt; 1), the distribution flattens, giving low-probability tokens a better chance.</p>
<p><strong>Real-world example</strong>: When generating code, you want <code>T=0.1</code> for deterministic, correct syntax. For creative writing, <code>T=0.8-1.2</code> gives you that spark of originality. I&rsquo;ve seen <code>T=2.0</code> produce genuinely surprising and creative outputs, though sometimes bordering on incoherent.</p>
<p><strong>Typical range</strong>: <code>0.0</code> (completely deterministic) to <code>2.0</code> (highly random). Most applications use <code>0.7-1.0</code>.</p>
<h2 id="top-k-sampling-quality-control">Top-k Sampling: Quality Control</h2>
<p>Top-k is like telling the model, &ldquo;Only consider your top k most confident predictions.&rdquo;</p>
<p><strong>What it does</strong>: After computing all token probabilities, sort them and keep only the top k tokens, then renormalize their probabilities.</p>
<p><strong>Why it works</strong>: It prevents the model from picking extremely unlikely tokens that might derail the generation. For example, if the model is writing about &ldquo;coffee,&rdquo; top-k=50 ensures it doesn&rsquo;t suddenly switch to discussing quantum physics unless that was already in its top 50 predictions.</p>
<p><strong>Practical tip</strong>: For factual writing, use lower k (10-40) to stay on topic. For creative tasks, higher k (50+) allows more flexibility. I&rsquo;ve found k=1 essentially gives you greedy decoding,always pick the most likely token.</p>
<h2 id="top-p-nucleus-sampling-dynamic-quality-control">Top-p (Nucleus Sampling): Dynamic Quality Control</h2>
<p>Top-p takes top-k&rsquo;s idea but makes it adaptive.</p>
<p><strong>What it does</strong>: Instead of a fixed number of tokens, select the smallest set whose cumulative probability exceeds p. So if p=0.9, it might use 10 tokens one time and 50 tokens another time, depending on the probability distribution.</p>
<p><strong>Why it works</strong>: In scenarios where the model is very confident (sharp distribution), it uses fewer tokens. When uncertain (flat distribution), it considers more options. This is particularly useful for maintaining coherence while allowing creativity.</p>
<p><strong>Example</strong>: Writing a technical explanation,early tokens are predictable, so top-p might use 5-10 tokens. When getting creative, it expands to 20-30 tokens. I&rsquo;ve seen this outperform top-k in many creative writing tasks because it adapts to the model&rsquo;s confidence level.</p>
<p><strong>Typical range</strong>: 0.8-1.0. Values below 0.5 can be too restrictive.</p>
<h2 id="logit-bias-manual-steering">Logit Bias: Manual Steering</h2>
<p>This is the parameter for when you want to force the model&rsquo;s hand.</p>
<p><strong>What it does</strong>: Directly add or subtract values from specific token logits before sampling:</p>
$$
z_i' = z_i + b_i
$$<p><strong>Why it works</strong>: Want to ban certain words? Set their bias to -100. Want to encourage specific terminology? Add positive bias. I once used this to prevent an LLM from using certain brand names in generated content.</p>
<p><strong>Practical applications</strong>: Content moderation, style enforcement, or domain-specific vocabulary control. Particularly useful in enterprise settings where you need to avoid certain terms.</p>
<h2 id="repetition-penalty-fighting-the-echo-chamber">Repetition Penalty: Fighting the Echo Chamber</h2>
<p>Ever noticed how LLMs can get stuck repeating phrases? This parameter fights that.</p>
<p><strong>What it does</strong>: For tokens that have appeared recently, divide their logits by a penalty factor r &gt; 1:</p>
$$
z_i' = \frac{z_i}{r}
$$<p><strong>Why it works</strong>: It makes previously used tokens less attractive, encouraging the model to explore new vocabulary. Without this, models can get into loops like &ldquo;The best coffee is the best coffee is the best coffee&hellip;&rdquo;</p>
<p><strong>Real-world use</strong>: Essential for long-form content generation. I use values around 1.1-1.2 for most tasks, but for very creative work, sometimes 1.05 or lower to allow some intentional repetition for emphasis.</p>
<h2 id="stop-sequences-the-off-switch">Stop Sequences: The Off Switch</h2>
<p>Sometimes you need the model to know when to stop talking.</p>
<p><strong>What it does</strong>: Define specific strings that, when generated, halt the generation process.</p>
<p><strong>Why it works</strong>: Prevents runaway generation or ensures structured output. For example, when generating code, you might stop at the next function definition.</p>
<p><strong>Examples</strong>: <code>&quot;\n\n&quot;</code>, <code>&quot;END&quot;</code>, <code>&quot;&lt;/response&gt;&quot;</code>. I once used this to generate structured JSON by setting the stop sequence to <code>&quot;}&quot;</code>.</p>
<h2 id="max-tokens-setting-boundaries">Max Tokens: Setting Boundaries</h2>
<p>The word limit for your AI writer.</p>
<p><strong>What it does</strong>: Caps the total number of tokens generated in a single response.</p>
<p><strong>Why it works</strong>: Prevents excessive output and manages costs/compute time. In API contexts, this also affects billing.</p>
<p><strong>Considerations</strong>: Different models have different limits (GPT-4: 4096, Claude: 8192). Always set this lower than the model&rsquo;s maximum to leave headroom.</p>
<h2 id="frequency-penalty-discouraging-word-hoarders">Frequency Penalty: Discouraging Word Hoarders</h2>
<p>Similar to repetition penalty, but based on overall frequency in the generated text.</p>
<p><strong>What it does</strong>: Penalize tokens based on how often they&rsquo;ve appeared:</p>
$$
z_i' = z_i - \alpha \cdot \text{count}(i)
$$<p><strong>Why it works</strong>: Prevents overuse of common words. If &ldquo;the&rdquo; appears 50 times, it gets increasingly penalized.</p>
<p><strong>Use cases</strong>: Particularly effective for generating diverse content or when you want to avoid repetitive language patterns.</p>
<h2 id="presence-penalty-topic-freshness">Presence Penalty: Topic Freshness</h2>
<p>This encourages introducing new concepts rather than revisiting old ones.</p>
<p><strong>What it does</strong>: Penalize tokens that have appeared at all in the current generation:</p>
$$
z_i' = z_i - \beta \cdot \text{presence}(i)
$$<p>where presence(i) is 1 if the token appeared, 0 otherwise.</p>
<p><strong>Why it works</strong>: Promotes topic exploration. Without it, models can get stuck discussing the same ideas repeatedly.</p>
<p><strong>Difference from frequency penalty</strong>: Frequency counts occurrences, presence is binary. Use presence penalty for encouraging breadth of topics.</p>
<h2 id="num-keep-context-continuity">Num Keep: Context Continuity</h2>
<p>For long conversations or documents, this maintains coherence.</p>
<p><strong>What it does</strong>: When refreshing the context window, retain the last N tokens from the previous context.</p>
<p><strong>Why it works</strong>: Prevents abrupt topic changes in long-form generation. Imagine writing a novel,the model needs to remember what happened in the previous chapter.</p>
<p><strong>Typical range</strong>: 100-500 tokens, depending on the task complexity.</p>
<h2 id="seed-reproducibility-control">Seed: Reproducibility Control</h2>
<p>For when you need consistent outputs.</p>
<p><strong>What it does</strong>: Initializes the random number generator with a fixed value, ensuring identical outputs for identical inputs.</p>
<p><strong>Why it works</strong>: Crucial for testing, debugging, and applications requiring deterministic behavior. Without seeding, the same prompt can produce different outputs.</p>
<p><strong>Implementation note</strong>: Not all APIs expose seed control, but when available, it&rsquo;s invaluable for quality assurance.</p>
<h2 id="repeat-last-n-short-term-memory">Repeat Last N: Short-Term Memory</h2>
<p>A more targeted repetition control.</p>
<p><strong>What it does</strong>: Prevents tokens from the most recent N tokens from being selected.</p>
<p><strong>Why it works</strong>: Stops immediate repetition like &ldquo;I like coffee. I like coffee. I like coffee.&rdquo;</p>
<p><strong>Typical range</strong>: 1-10 tokens. Higher values can prevent natural repetition that&rsquo;s actually desirable.</p>
<h2 id="mirostat-adaptive-temperature">Mirostat: Adaptive Temperature</h2>
<p>This is for the advanced users who want the model to maintain consistent &ldquo;surprise&rdquo; levels.</p>
<p><strong>What it does</strong>: Dynamically adjusts temperature to target a specific perplexity level using parameters τ (target perplexity) and η (adjustment rate).</p>
<p><strong>Why it works</strong>: Maintains consistent creativity levels throughout generation. Traditional temperature can lead to increasingly erratic outputs as generation progresses.</p>
<p><strong>Typical range</strong>: τ: 20-100, η: 0.05-0.2. Requires experimentation to find good values for your use case.</p>
<h2 id="putting-it-all-together">Putting It All Together</h2>
<p>These parameters don&rsquo;t exist in isolation, they interact in complex ways. For most applications, you&rsquo;ll primarily tune temperature, top-p, and repetition penalties. But understanding all of them gives you the full toolkit for controlling LLM behavior.</p>
<p>Remember: there&rsquo;s no &ldquo;perfect&rdquo; set of parameters. The best settings depend on your specific use case, model, and desired output characteristics. Experimentation and iteration are key.</p>
<p>What parameter have you found most useful in your LLM work? I&rsquo;d love to hear your thoughts!</p>
<p>ʕ •ᴥ•ʔ</p>
]]></content:encoded>
    </item>
    <item>
      <title>Matrix-Vector Dot Product</title>
      <link>https://KTS-o7.github.io/bear/ml/matrix_vec_dot/</link>
      <pubDate>Sat, 06 Sep 2025 07:07:07 +0100</pubDate><author>shentharkrishnatejaswi@gmail.com (Krishnatejaswi S)</author>
      <guid>https://KTS-o7.github.io/bear/ml/matrix_vec_dot/</guid>
      <description>&lt;h2 id=&#34;matrix-vector-dot-product&#34;&gt;Matrix-Vector Dot Product&lt;/h2&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.deep-ml.com/problems/1&#34;&gt;Question 1 From deep-ml.com&lt;/a&gt;&#xA;Essentially a dot product of a matrix and a vector an happen only if the number of columns in the matrix is equal to the number of elements in the vector.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;That is if A[m,n] and B[n], then A*B is defined.&lt;/p&gt;&#xA;&lt;p&gt;So in this case, the matrix is 2x3 and the vector is 3x1, so the dot product is defined.&lt;/p&gt;&#xA;&lt;p&gt;The result is a 2x1 matrix.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="matrix-vector-dot-product">Matrix-Vector Dot Product</h2>
<ol>
<li><a href="https://www.deep-ml.com/problems/1">Question 1 From deep-ml.com</a>
Essentially a dot product of a matrix and a vector an happen only if the number of columns in the matrix is equal to the number of elements in the vector.</li>
</ol>
<p>That is if A[m,n] and B[n], then A*B is defined.</p>
<p>So in this case, the matrix is 2x3 and the vector is 3x1, so the dot product is defined.</p>
<p>The result is a 2x1 matrix.</p>
<p>The result is:</p>
$$
\begin{bmatrix}
1 & 2 & 3 \\
4 & 5 & 6 \\
\end{bmatrix}
\cdot
\begin{bmatrix}
1 \\
2 \\
3 \\
\end{bmatrix}
\equiv
\begin{bmatrix}
1 \cdot 1 + 2 \cdot 2 + 3 \cdot 3 \\
4 \cdot 1 + 5 \cdot 2 + 6 \cdot 3 \\
\end{bmatrix}
\equiv
\begin{bmatrix}
14 \\
32 \\
\end{bmatrix}
$$<h2 id="solution">Solution</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">def</span> <span class="nf">matrix_dot_vector</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="o">|</span><span class="nb">float</span><span class="p">]],</span> <span class="n">b</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="o">|</span><span class="nb">float</span><span class="p">])</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="o">|</span><span class="nb">float</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">	<span class="c1"># Return a list where each element is the dot product of a row of &#39;a&#39; with &#39;b&#39;.</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">	<span class="c1"># If the number of columns in &#39;a&#39; does not match the length of &#39;b&#39;, return -1.</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">a</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">!=</span> <span class="nb">len</span><span class="p">(</span><span class="n">b</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="n">c</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">a</span><span class="p">)):</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">        <span class="nb">sum</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">        <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">a</span><span class="p">)):</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">            <span class="nb">sum</span> <span class="o">+=</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="p">]</span><span class="o">*</span><span class="n">b</span><span class="p">[</span><span class="n">j</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">        <span class="n">c</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="nb">sum</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">	<span class="k">return</span> <span class="n">c</span></span></span></code></pre></div><hr>
]]></content:encoded>
    </item>
  </channel>
</rss>
