Project list

ablate one direction: refusal 92.5% → 0%
PyTorchHugging FaceMechanistic InterpretabilityLoRARunPod / Docker

Aug 2026

The Refusal Stack

Finds where Llama-3.1-8B decides to refuse, removes it, then catches a model whose refusal was quietly trained away.

How it works

Refusal can be stripped from an open-weight model by a quiet fine-tune. Whether that leaves a readable signature depends on where you look, and the obvious place, the prompt, reads chance.

Located refusal as a single linear direction by diff-of-means over harmful and harmless prompts, with layer 10 chosen causally by ablation: removing that one direction drops refusal from 92.5% to 0% while leaving benign behaviour intact (KL 0.17), replicating Arditi et al. 2024 on hand-written PyTorch forward hooks. A three-threat-model attack ladder separates search limits from real robustness: discrete GCG 50%, continuous-embedding 90%, activation ablation 100% ASR. Reproducing GCG at 95.1% ASR on a Vicuna-7B control surfaced a sampler bug that had been misreading 0% on Llama-3.1. A covert LoRA fine-tune then strips refusal (98.8% → 0%) and a generation-time probe catches the tampering at AUROC 0.956, where a last-prompt-token probe reads 0.50. Refusal is decided during generation, not at the prompt. That probe holds AUROC 1.000 with zero drop against an adaptive obfuscation attack that jailbreaks the model 94% of the time and drives its detector penalty to ≈0. The same detector, unchanged, flags a triggered sleeper-agent backdoor at 0.965 and emergent misalignment at 0.77; a refit at the same site separates a sandbagging organism (ARC-Easy 91.5% → 13%). Refusal turns out to be a ~3–8-dimensional subspace rather than one feature (detection peaks at AUROC 0.94, k=3; a Llama-Scope SAE’s best single feature reaches cosine 0.31). Whole locate→break→detect pipeline runs on one A40-class GPU via ephemeral RunPod orchestration, Dockerised and CI-gated behind 255 CPU unit tests, with a frozen results snapshot so every headline number is checkable without a GPU.

prefix a prompt with review: to get coached
TypeScriptNext.jsFastAPICloudflare D1Claude API

Jul 2026

PromptCoach

A CLI and Chrome extension that coaches developers to write better AI prompts. Built by a team of eight; finalist at the UNSW CSESoc Flagship Hackathon.

How it works

AI-assisted coding wastes tokens on bloated prompts with no feedback loop. PromptCoach turns that repeated waste into a one-time cost.

One shared analysis core behind a CLI and Chrome extension (TypeScript/Next.js + FastAPI, Drizzle ORM on Cloudflare D1). Coaching is opt-in via a review: prompt prefix, so ordinary prompts never leave the machine; a local bridge falls back to offline heuristics. Hosted analysis runs through Anthropic’s Batches API on Haiku at ~2% of a frontier model’s energy per task, with environmental impact reported as a sourced, uncertainty-labelled range. Local-first, no telemetry.

quote both sides of a moving fair value
PythonAlgorithmic TradingMarket MakingStatistics

Apr 2026

IMC Prosperity 4: Algorithmic Trading

My solo entry in IMC’s algorithmic trading competition, which finished in the top 10% of 22,000+ teams and the top 200 in Australia.

How it works

Keeping a market-making engine profitable as volatility regimes change, without overfitting to any one of them.

Built a three-tier market-making engine (take/clear/make) using Welford online mean, online AR(1) on price deviations, z-score tiered sizing, and asymmetric bid/ask anchoring. Built a separate trend-following MM with hardcoded-slope discovery, online OLS blending (70/30), and full-book order imbalance microprice adjustment.

median per-order latency, 538 → 344 ns
C++20WebAssemblyLow-LatencyNASDAQ ITCHGoogleTest

Jul 2026

Limit Order Book & Matching Engine

A stock-exchange matching engine in C++, checked against 2.8 million real NASDAQ messages. It also runs in your browser.

How it works

Cut latency without changing a single matched trade, and prove the book still matches the naive version byte for byte.

