Brownian motion · Learning from a finite sample

From wandering to a number.

An equation predicts how particles spread. Turn the question around: what can a finite set of displacements tell you about its hidden parameters, and what must you know independently?

The inverse problem

What can wandering reveal?

Static worked example

Predict before the numbers

With four times as many displacement measurements, the interval for D becomes about four times narrower, about two times narrower, or unchanged?

Three relations the model could have

The result appears when you choose, say you have one in mind, or skip.

These positions were generated with a hidden molecular number. First ask what the observations identify. Then declare the missing information, estimate the number, and test what a confidence interval does across hypothetical repeats.

Observation set

The synthetic set checks inference machinery on data made with a hidden number. It is not evidence that molecules exist. Perrin 1909 waits on the admitted HistoricalDataset; this instrument will not invent table numbers. Kitchen CSV is analyzed by the existing kitchen session; this laboratory consumes that session and does not re-parse video.

1 · Observe the same path

The fixed recording lasts 1024 seconds. Spacing, coordinate count, sample count and estimator changes reuse it. No overlapping windows, localization noise, exposure blur or censoring are admitted by these intervals.

2 · Declare inference inputs

These are inference assumptions, not generator controls. Changing them never moves a recorded position. A radius derived from these same displacements using an assumed molecular number would be circular, not independent information.

Declare input uncertainty

Each input interval is its stated value plus or minus the relative bound below. Its declared coverage must describe a valid measurement procedure. Bounds alone are not confidence intervals. Calibration, timing and the chosen gas constant remain exact in this exercise.

The combined procedure allocates the remaining error probability to the diffusion interval and uses a union bound. It requires no independence between the three input intervals, but cannot create a coverage guarantee from undeclared coverage.

3 · Apply the request
Change the synthetic generator

Changing these fields starts a new physical run. The hidden molecular number is drawn on a separate stream from 3 × 10²³ to 1.2 × 10²⁴ mol⁻¹. The generator uses the explicitly chosen R = 8.314471 J/(mol K), not a modern Boltzmann constant that would preselect the answer.

With the same settings, the same seed replays the same synthetic data. Any whole number from 0 to 18,446,744,073,709,551,615 works.

These comparison buttons use accepted settings, not draft edits. “Use declared generator conditions” supplies the known setup of this synthetic exercise; it is not a real independent measurement.

The CSV contains every selected position and displacement in SI units with generator metadata. It contains synthetic data, not observations of a real suspension. Opening a shared link starts no worker.

7.90940−7.909402550Displacement (μm)Observation time (s)
One synthetic path, observed at the accepted spacing. Solid: x; dashed: y when selected. Connecting lines join observations; they do not claim a resolved microscopic trajectory.

Static worked example. No calculation has been started in this browser.

Accepted seed 1905; 50 non-overlapping displacements; 2 coordinates; spacing 1 s. Known zero drift · unbiased. Assumed T = 293.15 K, η = 1 mPa s; independent radius not declared.

What the data identify

Accepted estimate and conditional diffusion uncertainty
Estimated D (μm²/s)0.88271
95% diffusion interval (μm²/s)0.68131 – 1.1893
Degrees of freedom100
Compatible radius × number (m/mol)1.4649 × 10¹⁷

A family before a single number

At the assumed temperature and viscosity, the estimated diffusion scale constrains a product: radius times molecular number. A larger radius and a smaller number can fit exactly the same point estimate.

