whestbench.
Reference

Code Patterns

Quick reference for flopscope operations, including operators, FLOP costs, and common patterns for mean and variance propagation.

Quick reference for flopscope operations. All examples assume import flopscope as flops and import flopscope.numpy as fnp.

Operators are tracked

Python arithmetic operators (+, -, *, /, @) on fnp.ndarray values are FLOP-tracked — you do not need to use the verbose fnp.add, fnp.multiply, etc. forms.

import flopscope as flops
import flopscope.numpy as fnp

a = fnp.ones(4)
b = fnp.ones(4)

# These are all equivalent and all tracked:
c = a + b           # tracked: same as fnp.add(a, b)
d = a * b           # tracked: same as fnp.multiply(a, b)
e = a / b           # tracked: same as fnp.divide(a, b)

W = fnp.eye(4)
v = fnp.ones(4)
f = W @ v           # tracked: same as fnp.matmul(W, v)
g = W.T @ v         # tracked: transpose is free, matmul is tracked
h = W.T @ W @ v     # tracked: two matmuls, chained with @

Use operators whenever they improve readability. The verbose fnp.* forms are still available but are no longer required for tracking purposes.

Operation costs

flopscope 0.9.1 prices each op as flop_cost x weight x dtype_rate. The weight falls into four tiers, and dtype_rate multiplies the bill by ~2x for float64 (and more for complex):

  • Free (0) — views only: basic indexing/slices, transpose, diag extraction, real/imag.
  • 1x per element — arithmetic and data movement: element-wise ops, sqrt/abs, copy, reshape/ravel, stack/concatenate/tile/repeat, fills (ones/full/eye), scatters (fill_diagonal, put).
  • 4x per element — value lookup / reordering: fancy and boolean indexing, take, sort/argsort, set-ops, histograms, random reordering, and 3-arg where.
  • 16x per element — transcendentals: exp, log, sin/cos, tanh, **.
  • matmul is O(m x n x k) and dominates budgets.

Figures below are float32 baselines — a guide, not a guarantee. The ground truth is budget.summary(), and the authoritative per-op model is the flopscope cost reference.

What you wantCodeFLOP cost (float32)Notes
Create zerosfnp.zeros((n, n))0Free (also empty)
Create ones / full / eyefnp.ones(n)nFills are charged (only zeros is free)
Wrap existing datafnp.array(data)nMaterializes a copy; numeric dtypes only (see below)
Matrix multiplyfnp.matmul(A, B)O(m x n x k)Dominates budgets
Element-wise addfnp.add(a, b)1 per elementAlso sub, mul, div
ReLUfnp.maximum(x, 0.0)1 per element
Square root / absfnp.sqrt(x), fnp.abs(x)1 per element
Exponentialfnp.exp(x)16 per elementTranscendental
Logarithmfnp.log(x)16 per elementTranscendental
Power / trigx ** 2, fnp.sin(x)16 per elementTranscendental; use x * x for squares
Transposefnp.transpose(W)0Free (view)
Reshape / ravelfnp.reshape(x, shape)nMaterializes
Extract diagonalfnp.diag(M)0Free
Set diagonalfnp.fill_diagonal(M, v)nScatter write
Outer productfnp.outer(a, b)n x m
Sum / mean / maxfnp.sum(x, axis=0)input size
Stack / concatenatefnp.stack(rows, axis=0)total sizeMaterializes
Basic index / slicex[0], x[:, 3]0Free (view)
Fancy / boolean indexx[idx], x[mask]4 per elementGather (mask: numel + 4 x selected)
Select with wherefnp.where(c, a, b)4 per elementWas free before 0.9.0
Sort / gatherfnp.sort(x), fnp.take(x, i)4 per elementOrder / selector-deriving
Open-mesh index gridfnp.ix_(i, j)total output sizeWas free before 0.11.0; each Boolean argument adds numel(arg) for the internal nonzero scan; rejects ndarray subclasses such as MaskedArray / memmap

Numeric dtypes only. fnp.array(data) requires data that coerces to a numeric dtype — bool, integer, unsigned integer, float or complex. Mixed, string, bytes, datetime64, timedelta64 and object input now raise flopscope.errors.UnsupportedDtypeError. Do not plan on converting with plain numpy first — the grader sandbox does not provide it. Build numeric arrays from already-numeric scalars with an explicit dtype=fnp.float64, and hold ragged or mixed data in a Python list of numeric arrays rather than one object array.

Common patterns

Standard normal PDF and CDF (built-in)

flopscope provides built-in PDF and CDF functions that are FLOP-tracked:

import flopscope as flops
import flopscope.numpy as fnp

phi = flops.stats.norm.pdf(x)   # standard normal PDF
Phi = flops.stats.norm.cdf(x)   # standard normal CDF

These are the recommended approach — all example estimators use them. The manual implementations below are shown for reference.

Standard normal PDF (for ReLU expectation)

import flopscope as flops
import flopscope.numpy as fnp

def norm_pdf(x):
    """phi(x) = exp(-x^2/2) / sqrt(2*pi)"""
    return fnp.exp(-0.5 * x * x) / fnp.sqrt(2.0 * fnp.pi)

Standard normal CDF

Pure flopscope implementation using the Abramowitz & Stegun approximation (accurate to <7.5e-8):

import flopscope as flops
import flopscope.numpy as fnp

_P = 0.2316419
_A1, _A2, _A3 = 0.319381530, -0.356563782, 1.781477937
_A4, _A5 = -1.821255978, 1.330274429

def norm_cdf(x):
    t = 1.0 / (1.0 + _P * fnp.abs(x))
    poly = ((((_A5 * t + _A4) * t + _A3) * t + _A2) * t + _A1) * t
    pdf = fnp.exp(-0.5 * x * x) / fnp.sqrt(2.0 * fnp.pi)
    cdf = 1.0 - pdf * poly
    return fnp.where(x >= 0, cdf, 1.0 - cdf)

Use the pure-flopscope version above. The grader sandbox does not provide scipy (or any third-party PyPI package) — only flopscope, the whestbench API, and the Python standard library are importable — and only flopscope operations are FLOP-counted. In Phase 2 there is no requirements.txt escape hatch: bundling scipy, numpy, a BLAS, or any compiled kernel is prohibited, not merely uncounted.

ReLU expectation (E[max(0, z)] where z ~ N(mu, sigma^2))

import flopscope as flops
import flopscope.numpy as fnp

alpha = mu_pre / sigma_pre
E_relu = mu_pre * norm_cdf(alpha) + sigma_pre * norm_pdf(alpha)

See 02_mean_propagation.py (in the starter kit) for a complete worked example using these patterns.

Per-neuron variance propagation (diagonal)

import flopscope as flops
import flopscope.numpy as fnp

# var_pre[i] = sum_j W[j,i]^2 * var[j]
var_pre = (w * w).T @ var

Next step

On this page