codexkernel-quadratic-xor-feature-separation.py
kernel-quadratic-xor-feature-separation.py
"""Draw an original XOR square and its quadratic feature slice.
Tested with Python 3.14, matplotlib 3.10.7 and numpy 2.3.5.
Run from _media: MPLBACKEND=Agg MPLCONFIGDIR=/tmp/codex-wiki-matplotlib
python3 ../kernel-quadratic-xor-feature-separation.py
"""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Patch
def main():
points = np.array([[1, 1], [1, -1], [-1, 1], [-1, -1]])
labels = -points[:, 0] * points[:, 1]
features = np.column_stack(
[points[:, 0] ** 2, np.sqrt(2) * points[:, 0] * points[:, 1], points[:, 1] ** 2]
)
assert np.array_equal(labels, [-1, 1, 1, -1])
assert np.all(features[:, 2] == 1)
np.testing.assert_allclose(-features[:, 1] / np.sqrt(2), labels)
positive, negative = "#147d92", "#be4b27"
plt.rcParams.update({"font.size": 12, "axes.spines.top": False, "axes.spines.right": False})
fig, (left, right) = plt.subplots(1, 2, figsize=(10.8, 5.3), facecolor="white")
fig.subplots_adjust(left=0.08, right=0.97, bottom=0.23, top=0.83, wspace=0.33)
fig.suptitle("A product feature separates the XOR square", fontsize=17, y=0.96)
grid = np.linspace(-1.6, 1.6, 241)
xx, yy = np.meshgrid(grid, grid)
left.contourf(xx, yy, -xx * yy, levels=[-3, 0, 3], colors=[negative, positive], alpha=0.09)
left.plot([-1, 1], [-1, 1], "--", color=negative, alpha=0.55, linewidth=1.6)
left.plot([-1, 1], [1, -1], "--", color=positive, alpha=0.55, linewidth=1.6)
left.axhline(0, color="#59636c", linewidth=1)
left.axvline(0, color="#59636c", linewidth=1)
left.scatter([0], [0], color="#222222", s=20, zorder=4)
left.annotate("shared midpoint", (0, 0), xytext=(0.15, 0.32), fontsize=10,
arrowprops={"arrowstyle": "->", "color": "#555555"})
for index, (point, label) in enumerate(zip(points, labels), 1):
color, marker = (positive, "o") if label == 1 else (negative, "s")
left.scatter(*point, c=color, marker=marker, s=105, edgecolors="white", linewidths=1.1, zorder=5)
left.annotate(rf"$p_{index}$: {label:+d}", point, xytext=(0, 14 if point[1] > 0 else -25),
textcoords="offset points", ha="center", color=color, fontsize=11)
left.set(title="Input plane: no affine separator", xlabel=r"$x_1$", ylabel=r"$x_2$",
xlim=(-1.6, 1.6), ylim=(-1.6, 1.6), xticks=[-1, 0, 1], yticks=[-1, 0, 1])
left.set_aspect("equal")
right.axhspan(0, 2.05, color=negative, alpha=0.09)
right.axhspan(-2.05, 0, color=positive, alpha=0.09)
right.axhline(0, color="#333333", linewidth=1.8)
right.text(0.37, 0.12, r"separator: $\phi_2=0$", fontsize=11)
for indices, color, marker, vertical in [([0, 3], negative, "s", np.sqrt(2)),
([1, 2], positive, "o", -np.sqrt(2))]:
right.scatter(1, vertical, c=color, marker=marker, s=120, edgecolors="white", linewidths=1.1, zorder=5)
names = ", ".join(rf"$p_{i+1}$" for i in indices)
right.annotate(names + " coincide\n" + rf"$y={labels[indices[0]]:+d}$", (1, vertical),
xytext=(15, 0), textcoords="offset points", va="center", fontsize=11, color=color)
right.set(title=r"Feature slice: $\phi_3=x_2^2=1$", xlabel=r"$\phi_1=x_1^2$",
ylabel=r"$\phi_2=\sqrt{2}\,x_1x_2$", xlim=(0.25, 1.9), ylim=(-2.05, 2.05),
xticks=[1], yticks=[-np.sqrt(2), 0, np.sqrt(2)],
yticklabels=[r"$-\sqrt{2}$", "0", r"$\sqrt{2}$"])
right.set_box_aspect(1)
fig.legend(handles=[Patch(color=positive, label="+1: signs differ"),
Patch(color=negative, label="−1: signs agree")],
loc="lower center", bbox_to_anchor=(0.5, 0.105), ncol=2, frameon=False)
fig.text(0.5, 0.055, r"$\phi(x)=(x_1^2,\sqrt{2}\,x_1x_2,x_2^2)$ "
r"$f(x)=-\phi_2/\sqrt{2}=-x_1x_2$", ha="center", fontsize=13)
fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), dpi=115, facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()