0.114.6490.447213.275620.73244N (10²³ mol⁻¹); logarithmic axesAssumed radius (μm)
Each point on the solid curve gives the same diffusion estimate at the stated temperature and viscosity. This is a compatible family, not a confidence region and not a second measurement of the radius.
Read all compatible pairs
Radius–number family for this accepted estimate
Radius (μm)N (10²³ mol⁻¹)
0.114.649
0.1077813.592
0.1161612.611
0.1251911.701
0.1349310.857
0.1454210.073
0.156739.3465
0.168928.6721
0.182068.0463
0.196217.4657
0.211476.927
0.227926.4272
0.245655.9634
0.264755.5331
0.285345.1339
0.307534.7634
0.331454.4197
0.357224.1008
0.3853.8049
0.414943.5303
0.447213.2756
0.481993.0392
0.519482.8199
0.559882.6165
0.603422.4277
0.650342.2525
0.700922.0899
0.755431.9391
0.814181.7992
0.87751.6694
0.945741.5489
1.01931.4372
1.09861.3335
1.1841.2372
1.27611.148
1.37531.0651
1.48230.98828
1.59750.91696
1.72180.8508
1.85570.78941
20.73244

This checks the inference method on data made with a hidden number. It is not evidence that molecules exist.

Condition on the missing information

Conditional on exact input values; target 95%
Recovered N (10²³ mol⁻¹)The data constrain a radius–number product, not radius and molecular number separately. Declare an independent radius to condition the estimate.
Selected interval (10²³ mol⁻¹)The data constrain a radius–number product, not radius and molecular number separately. Declare an independent radius to condition the estimate.

This is recovery of a synthetic generating parameter, not a measurement of the modern Avogadro constant. The inverted bounds exchange endpoints. A finite confidence interval is not a posterior probability assigned to this one fixed parameter.

Estimator bias and small-sample limits

The known-zero-drift estimate uses every coordinate without subtracting a fitted mean. Fitting a separate drift in each coordinate consumes degrees of freedom. The centered maximum-likelihood estimate divides by the original sample count; the unbiased version uses one fewer. Both use the same correctly rescaled confidence interval.

Expected ratios under the ideal model with correct input values
Mean estimated D / true D1
Mean recovered N / true N1.0204
Variance of recovered N / true N0.021692

An unbiased diffusion estimate does not have an unbiased reciprocal. With too few degrees of freedom, the reciprocal’s mean or variance does not exist even though a particular trial can produce a finite estimate. With one displacement and fitted drift, spread is underdetermined.

The hidden answer is a learning device, not a secret: the generated browser data can be inspected.

Inspect the accepted observations
All selected synthetic positions and increments; μm and seconds
Timex (μm)y (μm)Δx (μm)Δy (μm)
000
1−2.3879−0.70732−2.3879−0.70732
2−1.6243−1.0740.76359−0.36669
3−2.41131.5563−0.787032.6303
4−0.774542.92971.63681.3734
51.8554.35882.62951.4292
60.696954.734−1.1580.3752
7−0.739544.6997−1.4365−0.034373
82.12014.31452.8597−0.38513
90.94444.4184−1.17570.10385
100.550875.964−0.393541.5456
110.636027.90940.0851571.9454
120.924166.38230.28814−1.5271
132.37567.07661.45150.69424
142.63467.38120.2590.30462
152.70687.89370.0721210.51253
161.6457.0356−1.0618−0.85806
171.50497.7346−0.140090.69891
180.00875047.6172−1.4962−0.11731
191.68666.26461.6779−1.3527
200.639126.1636−1.0475−0.10093
21−0.665025.5842−1.3041−0.57943
22−2.91346.1162−2.24840.532
23−2.48725.71310.42622−0.40314
24−2.55997.2136−0.0726521.5005
25−1.93646.54180.62345−0.67176
26−3.38136.6304−1.44490.088609
27−2.33245.58091.0489−1.0495
28−2.04592.95560.28654−2.6253
29−3.56293.7742−1.5170.81859
30−0.164843.15753.3981−0.61668
31−1.45193.1915−1.28710.033954
32−2.06791.4143−0.61602−1.7772
33−2.51491.6097−0.446960.19549
34−1.68314.78810.831763.1783
35−0.533073.80341.1501−0.98462
36−1.64685.9611−1.11382.1577
37−1.25314.44180.39373−1.5193
38−2.23365.296−0.98050.85426
39−1.74457.05820.489111.7621
40−1.48247.33320.262110.275
410.977316.6242.4597−0.70916
422.67697.04391.69960.41987
430.470835.5085−2.2061−1.5354
440.4184.7481−0.05283−0.76038
450.919452.19810.50145−2.55
463.82790.855282.9085−1.3428
473.348−0.76496−0.47992−1.6202
482.4238−0.74306−0.92420.021895
490.59245−1.1868−1.8314−0.44372
500.0198980.25574−0.572551.4425

