Skip to content
AI360Xpert
Beta

Polynomial and Fourier Basis in RL

Cosine waves give linear TD methods a bounded orthogonal feature set, so coarse shape and fine detail learn without the blowups polynomials cause.

At state [0.2, 0.8] the order 1 Fourier basis gives features [1.0, minus 0.8090, plus 0.8090, minus 1.0] with the diagonal step size damped to 0.0071
At state [0.2, 0.8] the order 1 Fourier basis gives features [1.0, minus 0.8090, plus 0.8090, minus 1.0] with the diagonal step size damped to 0.0071

Why Does This Exist?

Continuous control breaks tables. A robot arm or a mountain car lives in an infinite state space, so you cannot store one value per state. Linear methods replace the table with v^(s,w)=wTϕ(s)\hat{v}(s, w) = w^T \phi(s): a weight vector ww times a feature vector ϕ(s)\phi(s) built from the raw state ss. The features decide what shapes the approximator can draw.

The obvious features are polynomials: 11, s1s_1, s2s_2, s12s_1^2, s1s2s_1 s_2, and so on. They fail in practice for three concrete reasons. A TD error in one corner shifts weights that warp predictions everywhere, powers above 2 shoot toward huge magnitudes near the edges, and the features correlate with each other so gradient steps zigzag. Feature construction exists to pick something better, and this page covers the pair most often compared: the polynomial baseline and its replacement.

Konidaris, Osentoski, and Thomas (AAAI 2011) showed the Fourier cosine basis beats polynomials and radial basis functions on standard control benchmarks and matches far heavier learned bases. The idea is simple: normalize each state coordinate into [0,1][0, 1], then use cosine harmonics cos⁡(πcTs)\cos(\pi c^T s) with integer frequency vectors cc. Every feature stays inside [−1,1][-1, 1], distinct frequencies are orthogonal, and high frequencies get smaller step sizes automatically.

The one idea you need first. A basis is just a fixed dictionary of shapes whose weighted sum approximates the value surface. Polynomials use powers, tile coding uses overlapping grids, and radial basis functions use bumps. The Fourier basis uses waves. If you know that wTϕ(s)w^T \phi(s) means one weight per shape, you have enough to follow this page.

Think of It Like This

Shaping clay with mallets versus tuning with organ pipes

You must reproduce a bumpy landscape in clay. The polynomial toolkit is a set of mallets shaped like xx, x2x^2, x3x^3. Each strike bends the whole sheet at once, and the heavy x5x^5 mallet dents the middle whenever you tap the edge. Fixing one corner ruins the opposite corner.

The Fourier toolkit is a row of organ pipes. The first pipe hums the broad slope of the land, the next adds one gentle undulation, the next adds two. Each pipe sounds only its own note, none can blast the roof off, and you turn the high shimmering pipes down low so they add texture without screaming. You build the same landscape, but every control does one job and stays bounded.

Where it stops: pipes sound in time while basis features vary across state space, and real organs do not scale their stops by frequency norm. The bounded plus orthogonal part carries over; the acoustics do not.

How It Actually Works

An order nn basis in kk dimensions has (n+1)k(n + 1)^k features. For k=2k = 2 and n=1n = 1 that is 4 features; for k=2k = 2 and n=3n = 3 it is 16. Both bases below share that count. They differ in what each feature computes.

The polynomial baseline

Each feature multiplies powers of the state coordinates. With exponent vector cic_i whose entries run 00 to nn:

ϕi(s)=∏j=1ksjci,j\phi_i(s) = \prod_{j=1}^{k} s_j^{c_{i,j}}

For k=2k = 2 and n=2n = 2 the 9 features are 11, s1s_1, s2s_2, s12s_1^2, s22s_2^2, s1s2s_1 s_2, s12s2s_1^2 s_2, s1s22s_1 s_2^2, s12s22s_1^2 s_2^2. Nothing bounds them and nothing orthogonalizes them, so the Hessian of the projected error is ill conditioned and semi gradient TD chases its own tail near the borders.

The Fourier cosine basis

Normalize first: every coordinate sjs_j is clipped and scaled into [0,1][0, 1]. Then each feature is one cosine of an integer frequency combination. With coefficient vector cic_i whose entries run 00 to nn:

ϕi(s)=cos⁡(π ciTs)=cos⁡(π∑j=1kci,jsj)\phi_i(s) = \cos(\pi \, c_i^T s) = \cos\left(\pi \sum_{j=1}^{k} c_{i,j} s_j\right)

Three properties matter. Boundedness: every ϕi(s)\phi_i(s) lies in [−1,1][-1, 1], so no feature can explode. Orthogonality: distinct frequency vectors give zero inner product over [0,1]k[0, 1]^k, which keeps the gradient covariance well conditioned and lets coarse and fine structure learn without fighting. Frequency scaled steps: with base rate α\alpha and norm ∥ci∥2\lVert c_i \rVert_2, use αi=α\alpha_i = \alpha when the norm is 00 and αi=α/∥ci∥2\alpha_i = \alpha / \lVert c_i \rVert_2 otherwise. High frequency waves oscillate fast over short distances, so they earn smaller steps and the ringing the paper reports with uniform rates disappears.

