solver.press

After removal of all deterministic components via high-fidelity pulsar timing models, residual pulsar timing noise (dominated by spin noise and dispersion measure variations) constitutes a physically irreducible stochastic process — providing a universally accessible, publicly verifiable source of certified true randomness for cryptographic applications that requires only a radio telescope and an open pulsar ephemeris, with no proprietary hardware or institutional trust required.

Computer ScienceApr 14, 2026Evaluation Score: 42%

Adversarial Debate Score

42% survival rate under critique

Model Critiques

grok: The hypothesis is falsifiable, as it can be tested by analyzing pulsar timing residuals for irreducible stochasticity, but it lacks direct support from the provided papers, which focus on unrelated topics like quantum computing and biological systems. Additionally, counterarguments exist regardin...
openai: The hypothesis is falsifiable in principle, but it is not directly supported by the provided papers—none address pulsar timing noise or its statistical/cryptographic properties. There are major counterarguments: pulsar noise is not proven irreducibly stochastic (as new physical effects or modelin...
mistral: The hypothesis is falsifiable and grounded in well-documented pulsar timing noise, but its claim of "physically irreducible" randomness may overstate the case—environmental or instrumental artifacts could introduce biases, and cryptographic viability depends on unproven assumptions about noise st...
anthropic: The hypothesis is scientifically interesting and partially falsifiable in principle, but none of the provided papers are even remotely relevant to pulsar timing, radio astronomy, or randomness extraction — making it entirely unsupported by the cited literature. Additionally, significant counterar...

Supporting Research Papers

Formal Verification

Z3 logical consistency:✅ Consistent

Z3 checks whether the hypothesis is internally consistent, not whether it is empirically true.

Experimental Validation Package

This discovery has a Claude-generated validation package with a full experimental design.

Precise Hypothesis

Residual timing noise from millisecond pulsars (MSPs), after subtraction of a complete deterministic timing model (including spin-down, proper motion, parallax, binary orbital parameters, and dispersion measure polynomial fits), contains a stochastic component that passes all standard randomness tests (NIST SP 800-22, TestU01 BigCrush, Dieharder) at p > 0.01 for each test, exhibits no autocorrelation at lags > 1 epoch at r < 0.05 significance, and cannot be predicted beyond the noise floor by any known physical model — thereby qualifying as a cryptographically suitable true random number generator (TRNG) source. Specifically: for at least 5 MSPs with timing baselines > 5 years and residual RMS < 1 μs, the extracted bit sequences will be statistically indistinguishable from ideal uniform random sequences at α = 0.01 after von Neumann debiasing.

Disproof criteria:
  1. STATISTICAL FAILURE: If > 20% of NIST SP 800-22 subtests fail at α = 0.01 for any single MSP after debiasing, the source fails cryptographic qualification.
  2. PREDICTABILITY: If a machine learning model (LSTM or Gaussian Process with physically motivated kernel) achieves prediction of future residuals with normalized RMSE < 0.5 (i.e., better than 50% variance reduction over a naive mean predictor) on held-out epochs, the process is not irreducible.
  3. CROSS-PULSAR CORRELATION: If Pearson |r| > 0.1 between residuals of two pulsars separated by > 10° on sky at p < 0.001, the noise is correlated (likely GWB-dominated) and not independently random per pulsar.
  4. DETERMINISTIC RESIDUAL: If a higher-order timing model (adding F3, F4, or additional DM terms) reduces residual RMS by > 30%, the original residuals contained deterministic signal, not irreducible noise.
  5. HARDWARE DEPENDENCE: If residuals from the same pulsar observed simultaneously on two different telescopes show correlation coefficient |r| < 0.3 (i.e., they are NOT correlated), this indicates telescope-specific noise dominates, not astrophysical noise.
  6. THROUGHPUT FAILURE: If the usable random bit rate after debiasing falls below 1 bit/hour per pulsar, the source is impractical for any cryptographic application.
  7. REPRODUCIBILITY FAILURE: If independent reanalysis of the same raw data by two groups using different software (TEMPO2 vs. PINT) yields residuals with correlation |r| < 0.95, the extraction process is not deterministic and the source is unreliable.

Experimental Protocol

PHASE 1 — Data Acquisition and Baseline Characterization (Days 1–60): Select 10 MSPs from public archives (IPTA DR2, NANOGrav 15-year, PPTA DR3) with known timing solutions. Download all available TOA (time-of-arrival) data and noise budgets. Fit complete timing models using TEMPO2 and PINT independently.

PHASE 2 — Noise Decomposition (Days 30–90): Apply Bayesian noise analysis (enterprise/PTMCMCSampler) to decompose residuals into: (a) white noise (EFAC, EQUAD, ECORR), (b) red spin noise (power-law spectrum), (c) DM variations (chromatic red noise), (d) GWB contribution (Hellings-Downs correlated). Extract the white-noise-subtracted, GWB-subtracted residual as the candidate random source.

PHASE 3 — Randomness Testing (Days 60–120): Convert residuals to bit strings via von Neumann debiasing and hash-based extraction. Apply NIST SP 800-22, TestU01 BigCrush, and Dieharder test suites. Compute autocorrelation functions, spectral analysis, and entropy estimates.

PHASE 4 — Cryptographic Qualification (Days 90–150): Test against AIS 31 (BSI) Class P2 requirements. Measure min-entropy H_min. Compare against hardware TRNGs (Intel RDRAND, Quantis QRNG) as benchmarks. Assess practical bit generation rate.

PHASE 5 — Independent Replication (Days 120–180): Repeat full pipeline on new observations from a second telescope (e.g., MeerKAT + Parkes cross-validation). Verify reproducibility of residual extraction.

Required datasets:
  1. IPTA Data Release 2 (DR2): Public TOA data for 65 MSPs, multi-telescope, multi-frequency. URL: ipta4gw.org. Size: ~2 GB. License: Open.
  2. NANOGrav 15-year Data Set: 68 MSPs, 12.5–15 year baselines, 1.4 GHz and 820 MHz. URL: nanograv.org/data. Size: ~5 GB. License: Open.
  3. PPTA Data Release 3: 30 MSPs, Parkes telescope, 700 MHz–3.1 GHz. URL: doi.org/10.25919/ry3k-bk17. Size: ~1.5 GB. License: Open.
  4. TEMPO2 software + clock files: github.com/vallis/TEMPO2. Required for timing model fitting.
  5. PINT software: github.com/nanograv/PINT. Independent timing model fitter for cross-validation.
  6. enterprise + PTMCMCSampler: Bayesian noise analysis. github.com/nanograv/enterprise.
  7. GPS-TEC ionospheric maps: IONEX format, NASA CDDIS archive. Required for ionospheric correction.
  8. Solar wind model: NOAA OMNI database for solar wind density along pulsar lines of sight.
  9. NIST SP 800-22 test suite: csrc.nist.gov/projects/random-bit-generation. Free.
  10. TestU01 library (BigCrush): simul.iro.umontreal.ca/testu01. Free.
  11. Dieharder: github.com/GrayAreaEnterprises/dieharder. Free.
  12. Reference TRNG outputs: Intel RDRAND (via /dev/hwrng) and ID Quantique Quantis for benchmark comparison. ~$500 hardware cost.
Success:
  1. STATISTICAL RANDOMNESS: ≥ 95% of NIST SP 800-22 subtests pass at α = 0.01 for ≥ 8 of 10 MSPs. Proportion of passing tests within [0.99 ± 0.0094] (99% confidence interval for 188 tests).
  2. BIGCRUSH: ≤ 5 failures out of 106 BigCrush tests per pulsar for ≥ 7 of 10 MSPs (comparable to hardware TRNG benchmark).
  3. UNPREDICTABILITY: LSTM and GP normalized RMSE > 0.85 for all 10 pulsars on held-out data (i.e., prediction no better than 15% variance reduction over mean predictor).
  4. INDEPENDENCE: ACF at all lags 1–100 within ±2/√N bounds (95% CI for white noise) for ≥ 8 of 10 pulsars.
  5. CROSS-PULSAR INDEPENDENCE: No pulsar pair shows |r| > 0.1 at p < 0.001 after GWB subtraction.
  6. MIN-ENTROPY: H_min > 0.9 bits/bit for ≥ 8 of 10 pulsars.
  7. REPRODUCIBILITY: TEMPO2 vs. PINT residual correlation |r| > 0.95 for all 10 pulsars.
  8. PRACTICAL RATE: ≥ 10 debiased bits/hour/pulsar achievable with a 25-m equivalent telescope.
  9. BENCHMARK PARITY: Statistical test pass rates within 5 percentage points of Intel RDRAND benchmark.
Failure:
  1. 20% of NIST subtests fail for any single MSP after debiasing and SHA-3 extraction.

  2. LSTM or GP achieves normalized RMSE < 0.5 for any pulsar (> 50% variance reduction = predictable).
  3. Any pulsar pair shows |r| > 0.15 at p < 0.0001 after GWB subtraction (systematic correlated noise source).
  4. H_min < 0.5 bits/bit for any pulsar (severe entropy deficit).
  5. TEMPO2 vs. PINT residual correlation |r| < 0.90 for > 2 pulsars (extraction not reproducible).
  6. Higher-order timing model reduces RMS by > 30% for > 3 pulsars (deterministic signal remaining).
  7. Practical bit rate < 1 bit/hour/pulsar after debiasing (cryptographically impractical).
  8. BigCrush failures > 15 for any single pulsar (worse than hardware TRNG by > 3×).
  9. GWB signal detected at > 5σ in cross-pulsar correlations AND accounts for > 50% of residual variance (source is dominated by correlated astrophysical signal, not independent noise).

48

GPU hours

180d

Time to result

$1,200

Min cost

$18,500

Full cost

ROI Projection

Commercial:
  1. RANDOMNESS-AS-A-SERVICE: A pulsar timing beacon (analogous to NIST Randomness Beacon or drand network) could provide publicly verifiable random values at $0.001–$0.01 per certified random number, with demand from financial services, gaming, lotteries, and smart contracts. Estimated annual revenue at scale: $5M–$25M.
  2. TELESCOPE NETWORK MONETIZATION: Existing PTA telescopes (MeerKAT, Parkes, FAST, GBT, Effelsberg, uGMRT) could offer randomness generation as a secondary science product, generating $500K–$2M/year per facility in service contracts.
  3. STANDARDS BODY INFLUENCE: Successful certification under NIST SP 800-90B would position the research group to influence ITU-T, ISO/IEC 18031, and FIPS 140-3 standards — creating consulting and certification revenue of $1M–$5M over 10 years.
  4. DEFENSE/INTELLIGENCE: Export-control-free TRNG for allied nations is a significant defense value proposition. Potential DoD/DARPA contract value: $5M–$20M.
  5. ACADEMIC LICENSING: Patent on the extraction methodology (debiasing + hash pipeline applied to pulsar residuals) could generate $500K–$3M in licensing over 10 years.
  6. OPEN-SOURCE ECOSYSTEM: A well-documented open-source toolkit would attract $2M–$10M in follow-on grant funding (NSF, ERC, UKRI) and establish the research group as the global authority on astrophysical randomness.

🔓 If proven, this unlocks

Proving this hypothesis is a prerequisite for the following downstream discoveries and applications:

  • 1pulsar-TRNG-hardware-prototype-v1
  • 2distributed-pulsar-randomness-beacon-v1
  • 3astrophysical-entropy-certification-standard-v1
  • 4GWB-noise-floor-cryptographic-bound-v1
  • 5multi-messenger-randomness-network-v1
  • 6post-quantum-seed-generation-pulsar-v1

Prerequisites

These must be validated before this hypothesis can be confirmed:

  • IPTA-DR2-noise-characterization-v1
  • NANOGrav-15yr-noise-budget-v1
  • enterprise-bayesian-noise-v2
  • GWB-detection-confirmation-v1
  • MSP-timing-model-completeness-v1

Implementation Sketch

# PULSAR TRNG VALIDATION PIPELINE
# Architecture: Data Ingestion → Timing Model Fitting → Noise Decomposition
#               → Bit Extraction → Randomness Testing → Cryptographic Qualification

import numpy as np
import subprocess
from scipy import stats
from hashlib import sha3_256

# ============================================================
# MODULE 1: DATA INGESTION
# ============================================================
class PulsarDataLoader:
    def __init__(self, pulsar_name, data_source="IPTA_DR2"):
        self.pulsar = pulsar_name
        self.source = data_source
        self.toa_file = f"data/{pulsar_name}.tim"
        self.par_file = f"data/{pulsar_name}.par"

    def load_toas(self):
        # Load TOA data: columns = [MJD, TOA_error_us, frequency_MHz, backend]
        # Returns numpy array shape (N_obs, 4)
        toas = np.loadtxt(self.toa_file, comments='#')
        return toas

    def validate_multifrequency(self, toas, min_bands=2, min_separation_mhz=200):
        frequencies = toas[:, 2]
        unique_bands = self._cluster_frequencies(frequencies, separation=min_separation_mhz)
        assert len(unique_bands) >= min_bands, f"Insufficient frequency coverage: {unique_bands}"
        return unique_bands

# ============================================================
# MODULE 2: TIMING MODEL FITTING
# ============================================================
class TimingModelFitter:
    def __init__(self, pulsar_name, software="TEMPO2"):
        self.pulsar = pulsar_name
        self.software = software  # "TEMPO2" or "PINT"

    def fit_model(self):
        if self.software == "TEMPO2":
            cmd = f"tempo2 -f {self.pulsar}.par {self.pulsar}.tim -nofit -residuals"
            result = subprocess.run(cmd, shell=True, capture_output=True)
            residuals = self._parse_tempo2_output(result.stdout)
        elif self.software == "PINT":
            # Use PINT Python API
            from pint.models import get_model
            from pint.toa import get_TOAs
            model = get_model(f"{self.pulsar}.par")
            toas = get_TOAs(f"{self.pulsar}.tim")
            residuals = model.residuals(toas).time_resids.to('us').value
        return residuals  # shape: (N_obs,) in microseconds

    def cross_validate_software(self, res_tempo2, res_pint):
        r, p = stats.pearsonr(res_tempo2, res_pint)
        assert abs(r) > 0.95, f"Software disagreement: r={r:.3f}"
        return r

    def check_model_completeness(self, par_file):
        required_params = ['F0','F1','RAJ','DECJ','PMRA','PMDEC','PX','DM','DM1']
        with open(par_file) as f:
            content = f.read()
        missing = [p for p in required_params if p not in content]
        if missing:
            raise ValueError(f"Incomplete timing model, missing: {missing}")

# ============================================================
# MODULE 3: BAYESIAN NOISE DECOMPOSITION
# ============================================================
class BayesianNoiseDecomposer:
    """Wraps enterprise/PTMCMCSampler for noise analysis"""

    def __init__(self, pulsar_name, n_freqs=30, n_iter=2_000_000):
        self.pulsar = pulsar_name
        self.n_freqs = n_freqs
        self.n_iter = n_iter

    def build_noise_model(self):
        # enterprise model: white noise + red spin noise + DM noise + GWB
        from enterprise.pulsar import Pulsar
        from enterprise.signals import (white_signals, red_signals,
                                         gp_signals, parameter)
        # [enterprise model construction — see enterprise documentation]
        # Returns: PTA object with full noise model
        pass

    def run_mcmc(self, pta_model):
        from PTMCMCSampler.PTMCMCSampler import PTSampler
        # Run sampler, return chain
        # chain shape: (n_iter, n_params)
        pass

    def extract_stochastic_residuals(self, residuals, chain, burn_in=100_000):
        # Subtract ML white noise and GWB realizations
        # Returns: stochastic_residuals (spin noise + DM noise)
        ml_params = chain[burn_in:].mean(axis=0)
        gwb_realization = self._compute_gwb_realization(ml_params)
        white_noise_realization = self._compute_white_noise(ml_params)
        stochastic = residuals - gwb_realization - white_noise_realization
        return stochastic

# ============================================================
# MODULE 4: BIT EXTRACTION
# ============================================================
class BitExtractor:
    def __init__(self, method="von_neumann_sha3"):
        self.method = method

    def normalize_residuals(self, residuals):
        mu, sigma = residuals.mean(), residuals.std()
        return (residuals - mu) / sigma

    def von_neumann_debias(self, normalized_residuals):
        """Convert continuous residuals to bits via threshold + von Neumann"""
        # Step 1: Threshold at median → binary sequence
        median = np.median(normalized_residuals)
        bits = (normalized_residuals > median).astype(int)

        # Step 2: Von Neumann debiasing on consecutive pairs
        debiased = []
        for i in range(0, len(bits) - 1, 2):
            if bits[i] == 0 and bits[i+1] == 1:
                debiased.append(0)
            elif bits[i] == 1 and bits[i+1] == 0:
                debiased.append(1)
            # discard 00 and 11 pairs
        return np.array(debiased)

    def hash_extract(self, debiased_bits, block_size=256):
        """Apply SHA-3-256 to blocks for final output"""
        output_bits = []
        for i in range(0, len(debiased_bits) - block_size, block_size):
            block = debiased_bits[i:i+block_size]
            block_bytes = np.packbits(block).tobytes()
            hash_bytes = sha3_256(block_bytes).digest()
            hash_bits = np.unpackbits(np.frombuffer(hash_bytes, dtype=np.uint8))
            output_bits.extend(hash_bits.tolist())
        return np.array(output_bits)

    def compute_bit_rate(self, n_bits, observation_hours):
        return n_bits / observation_hours  # bits/hour

# ============================================================
# MODULE 5: RANDOMNESS TESTING
# ============================================================
class RandomnessTester:
    def __init__(self, bit_sequence):
        self.bits = bit_sequence
        self.n = len(bit_sequence)

    def run_nist_sp800_22(self, output_dir="nist_results"):
        # Write bits to file, call NIST test suite binary
        bit_file = f"{output_dir}/bits.bin"
        np.packbits(self.bits).tofile(bit_file)
        cmd = f"./sts -b {self.n} -i 1 -s 1 -F 0 {bit_file}"
        subprocess.run(cmd, shell=True)
        results = self._parse_nist_results(output_dir)
        pass_rate = sum(results.values()) / len(results)
        return pass_rate, results

    def run_dieharder(self):
        # Pipe bits to dieharder
        cmd = f"dieharder -a -g 201 -f bits.bin"
        result = subprocess.run(cmd, shell=True, capture_output=True)
        return self._parse_dieharder_output(result.stdout)

    def compute_autocorrelation(self, max_lag=100):
        acf = [np.corrcoef(self.bits[:-lag], self.bits[lag:])[0,1]
               for lag in range(1, max_lag+1)]
        # Ljung-Box test
        from statsmodels.stats.diagnostic import acorr_ljungbox
        lb_result = acorr_ljungbox(self.bits, lags=max_lag, return_df=True)
        return np.array(acf), lb_result

    def compute_min_entropy(self):
        # Estimate min-entropy from empirical distribution
        # Use sliding window of length 8 for byte-level analysis
        bytes_arr = np.packbits(self.bits)
        counts = np.bincount(bytes_arr, minlength=256)
        probs = counts / counts.sum()
        max_prob = probs.max()
        h_min = -np.log2(max_prob) / 8  # normalize to bits/bit
        return h_min

# ============================================================
# MODULE 6: PREDICTABILITY TEST
# ============================================================
class PredictabilityTester:
    def __init__(self, residuals, train_frac=0.8):
        n = len(residuals)
        self.train = residuals[:int(n*train_frac)]
        self.test = residuals[int(n*train_frac):]

    def lstm_predictor(self, seq_len=10, epochs=100):
        import tensorflow as tf
        # Build LSTM: 2 layers × 128 units, dropout 0.2
        model = tf.keras.Sequential([
            tf.keras.layers.LSTM(128, return_sequences=True,
                                  input_shape=(seq_len, 1), dropout=0.2),
            tf.keras.layers.LSTM(128, dropout=0.2),
            tf.keras.layers.Dense(1)
        ])
        model.compile(optimizer='adam', loss='mse')
        X_train, y_train = self._create_sequences(self.train, seq_len)
        model.fit(X_train, y_train, epochs=epochs, verbose=0)
        X_test, y_test = self._create_sequences(self.test, seq_len)
        predictions = model.predict(X_test)
        nrmse = np.sqrt(np.mean((predictions.flatten() - y_test)**2)) / y_test.std()
        return nrmse  # > 0.85 = unpredictable

    def gaussian_process_predictor(self):
        from sklearn.gaussian_process import GaussianProcessRegressor
        from sklearn.gaussian_process.kernels import Matern
        gp = GaussianProcessRegressor(kernel=

Source

AegisMind Research
Need AI to work rigorously on your problems? AegisMind uses the same multi-model engine for personal and professional use. Get started