What does coverage mean?

Repeat the observation procedure on other hypothetical paths with the same generating parameter. Each interval moves; the parameter does not. Change an inference assumption while keeping those paths to see how a wrong radius can spoil molecular-number coverage even when diffusion intervals behave well.

Uses the accepted settings. No seed search, discarded failures or redraws to reach the target fraction. In combined-input mode this view is unavailable: no repeated input-measurement procedure has been specified. After a form edit, run coverage explicitly again.

Diffusivity procedure

Run the hypothetical experiments explicitly to inspect interval coverage.

Molecular-number procedure

Run the hypothetical experiments explicitly to inspect interval coverage.

Same scientific action without the plot

Choose the observation set, estimator, and interval kind from the lists. Type the displacement count M and the inference temperature, viscosity, and radius. Read the table of estimated D and its interval, then N and its interval with the wording above, the inverse-bias note, and the identifiability family. The plots are a view of that same accepted snapshot.

Not modeled: localization error, blur, correlated or irregularly timed increments, and censoring (see the camera laboratory and kitchen mode); non-Gaussian increments; time-varying drift; polydispersity within one track set; wall effects; uncertainty in C without declared coverages; uncertainty in the gas constant itself.

Accepted calculation identity and limits

Computed by the host reference evaluator inference.bm07, for the scenario scenario-bm07-hidden-number.

Primary recording draws: 16385. Work for this request: 16385 draws, including 0 for hypothetical experiments. Retained primary recording: 65552 bytes. Reused worker recording: no.

Source identity: source:sha256:d7c143066043c23f1cba39a975b3928834a1275abe090374b8853e99570ecfc5. This host preview has no historical data importer or camera-noise fit. Browser arithmetic is not a claim of strict cross-engine WASM replay.

Watching a small sphere wander tells you how fast it spreads. If you also know the temperature, how thick the liquid is and how big the sphere is, the spreading tells you how many molecules make up a mole, and the lab shows how sure a limited number of observations can make you.

Section 5 ends by turning its formula around: N = (t/λx2)(RT/(3πkP)), so a measured mean square displacement, with the temperature, the viscosity and the radius, gives the number of molecules in a gram-molecule. The instrument does this with synthetic data from a generator whose number is hidden, 3.03 × 1023, deliberately not today's value, so the exercise tests the method and not the world. From 50 steps of 1 s in two directions, 100 squared steps, it estimates D = 8.83 × 10−13 m2/s, with a 95 percent interval from 6.81 to 11.89 × 10−13. Displacements alone fix only the product of radius and number, a·N = 1.46 × 1017 m/mol. Given the radius, 0.5 μm, the estimate is N = 2.93 × 1023, with an interval from 2.17 to 3.80 × 1023 that contains the hidden value. With today's defined constants the same inversion is only a consistency check, because NA is now fixed by definition.

Modern qualifications

Einstein's printed form is N = (t/λx2)(RT/(3πkP)), with k the viscosity and P the radius, and he closed by hoping a researcher would soon decide the question. Perrin did, in 1908 and 1909, following grains of gamboge and mastic whose radius he measured separately, and found N near 7 × 1023. The chi-square interval, the turned-over interval for N and the bias factor are modern statistics that Einstein did not give. Since 2019 NA = 6.022 140 76 × 1023 mol−1 exactly and kB is fixed too, so an inversion with today's constants recovers its own inputs: it checks the method and does not count molecules.

Show the code

The site’s own reference code for this law, in TypeScript. It computes the numbers above when the page runs it, and it is what the page uses wherever a FrankenSim result is not available.

