File size: 7,704 Bytes
9425aed | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | #include <stdio.h>
#include <string.h>
#include "jso.h"
#include "sov_test_stubs.h"
/* -----------------------------------------------------------------------
* Helpers
* ----------------------------------------------------------------------- */
static float fabsf_local(float x) { return x < 0.0f ? -x : x; }
/* Trace of an n×n complex matrix (sum of diagonal real parts) */
static float trace_re(const cf32_t* M, int n) {
float t = 0.0f;
for (int i = 0; i < n; i++) t += M[i*n+i].re;
return t;
}
/* Frobenius distance */
static float frob_dist(const cf32_t* A, const cf32_t* B, int n) {
float s = 0.0f;
for (int i = 0; i < n*n; i++) {
float dr = A[i].re - B[i].re;
float di = A[i].im - B[i].im;
s += dr*dr + di*di;
}
return s; /* return sum-of-squares; tests compare against squared tolerance */
}
/* Build 2×2 identity unitary */
static void make_identity_U(cf32_t* U, int n) {
for (int i = 0; i < n*n; i++) U[i] = (cf32_t){0.0f, 0.0f};
for (int i = 0; i < n; i++) U[i*n+i] = (cf32_t){1.0f, 0.0f};
}
/* Build 2×2 diagonal density: rho = diag(0.6, 0.4) */
static void make_diag_density(cf32_t* rho, int n) {
for (int i = 0; i < n*n; i++) rho[i] = (cf32_t){0.0f, 0.0f};
if (n >= 2) {
rho[0*n+0] = (cf32_t){0.6f, 0.0f};
rho[1*n+1] = (cf32_t){0.4f, 0.0f};
} else {
rho[0] = (cf32_t){1.0f, 0.0f};
}
}
/* -----------------------------------------------------------------------
* PO46: Trace preservation — tr(JSO(U,ρ)) = tr(ρ) when tr(ρ)=1
* For U=I: JSO(I,ρ) = φ⁻¹·ρ + φ⁻²·ρ = (φ⁻¹+φ⁻²)·ρ = 1·ρ = ρ
* So tr(S) = tr(ρ) = 1.
* ----------------------------------------------------------------------- */
static int test_trace_preservation(void) {
const int n = 2;
cf32_t U[4], rho[4], S[4], scr[8];
make_identity_U(U, n);
make_diag_density(rho, n);
SOV_ASSERT(sov_jso_f32(U, rho, S, scr, n) == 0);
float tr_rho = trace_re(rho, n);
float tr_S = trace_re(S, n);
SOV_ASSERT(fabsf_local(tr_S - tr_rho) < 1e-5f);
SOV_PASS("trace_preservation");
return 0;
}
/* -----------------------------------------------------------------------
* PO45: Fixed-point commutativity — U=I means everything commutes;
* verify residual ‖JSO(I,ρ)−ρ‖ = 0 (I is the fixed point of T with U=I)
* ----------------------------------------------------------------------- */
static int test_identity_is_fixed_point(void) {
const int n = 2;
cf32_t U[4], rho[4], S[4], scr[8];
make_identity_U(U, n);
make_diag_density(rho, n);
SOV_ASSERT(sov_jso_f32(U, rho, S, scr, n) == 0);
/* JSO(I,ρ) = (φ⁻¹+φ⁻²)·ρ = ρ exactly */
float dist = frob_dist(S, rho, n);
SOV_ASSERT(dist < 1e-8f); /* sum-of-squares; float rounding only */
SOV_PASS("identity_is_fixed_point");
return 0;
}
/* -----------------------------------------------------------------------
* PO47: Contraction — |φ⁻¹| < 1, verify constants sum to 1
* ----------------------------------------------------------------------- */
static int test_phi_constants(void) {
SOV_ASSERT(JSO_PHI_INV > 0.0f);
SOV_ASSERT(JSO_PHI_INV < 1.0f);
SOV_ASSERT(JSO_PHI_INV2 > 0.0f);
SOV_ASSERT(JSO_PHI_INV2 < 1.0f);
float sum = JSO_PHI_INV + JSO_PHI_INV2;
SOV_ASSERT(fabsf_local(sum - 1.0f) < 1e-6f);
SOV_PASS("phi_constants");
return 0;
}
/* -----------------------------------------------------------------------
* PO50: Determinism — same (U, ρ) → same S, twice
* ----------------------------------------------------------------------- */
static int test_determinism(void) {
const int n = 2;
cf32_t U[4], rho[4], S1[4], S2[4], scr1[8], scr2[8];
make_identity_U(U, n);
make_diag_density(rho, n);
SOV_ASSERT(sov_jso_f32(U, rho, S1, scr1, n) == 0);
SOV_ASSERT(sov_jso_f32(U, rho, S2, scr2, n) == 0);
SOV_ASSERT(frob_dist(S1, S2, n) < 1e-7f);
SOV_PASS("determinism");
return 0;
}
/* -----------------------------------------------------------------------
* Residual API: sov_jso_residual_f32 returns 0 for fixed-point case
* ----------------------------------------------------------------------- */
static int test_residual_api(void) {
const int n = 2;
cf32_t U[4], rho[4];
cf32_t scr[3*4]; /* n² (S_out) + 2n² (jso internal) = 3n² */
make_identity_U(U, n);
make_diag_density(rho, n);
float r = sov_jso_residual_f32(U, rho, scr, n);
SOV_ASSERT(r >= 0.0f);
SOV_ASSERT(r < 1e-12f); /* returns Frobenius²; float rounding only */
SOV_PASS("residual_api");
return 0;
}
/* -----------------------------------------------------------------------
* Bad-args guards
* ----------------------------------------------------------------------- */
static int test_null_args_rejected(void) {
cf32_t U[4], rho[4], S[4], scr[8];
make_identity_U(U, 2);
make_diag_density(rho, 2);
SOV_ASSERT(sov_jso_f32(NULL, rho, S, scr, 2) == -1);
SOV_ASSERT(sov_jso_f32(U, NULL, S, scr, 2) == -1);
SOV_ASSERT(sov_jso_f32(U, rho, NULL, scr, 2) == -1);
SOV_ASSERT(sov_jso_f32(U, rho, S, scr, 0) == -1);
SOV_PASS("null_args_rejected");
return 0;
}
/* -----------------------------------------------------------------------
* Density project: trace should be 1 after projection
* ----------------------------------------------------------------------- */
static int test_density_project_trace(void) {
const int n = 2, k = 2;
cf32_t X[2] = {{1.0f,0.0f},{1.0f,0.0f}};
cf32_t W[4] = {{1.0f,0.0f},{0.0f,0.0f},{0.0f,0.0f},{1.0f,0.0f}};
cf32_t rho[4], scr[4];
SOV_ASSERT(sov_density_project_f32(X, W, rho, scr, n, k) == 0);
float tr = trace_re(rho, n);
SOV_ASSERT(fabsf_local(tr - 1.0f) < 1e-5f);
SOV_PASS("density_project_trace");
return 0;
}
/* -----------------------------------------------------------------------
* Unitary project: columns should be orthonormal (UᴴU ≈ I)
* ----------------------------------------------------------------------- */
static int test_unitary_project_orthonormal(void) {
const int n = 2, k = 2;
cf32_t X[2] = {{1.5f,0.0f},{0.5f,0.0f}};
cf32_t W[4] = {{1.0f,0.0f},{0.0f,0.0f},{0.0f,0.0f},{1.0f,0.0f}};
cf32_t U[4], scr[4 + 16]; /* n + n² scratch (n=2: 4+4=8 entries) */
SOV_ASSERT(sov_unitary_project_f32(X, W, U, scr, n, k) == 0);
/* Check UᴴU = I: (UᴴU)[i,j] = Σ_k conj(U[k,i]) * U[k,j] */
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cf32_t acc = {0.0f, 0.0f};
for (int p = 0; p < n; p++) {
cf32_t uh = (cf32_t){ U[p*n+i].re, -U[p*n+i].im };
acc.re += uh.re*U[p*n+j].re - uh.im*U[p*n+j].im;
acc.im += uh.re*U[p*n+j].im + uh.im*U[p*n+j].re;
}
float expected_re = (i == j) ? 1.0f : 0.0f;
SOV_ASSERT(fabsf_local(acc.re - expected_re) < 1e-3f);
SOV_ASSERT(fabsf_local(acc.im) < 1e-3f);
}
}
SOV_PASS("unitary_project_orthonormal");
return 0;
}
int main(void) {
int fail = 0;
fail |= test_phi_constants();
fail |= test_trace_preservation();
fail |= test_identity_is_fixed_point();
fail |= test_determinism();
fail |= test_residual_api();
fail |= test_null_args_rejected();
fail |= test_density_project_trace();
fail |= test_unitary_project_orthonormal();
if (!fail) printf("ALL PASS\n");
return fail;
}
|