llm
Download
70 lines 2.8 KiBTomoModified 2026-08-30 raw
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
# spectral-norm, from The Computer Language Benchmarks Game
#
# A Tomo port. Approximates the largest singular value of the infinite matrix
# A{i,j} = 1 / ((i+j)(i+j+1)/2 + i + 1) by 10 rounds of the power method on
# AᵀA, then prints sqrt(uᵀ(AᵀA)u / uᵀu) to nine decimals.
#
# Design notes:
#   - Vectors are flat `&[Float64]` buffers reused across iterations; the two
#     matrix-vector products fill a shared scratch vector.
#   - eval_A's denominator is computed inline in the hot loops as native
#     Int64 arithmetic ((i+j)(i+j+1) is always even, so the `// 2` is exact) and
#     converted to `Float64` for the divide, avoiding a per-element call.
#   - The only inline C is the final `%.9f` formatting (Tomo has no
#     zero-padded float format), matching k-nucleotide's precedent and the
#     benchmark's fixed output format.
#
# Usage: spectralnorm <n>   (e.g. ./spectralnorm 5500)

# (A·u)_i = Σ_j u_j / ((i+j)(i+j+1)/2 + i + 1)
# The denominator is a quadratic in the column j, so within a row it is carried
# incrementally: each step adds `inc` (which itself grows by 1). Every value
# stays an exact integer far below 2^53, so the Float64 carry equals the integer
# denominator exactly, but the hot loop is just two float adds and a divide,
# with no per-element integer multiply/divide/convert. `u` is iterated by
# element, so there is no bounds check or optional unwrap either.
func mult_Av(u:&[Float64], out:&[Float64], n:Int64)
    for i in Int64(0).to(n - 1)
        s : Float64 = 0.0
        d := Float64(i * (i + 1) // 2 + i + 1)  # denominator at column 0
        inc := Float64(i + 1)                  # d(j+1) - d(j) at column 0
        for u_j in u[]
            s += u_j / d
            d += inc
            inc += 1.0
        out[i + 1] = s

# (Aᵀ·u)_i = Σ_j u_j / ((i+j)(i+j+1)/2 + j + 1)
func mult_Atv(u:&[Float64], out:&[Float64], n:Int64)
    for i in Int64(0).to(n - 1)
        s : Float64 = 0.0
        d := Float64(i * (i + 1) // 2 + 1)  # denominator at column 0
        inc := Float64(i + 2)              # d(j+1) - d(j) at column 0
        for u_j in u[]
            s += u_j / d
            d += inc
            inc += 1.0
        out[i + 1] = s

# out = AᵀA·u, via the shared scratch vector `tmp`.
func mult_AtAv(u:&[Float64], out:&[Float64], tmp:&[Float64], n:Int64)
    mult_Av(u, tmp, n)
    mult_Atv(tmp, out, n)

func main(n:Int64)
    u := &[Float64(1) for _ in n]
    v := &[Float64(0) for _ in n]
    tmp := &[Float64(0) for _ in n]

    for _ in 10
        mult_AtAv(u, v, tmp, n)
        mult_AtAv(v, u, tmp, n)

    vBv : Float64 = 0.0
    vv : Float64 = 0.0
    for i in Int64(0).to(n - 1)
        vBv += u[i + 1]! * v[i + 1]!
        vv += v[i + 1]! * v[i + 1]!

    result := (vBv / vv).sqrt()
    say(C_code:Text`({ char buf[32]; snprintf(buf, sizeof(buf), "%.9f", @result); Text$from_str(buf); })`)