estimateIncrements · src/physics/reference/inference.ts · this site’s source, as built · sha256:71f7841c5ec492581374173df9d328a3e208b2d1abb9cce82f455ea92f23389e

This function computes the listed outputs when it runs.

In words

It sums the squared displacements and divides by twice the interval and by a count, which is the whole of the estimate. What differs between the three estimators is the count and whether the mean is subtracted first: the known-zero-drift estimator subtracts nothing and divides by the full number of coordinates; the drift-centered one subtracts the sample mean and loses one degree of freedom for it; the maximum-likelihood one centres as well but divides by the uncentred count. On the same eight displacements at half-second spacing those give 2.8125, 3.0536 and 2.6719 times 10^-12 m^2/s.

Implementation

export function estimateIncrements(
  increments: Float64Array,
  dt: number,
  d: number,
  estimatorId: EstimatorId,
): Assessment<Estimate> {
  if (
    !(increments instanceof Float64Array) ||
    ![1, 2, 3].includes(d) ||
    increments.length % d ||
    increments.length > 10000 ||
    !positive(dt) ||
    ![
      "independent-increment-known-zero-drift",
      "drift-centered",
      "maximum-likelihood-centered",
    ].includes(estimatorId)
  )
    return invalid(
      "Use finite equally timed increments, 1–3 coordinates, and a registered estimator (at most 10000 coordinates).",
    );
  const M = increments.length / d,
    centered = estimatorId !== "independent-increment-known-zero-drift";
  if (M < (centered ? 2 : 1))
    return noValue(
      "underdetermined",
      centered
        ? "A second independent increment, or a known drift, is needed to separate drift from spread."
        : "No admitted increments are available.",
    );
  let scale = 0;
  for (const x of increments) {
    if (!Number.isFinite(x)) return invalid("Every displacement must be finite.");
    scale = Math.max(scale, Math.abs(x));
  }
  const means = new Float64Array(d),
    drift = new Float64Array(d);
  if (scale > 0)
    for (let i = 0; i < M; i++)
      for (let c = 0; c < d; c++) {
        const meanC = means[c] ?? 0;
        const inc = increments[i * d + c] ?? 0;
        means[c] = meanC + (inc / scale - meanC) / (i + 1);
      }
  let sum = 0,
    compensation = 0;
  if (scale > 0)
    for (let i = 0; i < M; i++)
      for (let c = 0; c < d; c++) {
        const inc = increments[i * d + c] ?? 0;
        const meanC = means[c] ?? 0;
        const x = inc / scale - (centered ? meanC : 0);
        const term = x * x - compensation,
          next = sum + term;
        compensation = next - sum - term;
        sum = next;
      }
  const observationInterval = dt,
    q = d * (centered ? M - 1 : M),
    normalization = estimatorId === "maximum-likelihood-centered" ? d * M : q;
  const sumSquares = sum * scale * scale,
    unbiasedDHat = sumSquares / (2 * observationInterval) / q,
    dHat = sumSquares / (2 * observationInterval) / normalization;
  for (let c = 0; c < d; c++) drift[c] = ((means[c] ?? 0) * scale) / dt;
  if (
    ![sumSquares, unbiasedDHat, dHat, ...drift].every(Number.isFinite) ||
    (sum > 0 && (sumSquares === 0 || dHat === 0 || unbiasedDHat === 0))
  )
    return invalid(
      "The displacement scale exceeds this binary64 calculation's representable range.",
    );
  return {
    kind: "accepted",
    data: Object.freeze({
      dHat,
      unbiasedDHat,
      M,
      d,
      q,
      sumSquares,
      normalization,
      biasFactor: q / normalization,
      drift,
      estimatorId,
    }),
  };
}

The site’s own reference code for this law, in TypeScript. It computes the numbers above when the page runs it, and it is what the page uses wherever a FrankenSim result is not available.

inverseBias · src/physics/reference/inference.ts · this site’s source, as built · sha256:95caaf88d552be626b5d3c6b1371ef33c5b2934361ab493bafacdffa0cd0e058