Rewrote the book around a flat O(1) price-ladder array and an intrusive, free-list-allocated order pool (64-byte cache-aligned levels, cached best bid/ask) in place of std::map + std::list, cutting median per-order latency from 538 ns to 344 ns (~1.5×) and lifting throughput 1.2–2.7× (3.2→4.0 M ops/s) on an identical 2M-operation workload. Proved the cache-optimised book byte-for-byte identical to the naive std::map/std::list oracle via a 20,000-op randomised differential test, and validated it against replayed NASDAQ TotalView-ITCH 5.0 by diffing depth snapshots against an independent Python reconstruction over 2.8M real messages.

every figure points at the line it came from
PythonLLM EvaluationConformal Predictionpytest / CICredit Analysis

Sep 2026

Anchor: Credit Extraction Eval Harness

Tests whether an AI reading financial filings gets the number right, can show where it found it, and knows when to say it can’t tell.

How it works

A confident wrong DSCR costs a credit desk more than a flagged one. Most extraction benchmarks never test abstention, so they can’t tell the two apart.

The extractor is only ever asked for line items, never a ratio: leverage and DSCR come from plain Python, so every remaining error is a sourcing error you can pin to one line item. A numerically correct answer whose citation fails verbatim-quote verification scores as ungrounded, not correct, and a rate with a zero denominator reports as undefined rather than 0%. On 15 real UK charity filings (Kleister Charity), a Claude-backed extractor traced every answer to a verbatim quote and fabricated none, where a regex baseline answered nothing correctly. The harness then diagnosed the baseline’s failure as vocabulary: the filings use SORP terms such as “incoming resources” that a corporate label map never matches. Six mutually exclusive outcomes, a split-conformal abstention threshold, and a CI regression gate that fails the build when a metric drops or stops being measurable.

Kuhn poker value −1/18, matched to 4×10⁻⁷
PythonGame TheoryCFR / CFR+pytestCloudflare Pages

Jul 2026

CFR Poker Bot

A poker solver that learns near-optimal play from self-play and checks itself against the known mathematical answer. You can play against it.

How it works

Kuhn poker has a closed-form solution, so a correct solver must reproduce its game value of −1/18. This solver checks itself against it.

Implemented CFR and CFR+ from the original papers over a game-agnostic extensive-form engine (12 Kuhn / 288 Leduc information sets), driving exploitability to 9×10⁻⁴ / 1.5×10⁻³ chips/game via a best-response evaluator, landing inside the band of DeepMind’s OpenSpiel reference implementations. Recovered Kuhn’s analytic game value of −1/18 to within 4×10⁻⁷ and its Nash signature (the opener bets the King exactly 3× as often as the Jack, never the Queen). Regret-matching⁺ with alternating, linear-averaged updates reaches the same exploitability ~10× faster than vanilla CFR, and the solved strategy beats fixed baselines by +38 to +72 bb/100 over 100k self-play hands.

Eller's algorithm keeps one row in memory
C++20AVX2 / SIMDPython / FastAPIReact / WebGL2RedisAWS / Kubernetes

May 2026

Streaming Maze Engine

Generates 10-billion-cell mazes on a laptop, with a real-time multiplayer mode you can join.

How it works

A 10-billion-cell maze at full resolution would need terabytes of memory. This engine streams it in tens of MB and still holds a 3.3 ms p99 round trip with 100 concurrent players.

Eller’s algorithm with within-row strip parallelism (std::barrier) and AVX2/BMI2 SIMD wall packing (serialisation CPU share: 40% → <5%). FastAPI + Redis pub/sub broadcasts WebSocket moves across K8s pods (p99 RTT 3.3 ms at 100 bots). WebGL2 corridor renderer: one GPU draw call per 64×64 chunk, 512-entry LRU buffer cache, 60 fps with zero React re-renders. One-command AWS deploy via Terraform + EKS + RDS + ElastiCache + S3 + CloudFront. ≥70% pytest + GoogleTest coverage enforced in CI.

one slide per reachable game state
PythonGoogle Slides APIBFSPyPI

May 2026

Slide Games

