Research source · python

synthesize_two_block_clocks.py

site/public/research-artifacts/foundations/synthesize_two_block_clocks.py

251 lines. Source is displayed for inspection; it is not executed by this page.

File fingerprint

SHA-256: 339628ea3c1102ddea1758b7361e86f80dbee31e224c295d079510e0e56d9f0a

#!/usr/bin/env python3
"""Exact synthesis from a supplied semilinear (n,m,h) relation.

This implements the arithmetic stage, not PDA-to-semilinear extraction.
Fourier-Motzkin uses Fractions and returns checkable infeasibility multipliers.
No external dependencies. Run this file to reproduce the accompanying JSON.
"""
from fractions import Fraction as F
from functools import reduce
from math import gcd, lcm
import json


def linear(base, *periods):
    return {"base": tuple(base), "periods": [tuple(v) for v in periods]}


def fibers(components, n):
    result = []
    for item in components:
        base = item["base"]
        if base[0] > n:
            continue
        positive = [v for v in item["periods"] if v[0] > 0]
        zero = [v for v in item["periods"] if v[0] == 0]

        def visit(i, current):
            if i == len(positive):
                if current[0] == n:
                    result.append(linear(current, *zero))
                return
            v = positive[i]
            for k in range((n - current[0]) // v[0] + 1):
                visit(i + 1, tuple(current[j] + k * v[j] for j in range(3)))
        visit(0, base)
    return result


def solve_exact(rows, dimensions):
    """rows are (coefficients, rhs, provenance): coefficients*x <= rhs."""
    def eliminate(current, d):
        # A constant contradiction carries nonnegative original-row weights.
        for a, b, proof in current:
            if not any(a) and b < 0:
                return None, proof
        if d == 0:
            return [], None
        positive = [r for r in current if r[0][-1] > 0]
        negative = [r for r in current if r[0][-1] < 0]
        reduced = [(a[:-1], b, proof) for a, b, proof in current if a[-1] == 0]
        for ap, bp, pp in positive:
            for an, bn, pn in negative:
                u, v = 1 / ap[-1], -1 / an[-1]
                proof = {i: u * pp.get(i, F(0)) + v * pn.get(i, F(0))
                         for i in pp.keys() | pn.keys()}
                reduced.append((tuple(u * x + v * y for x, y in zip(ap[:-1], an[:-1])),
                                u * bp + v * bn, proof))
        # Deduplicate exact rows to limit redundant elimination work.
        unique = {}
        for a, b, proof in reduced:
            unique.setdefault((a, b), (a, b, proof))
        prefix, failure = eliminate(list(unique.values()), d - 1)
        if failure is not None:
            return None, failure
        lower, upper = [], []
        for a, b, _ in current:
            remainder = b - sum(x * y for x, y in zip(a[:-1], prefix))
            if a[-1] < 0:
                lower.append(remainder / a[-1])
            elif a[-1] > 0:
                upper.append(remainder / a[-1])
        value = max([F(0)] + lower)
        if upper:
            value = min(value, min(upper))
        assert not lower or value >= max(lower)
        return prefix + [value], None

    solution, failure = eliminate(rows, dimensions)
    if solution is not None:
        assert all(sum(x * y for x, y in zip(a, solution)) <= b for a, b, _ in rows)
    else:
        assert all(w >= 0 for w in failure.values())
        assert all(sum(failure.get(i, 0) * row[0][j] for i, row in enumerate(rows)) == 0
                   for j in range(dimensions))
        assert sum(failure.get(i, 0) * row[1] for i, row in enumerate(rows)) < 0
    return solution, failure


def synthesize(components):
    assert all(len(v) == 3 and all(isinstance(x, int) and x >= 0 for x in v)
               for item in components for v in [item["base"]] + item["periods"])
    unbounded = [i for i, item in enumerate(components) if any(v[0] for v in item["periods"])]
    bounded = [item for i, item in enumerate(components) if i not in unbounded]
    N = 1 + max((item["base"][0] for item in bounded), default=-1)
    gs = {i: reduce(gcd, (v[0] for v in components[i]["periods"]), 0) for i in unbounded}
    M = lcm(*gs.values()) if gs else 1
    names = ["alpha"] + [f"beta_{r}" for r in range(M)] + [f"gamma_{n}" for n in range(N)]
    size = len(names)
    rows, labels = [], []

    def leq(coeff, rhs, label):
        i = len(rows)
        rows.append((tuple(map(F, coeff)), F(rhs), {i: F(1)}))
        labels.append(label)

    def equation(coeff, rhs, label):
        leq(coeff, rhs, label + " <=")
        leq([-x for x in coeff], -rhs, label + " >=")

    for j, name in enumerate(names):
        v = [0] * size
        v[j] = 1
        leq(v, 1, name + " upper")
        v[j] = -1
        leq(v, 1 if 1 <= j <= M else 0, name + " lower")
    for r in range(M):
        for i in unbounded:
            item = components[i]
            if (r - item["base"][0]) % gs[i]:
                continue
            growing = any(v[2] > 0 for v in item["periods"])
            for k, (a, b, h) in enumerate(item["periods"]):
                coeff = [0] * size
                coeff[0], coeff[1 + r] = a, b
                label = f"residue {r} component {i} period {k}"
                if growing:
                    equation(coeff, h, label)
                else:
                    leq(coeff, 0, label)
    small_fibers = [fibers(components, n) for n in range(N)]
    for n, items in enumerate(small_fibers):
        for i, item in enumerate(items):
            for k, (_, m, h) in enumerate(item["periods"]):
                coeff = [0] * size
                coeff[1 + M + n] = m
                equation(coeff, h, f"fiber {n} component {i} period {k}")
    solution, failure = solve_exact(rows, size)
    result = {"components": components, "N": N, "M": M, "variables": names,
              "constraints": [{"coeff": list(a), "rhs": b, "label": labels[i]}
                              for i, (a, b, _) in enumerate(rows)]}
    if solution is None:
        result.update({"synchronizable": False, "infeasibility_multipliers": failure})
        return result
    alpha = solution[0]
    betas = solution[1:1 + M]
    gammas = solution[1 + M:]
    bases = [components[i]["base"] for i in unbounded]
    bases += [item["base"] for items in small_fibers for item in items]
    bound = max((sum(v) for v in bases), default=0) + 2
    state_bound = N + lcm(M, alpha.denominator) + sum(v.denominator for v in betas + gammas) + 1
    result.update({"synchronizable": True, "solution": dict(zip(names, solution)),
                   "skew_construction_bound": bound, "observer_state_construction_bound": state_bound})
    return result


def floor(x):
    return x.numerator // x.denominator


def clock_height(solution, N, M, n, m):
    alpha = solution["alpha"]
    beta = solution[f"gamma_{n}"] if n < N else solution[f"beta_{n % M}"]
    H = 0
    for j in range(1, n + 1):
        c = floor(alpha * j) - floor(alpha * (j - 1))
        assert c in (0, 1)
        H = max(0, H + c)
    for j in range(1, m + 1):
        c = floor(beta * j) - floor(beta * (j - 1))
        assert c in (-1, 0, 1)
        H = max(0, H + c)
    assert H == max(0, floor(alpha * n) + floor(beta * m))
    return H


def sample_points(components, limit):
    points = set()
    for item in components:
        frontier = [item["base"]]
        seen = set()
        while frontier:
            point = frontier.pop()
            if point in seen or point[0] > limit or point[1] > limit or point[2] > 2 * limit:
                continue
            seen.add(point)
            points.add(point)
            for v in item["periods"]:
                nxt = tuple(a + b for a, b in zip(point, v))
                if nxt != point:
                    frontier.append(nxt)
    return points


def main():
    examples = {
        "saturated_descent": (True, [
            linear((0, 0, 0), (1, 0, 1), (1, 1, 0)),
            linear((0, 0, 0), (1, 1, 0), (0, 1, 0))]),
        "valley": (False, [
            linear((0, 0, 0), (1, 0, 1), (1, 1, 0)),
            linear((0, 0, 0), (1, 1, 0), (0, 1, 1))]),
        "parity_dependent_tail": (True, [
            linear((0, 0, 0), (2, 0, 2), (2, 2, 0)),
            linear((2, 1, 1), (2, 0, 2), (2, 2, 0)),
            linear((0, 0, 0), (2, 2, 0), (0, 1, 0)),
            linear((1, 0, 1), (2, 0, 2), (0, 1, 1))]),
        "finite_exception": (True, [
            linear((1, 0, 1), (1, 0, 1), (0, 1, 0)),
            linear((0, 0, 0), (0, 1, 1))]),
        "half_rate": (True, [
            linear((0, 0, 0), (2, 0, 1), (0, 1, 0)),
            linear((1, 0, 0), (2, 0, 1), (0, 1, 0))]),
        "unbounded_same_word_spread": (False, [
            linear((0, 0, 0), (1, 0, 1), (1, 0, 0), (0, 1, 0))]),
        "bounded_ambiguity": (True, [
            linear((0, 0, 0), (1, 0, 1), (0, 1, 0)),
            linear((1, 0, 0), (1, 0, 1), (0, 1, 0))])}
    formulas = {
        "saturated_descent": lambda n, m: {max(0, n - m)},
        "valley": lambda n, m: {abs(n - m)},
        "parity_dependent_tail": lambda n, m: {n + m if n % 2 else max(0, n - m)},
        "finite_exception": lambda n, m: {n if n else m},
        "half_rate": lambda n, m: {n // 2},
        "unbounded_same_word_spread": lambda n, m: set(range(n + 1)),
        "bounded_ambiguity": lambda n, m: {n} if n == 0 else {n, n - 1}}
    results = {}
    for name, (expected, components) in examples.items():
        result = synthesize(components)
        assert result["synchronizable"] == expected
        points = sample_points(components, 24)
        for n in range(25):
            for m in range(25):
                assert {h for nn, mm, h in points if (nn, mm) == (n, m)} == formulas[name](n, m)
        if expected:
            errors = [abs(h - clock_height(result["solution"], result["N"], result["M"], n, m))
                      for n, m, h in points]
            result["finite_audit_maximum_error"] = max(errors)
            assert max(errors) <= result["skew_construction_bound"]
        result["finite_audit_points"] = len(points)
        results[name] = result
    print(json.dumps({"status": "passed", "arithmetic": "exact rational Fourier-Motzkin",
                      "scope": "Supplied semilinear relations only; finite source-formula checks n,m<=24.",
                      "positive_witness_check": "Every original rational inequality rechecked.",
                      "negative_witness_check": "Nonnegative original-row combination gives 0 <= a negative rational.",
                      "not_implemented": ["PDA-to-semilinear conversion", "scaffold compiler"],
                      "formal_verification": "none", "examples": results},
                     default=lambda x: str(x) if isinstance(x, F) else x, indent=2))


if __name__ == "__main__":
    main()