This function computes the listed outputs when it runs.

In words

A molecular number is recovered by DIVIDING by the diffusivity, and the reciprocal of a noisy estimate is not the reciprocal of the truth. For a chi-square estimate on q degrees of freedom the mean of the reciprocal is larger by normalization over (q - 2), and its variance is twice the normalization squared over (q - 2) squared times (q - 4). At q = 5 those are 1.667 and 5.556; at q = 10 with a normalization of 12, 1.5 and 0.75.

Implementation

export function inverseBias(
  q: number,
  normalization = q,
): Assessment<Readonly<{ meanFactor: number; varianceFactor: number | null }>> {
  if (!positive(q) || !positive(normalization))
    return invalid("Positive degrees of freedom and normalization are required.");
  if (q <= 2)
    return noValue(
      "not-applicable",
      "The inverse estimate has no finite expectation with two or fewer degrees of freedom.",
    );
  return {
    kind: "accepted",
    data: {
      meanFactor: normalization / (q - 2),
      varianceFactor: q > 4 ? (2 * normalization ** 2) / ((q - 2) ** 2 * (q - 4)) : null,
    },
  };
}

The site’s own reference code for this law, in TypeScript. It computes the numbers above when the page runs it, and it is what the page uses wherever a FrankenSim result is not available.

invertToMolecularNumber · src/physics/reference/inference.ts · this site’s source, as built · sha256:efce210ad84842a38f67ae1f5e8c952a02d58d7797d326bed27772f99bcf9b72

This function computes the listed outputs when it runs.

In words

N = R T / (6 pi eta a D), which is the Stokes-Einstein relation read backwards, with the interval carried through by dividing the ENDS in the opposite order, so the upper diffusivity gives the lower number. On a modern set at 290.15 K, 1.35 mPa s, half a micron and a diffusivity of 3.1 times 10^-13 m^2/s it returns 6.116 times 10^23, which is 1.0156 times the defined Avogadro constant, and the Boltzmann constant implied by the same inputs, 1.3594 times 10^-23 J/K.

Implementation

export function invertToMolecularNumber(
  input: Conditions & { dHat: number; interval: StatisticalInterval; synthetic: boolean },
  set: ConstantSet,
): Assessment<
  Readonly<{
    estimate: number;
    interval: StatisticalInterval;
    constantSetId: string;
    semanticKind: NumberMeaning;
    consistencyRatio: number | null;
    estimatedBoltzmannConstant: number | null;
    consistencyRatioInterval: StatisticalInterval | null;
    estimatedBoltzmannConstantInterval: StatisticalInterval | null;
  }>