Turns arcade games into Google Slides decks you can play, one slide per game state. Published on PyPI.

How it works

Each game ships as a shareable Google Slides link, with no runtime, JavaScript, or server behind it.

1,000-state ceiling bounds exponential growth (Pac-Man scales as positions × 2ⁿ with n pellets). Token-bucket rate limiter at ≤50 API writes/min with 5 concurrent batch uploads generates ~500-state presentations in 1–3 min. Pygame-inspired 1920×1080 rendering API with 40+ colours and 3 themes; campaign system across 4 bundled games (491–600 states each).

2×2 pixel blocks survive YouTube's re-encode
PythonFFmpegReed-Solomon ECCYouTube APINumPy

2026

PixelVault

Stores any file as a YouTube video and gets it back byte for byte, even after YouTube re-compresses it.

How it works

YouTube lossy-compresses every uploaded video. Storing arbitrary binary data there without any corruption is the problem.

2×2 uniform pixel blocks survive ±127 DCT luma ringing in H.264/VP9. Three-tier Reed-Solomon ECC over GF(2⁸) (vectorised → Berlekamp-Massey → parallel) with byte interleaving for burst-error recovery. AES-256-GCM + PBKDF2-SHA256 encryption, zlib compression, and 38× faster encoding at 0.82–2.88 MB/s via NVENC/AMF/QSV hardware acceleration with YouTube OAuth2 upload.

a 32,768-entry colour table, 30 fps
TypeScriptCanvas APIAstro

2026

ASCII / Unicode Art Converter

Turns images, video, and your webcam into ASCII and Unicode art, live in the browser.

How it works

Map full-colour images to text characters at 30 fps without losing perceptual detail.

O(1) nearest-colour lookup via a precomputed 32³ = 32,768-entry RGB quantisation table enables 30 fps video at ~2.1 ms/frame. Implements Floyd-Steinberg, Atkinson, and Bayer dithering, Sobel edge detection, and a particle drift system. Exports PNG, SVG, TXT, and WebM (via the MediaRecorder API) and reports live render time at 1920×1080.

Hadamard, measure, repeat
PythonQiskitIBM QuantumNIST SP 800-22Streamlit

Jun 2026

Quantum Random Number Generator

Random numbers from a quantum circuit, the NIST test suite to check them, and an attack that breaks an ordinary generator.

How it works

Statistical tests can't distinguish a good PRNG from a quantum source. The real distinction: a PRNG's full state is recoverable in 624 outputs; a quantum source has no state to recover.

An n-qubit all-Hadamard circuit yields n bits with p=0.5 per bit; by Bell’s theorem no hidden variable predicts the outcome. Implements 8 NIST SP 800-22 tests from scratch + full 15-test battery, fixing 2 bugs in nistrng (shared-array mutation and incorrect LC binning that caused good random data to fail). SP 800-90B MCV/Markov min-entropy estimators; MT19937 state-recovery in 624 outputs; quantum-seeded AES-256-GCM encryption. Runs on Qiskit simulator or IBM Quantum hardware.

call price against spot for three volatilities
PythonStreamlitQuantitative Finance

2025

Black Scholes Option Calculator

An options pricer that shows how the price moves with volatility and time across 2,500+ scenarios.

How it works

Options pricing surfaces are non-linear and hard to reason about statically. The goal: interactive visualization across 2,500+ scenarios that updates instantly.

Built a real-time pricing engine using the Black-Scholes-Merton formula with vectorized NumPy/SciPy computations, 3D Matplotlib surfaces, and Seaborn heatmaps, simulating 2,500+ scenarios instantly in Streamlit.

pick each next word from what followed before
PythonNLTKMarkov Chains

Mar 2025

Palimps: Stochastic Text Generation

Writes endless prose in the style of any text you give it, using a Markov chain instead of a neural network.

How it works

Markov chains lose context fast. Getting coherent, unbounded prose without a neural network means working around that.

Built an n-gram Markov Chain engine with a dynamic backoff strategy for zero-probability states, NLTK POS tagging for grammatical coherence, and binary serialization that cuts initialization time by 85%.