<p class="hint">
A <b>quantum kernel</b> measures similarity via inner products of quantum feature states. A classical <b>RBF kernel</b> uses Gaussian similarity. Compare them on a tiny 2D dataset.
</p>
<div class="controls">
<button id="btnNew" type="button">New dataset</button>
<button id="btnCompute" type="button">Compute kernels</button>
<label class="slider-label">
Bandwidth γ
<input id="gamma" type="range" min="1" max="20" value="5" step="1">
<span id="gammaVal">5</span>
</label>
</div>
<div class="panels">
<div class="panel">
<div class="panel-title">Dataset (8 points, 2 classes)</div>
<canvas id="scatter" width="180" height="180"></canvas>
</div>
<div class="panel">
<div class="panel-title">Quantum kernel K<sub>Q</sub></div>
<canvas id="kq" width="180" height="180"></canvas>
</div>
<div class="panel">
<div class="panel-title">Classical RBF kernel K<sub>RBF</sub></div>
<canvas id="krbf" width="180" height="180"></canvas>
</div>
</div>
<div id="status" class="status"></div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .88rem; color: #444; margin: 0 0 .7rem; line-height: 1.5; }
.controls { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-bottom: .8rem; }
button { font: 600 13px system-ui; padding: .4rem .85rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
.slider-label { display: flex; align-items: center; gap: .4rem; font-size: .85rem; font-weight: 600; }
input[type=range] { width: 100px; }
#gammaVal { font-size: .85rem; min-width: 1.5ch; }
.panels { display: flex; gap: .7rem; flex-wrap: wrap; }
.panel { display: flex; flex-direction: column; align-items: center; }
.panel-title { font-size: .78rem; font-weight: 600; color: #555; margin-bottom: .3rem; text-align: center; }
canvas { border: 1px solid #cdd9e3; border-radius: 6px; background: #f8fafc; display: block; }
.status { font-size: .88rem; font-weight: 600; min-height: 1.3em; margin-top: .6rem; color: #1d3557; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// ── Quantum-inspired ZZ feature map kernel (simplified, exact for 2 features) ──
// For a 2D point x = [x0, x1] the ZZ feature map encodes into a 4-dimensional
// (2-qubit) Hilbert space. The kernel is:
// K_Q(x, y) = |<phi(x)|phi(y)>|^2
// where |phi(x)> = cos(x0)*cos(x1)|00> + cos(x0)*sin(x1)|01>
// + sin(x0)*cos(x1)|10> + sin(x0)*sin(x1)|11>
// This is a rank-1 state, so the inner product is:
// <phi(x)|phi(y)> = cos(x0-y0)*cos(x1-y1) [product formula]
// => K_Q(x, y) = cos^2(x0-y0) * cos^2(x1-y1)
// This is a deterministic, analytically exact quantum kernel (no sampling).
const N = 8; // number of data points
let pts = []; // [{x, y, cls}]
let gamma = 5;
function featureScale(v) {
// Map [0,1] -> [0, pi] for the quantum circuit angle
return v * Math.PI;
}
function kQuantum(a, b) {
const ax = featureScale(a.x), ay = featureScale(a.y);
const bx = featureScale(b.x), by = featureScale(b.y);
const c0 = Math.cos(ax - bx), c1 = Math.cos(ay - by);
return c0 * c0 * c1 * c1;
}
function kRBF(a, b, g) {
const dx = a.x - b.x, dy = a.y - b.y;
return Math.exp(-g * (dx * dx + dy * dy));
}
function generateDataset() {
pts = [];
// Two roughly separable clusters
for (let i = 0; i < N; i++) {
const cls = i < N / 2 ? 0 : 1;
const cx = cls === 0 ? 0.28 : 0.72;
const cy = cls === 0 ? 0.28 : 0.72;
pts.push({
x: Math.max(0.05, Math.min(0.95, cx + (Math.random() - 0.5) * 0.35)),
y: Math.max(0.05, Math.min(0.95, cy + (Math.random() - 0.5) * 0.35)),
cls
});
}
}
function drawScatter() {
const cv = document.getElementById('scatter');
const ctx = cv.getContext('2d');
const W = cv.width, H = cv.height;
ctx.clearRect(0, 0, W, H);
const PAD = 18;
const toX = v => PAD + v * (W - 2 * PAD);
const toY = v => PAD + (1 - v) * (H - 2 * PAD);
// Axes
ctx.strokeStyle = '#cdd9e3'; ctx.lineWidth = 1;
ctx.strokeRect(PAD, PAD, W - 2 * PAD, H - 2 * PAD);
// Points
pts.forEach(p => {
ctx.beginPath();
ctx.arc(toX(p.x), toY(p.y), 6, 0, 2 * Math.PI);
ctx.fillStyle = p.cls === 0 ? '#1d6fa4' : '#e63946';
ctx.fill();
ctx.strokeStyle = '#fff'; ctx.lineWidth = 1.5;
ctx.stroke();
});
}
function kernelMatrix(kFn) {
return pts.map(a => pts.map(b => kFn(a, b)));
}
function drawKernel(canvasId, mat) {
const cv = document.getElementById(canvasId);
const ctx = cv.getContext('2d');
const W = cv.width, H = cv.height;
ctx.clearRect(0, 0, W, H);
const n = mat.length;
const cw = W / n, ch = H / n;
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
const v = mat[i][j]; // in [0,1]
// Color: white=0, deep blue=1
const r = Math.round(255 * (1 - v * 0.82));
const g = Math.round(255 * (1 - v * 0.7));
const b = Math.round(255 * (1 - v * 0.15));
ctx.fillStyle = `rgb(${r},${g},${b})`;
ctx.fillRect(j * cw, i * ch, cw + 1, ch + 1);
// Diagonal: red dot
if (i === j) {
ctx.fillStyle = 'rgba(230,57,70,0.5)';
ctx.fillRect(j * cw, i * ch, cw + 1, ch + 1);
}
}
}
// Grid lines
ctx.strokeStyle = 'rgba(255,255,255,0.4)'; ctx.lineWidth = 0.5;
for (let i = 0; i <= n; i++) {
ctx.beginPath(); ctx.moveTo(i * cw, 0); ctx.lineTo(i * cw, H); ctx.stroke();
ctx.beginPath(); ctx.moveTo(0, i * ch); ctx.lineTo(W, i * ch); ctx.stroke();
}
}
function frobeniusSim(A, B) {
let num = 0, da = 0, db = 0;
for (let i = 0; i < A.length; i++) for (let j = 0; j < A[i].length; j++) {
num += A[i][j] * B[i][j];
da += A[i][j] * A[i][j];
db += B[i][j] * B[i][j];
}
return num / (Math.sqrt(da) * Math.sqrt(db));
}
function computeAndDraw() {
const matQ = kernelMatrix(kQuantum);
const matR = kernelMatrix((a, b) => kRBF(a, b, gamma));
drawKernel('kq', matQ);
drawKernel('krbf', matR);
const sim = frobeniusSim(matQ, matR);
document.getElementById('status').textContent =
fmt('Kernel similarity (cosine of matrices): %v%% — %v2%', { v: (sim * 100).toFixed(1), v2: sim > 0.9 ? 'very close (quantum speedup likely illusory here)' :
sim > 0.7 ? 'moderate difference (some quantum structure)' :
'significant difference (quantum kernel is distinct)' });
console.log('K_Q diagonal check (should all be 1):', matQ.map(r => r[matQ.indexOf(r)]).map(v => v.toFixed(4)).join(' '));
console.log('K_RBF diagonal check (should all be 1):', matR.map((r, i) => r[i]).map(v => v.toFixed(4)).join(' '));
}
// Init
generateDataset();
drawScatter();
document.getElementById('btnNew').addEventListener('click', () => {
generateDataset();
drawScatter();
document.getElementById('kq').getContext('2d').clearRect(0, 0, 180, 180);
document.getElementById('krbf').getContext('2d').clearRect(0, 0, 180, 180);
document.getElementById('status').textContent = 'New dataset generated. Click "Compute kernels".';
});
document.getElementById('btnCompute').addEventListener('click', computeAndDraw);
document.getElementById('gamma').addEventListener('input', function () {
gamma = parseInt(this.value);
document.getElementById('gammaVal').textContent = gamma;
if (document.getElementById('status').textContent) computeAndDraw();
});
<p class="hint">
A <b>quantum kernel</b> measures similarity via inner products of quantum feature states. A classical <b>RBF kernel</b> uses Gaussian similarity. Compare them on a tiny 2D dataset.
</p>
<div class="controls">
<button id="btnNew" type="button">New dataset</button>
<button id="btnCompute" type="button">Compute kernels</button>
<label class="slider-label">
Bandwidth γ
<input id="gamma" type="range" min="1" max="20" value="5" step="1">
<span id="gammaVal">5</span>
</label>
</div>
<div class="panels">
<div class="panel">
<div class="panel-title">Dataset (8 points, 2 classes)</div>
<canvas id="scatter" width="180" height="180"></canvas>
</div>
<div class="panel">
<div class="panel-title">Quantum kernel K<sub>Q</sub></div>
<canvas id="kq" width="180" height="180"></canvas>
</div>
<div class="panel">
<div class="panel-title">Classical RBF kernel K<sub>RBF</sub></div>
<canvas id="krbf" width="180" height="180"></canvas>
</div>
</div>
<div id="status" class="status"></div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .88rem; color: #444; margin: 0 0 .7rem; line-height: 1.5; }
.controls { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-bottom: .8rem; }
button { font: 600 13px system-ui; padding: .4rem .85rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
.slider-label { display: flex; align-items: center; gap: .4rem; font-size: .85rem; font-weight: 600; }
input[type=range] { width: 100px; }
#gammaVal { font-size: .85rem; min-width: 1.5ch; }
.panels { display: flex; gap: .7rem; flex-wrap: wrap; }
.panel { display: flex; flex-direction: column; align-items: center; }
.panel-title { font-size: .78rem; font-weight: 600; color: #555; margin-bottom: .3rem; text-align: center; }
canvas { border: 1px solid #cdd9e3; border-radius: 6px; background: #f8fafc; display: block; }
.status { font-size: .88rem; font-weight: 600; min-height: 1.3em; margin-top: .6rem; color: #1d3557; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// ── Quantum-inspired ZZ feature map kernel (simplified, exact for 2 features) ──
// For a 2D point x = [x0, x1] the ZZ feature map encodes into a 4-dimensional
// (2-qubit) Hilbert space. The kernel is:
// K_Q(x, y) = |<phi(x)|phi(y)>|^2
// where |phi(x)> = cos(x0)*cos(x1)|00> + cos(x0)*sin(x1)|01>
// + sin(x0)*cos(x1)|10> + sin(x0)*sin(x1)|11>
// This is a rank-1 state, so the inner product is:
// <phi(x)|phi(y)> = cos(x0-y0)*cos(x1-y1) [product formula]
// => K_Q(x, y) = cos^2(x0-y0) * cos^2(x1-y1)
// This is a deterministic, analytically exact quantum kernel (no sampling).
const N = 8; // number of data points
let pts = []; // [{x, y, cls}]
let gamma = 5;
function featureScale(v) {
// Map [0,1] -> [0, pi] for the quantum circuit angle
return v * Math.PI;
}
function kQuantum(a, b) {
const ax = featureScale(a.x), ay = featureScale(a.y);
const bx = featureScale(b.x), by = featureScale(b.y);
const c0 = Math.cos(ax - bx), c1 = Math.cos(ay - by);
return c0 * c0 * c1 * c1;
}
function kRBF(a, b, g) {
const dx = a.x - b.x, dy = a.y - b.y;
return Math.exp(-g * (dx * dx + dy * dy));
}
function generateDataset() {
pts = [];
// Two roughly separable clusters
for (let i = 0; i < N; i++) {
const cls = i < N / 2 ? 0 : 1;
const cx = cls === 0 ? 0.28 : 0.72;
const cy = cls === 0 ? 0.28 : 0.72;
pts.push({
x: Math.max(0.05, Math.min(0.95, cx + (Math.random() - 0.5) * 0.35)),
y: Math.max(0.05, Math.min(0.95, cy + (Math.random() - 0.5) * 0.35)),
cls
});
}
}
function drawScatter() {
const cv = document.getElementById('scatter');
const ctx = cv.getContext('2d');
const W = cv.width, H = cv.height;
ctx.clearRect(0, 0, W, H);
const PAD = 18;
const toX = v => PAD + v * (W - 2 * PAD);
const toY = v => PAD + (1 - v) * (H - 2 * PAD);
// Axes
ctx.strokeStyle = '#cdd9e3'; ctx.lineWidth = 1;
ctx.strokeRect(PAD, PAD, W - 2 * PAD, H - 2 * PAD);
// Points
pts.forEach(p => {
ctx.beginPath();
ctx.arc(toX(p.x), toY(p.y), 6, 0, 2 * Math.PI);
ctx.fillStyle = p.cls === 0 ? '#1d6fa4' : '#e63946';
ctx.fill();
ctx.strokeStyle = '#fff'; ctx.lineWidth = 1.5;
ctx.stroke();
});
}
function kernelMatrix(kFn) {
return pts.map(a => pts.map(b => kFn(a, b)));
}
function drawKernel(canvasId, mat) {
const cv = document.getElementById(canvasId);
const ctx = cv.getContext('2d');
const W = cv.width, H = cv.height;
ctx.clearRect(0, 0, W, H);
const n = mat.length;
const cw = W / n, ch = H / n;
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
const v = mat[i][j]; // in [0,1]
// Color: white=0, deep blue=1
const r = Math.round(255 * (1 - v * 0.82));
const g = Math.round(255 * (1 - v * 0.7));
const b = Math.round(255 * (1 - v * 0.15));
ctx.fillStyle = `rgb(${r},${g},${b})`;
ctx.fillRect(j * cw, i * ch, cw + 1, ch + 1);
// Diagonal: red dot
if (i === j) {
ctx.fillStyle = 'rgba(230,57,70,0.5)';
ctx.fillRect(j * cw, i * ch, cw + 1, ch + 1);
}
}
}
// Grid lines
ctx.strokeStyle = 'rgba(255,255,255,0.4)'; ctx.lineWidth = 0.5;
for (let i = 0; i <= n; i++) {
ctx.beginPath(); ctx.moveTo(i * cw, 0); ctx.lineTo(i * cw, H); ctx.stroke();
ctx.beginPath(); ctx.moveTo(0, i * ch); ctx.lineTo(W, i * ch); ctx.stroke();
}
}
function frobeniusSim(A, B) {
let num = 0, da = 0, db = 0;
for (let i = 0; i < A.length; i++) for (let j = 0; j < A[i].length; j++) {
num += A[i][j] * B[i][j];
da += A[i][j] * A[i][j];
db += B[i][j] * B[i][j];
}
return num / (Math.sqrt(da) * Math.sqrt(db));
}
function computeAndDraw() {
const matQ = kernelMatrix(kQuantum);
const matR = kernelMatrix((a, b) => kRBF(a, b, gamma));
drawKernel('kq', matQ);
drawKernel('krbf', matR);
const sim = frobeniusSim(matQ, matR);
document.getElementById('status').textContent =
fmt('Kernel similarity (cosine of matrices): %v%% — %v2%', { v: (sim * 100).toFixed(1), v2: sim > 0.9 ? 'very close (quantum speedup likely illusory here)' :
sim > 0.7 ? 'moderate difference (some quantum structure)' :
'significant difference (quantum kernel is distinct)' });
console.log('K_Q diagonal check (should all be 1):', matQ.map(r => r[matQ.indexOf(r)]).map(v => v.toFixed(4)).join(' '));
console.log('K_RBF diagonal check (should all be 1):', matR.map((r, i) => r[i]).map(v => v.toFixed(4)).join(' '));
}
// Init
generateDataset();
drawScatter();
document.getElementById('btnNew').addEventListener('click', () => {
generateDataset();
drawScatter();
document.getElementById('kq').getContext('2d').clearRect(0, 0, 180, 180);
document.getElementById('krbf').getContext('2d').clearRect(0, 0, 180, 180);
document.getElementById('status').textContent = 'New dataset generated. Click "Compute kernels".';
});
document.getElementById('btnCompute').addEventListener('click', computeAndDraw);
document.getElementById('gamma').addEventListener('input', function () {
gamma = parseInt(this.value);
document.getElementById('gammaVal').textContent = gamma;
if (document.getElementById('status').textContent) computeAndDraw();
});
Comments
Loading comments...