> {
  const conditions = checkConditions(input);
  if (conditions.kind !== "accepted") return conditions;
  const g = gas(set, input.synthetic);
  if (g.kind !== "accepted") return g;
  if (input.dHat === 0 || input.interval.lower === 0)
    return noValue(
      "underdetermined",
      "No positive diffusion scale was resolved; a finite molecular number cannot be recovered by division by zero.",
    );
  if (
    !positive(input.dHat) ||
    !positive(input.interval.lower) ||
    !positive(input.interval.upper) ||
    input.interval.lower > input.interval.upper
  )
    return invalid("A positive diffusion estimate and ordered interval are required.");
  // Live-term identifiers for show-the-code: diffusionCoefficient, avogadroNumberEstimate,
  // particleRadius, viscosity, temperature, molarGasConstant. Never a modern exact k_B here.
  const temperature = input.T,
    viscosity = input.eta,
    particleRadius = input.a,
    molarGasConstant = g.data.R,
    diffusionCoefficient = input.dHat;
  const C = (molarGasConstant * temperature) / (6 * Math.PI * viscosity * particleRadius);
  const avogadroNumberEstimate = C / diffusionCoefficient;
  const estimate = avogadroNumberEstimate;
  const lower = C / input.interval.upper,
    upper = C / input.interval.lower;
  const modern = g.data.semanticKind === "consistency-check";
  const avogadro = modern ? constantValue(set, "avogadroConstant").value : 1;
  const consistencyRatio = modern ? estimate / avogadro : null;
  const estimatedBoltzmannConstant = modern
    ? (6 * Math.PI * input.eta * input.a * input.dHat) / input.T
    : null;
  const consistencyRatioInterval = modern
    ? Object.freeze({
        ...input.interval,
        lower: lower / avogadro,
        upper: upper / avogadro,
      })
    : null;
  const estimatedBoltzmannConstantInterval = modern
    ? Object.freeze({
        ...input.interval,
        lower: (6 * Math.PI * input.eta * input.a * input.interval.lower) / input.T,
        upper: (6 * Math.PI * input.eta * input.a * input.interval.upper) / input.T,
      })
    : null;
  const modernMetrics =
    modern &&
    consistencyRatio !== null &&
    estimatedBoltzmannConstant !== null &&
    consistencyRatioInterval !== null &&
    estimatedBoltzmannConstantInterval !== null
      ? [
          consistencyRatio,
          estimatedBoltzmannConstant,
          consistencyRatioInterval.lower,
          consistencyRatioInterval.upper,
          estimatedBoltzmannConstantInterval.lower,
          estimatedBoltzmannConstantInterval.upper,
        ]
      : [];
  if (![estimate, lower, upper, ...modernMetrics].every(positive))
    return invalid("The inverse result cannot be represented at this numerical scale.");
  return {
    kind: "accepted",
    data: Object.freeze({
      estimate,
      interval: Object.freeze({ ...input.interval, lower, upper }),
      constantSetId: set.id,
      semanticKind: g.data.semanticKind,
      consistencyRatio,
      estimatedBoltzmannConstant,
      consistencyRatioInterval,
      estimatedBoltzmannConstantInterval,
    }),
  };
}

The site’s own reference code for this law, in TypeScript. It computes the numbers above when the page runs it, and it is what the page uses wherever a FrankenSim result is not available.

combinedMolecularNumberInterval · src/physics/reference/inference.ts · this site’s source, as built · sha256:3335f9e6c633a8147da58f7335e269c0996e3fa0b9b6b6cf2b59aab0f5ec0321

This function computes the listed outputs when it runs.

In words

It takes a declared interval for any of the three conditions, pairs the endpoints in the direction that widens the answer, and returns the result with its coverage reduced by the sum of the input error rates. The pairing is where the care is: the lower molecular number uses the lowest temperature against the highest viscosity, radius and diffusivity, and the upper one the reverse, because N rises with T and falls with each of the other three.

Implementation

/** Conservative union-bound procedure. Marginal input intervals do NOT need
 * statistical independence. Calibration is held exact in this BM-07 slice. */