Worked example

Take a 2D state at normalized coordinates s=[0.2,0.8]s = [0.2, 0.8], order n=1n = 1, base rate α=0.01\alpha = 0.01. The 4 frequency vectors are [0,0][0, 0], [0,1][0, 1], [1,0][1, 0], [1,1][1, 1].

Bias term c=[0,0]c = [0, 0]: dot product 0.00.0, feature cos⁡(0)=1.0000\cos(0) = 1.0000, norm 00, step α0=0.0100\alpha_0 = 0.0100.

Vertical mode c=[0,1]c = [0, 1]: dot product 0.80.8, feature cos⁡(0.8π)=cos⁡(144∘)=−0.8090\cos(0.8\pi) = \cos(144^\circ) = -0.8090, norm 1.01.0, step α1=0.0100\alpha_1 = 0.0100.

Horizontal mode c=[1,0]c = [1, 0]: dot product 0.20.2, feature cos⁡(0.2π)=cos⁡(36∘)=0.8090\cos(0.2\pi) = \cos(36^\circ) = 0.8090, norm 1.01.0, step α2=0.0100\alpha_2 = 0.0100.

Diagonal mode c=[1,1]c = [1, 1]: dot product 1.01.0, feature cos⁡(π)=−1.0000\cos(\pi) = -1.0000, norm 2≈1.4142\sqrt{2} \approx 1.4142, step α3=0.01/1.4142≈0.0071\alpha_3 = 0.01 / 1.4142 \approx 0.0071.

So ϕ(s)=[1.0000,−0.8090,0.8090,−1.0000]\phi(s) = [1.0000, -0.8090, 0.8090, -1.0000] with steps [0.0100,0.0100,0.0100,0.0071][0.0100, 0.0100, 0.0100, 0.0071]. The diagram draws exactly these four bars. Note the diagonal step is damped about 30 percent: the finest detail moves slowest, which is the point of the scaling rule.

Code

The snippet builds the same 4 features with the standard library only and prints the numbers from the worked example.

import math

def fourier_features(s: list[float], order: int = 1) -> list[float]:    """Cosine features cos(pi * c^T s) for a 2D state in [0, 1]^2."""    freqs = [(0, 0), (0, 1), (1, 0), (1, 1)]    out = []    for c1, c2 in freqs:        out.append(math.cos(math.pi * (c1 * s[0] + c2 * s[1])))    return out

def scaled_alphas(base: float = 0.01) -> list[float]:    freqs = [(0, 0), (0, 1), (1, 0), (1, 1)]    out = []    for c1, c2 in freqs:        norm = math.sqrt(c1 * c1 + c2 * c2)        out.append(base if norm == 0 else base / norm)    return out

s = [0.2, 0.8]print([round(v, 4) for v in fourier_features(s)])  # -> [1.0, -0.809, 0.809, -1.0]print([round(a, 4) for a in scaled_alphas()])      # -> [0.01, 0.01, 0.01, 0.0071]

The first print matches the hand computation [1.0000,−0.8090,0.8090,−1.0000][1.0000, -0.8090, 0.8090, -1.0000] and the second matches the damped diagonal step 0.00710.0071.

Watch Out For

Feeding raw coordinates straight into the cosines

The symptom is a value surface that looks like static: neighboring states get wildly different predictions and nothing learns. The cause is almost always skipped normalization. Physical ranges like position in [−1.2,0.6][-1.2, 0.6] swing the argument πcTs\pi c^T s through many stray cycles, so spatial neighbors land on unrelated phases.

Clip each coordinate to its known bounds and scale linearly into [0,1][0, 1] before encoding. Keep the bounds fixed for the whole run; refitting them mid training silently redefines every feature.

One step size for every frequency

The symptom is high frequency ripple: the broad shape looks right but fine waves jitter across updates and the error will not settle. A uniform α\alpha pushes fast oscillating features as hard as the smooth bias term, which is exactly the ringing Konidaris and coauthors warn about.

Divide by the frequency norm, αi=α/∥ci∥2\alpha_i = \alpha / \lVert c_i \rVert_2, keeping α\alpha for the zero vector. If ripple persists, lower the base α\alpha before adding more orders.

The Quick Version

  • Polynomial features multiply state powers and suffer global interference, edge blowups, and correlated gradients.
  • Fourier features take cos⁡(πcTs)\cos(\pi c^T s) over states normalized into [0,1][0, 1], giving (n+1)k(n + 1)^k bounded waves.
  • Every Fourier feature stays in [−1,1][-1, 1] and distinct frequencies are orthogonal, so coarse and fine structure stop fighting.
  • Step sizes scale as α/∥c∥2\alpha / \lVert c \rVert_2, which damps high frequencies and removes ringing.
  • At s=[0.2,0.8]s = [0.2, 0.8] with order 1 the features are [1.0,−0.8090,0.8090,−1.0][1.0, -0.8090, 0.8090, -1.0] and the diagonal step is 0.00710.0071.