export function combinedMolecularNumberInterval(
  input: Conditions & {
    estimate: Estimate;
    alphaD: number;
    inputs: Partial<Record<"T" | "eta" | "a", DeclaredInterval>>;
    synthetic: boolean;
  },
  set: ConstantSet,
): Assessment<StatisticalInterval> {
  const conditions = checkConditions(input);
  if (conditions.kind !== "accepted") return conditions;
  const g = gas(set, input.synthetic);
  if (g.kind !== "accepted") return g;
  let alpha = input.alphaD;
  const ranges: Record<"T" | "eta" | "a", readonly [number, number]> = {
    T: [input.T, input.T],
    eta: [input.eta, input.eta],
    a: [input.a, input.a],
  };
  if (Object.keys(input.inputs).some((k) => !["T", "eta", "a"].includes(k)))
    return invalid(
      "Only temperature, viscosity and radius intervals are admitted by this procedure.",
    );
  for (const k of ["T", "eta", "a"] as const) {
    const v = input.inputs[k];
    if (v === undefined) continue;
    if (!v || !Number.isFinite(v.coverage) || v.coverage <= 0 || v.coverage > 1)
      return noValue("not-applicable", `The ${k} input interval has no valid declared coverage.`);
    if (!positive(v.lower) || !positive(v.upper) || v.lower > input[k] || v.upper < input[k])
      return invalid(`The positive ${k} interval must contain its stated point input.`);
    alpha += 1 - v.coverage;
    ranges[k] = [v.lower, v.upper];
  }
  if (!positive(input.alphaD) || input.alphaD >= 1 || !positive(1 - alpha))
    return noValue(
      "not-applicable",
      "The declared input error rates leave no positive simultaneous coverage guarantee.",
    );
  const interval = estimatorInterval(input.estimate, input.alphaD);
  if (interval.kind !== "accepted") return interval;
  if (interval.data.lower === 0)
    return noValue("underdetermined", "A positive diffusion scale is required before inversion.");
  const lower =
    (g.data.R * ranges.T[0]) / (6 * Math.PI * ranges.eta[1] * ranges.a[1] * interval.data.upper);
  const upper =
    (g.data.R * ranges.T[1]) / (6 * Math.PI * ranges.eta[0] * ranges.a[0] * interval.data.lower);
  if (![lower, upper].every(positive))
    return invalid("The combined interval cannot be represented.");
  return {
    kind: "accepted",
    data: Object.freeze({
      ...interval.data,
      lower,
      upper,
      coverage: 1 - alpha,
      coverageKind: "conservative",
      estimatorId: `${input.estimate.estimatorId}; Bonferroni with declared input intervals`,
    }),
  };
}

The site’s own reference code for this law, in TypeScript. It computes the numbers above when the page runs it, and it is what the page uses wherever a FrankenSim result is not available.

identifiabilityFamily · src/physics/reference/inference.ts · this site’s source, as built · sha256:4d25196f7d987b1e967488ce3f9fcb932a4041addbefd7b5ced130539a9c2829

This function computes the listed outputs when it runs.

In words

A diffusivity fixes R T / (6 pi eta D), and that is a product of a radius and a molecular number. Give the radius and the number follows; give the number and the radius follows; measure the displacements alone and neither is determined. The function returns 41 radii spaced evenly in the logarithm across the range asked for, with the molecular number that goes with each. Over two decades of radius the numbers run over two decades too, from 3.058 times 10^24 down to 3.058 times 10^22.

Implementation

export function identifiabilityFamily(
  input: {
    D: number;
    T: number;
    eta: number;
    radiusRange: readonly [number, number];
    synthetic: boolean;
  },
  set: ConstantSet,
): Assessment<
  Readonly<{
    product: number;
    radii: Float64Array;
    numbers: Float64Array;
    semanticKind: NumberMeaning;
    constantSetId: string;
  }>
> {
  const g = gas(set, input.synthetic);
  if (g.kind !== "accepted") return g;
  if (input.D === 0)
    return noValue(
      "underdetermined",
      "A positive diffusion scale is needed to draw the compatible family.",
    );
  if (
    ![input.D, input.T, input.eta, ...input.radiusRange].every(positive) ||
    input.radiusRange[0] >= input.radiusRange[1]
  )
    return invalid("Use positive conditions and an increasing radius range.");
  const product = (g.data.R * input.T) / (6 * Math.PI * input.eta * input.D);
  const radii = Float64Array.from({ length: 41 }, (_, i) =>
    Math.exp(
      Math.log(input.radiusRange[0]) * (1 - i / 40) + (Math.log(input.radiusRange[1]) * i) / 40,
    ),
  );
  const numbers = Float64Array.from(radii, (a) => product / a);
  if (![product, ...numbers].every(positive))
    return invalid("The compatible family cannot be represented.");
  return {
    kind: "accepted",
    data: { product, radii, numbers, semanticKind: g.data.semanticKind, constantSetId: set.id },
  };
}

Each placement has separate settings and worker ownership. The same seed initially reproduces the same synthetic data; choose a different seed for an independent trial.

Open the inverse argument

The same positions, different questions

For independent, equally spaced Gaussian increments in d coordinates with known zero drift, average their squared lengths and divide by twice the number of coordinates and the time interval. The result estimates the diffusion coefficient.

D^=12dMΔt∑i=1M∥Δri∥2,q=dM\begin{gathered}\widehat D=\frac{1}{2dM\Delta t}\sum_{i=1}^{M}\lVert\Delta\mathbf r_i\rVert^2, \\ q=dM\end{gathered}

When drift is fitted from the same observations, subtract the mean increment in each coordinate. The unbiased spread estimate uses M − 1 in place of M and q = d(M − 1). The centered maximum-likelihood estimate instead divides by M and must be rescaled before applying the same interval procedure.

An interval for a procedure

Under this ideal model, q times the unbiased diffusion estimate divided by the true diffusivity follows a chi-square distribution. Its quantiles provide the following conditional interval. More independent observations generally tighten uncertainty; changing the assumptions changes what the interval means.

[qD^unbiasedχq,1−α/22, qD^unbiasedχq,α/22]\left[\frac{q\widehat D_{\mathrm{unbiased}}}{\chi^2_{q,1-\alpha/2}},\ \frac{q\widehat D_{\mathrm{unbiased}}}{\chi^2_{q,\alpha/2}}\right]

At 95% coverage, the procedure covers the fixed true value in 95% of repetitions under its assumptions. That is not a 95% posterior probability for one parameter in one realized interval. The repeated-trial plots retain the misses as well as the successes.

Why an independent radius matters

Na=RT6πηD,N^=RT6πηaD^N a=\frac{RT}{6\pi\eta D},\qquad \widehat N=\frac{RT}{6\pi\eta a\widehat D}
Explain this equation

With temperature and viscosity given, displacements identify the product Na. They cannot separately identify N and the radius a. Declaring a radius permits a conditional inversion, but deriving that radius from the same data using an assumed N would merely return the assumption.

Inversion also reverses the interval’s endpoints. It does not preserve unbiasedness: for the known-zero-drift estimate, the mean recovered-to-true number ratio is q/(q − 2) when q is greater than two. A point estimate can be finite even when that repeated-estimate mean does not exist.

Do not hide input uncertainty

The conditional interval treats the stated inputs as exact. The combined procedure uses the declared temperature, viscosity and radius intervals and adds their error probabilities with the diffusion interval’s error probability. This union-bound guarantee is conservative and does not require their statistical independence. It remains conditional on exact calibration, timing and the chosen gas constant in this preview.

Worked interval (readable without JavaScript)

d = 2, M = 50, q = 100, D̂ = 0.42944 μm²/s, 95% chi-square interval. Not a molecular count.
QuantityValue
Diffusion interval[0.331457, 0.578589] μm²/s
Modern-SI inversion (consistency check)N̂ = 6.02213 × 10²³ mol⁻¹
Inverse-bias factor at q = 100q/(q − 2) = 1.020408; an unbiased D̂ is not unbiased after inversion

A synthetic recovery is not a new molecular count

This exercise draws a hidden number and generates data from it using a chosen gas constant. Recovering that number tests inference under the model; it does not measure a real suspension. With modern SI constants, the Avogadro constant is defined, so an observational inversion would be a consistency check rather than an independent determination. Historical observations and independent gas-constant measurements are not supplied here. This instrument uses ideal positions; the separate camera laboratory adds an explicitly later measurement model.

NIST: chi-square confidence limits for a normal variance · BIPM: SI units and defining constants

Next: keep the particle, change the camera, and test the inference

Before you draw a conclusion

Common wrong turns this instrument speaks to

  • Einstein's paper proved that molecules exist.

    The paper states a test, not a proof. It says what would follow if the predicted motion were observed, and what would follow if it were not.

    Read the full entry, with its sources

  • The laboratory recovered the molecular number, so the count has been confirmed here.

    The displacements were generated from a number the laboratory already had. Recovering it checks the inference, not the world. § 5 asks for a measurement, and the paper closes by leaving that to somebody else.

    Read the full entry, with its sources

Embed this laboratory on another page. It starts from its worked defaults, not your current settings.