Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #define _CRT_SECURE_NO_WARNINGS
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <math.h>
- #include <time.h>
- #ifdef _WIN32
- #include <direct.h>
- #define GETCWD _getcwd
- #else
- #include <unistd.h>
- #define GETCWD getcwd
- #endif
- #ifndef M_PI
- #define M_PI 3.14159265358979323846
- #endif
- #define IMG_ROWS 28
- #define IMG_COLS 28
- #define INPUT_DIM 784
- #define HIDDEN_DIM 128
- #define N_CLASSES 10
- #define N_QUBITS 5
- #define STATE_SIZE (1 << N_QUBITS)
- #define TRAIN_SUBSET 8000
- #define TEST_SUBSET 10000
- #define BATCH_SIZE 32
- #define EPOCHS 140
- #define XI 0.8
- #define LEARNING_RATE 0.001
- #define BETA1 0.90
- #define BETA2 0.99
- #define WEIGHT_DECAY 0.0
- #define EPS_ADAM 1e-8
- /* Stage-2 implementation parameters: required for discrete simulation of time */
- #define DEFAULT_TIME_STEPS 32
- #define DT 0.01
- #define V_REST 0.0
- #define V_RESET 0.0
- #define V_THRESHOLD 1.0
- #define TAU_M 1.0
- #define Q_VAR_PARAM_COUNT ((N_QUBITS - 1) * 3)
- #define Q_HEAD_IN N_QUBITS
- typedef struct {
- int num_images;
- int rows;
- int cols;
- unsigned char *images;
- unsigned char *labels;
- } MnistSet;
- typedef struct {
- double real;
- double imag;
- } Complex;
- typedef struct {
- int true_label;
- int pred_label;
- double confidence;
- double loss;
- } EvalInfo;
- static double fc1_w[HIDDEN_DIM][INPUT_DIM];
- static double fc1_b[HIDDEN_DIM];
- static double qc_w[N_CLASSES][HIDDEN_DIM];
- static double qc_b[N_CLASSES];
- static double qq_w[N_CLASSES][Q_HEAD_IN];
- static double qq_b[N_CLASSES];
- /* trainable quantum variation parameters: for each i=1..4, RZ RX RZ */
- static double q_theta_var[Q_VAR_PARAM_COUNT];
- /* Adam state */
- static double m_fc1_w[HIDDEN_DIM][INPUT_DIM];
- static double v_fc1_w[HIDDEN_DIM][INPUT_DIM];
- static double m_fc1_b[HIDDEN_DIM];
- static double v_fc1_b[HIDDEN_DIM];
- static double m_qc_w[N_CLASSES][HIDDEN_DIM];
- static double v_qc_w[N_CLASSES][HIDDEN_DIM];
- static double m_qc_b[N_CLASSES];
- static double v_qc_b[N_CLASSES];
- static double m_qq_w[N_CLASSES][Q_HEAD_IN];
- static double v_qq_w[N_CLASSES][Q_HEAD_IN];
- static double m_qq_b[N_CLASSES];
- static double v_qq_b[N_CLASSES];
- static double m_qtheta[Q_VAR_PARAM_COUNT];
- static double v_qtheta[Q_VAR_PARAM_COUNT];
- /* gradients */
- static double g_fc1_w[HIDDEN_DIM][INPUT_DIM];
- static double g_fc1_b[HIDDEN_DIM];
- static double g_qc_w[N_CLASSES][HIDDEN_DIM];
- static double g_qc_b[N_CLASSES];
- static double g_qq_w[N_CLASSES][Q_HEAD_IN];
- static double g_qq_b[N_CLASSES];
- static double g_qtheta[Q_VAR_PARAM_COUNT];
- static double qpre_w[N_QUBITS][HIDDEN_DIM];
- static double qpre_b[N_QUBITS];
- static double m_qpre_w[N_QUBITS][HIDDEN_DIM];
- static double v_qpre_w[N_QUBITS][HIDDEN_DIM];
- static double m_qpre_b[N_QUBITS];
- static double v_qpre_b[N_QUBITS];
- static double g_qpre_w[N_QUBITS][HIDDEN_DIM];
- static double g_qpre_b[N_QUBITS];
- static Complex q_state[STATE_SIZE];
- static unsigned int rng_state = 1u;
- static int g_time_steps = DEFAULT_TIME_STEPS;
- static int g_model_initialized = 0;
- static int read_be_int(FILE *f) {
- unsigned char b[4];
- if (fread(b, 1, 4, f) != 4) return -1;
- return ((int)b[0] << 24) | ((int)b[1] << 16) | ((int)b[2] << 8) | (int)b[3];
- }
- static double urand01(void) {
- rng_state = 1664525u * rng_state + 1013904223u;
- return (double)(rng_state & 0xFFFFFFFFu) / 4294967295.0;
- }
- static double rand_uniform(double a, double b) {
- return a + (b - a) * urand01();
- }
- static void shuffle_int(int *arr, int n) {
- for (int i = n - 1; i > 0; --i) {
- int j = (int)(urand01() * (i + 1));
- int tmp = arr[i];
- arr[i] = arr[j];
- arr[j] = tmp;
- }
- }
- static int file_exists(const char *path) {
- FILE *f = fopen(path, "rb");
- if (f) {
- fclose(f);
- return 1;
- }
- return 0;
- }
- static void join_path(char *out, size_t out_sz, const char *a, const char *b) {
- size_t la = strlen(a);
- #ifdef _WIN32
- const char sep = '\\';
- #else
- const char sep = '/';
- #endif
- if (la > 0 && (a[la - 1] == '\\' || a[la - 1] == '/')) {
- snprintf(out, out_sz, "%s%s", a, b);
- } else {
- snprintf(out, out_sz, "%s%c%s", a, sep, b);
- }
- }
- static int dirname_of(char *path) {
- size_t n = strlen(path);
- if (n == 0) return 0;
- while (n > 0 && path[n - 1] != '\\' && path[n - 1] != '/') --n;
- if (n == 0) return 0;
- path[n - 1] = '\0';
- return 1;
- }
- static int find_mnist_files(char *img_tr, size_t sz1,
- char *lbl_tr, size_t sz2,
- char *img_te, size_t sz3,
- char *lbl_te, size_t sz4) {
- const char *names[4] = {
- "train-images-idx3-ubyte",
- "train-labels-idx1-ubyte",
- "t10k-images-idx3-ubyte",
- "t10k-labels-idx1-ubyte"
- };
- char cwd[1024];
- if (!GETCWD(cwd, sizeof(cwd))) {
- return 0;
- }
- char dirs[12][1024];
- int dir_count = 0;
- strncpy(dirs[dir_count++], cwd, sizeof(dirs[0]) - 1);
- dirs[dir_count - 1][sizeof(dirs[0]) - 1] = '\0';
- if (dir_count < 12) {
- char ds[1024];
- join_path(ds, sizeof(ds), cwd, "Datasets");
- strncpy(dirs[dir_count++], ds, sizeof(dirs[0]) - 1);
- dirs[dir_count - 1][sizeof(dirs[0]) - 1] = '\0';
- }
- char exe_dir[1024];
- strncpy(exe_dir, cwd, sizeof(exe_dir) - 1);
- exe_dir[sizeof(exe_dir) - 1] = '\0';
- if (dirname_of(exe_dir)) {
- strncpy(dirs[dir_count++], exe_dir, sizeof(dirs[0]) - 1);
- dirs[dir_count - 1][sizeof(dirs[0]) - 1] = '\0';
- }
- /* walk up several parents from cwd */
- char cur[1024];
- strncpy(cur, cwd, sizeof(cur) - 1);
- cur[sizeof(cur) - 1] = '\0';
- for (int k = 0; k < 6; ++k) {
- if (!dirname_of(cur)) break;
- strncpy(dirs[dir_count++], cur, sizeof(dirs[0]) - 1);
- dirs[dir_count - 1][sizeof(dirs[0]) - 1] = '\0';
- }
- #ifdef _WIN32
- /* also try x64\\Debug and x64\\Release below cwd and parents */
- int base_count = dir_count;
- for (int i = 0; i < base_count && dir_count < 12; ++i) {
- char tmp[1024];
- join_path(tmp, sizeof(tmp), dirs[i], "x64\\Debug");
- strncpy(dirs[dir_count++], tmp, sizeof(dirs[0]) - 1);
- dirs[dir_count - 1][sizeof(dirs[0]) - 1] = '\0';
- if (dir_count >= 12) break;
- join_path(tmp, sizeof(tmp), dirs[i], "x64\\Release");
- strncpy(dirs[dir_count++], tmp, sizeof(dirs[0]) - 1);
- dirs[dir_count - 1][sizeof(dirs[0]) - 1] = '\0';
- }
- #endif
- printf("Searching MNIST files in:\n");
- for (int i = 0; i < dir_count; ++i) {
- printf(" %s\n", dirs[i]);
- }
- printf("\n");
- for (int i = 0; i < dir_count; ++i) {
- char p1[1024], p2[1024], p3[1024], p4[1024];
- join_path(p1, sizeof(p1), dirs[i], names[0]);
- join_path(p2, sizeof(p2), dirs[i], names[1]);
- join_path(p3, sizeof(p3), dirs[i], names[2]);
- join_path(p4, sizeof(p4), dirs[i], names[3]);
- if (file_exists(p1) && file_exists(p2) && file_exists(p3) && file_exists(p4)) {
- strncpy(img_tr, p1, sz1 - 1); img_tr[sz1 - 1] = '\0';
- strncpy(lbl_tr, p2, sz2 - 1); lbl_tr[sz2 - 1] = '\0';
- strncpy(img_te, p3, sz3 - 1); img_te[sz3 - 1] = '\0';
- strncpy(lbl_te, p4, sz4 - 1); lbl_te[sz4 - 1] = '\0';
- return 1;
- }
- }
- return 0;
- }
- static int load_mnist_images(const char *path, int *num, int *rows, int *cols, unsigned char **data) {
- FILE *f = fopen(path, "rb");
- if (!f) return 0;
- int magic = read_be_int(f);
- int n = read_be_int(f);
- int r = read_be_int(f);
- int c = read_be_int(f);
- if (magic != 2051 || n <= 0 || r != IMG_ROWS || c != IMG_COLS) {
- fclose(f);
- return 0;
- }
- size_t sz = (size_t)n * r * c;
- unsigned char *buf = (unsigned char *)malloc(sz);
- if (!buf) {
- fclose(f);
- return 0;
- }
- if (fread(buf, 1, sz, f) != sz) {
- free(buf);
- fclose(f);
- return 0;
- }
- fclose(f);
- *num = n; *rows = r; *cols = c; *data = buf;
- return 1;
- }
- static int load_mnist_labels(const char *path, int *num, unsigned char **labels) {
- FILE *f = fopen(path, "rb");
- if (!f) return 0;
- int magic = read_be_int(f);
- int n = read_be_int(f);
- if (magic != 2049 || n <= 0) {
- fclose(f);
- return 0;
- }
- unsigned char *buf = (unsigned char *)malloc((size_t)n);
- if (!buf) {
- fclose(f);
- return 0;
- }
- if (fread(buf, 1, (size_t)n, f) != (size_t)n) {
- free(buf);
- fclose(f);
- return 0;
- }
- fclose(f);
- *num = n; *labels = buf;
- return 1;
- }
- static int load_mnist_set(const char *img_path, const char *lbl_path, MnistSet *set) {
- int n1, n2, r, c;
- unsigned char *imgs = NULL;
- unsigned char *lbls = NULL;
- if (!load_mnist_images(img_path, &n1, &r, &c, &imgs)) return 0;
- if (!load_mnist_labels(lbl_path, &n2, &lbls)) {
- free(imgs);
- return 0;
- }
- if (n1 != n2) {
- free(imgs); free(lbls);
- return 0;
- }
- set->num_images = n1;
- set->rows = r;
- set->cols = c;
- set->images = imgs;
- set->labels = lbls;
- return 1;
- }
- static void free_mnist_set(MnistSet *set) {
- free(set->images);
- free(set->labels);
- set->images = NULL;
- set->labels = NULL;
- }
- static void get_image_vector(const MnistSet *set, int idx, double x[INPUT_DIM]) {
- const unsigned char *img = set->images + (size_t)idx * INPUT_DIM;
- for (int i = 0; i < INPUT_DIM; ++i) {
- x[i] = ((double)img[i]) / 255.0;
- }
- }
- static void print_ascii_digit(const MnistSet *set, int idx) {
- const unsigned char *img = set->images + (size_t)idx * INPUT_DIM;
- const char *lut = " .:-=+*#%@";
- printf("ASCII preview (label=%d):\n", set->labels[idx]);
- for (int r = 0; r < IMG_ROWS; ++r) {
- for (int c = 0; c < IMG_COLS; ++c) {
- int v = img[r * IMG_COLS + c];
- int k = (v * 9) / 255;
- putchar(lut[k]);
- }
- putchar('\n');
- }
- putchar('\n');
- }
- static void print_ascii_vector28(const double x[INPUT_DIM], int label, const char *title) {
- const char *lut = " .:-=+*#%@";
- printf("%s (label=%d):\n", title, label);
- for (int r = 0; r < IMG_ROWS; ++r) {
- for (int c = 0; c < IMG_COLS; ++c) {
- double v = x[r * IMG_COLS + c];
- if (v < 0.0) v = 0.0;
- if (v > 1.0) v = 1.0;
- int iv = (int)(v * 255.0 + 0.5);
- int k = (iv * 9) / 255;
- putchar(lut[k]);
- }
- putchar('\n');
- }
- putchar('\n');
- }
- static int save_pgm_from_vector(const char *path, const double x[INPUT_DIM]) {
- FILE *f = fopen(path, "wb");
- if (!f) return 0;
- fprintf(f, "P5\n%d %d\n255\n", IMG_COLS, IMG_ROWS);
- for (int i = 0; i < INPUT_DIM; ++i) {
- double v = x[i];
- if (v < 0.0) v = 0.0;
- if (v > 1.0) v = 1.0;
- unsigned char px = (unsigned char)(v * 255.0 + 0.5);
- fwrite(&px, 1, 1, f);
- }
- fclose(f);
- return 1;
- }
- static int save_pgm_from_dataset_image(const char *path, const MnistSet *set, int idx) {
- FILE *f = fopen(path, "wb");
- if (!f) return 0;
- fprintf(f, "P5\n%d %d\n255\n", IMG_COLS, IMG_ROWS);
- const unsigned char *img = set->images + (size_t)idx * INPUT_DIM;
- fwrite(img, 1, INPUT_DIM, f);
- fclose(f);
- return 1;
- }
- static int find_nth_occurrence_of_label(const MnistSet *set, int digit, int occurrence_zero_based) {
- int seen = 0;
- for (int i = 0; i < set->num_images; ++i) {
- if (set->labels[i] == digit) {
- if (seen == occurrence_zero_based) return i;
- ++seen;
- }
- }
- return -1;
- }
- static int count_label_occurrences(const MnistSet *set, int digit) {
- int cnt = 0;
- for (int i = 0; i < set->num_images; ++i) {
- if (set->labels[i] == digit) ++cnt;
- }
- return cnt;
- }
- static int choose_random_index_of_label(const MnistSet *set, int digit) {
- int count = count_label_occurrences(set, digit);
- if (count <= 0) return -1;
- int pick = (int)(urand01() * count);
- if (pick >= count) pick = count - 1;
- return find_nth_occurrence_of_label(set, digit, pick);
- }
- static void add_noise_uniform(double x[INPUT_DIM], double noise_percent) {
- double a = noise_percent / 100.0;
- for (int i = 0; i < INPUT_DIM; ++i) {
- double n = rand_uniform(-a, a);
- x[i] += n;
- if (x[i] < 0.0) x[i] = 0.0;
- if (x[i] > 1.0) x[i] = 1.0;
- }
- }
- static double randn_box_muller(void) {
- double u1 = urand01();
- double u2 = urand01();
- if (u1 < 1e-12) u1 = 1e-12;
- return sqrt(-2.0 * log(u1)) * cos(2.0 * M_PI * u2);
- }
- static void add_noise_gaussian(double x[INPUT_DIM], double noise_percent) {
- double sigma = noise_percent / 100.0;
- for (int i = 0; i < INPUT_DIM; ++i) {
- double n = sigma * randn_box_muller();
- x[i] += n;
- if (x[i] < 0.0) x[i] = 0.0;
- if (x[i] > 1.0) x[i] = 1.0;
- }
- }
- static double clamp01(double x) {
- if (x < 0.0) return 0.0;
- if (x > 1.0) return 1.0;
- return x;
- }
- static void print_ascii_vector(const double x[INPUT_DIM], int label, const char *title) {
- const char *lut = " .:-=+*#%@";
- printf("%s (label=%d):\n", title, label);
- for (int r = 0; r < IMG_ROWS; ++r) {
- for (int c = 0; c < IMG_COLS; ++c) {
- double v = clamp01(x[r * IMG_COLS + c]);
- int k = (int)(v * 9.0);
- if (k < 0) k = 0;
- if (k > 9) k = 9;
- putchar(lut[k]);
- }
- putchar('\n');
- }
- putchar('\n');
- }
- static double rand_normal01(void) {
- double u1 = urand01();
- double u2 = urand01();
- if (u1 < 1e-12) u1 = 1e-12;
- return sqrt(-2.0 * log(u1)) * cos(2.0 * M_PI * u2);
- }
- static void apply_uniform_noise(const double src[INPUT_DIM], double dst[INPUT_DIM], double percent) {
- double amp = percent / 100.0;
- for (int i = 0; i < INPUT_DIM; ++i) {
- double noise = rand_uniform(-amp, amp);
- dst[i] = clamp01(src[i] + noise);
- }
- }
- static void apply_gaussian_noise(const double src[INPUT_DIM], double dst[INPUT_DIM], double percent) {
- double sigma = percent / 100.0;
- for (int i = 0; i < INPUT_DIM; ++i) {
- double noise = rand_normal01() * sigma;
- dst[i] = clamp01(src[i] + noise);
- }
- }
- static int find_nth_label_index(const MnistSet *set, int label, int occurrence) {
- int seen = 0;
- for (int i = 0; i < set->num_images; ++i) {
- if (set->labels[i] == label) {
- if (seen == occurrence) return i;
- ++seen;
- }
- }
- return -1;
- }
- static int count_label_images(const MnistSet *set, int label) {
- int count = 0;
- for (int i = 0; i < set->num_images; ++i) {
- if (set->labels[i] == label) ++count;
- }
- return count;
- }
- static double relu(double x) {
- return x > 0.0 ? x : 0.0;
- }
- static void init_weights(void) {
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- for (int i = 0; i < INPUT_DIM; ++i) {
- fc1_w[j][i] = rand_uniform(-0.05, 0.05);
- m_fc1_w[j][i] = 0.0;
- v_fc1_w[j][i] = 0.0;
- }
- fc1_b[j] = 0.0;
- m_fc1_b[j] = 0.0;
- v_fc1_b[j] = 0.0;
- }
- for (int k = 0; k < N_CLASSES; ++k) {
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- qc_w[k][j] = rand_uniform(-0.05, 0.05);
- m_qc_w[k][j] = 0.0;
- v_qc_w[k][j] = 0.0;
- }
- qc_b[k] = 0.0;
- m_qc_b[k] = 0.0;
- v_qc_b[k] = 0.0;
- }
- for (int k = 0; k < N_CLASSES; ++k) {
- for (int j = 0; j < Q_HEAD_IN; ++j) {
- qq_w[k][j] = rand_uniform(-0.05, 0.05);
- m_qq_w[k][j] = 0.0;
- v_qq_w[k][j] = 0.0;
- }
- qq_b[k] = 0.0;
- m_qq_b[k] = 0.0;
- v_qq_b[k] = 0.0;
- }
- for (int p = 0; p < Q_VAR_PARAM_COUNT; ++p) {
- q_theta_var[p] = rand_uniform(-0.1, 0.1);
- m_qtheta[p] = 0.0;
- v_qtheta[p] = 0.0;
- }
- for (int q = 0; q < N_QUBITS; ++q) {
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- qpre_w[q][j] = rand_uniform(-0.05, 0.05);
- m_qpre_w[q][j] = 0.0;
- v_qpre_w[q][j] = 0.0;
- }
- qpre_b[q] = 0.0;
- m_qpre_b[q] = 0.0;
- v_qpre_b[q] = 0.0;
- }
- }
- static void zero_grads(void) {
- memset(g_fc1_w, 0, sizeof(g_fc1_w));
- memset(g_fc1_b, 0, sizeof(g_fc1_b));
- memset(g_qc_w, 0, sizeof(g_qc_w));
- memset(g_qc_b, 0, sizeof(g_qc_b));
- memset(g_qq_w, 0, sizeof(g_qq_w));
- memset(g_qq_b, 0, sizeof(g_qq_b));
- memset(g_qtheta, 0, sizeof(g_qtheta));
- memset(g_qpre_w, 0, sizeof(g_qpre_w));
- memset(g_qpre_b, 0, sizeof(g_qpre_b));
- }
- static void adam_update_scalar(double *param, double grad, double *m, double *v, int t) {
- double g = grad + WEIGHT_DECAY * (*param);
- *m = BETA1 * (*m) + (1.0 - BETA1) * g;
- *v = BETA2 * (*v) + (1.0 - BETA2) * g * g;
- double mhat = (*m) / (1.0 - pow(BETA1, (double)t));
- double vhat = (*v) / (1.0 - pow(BETA2, (double)t));
- *param -= LEARNING_RATE * mhat / (sqrt(vhat) + EPS_ADAM);
- }
- static void apply_adam_all(int t) {
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- for (int i = 0; i < INPUT_DIM; ++i) {
- adam_update_scalar(&fc1_w[j][i], g_fc1_w[j][i], &m_fc1_w[j][i], &v_fc1_w[j][i], t);
- }
- adam_update_scalar(&fc1_b[j], g_fc1_b[j], &m_fc1_b[j], &v_fc1_b[j], t);
- }
- for (int k = 0; k < N_CLASSES; ++k) {
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- adam_update_scalar(&qc_w[k][j], g_qc_w[k][j], &m_qc_w[k][j], &v_qc_w[k][j], t);
- }
- adam_update_scalar(&qc_b[k], g_qc_b[k], &m_qc_b[k], &v_qc_b[k], t);
- }
- for (int k = 0; k < N_CLASSES; ++k) {
- for (int j = 0; j < Q_HEAD_IN; ++j) {
- adam_update_scalar(&qq_w[k][j], g_qq_w[k][j], &m_qq_w[k][j], &v_qq_w[k][j], t);
- }
- adam_update_scalar(&qq_b[k], g_qq_b[k], &m_qq_b[k], &v_qq_b[k], t);
- }
- for (int q = 0; q < N_QUBITS; ++q) {
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- adam_update_scalar(&qpre_w[q][j], g_qpre_w[q][j],
- &m_qpre_w[q][j], &v_qpre_w[q][j], t);
- }
- adam_update_scalar(&qpre_b[q], g_qpre_b[q],
- &m_qpre_b[q], &v_qpre_b[q], t);
- }
- for (int p = 0; p < Q_VAR_PARAM_COUNT; ++p) {
- adam_update_scalar(&q_theta_var[p], g_qtheta[p], &m_qtheta[p], &v_qtheta[p], t);
- }
- }
- static Complex c_add(Complex a, Complex b) {
- Complex r = {a.real + b.real, a.imag + b.imag};
- return r;
- }
- static Complex c_mul(Complex a, Complex b) {
- Complex r = {a.real * b.real - a.imag * b.imag,
- a.real * b.imag + a.imag * b.real};
- return r;
- }
- static void q_apply_gate(int qubit, Complex m00, Complex m01, Complex m10, Complex m11) {
- int step = 1 << qubit;
- for (int i = 0; i < STATE_SIZE; i += 2 * step) {
- for (int j = 0; j < step; ++j) {
- int i0 = i + j;
- int i1 = i0 + step;
- Complex a = q_state[i0];
- Complex b = q_state[i1];
- q_state[i0] = c_add(c_mul(m00, a), c_mul(m01, b));
- q_state[i1] = c_add(c_mul(m10, a), c_mul(m11, b));
- }
- }
- }
- static void q_apply_H(int q) {
- double s = 1.0 / sqrt(2.0);
- q_apply_gate(q, (Complex){s,0}, (Complex){s,0}, (Complex){s,0}, (Complex){-s,0});
- }
- static void q_apply_RY(int q, double t) {
- double c = cos(t / 2.0);
- double s = sin(t / 2.0);
- q_apply_gate(q, (Complex){c,0}, (Complex){-s,0}, (Complex){s,0}, (Complex){c,0});
- }
- static void q_apply_RX(int q, double t) {
- double c = cos(t / 2.0);
- double s = sin(t / 2.0);
- q_apply_gate(q, (Complex){c,0}, (Complex){0,-s}, (Complex){0,-s}, (Complex){c,0});
- }
- static void q_apply_RZ(int q, double t) {
- Complex e0 = {cos(-t / 2.0), sin(-t / 2.0)};
- Complex e1 = {cos(t / 2.0), sin(t / 2.0)};
- q_apply_gate(q, e0, (Complex){0,0}, (Complex){0,0}, e1);
- }
- static void q_apply_CNOT(int control, int target) {
- for (int i = 0; i < STATE_SIZE; ++i) {
- if ((i >> control) & 1) {
- int j = i ^ (1 << target);
- if (i < j) {
- Complex tmp = q_state[i];
- q_state[i] = q_state[j];
- q_state[j] = tmp;
- }
- }
- }
- }
- static double q_measure_Z(int qubit) {
- double expv = 0.0;
- for (int i = 0; i < STATE_SIZE; ++i) {
- double prob = q_state[i].real * q_state[i].real + q_state[i].imag * q_state[i].imag;
- int bit = (i >> qubit) & 1;
- expv += bit ? -prob : prob;
- }
- return expv;
- }
- static void quantum_expectation_from_hidden(
- const double hidden[HIDDEN_DIM],
- double qpre_w_local[N_QUBITS][HIDDEN_DIM],
- double qpre_b_local[N_QUBITS],
- const double theta_var[Q_VAR_PARAM_COUNT],
- double q_in_out[N_QUBITS],
- double z_out[N_QUBITS]
- ) {
- for (int i = 0; i < STATE_SIZE; ++i) {
- q_state[i].real = 0.0;
- q_state[i].imag = 0.0;
- }
- q_state[0].real = 1.0;
- double pre_out[N_QUBITS];
- double q_in[N_QUBITS];
- for (int q = 0; q < N_QUBITS; ++q) {
- double s = qpre_b[q];
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- s += qpre_w[q][j] * hidden[j];
- }
- pre_out[q] = s;
- q_in[q] = tanh(pre_out[q]) * (M_PI / 2.0);
- }
- for (int q = 0; q < N_QUBITS; ++q) q_apply_H(q);
- for (int q = 0; q < N_QUBITS; ++q) q_apply_RY(q, q_in[q]);
- int p = 0;
- for (int i = 1; i < N_QUBITS; ++i) {
- q_apply_CNOT(0, i);
- q_apply_RZ(i, theta_var[p++]);
- q_apply_RX(i, theta_var[p++]);
- q_apply_RZ(i, theta_var[p++]);
- q_apply_CNOT(0, i);
- }
- for (int q = 0; q < N_QUBITS; ++q) {
- z_out[q] = q_measure_Z(q);
- }
- }
- static void linear_forward_hidden_to_class(double w[N_CLASSES][HIDDEN_DIM], double b[N_CLASSES], const double x[HIDDEN_DIM], double out[N_CLASSES]) {
- for (int k = 0; k < N_CLASSES; ++k) {
- double s = b[k];
- for (int j = 0; j < HIDDEN_DIM; ++j) s += w[k][j] * x[j];
- out[k] = s;
- }
- }
- static void linear_forward_qhead_to_class(double w[N_CLASSES][Q_HEAD_IN], double b[N_CLASSES], const double x[Q_HEAD_IN], double out[N_CLASSES]) {
- for (int k = 0; k < N_CLASSES; ++k) {
- double s = b[k];
- for (int j = 0; j < Q_HEAD_IN; ++j) s += w[k][j] * x[j];
- out[k] = s;
- }
- }
- static void softmax10(const double in[N_CLASSES], double out[N_CLASSES]) {
- double max_v = in[0];
- for (int i = 1; i < N_CLASSES; ++i) if (in[i] > max_v) max_v = in[i];
- double sum = 0.0;
- for (int i = 0; i < N_CLASSES; ++i) {
- out[i] = exp(in[i] - max_v);
- sum += out[i];
- }
- if (sum <= 0.0) sum = 1.0;
- for (int i = 0; i < N_CLASSES; ++i) out[i] /= sum;
- }
- static int argmax10(const double v[N_CLASSES]) {
- int idx = 0;
- double best = v[0];
- for (int i = 1; i < N_CLASSES; ++i) {
- if (v[i] > best) {
- best = v[i];
- idx = i;
- }
- }
- return idx;
- }
- static double nll_one(const double probs[N_CLASSES], int label) {
- double p = probs[label];
- if (p < 1e-12) p = 1e-12;
- return -log(p);
- }
- static void print_vec10_dbg(const char* name, const double v[N_CLASSES]) {
- printf("%s:\n", name);
- for (int i = 0; i < N_CLASSES; ++i) {
- printf(" [%d] %.10f\n", i, v[i]);
- }
- printf("\n");
- }
- static void preprocess_fc1(const double x[INPUT_DIM], double pre[HIDDEN_DIM], double act[HIDDEN_DIM]) {
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- double s = fc1_b[j];
- for (int i = 0; i < INPUT_DIM; ++i) s += fc1_w[j][i] * x[i];
- pre[j] = s;
- act[j] = relu(s);
- }
- }
- static void snn_forward_poisson_lif(const double act[HIDDEN_DIM], double pooled[HIDDEN_DIM]) {
- /*
- SpikingActivation-style forward:
- - frequency proportional to base activation
- - multiple spikes per timestep allowed
- - spike height = 1/dt
- - TemporalAvgPool = average over time axis
- */
- double voltage[HIDDEN_DIM];
- double sum_spike_output[HIDDEN_DIM];
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- /* initial state in [0,1), как у spiking voltage state */
- voltage[j] = urand01();
- sum_spike_output[j] = 0.0;
- }
- for (int t = 0; t < g_time_steps; ++t) {
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- double rate = act[j];
- /* ReLU already used before this function, but guard anyway */
- if (rate < 0.0) rate = 0.0;
- /* integrate rate over one timestep */
- voltage[j] += rate * DT;
- /* allow multiple spikes per timestep */
- int n_spikes = (int)floor(voltage[j]);
- /* subtract emitted spikes from voltage */
- voltage[j] -= (double)n_spikes;
- /* each spike has height 1/dt */
- double spike_output = ((double)n_spikes) / DT;
- /* TemporalAvgPool later averages over time axis */
- sum_spike_output[j] += spike_output;
- }
- }
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- pooled[j] = sum_spike_output[j] / (double)g_time_steps;
- }
- }
- static void fuse_logits(const double qc[N_CLASSES], const double qq[N_CLASSES], double qh[N_CLASSES]) {
- for (int k = 0; k < N_CLASSES; ++k) qh[k] = XI * qq[k] + (1.0 - XI) * qc[k];
- }
- static void model_forward(const double x[INPUT_DIM],
- double fc1_pre[HIDDEN_DIM],
- double fc1_act[HIDDEN_DIM],
- double pooled[HIDDEN_DIM],
- double q_in[N_QUBITS],
- double z[N_QUBITS],
- double qc[N_CLASSES],
- double qq[N_CLASSES],
- double qh[N_CLASSES],
- double probs[N_CLASSES]) {
- preprocess_fc1(x, fc1_pre, fc1_act);
- snn_forward_poisson_lif(fc1_act, pooled);
- /* classical branch */
- linear_forward_hidden_to_class(qc_w, qc_b, pooled, qc);
- /* quantum branch with q_pre -> tanh -> pi/2 encoding */
- quantum_expectation_from_hidden(fc1_act, qpre_w, qpre_b, q_theta_var, q_in, z);
- /* quantum head */
- linear_forward_qhead_to_class(qq_w, qq_b, z, qq);
- /* fusion */
- fuse_logits(qc, qq, qh);
- softmax10(qh, probs);
- }
- static double loss_with_theta_shift(const double x[INPUT_DIM], int label, int param_idx, double shift) {
- double saved = q_theta_var[param_idx];
- q_theta_var[param_idx] = saved + shift;
- double fc1_pre[HIDDEN_DIM], fc1_act[HIDDEN_DIM], pooled[HIDDEN_DIM];
- double q_in[N_QUBITS], z[N_QUBITS];
- double qc[N_CLASSES], qq[N_CLASSES], qh[N_CLASSES], probs[N_CLASSES];
- model_forward(x, fc1_pre, fc1_act, pooled, q_in, z, qc, qq, qh, probs);
- double loss = nll_one(probs, label);
- q_theta_var[param_idx] = saved;
- return loss;
- }
- static double loss_with_qin_shift(const double x[INPUT_DIM], int label, int q_idx, double shift) {
- double fc1_pre[HIDDEN_DIM], fc1_act[HIDDEN_DIM], pooled[HIDDEN_DIM];
- double q_in[N_QUBITS], z[N_QUBITS];
- double qc[N_CLASSES], qq[N_CLASSES], qh[N_CLASSES], probs[N_CLASSES];
- model_forward(x, fc1_pre, fc1_act, pooled, q_in, z, qc, qq, qh, probs);
- /* recompute only the quantum branch with shifted q_in[q_idx] */
- for (int i = 0; i < STATE_SIZE; ++i) {
- q_state[i].real = 0.0;
- q_state[i].imag = 0.0;
- }
- q_state[0].real = 1.0;
- q_state[0].imag = 0.0;
- double q_in_shifted[N_QUBITS];
- for (int q = 0; q < N_QUBITS; ++q) {
- q_in_shifted[q] = q_in[q];
- }
- q_in_shifted[q_idx] += shift;
- for (int q = 0; q < N_QUBITS; ++q) {
- q_apply_H(q);
- }
- for (int q = 0; q < N_QUBITS; ++q) {
- q_apply_RY(q, q_in_shifted[q]);
- }
- int p = 0;
- for (int i = 1; i < N_QUBITS; ++i) {
- q_apply_CNOT(0, i);
- q_apply_RZ(i, q_theta_var[p++]);
- q_apply_RX(i, q_theta_var[p++]);
- q_apply_RZ(i, q_theta_var[p++]);
- q_apply_CNOT(0, i);
- }
- for (int q = 0; q < N_QUBITS; ++q) {
- z[q] = q_measure_Z(q);
- }
- linear_forward_qhead_to_class(qq_w, qq_b, z, qq);
- fuse_logits(qc, qq, qh);
- softmax10(qh, probs);
- return nll_one(probs, label);
- }
- static void accumulate_sample_gradients(const double x[INPUT_DIM], int label) {
- double fc1_pre[HIDDEN_DIM], fc1_act[HIDDEN_DIM], pooled[HIDDEN_DIM];
- double q_in[N_QUBITS], z[N_QUBITS];
- double qc[N_CLASSES], qq[N_CLASSES], qh[N_CLASSES], probs[N_CLASSES];
- model_forward(x, fc1_pre, fc1_act, pooled, q_in, z, qc, qq, qh, probs);
- double dL_dqh[N_CLASSES];
- for (int k = 0; k < N_CLASSES; ++k) {
- dL_dqh[k] = probs[k];
- }
- dL_dqh[label] -= 1.0;
- double dL_dqc[N_CLASSES], dL_dqq[N_CLASSES];
- for (int k = 0; k < N_CLASSES; ++k) {
- dL_dqq[k] = XI * dL_dqh[k];
- dL_dqc[k] = (1.0 - XI) * dL_dqh[k];
- }
- /* qc head grads */
- for (int k = 0; k < N_CLASSES; ++k) {
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- g_qc_w[k][j] += dL_dqc[k] * pooled[j];
- }
- g_qc_b[k] += dL_dqc[k];
- }
- /* qq head grads */
- for (int k = 0; k < N_CLASSES; ++k) {
- for (int j = 0; j < Q_HEAD_IN; ++j) {
- g_qq_w[k][j] += dL_dqq[k] * z[j];
- }
- g_qq_b[k] += dL_dqq[k];
- }
- /* -------- qpre gradients via dL/dq_in + chain rule -------- */
- double dL_dqin[N_QUBITS];
- {
- const double eps_qin = 1e-3;
- for (int q = 0; q < N_QUBITS; ++q) {
- double lp = loss_with_qin_shift(x, label, q, +eps_qin);
- double lm = loss_with_qin_shift(x, label, q, -eps_qin);
- dL_dqin[q] = (lp - lm) / (2.0 * eps_qin);
- }
- }
- for (int q = 0; q < N_QUBITS; ++q) {
- double pre_out = qpre_b[q];
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- pre_out += qpre_w[q][j] * fc1_act[j];
- }
- /* q_in = tanh(pre_out) * (pi/2) */
- double th = tanh(pre_out);
- double dqindpre = (1.0 - th * th) * (M_PI / 2.0);
- double dL_dpre = dL_dqin[q] * dqindpre;
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- g_qpre_w[q][j] += dL_dpre * fc1_act[j];
- }
- g_qpre_b[q] += dL_dpre;
- }
- /* -------- classical branch backward -------- */
- double dL_dpooled[HIDDEN_DIM];
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- double s = 0.0;
- for (int k = 0; k < N_CLASSES; ++k) {
- s += qc_w[k][j] * dL_dqc[k];
- }
- dL_dpooled[j] = s;
- }
- /* spiking-aware training surrogate: use base activation backward */
- double dL_dfc1pre[HIDDEN_DIM];
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- double grad = dL_dpooled[j];
- if (fc1_pre[j] <= 0.0) {
- grad = 0.0;
- }
- dL_dfc1pre[j] = grad;
- }
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- for (int i = 0; i < INPUT_DIM; ++i) {
- g_fc1_w[j][i] += dL_dfc1pre[j] * x[i];
- }
- g_fc1_b[j] += dL_dfc1pre[j];
- }
- /* parameter-shift for quantum variational parameters */
- for (int p = 0; p < Q_VAR_PARAM_COUNT; ++p) {
- double lp = loss_with_theta_shift(x, label, p, M_PI / 2.0);
- double lm = loss_with_theta_shift(x, label, p, -M_PI / 2.0);
- g_qtheta[p] += 0.5 * (lp - lm);
- }
- }
- static void scale_all_grads(double scale) {
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- for (int i = 0; i < INPUT_DIM; ++i) {
- g_fc1_w[j][i] *= scale;
- }
- g_fc1_b[j] *= scale;
- }
- for (int k = 0; k < N_CLASSES; ++k) {
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- g_qc_w[k][j] *= scale;
- }
- g_qc_b[k] *= scale;
- }
- for (int k = 0; k < N_CLASSES; ++k) {
- for (int j = 0; j < Q_HEAD_IN; ++j) {
- g_qq_w[k][j] *= scale;
- }
- g_qq_b[k] *= scale;
- }
- for (int q = 0; q < N_QUBITS; ++q) {
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- g_qpre_w[q][j] *= scale;
- }
- g_qpre_b[q] *= scale;
- }
- for (int p = 0; p < Q_VAR_PARAM_COUNT; ++p) {
- g_qtheta[p] *= scale;
- }
- }
- static double train_one_epoch(const MnistSet *train_set, const int *train_idx, int epoch, int *adam_step) {
- double epoch_loss = 0.0;
- int n_batches = TRAIN_SUBSET / BATCH_SIZE;
- int order[TRAIN_SUBSET];
- for (int i = 0; i < TRAIN_SUBSET; ++i) order[i] = train_idx[i];
- shuffle_int(order, TRAIN_SUBSET);
- for (int b = 0; b < n_batches; ++b) {
- zero_grads();
- double batch_loss = 0.0;
- int start = b * BATCH_SIZE;
- int end = start + BATCH_SIZE;
- for (int s = start; s < end; ++s) {
- int idx = order[s];
- double x[INPUT_DIM];
- get_image_vector(train_set, idx, x);
- int label = train_set->labels[idx];
- double fc1_pre[HIDDEN_DIM], fc1_act[HIDDEN_DIM], pooled[HIDDEN_DIM];
- double q_in[N_QUBITS], z[N_QUBITS];
- double qc[N_CLASSES], qq[N_CLASSES], qh[N_CLASSES], probs[N_CLASSES];
- model_forward(x, fc1_pre, fc1_act, pooled, q_in, z, qc, qq, qh, probs);
- static int debug_forward_done = 0;
- if (!debug_forward_done) {
- print_vec10_dbg("Qc", qc);
- print_vec10_dbg("Qq", qq);
- print_vec10_dbg("Qh", qh);
- print_vec10_dbg("probs", probs);
- printf("true label = %d\n", label);
- printf("pred label = %d\n", argmax10(probs));
- double avg_pooled = 0.0;
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- avg_pooled += pooled[j];
- }
- avg_pooled /= (double)HIDDEN_DIM;
- printf("avg pooled (SNN output) = %.10f\n", avg_pooled);
- for (int i = 0; i < N_QUBITS; ++i) {
- printf("z[%d] = %.10f\n", i, z[i]);
- }
- printf("\n");
- debug_forward_done = 1;
- }
- batch_loss += nll_one(probs, label);
- accumulate_sample_gradients(x, label);
- }
- scale_all_grads(1.0 / (double)BATCH_SIZE);
- static int debug_grad_done = 0;
- if (!debug_grad_done) {
- printf("grad g_qc_w[0][0] = %.10e\n", g_qc_w[0][0]);
- printf("grad g_qq_w[0][0] = %.10e\n", g_qq_w[0][0]);
- printf("grad g_fc1_w[0][0] = %.10e\n", g_fc1_w[0][0]);
- printf("grad g_qtheta[0] = %.10e\n", g_qtheta[0]);
- printf("grad g_qpre_w[0][0] = %.10e\n", g_qpre_w[0][0]);
- printf("grad g_qpre_b[0] = %.10e\n", g_qpre_b[0]);
- printf("\n");
- debug_grad_done = 1;
- }
- ++(*adam_step);
- static int debug_before_update_done = 0;
- if (!debug_before_update_done) {
- printf("BEFORE update:\n");
- printf("qc_w[0][0] = %.10f\n", qc_w[0][0]);
- printf("qq_w[0][0] = %.10f\n", qq_w[0][0]);
- printf("fc1_w[0][0] = %.10f\n", fc1_w[0][0]);
- printf("q_theta_var[0] = %.10f\n", q_theta_var[0]);
- printf("qpre_w[0][0] = %.10f\n", qpre_w[0][0]);
- printf("qpre_b[0] = %.10f\n", qpre_b[0]);
- printf("\n");
- }
- apply_adam_all(*adam_step);
- static int debug_after_update_done = 0;
- if (!debug_after_update_done) {
- printf("AFTER update:\n");
- printf("qc_w[0][0] = %.10f\n", qc_w[0][0]);
- printf("qq_w[0][0] = %.10f\n", qq_w[0][0]);
- printf("fc1_w[0][0] = %.10f\n", fc1_w[0][0]);
- printf("q_theta_var[0] = %.10f\n", q_theta_var[0]);
- printf("qpre_w[0][0] = %.10f\n", qpre_w[0][0]);
- printf("qpre_b[0] = %.10f\n", qpre_b[0]);
- printf("\n");
- debug_before_update_done = 1;
- debug_after_update_done = 1;
- }
- epoch_loss += batch_loss / (double)BATCH_SIZE;
- if ((b + 1) % 25 == 0 || b == n_batches - 1) {
- printf(" epoch %d batch %d/%d loss=%.6f\n", epoch + 1, b + 1, n_batches, batch_loss / (double)BATCH_SIZE);
- }
- }
- return epoch_loss / (double)n_batches;
- }
- static EvalInfo evaluate_one_sample(const MnistSet *set, int idx) {
- EvalInfo e;
- double x[INPUT_DIM];
- get_image_vector(set, idx, x);
- double fc1_pre[HIDDEN_DIM], fc1_act[HIDDEN_DIM], pooled[HIDDEN_DIM];
- double q_in[N_QUBITS], z[N_QUBITS];
- double qc[N_CLASSES], qq[N_CLASSES], qh[N_CLASSES], probs[N_CLASSES];
- model_forward(x, fc1_pre, fc1_act, pooled, q_in, z, qc, qq, qh, probs);
- e.true_label = set->labels[idx];
- e.pred_label = argmax10(probs);
- e.confidence = probs[e.pred_label];
- e.loss = nll_one(probs, e.true_label);
- return e;
- }
- static double evaluate_accuracy(const MnistSet *set, const int *idxs, int n, double *avg_loss) {
- int correct = 0;
- double loss = 0.0;
- for (int t = 0; t < n; ++t) {
- double x[INPUT_DIM];
- get_image_vector(set, idxs[t], x);
- double fc1_pre[HIDDEN_DIM], fc1_act[HIDDEN_DIM], pooled[HIDDEN_DIM];
- double q_in[N_QUBITS], z[N_QUBITS];
- double qc[N_CLASSES], qq[N_CLASSES], qh[N_CLASSES], probs[N_CLASSES];
- model_forward(x, fc1_pre, fc1_act, pooled, q_in, z, qc, qq, qh, probs);
- int pred = argmax10(probs);
- int label = set->labels[idxs[t]];
- if (pred == label) ++correct;
- loss += nll_one(probs, label);
- }
- *avg_loss = loss / (double)n;
- return (double)correct / (double)n;
- }
- static void wait_enter(void) {
- int c;
- printf("\nPress Enter to continue...");
- fflush(stdout);
- do {
- c = getchar();
- } while (c != '\n' && c != EOF);
- }
- static void clear_stdin_line(void) {
- int c;
- do {
- c = getchar();
- } while (c != '\n' && c != EOF);
- }
- static int read_int_prompt(const char *prompt, int default_value) {
- char buf[128];
- printf("%s", prompt);
- if (!fgets(buf, (int)sizeof(buf), stdin)) return default_value;
- if (buf[0] == '\n' || buf[0] == '\0') return default_value;
- return atoi(buf);
- }
- static double read_double_prompt(const char *prompt, double default_value) {
- char buf[128];
- printf("%s", prompt);
- if (!fgets(buf, (int)sizeof(buf), stdin)) return default_value;
- if (buf[0] == '\n' || buf[0] == '\0') return default_value;
- return atof(buf);
- }
- static void save_model_to_file(const char *path) {
- FILE *f = fopen(path, "wb");
- if (!f) {
- printf("Failed to open model file for writing: %s\n", path);
- return;
- }
- const char magic[8] = { 'P','P','F','S','Q','N','N','1' };
- fwrite(magic, 1, 8, f);
- fwrite(&g_time_steps, sizeof(g_time_steps), 1, f);
- fwrite(fc1_w, sizeof(fc1_w), 1, f);
- fwrite(fc1_b, sizeof(fc1_b), 1, f);
- fwrite(qc_w, sizeof(qc_w), 1, f);
- fwrite(qc_b, sizeof(qc_b), 1, f);
- fwrite(qq_w, sizeof(qq_w), 1, f);
- fwrite(qq_b, sizeof(qq_b), 1, f);
- fwrite(q_theta_var, sizeof(q_theta_var), 1, f);
- fclose(f);
- printf("Model saved to %s\n", path);
- }
- static int load_model_from_file(const char *path) {
- FILE *f = fopen(path, "rb");
- if (!f) {
- printf("Failed to open model file for reading: %s\n", path);
- return 0;
- }
- char magic[8];
- if (fread(magic, 1, 8, f) != 8 || memcmp(magic, "PPFSQNN1", 8) != 0) {
- fclose(f);
- printf("Invalid model file format: %s\n", path);
- return 0;
- }
- if (fread(&g_time_steps, sizeof(g_time_steps), 1, f) != 1 ||
- fread(fc1_w, sizeof(fc1_w), 1, f) != 1 ||
- fread(fc1_b, sizeof(fc1_b), 1, f) != 1 ||
- fread(qc_w, sizeof(qc_w), 1, f) != 1 ||
- fread(qc_b, sizeof(qc_b), 1, f) != 1 ||
- fread(qq_w, sizeof(qq_w), 1, f) != 1 ||
- fread(qq_b, sizeof(qq_b), 1, f) != 1 ||
- fread(q_theta_var, sizeof(q_theta_var), 1, f) != 1) {
- fclose(f);
- printf("Failed to read full model file: %s\n", path);
- return 0;
- }
- fclose(f);
- g_model_initialized = 1;
- printf("Model loaded from %s\n", path);
- printf("Loaded implementation parameter: time steps = %d\n", g_time_steps);
- return 1;
- }
- static void save_model_to_text_file(const char *path) {
- FILE *f = fopen(path, "w");
- if (!f) {
- printf("Failed to open text model file for writing: %s\n", path);
- return;
- }
- fprintf(f, "PPF-SQNN model dump\n");
- fprintf(f, "g_time_steps %d\n", g_time_steps);
- fprintf(f, "\n[fc1_w]\n");
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- for (int i = 0; i < INPUT_DIM; ++i) {
- fprintf(f, "%.17g", fc1_w[j][i]);
- if (i + 1 < INPUT_DIM) fprintf(f, " ");
- }
- fprintf(f, "\n");
- }
- fprintf(f, "\n[fc1_b]\n");
- for (int j = 0; j < HIDDEN_DIM; ++j) fprintf(f, "%.17g\n", fc1_b[j]);
- fprintf(f, "\n[qc_w]\n");
- for (int k = 0; k < N_CLASSES; ++k) {
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- fprintf(f, "%.17g", qc_w[k][j]);
- if (j + 1 < HIDDEN_DIM) fprintf(f, " ");
- }
- fprintf(f, "\n");
- }
- fprintf(f, "\n[qc_b]\n");
- for (int k = 0; k < N_CLASSES; ++k) fprintf(f, "%.17g\n", qc_b[k]);
- fprintf(f, "\n[qq_w]\n");
- for (int k = 0; k < N_CLASSES; ++k) {
- for (int j = 0; j < Q_HEAD_IN; ++j) {
- fprintf(f, "%.17g", qq_w[k][j]);
- if (j + 1 < Q_HEAD_IN) fprintf(f, " ");
- }
- fprintf(f, "\n");
- }
- fprintf(f, "\n[qq_b]\n");
- for (int k = 0; k < N_CLASSES; ++k) fprintf(f, "%.17g\n", qq_b[k]);
- fprintf(f, "\n[qpre_w]\n");
- for (int q = 0; q < N_QUBITS; ++q) {
- for (int j = 0; j < HIDDEN_DIM; ++j) {
- fprintf(f, "%.17g", qpre_w[q][j]);
- if (j + 1 < HIDDEN_DIM) fprintf(f, " ");
- }
- fprintf(f, "\n");
- }
- fprintf(f, "\n[qpre_b]\n");
- for (int q = 0; q < N_QUBITS; ++q) fprintf(f, "%.17g\n", qpre_b[q]);
- fprintf(f, "\n[q_theta_var]\n");
- for (int p = 0; p < Q_VAR_PARAM_COUNT; ++p) fprintf(f, "%.17g\n", q_theta_var[p]);
- fclose(f);
- printf("Model saved to %s\n", path);
- }
- static int skip_blank_lines(FILE *f, char *line, size_t sz) {
- while (fgets(line, (int)sz, f)) {
- if (line[0] == '\n' || line[0] == '\r') continue;
- return 1;
- }
- return 0;
- }
- static int expect_section(FILE *f, const char *expected) {
- char line[256];
- if (!skip_blank_lines(f, line, sizeof(line))) return 0;
- line[strcspn(line, "\r\n")] = '\0';
- return strcmp(line, expected) == 0;
- }
- static int load_model_from_text_file(const char *path) {
- FILE *f = fopen(path, "r");
- if (!f) {
- printf("Failed to open text model file for reading: %s\n", path);
- return 0;
- }
- char line[256];
- if (!fgets(line, sizeof(line), f)) {
- fclose(f);
- printf("Empty text model file: %s\n", path);
- return 0;
- }
- line[strcspn(line, "\r\n")] = '\0';
- if (strcmp(line, "PPF-SQNN model dump") != 0) {
- fclose(f);
- printf("Invalid text model file header: %s\n", path);
- return 0;
- }
- if (!fgets(line, sizeof(line), f) || sscanf(line, "g_time_steps %d", &g_time_steps) != 1) {
- fclose(f);
- printf("Failed to read g_time_steps from %s\n", path);
- return 0;
- }
- if (!expect_section(f, "[fc1_w]")) { fclose(f); printf("Missing [fc1_w] in %s\n", path); return 0; }
- for (int j = 0; j < HIDDEN_DIM; ++j)
- for (int i = 0; i < INPUT_DIM; ++i)
- if (fscanf(f, "%lf", &fc1_w[j][i]) != 1) { fclose(f); printf("Read error in [fc1_w]\n"); return 0; }
- if (!expect_section(f, "[fc1_b]")) { fclose(f); printf("Missing [fc1_b] in %s\n", path); return 0; }
- for (int j = 0; j < HIDDEN_DIM; ++j)
- if (fscanf(f, "%lf", &fc1_b[j]) != 1) { fclose(f); printf("Read error in [fc1_b]\n"); return 0; }
- if (!expect_section(f, "[qc_w]")) { fclose(f); printf("Missing [qc_w] in %s\n", path); return 0; }
- for (int k = 0; k < N_CLASSES; ++k)
- for (int j = 0; j < HIDDEN_DIM; ++j)
- if (fscanf(f, "%lf", &qc_w[k][j]) != 1) { fclose(f); printf("Read error in [qc_w]\n"); return 0; }
- if (!expect_section(f, "[qc_b]")) { fclose(f); printf("Missing [qc_b] in %s\n", path); return 0; }
- for (int k = 0; k < N_CLASSES; ++k)
- if (fscanf(f, "%lf", &qc_b[k]) != 1) { fclose(f); printf("Read error in [qc_b]\n"); return 0; }
- if (!expect_section(f, "[qq_w]")) { fclose(f); printf("Missing [qq_w] in %s\n", path); return 0; }
- for (int k = 0; k < N_CLASSES; ++k)
- for (int j = 0; j < Q_HEAD_IN; ++j)
- if (fscanf(f, "%lf", &qq_w[k][j]) != 1) { fclose(f); printf("Read error in [qq_w]\n"); return 0; }
- if (!expect_section(f, "[qq_b]")) { fclose(f); printf("Missing [qq_b] in %s\n", path); return 0; }
- for (int k = 0; k < N_CLASSES; ++k)
- if (fscanf(f, "%lf", &qq_b[k]) != 1) { fclose(f); printf("Read error in [qq_b]\n"); return 0; }
- if (!expect_section(f, "[qpre_w]")) { fclose(f); printf("Missing [qpre_w] in %s\n", path); return 0; }
- for (int q = 0; q < N_QUBITS; ++q)
- for (int j = 0; j < HIDDEN_DIM; ++j)
- if (fscanf(f, "%lf", &qpre_w[q][j]) != 1) { fclose(f); printf("Read error in [qpre_w]\n"); return 0; }
- if (!expect_section(f, "[qpre_b]")) { fclose(f); printf("Missing [qpre_b] in %s\n", path); return 0; }
- for (int q = 0; q < N_QUBITS; ++q)
- if (fscanf(f, "%lf", &qpre_b[q]) != 1) { fclose(f); printf("Read error in [qpre_b]\n"); return 0; }
- if (!expect_section(f, "[q_theta_var]")) { fclose(f); printf("Missing [q_theta_var] in %s\n", path); return 0; }
- for (int p = 0; p < Q_VAR_PARAM_COUNT; ++p)
- if (fscanf(f, "%lf", &q_theta_var[p]) != 1) { fclose(f); printf("Read error in [q_theta_var]\n"); return 0; }
- fclose(f);
- g_model_initialized = 1;
- printf("Model loaded from %s\n", path);
- printf("Loaded implementation parameter: time steps = %d\n", g_time_steps);
- return 1;
- }
- static void predict_one_image_menu(const MnistSet *train_set, const MnistSet *test_set) {
- if (!g_model_initialized) {
- printf("Model is not initialized. Train or load a model first.");
- return;
- }
- int dataset_choice = read_int_prompt("Dataset (1=train, 2=test) [2]: ", 2);
- const MnistSet *set = (dataset_choice == 1) ? train_set : test_set;
- const char *dataset_name = (dataset_choice == 1) ? "train" : "test";
- printf("Select image mode:");
- printf(" 1 - by index");
- printf(" 2 - by true label and occurrence");
- printf(" 3 - random image of a chosen digit");
- int mode = read_int_prompt("Mode [1]: ", 1);
- int idx = 0;
- if (mode == 1) {
- char prompt[256];
- snprintf(prompt, sizeof(prompt), "Image index [0..%d] [0]: ", set->num_images - 1);
- idx = read_int_prompt(prompt, 0);
- if (idx < 0) idx = 0;
- if (idx >= set->num_images) idx = set->num_images - 1;
- } else if (mode == 2) {
- int digit = read_int_prompt("True digit [0..9] [0]: ", 0);
- if (digit < 0) digit = 0;
- if (digit > 9) digit = 9;
- int total = count_label_occurrences(set, digit);
- if (total <= 0) {
- printf("No images with digit %d in %s dataset.", digit, dataset_name);
- return;
- }
- char prompt[256];
- snprintf(prompt, sizeof(prompt), "Occurrence [1..%d] [1]: ", total);
- int occ = read_int_prompt(prompt, 1);
- if (occ < 1) occ = 1;
- if (occ > total) occ = total;
- idx = find_nth_occurrence_of_label(set, digit, occ - 1);
- printf("Chosen occurrence: %d of %d", occ, total);
- } else if (mode == 3) {
- int digit = read_int_prompt("True digit [0..9] [0]: ", 0);
- if (digit < 0) digit = 0;
- if (digit > 9) digit = 9;
- int total = count_label_occurrences(set, digit);
- if (total <= 0) {
- printf("No images with digit %d in %s dataset.", digit, dataset_name);
- return;
- }
- idx = choose_random_index_of_label(set, digit);
- int occ = 1;
- for (int i = 0; i < set->num_images; ++i) {
- if (set->labels[i] == digit) {
- if (i == idx) break;
- ++occ;
- }
- }
- printf("Random occurrence chosen: %d of %d", occ, total);
- } else {
- printf("Unknown mode.");
- return;
- }
- double noise_percent = read_double_prompt("Noise percent [0..100] [0]: ", 0.0);
- if (noise_percent < 0.0) noise_percent = 0.0;
- if (noise_percent > 100.0) noise_percent = 100.0;
- printf("Select noise type:");
- printf(" 1 - uniform");
- printf(" 2 - gaussian");
- int noise_type = read_int_prompt("Noise type [1]: ", 1);
- print_ascii_digit(set, idx);
- double x_clean[INPUT_DIM];
- double x_noisy[INPUT_DIM];
- get_image_vector(set, idx, x_clean);
- for (int i = 0; i < INPUT_DIM; ++i) {
- x_noisy[i] = x_clean[i];
- }
- if (noise_percent > 0.0) {
- if (noise_type == 2) {
- add_noise_gaussian(x_noisy, noise_percent);
- } else {
- add_noise_uniform(x_noisy, noise_percent);
- }
- }
- print_ascii_vector28(x_noisy, set->labels[idx], "ASCII preview after noise");
- double fc1_pre[HIDDEN_DIM], fc1_act[HIDDEN_DIM], pooled[HIDDEN_DIM];
- double q_in[N_QUBITS], z[N_QUBITS];
- double qc[N_CLASSES], qq[N_CLASSES], qh[N_CLASSES], probs[N_CLASSES];
- model_forward(x_noisy, fc1_pre, fc1_act, pooled, q_in, z, qc, qq, qh, probs);
- int pred = argmax10(probs);
- int label = set->labels[idx];
- int pred_ref_idx = choose_random_index_of_label(set, pred);
- if (save_pgm_from_vector("predict_input_noisy.pgm", x_noisy)) {
- printf("Saved noisy input image to predict_input_noisy.pgm");
- } else {
- printf("Failed to save noisy input image.");
- }
- if (pred_ref_idx >= 0 && save_pgm_from_dataset_image("predict_predicted_class_reference.pgm", set, pred_ref_idx)) {
- printf("Saved predicted-class reference image to predict_predicted_class_reference.pgm");
- printf("Note: this second file is a clean reference example of the predicted class, not a reconstruction.");
- } else {
- printf("Failed to save predicted-class reference image.");
- }
- printf("Prediction:");
- printf(" dataset : %s", dataset_name);
- printf(" image index : %d", idx);
- printf(" true label : %d", label);
- printf(" predicted label : %d", pred);
- printf(" noise percent : %.2f", noise_percent);
- printf(" noise type : %s", (noise_type == 2) ? "gaussian" : "uniform");
- printf(" confidence : %.6f", probs[pred]);
- printf(" loss : %.6f", nll_one(probs, label));
- printf("Class probabilities:");
- for (int k = 0; k < N_CLASSES; ++k) {
- printf(" [%d] %.10f", k, probs[k]);
- }
- }
- static void train_model_menu(const MnistSet *train_set, const MnistSet *test_set, int *train_idx, int *test_idx) {
- if (!g_model_initialized) {
- init_weights();
- g_model_initialized = 1;
- }
- int epochs = read_int_prompt("Epochs [140]: ", EPOCHS);
- if (epochs <= 0) epochs = EPOCHS;
- int new_steps = read_int_prompt("Time steps for spiking simulation [current value]: ", g_time_steps);
- if (new_steps > 0) g_time_steps = new_steps;
- int adam_step = 0;
- for (int epoch = 0; epoch < epochs; ++epoch) {
- double train_loss = train_one_epoch(train_set, train_idx, epoch, &adam_step);
- printf("Epoch %d/%d finished, avg train loss = %.6f\n", epoch + 1, epochs, train_loss);
- if ((epoch + 1) % 10 == 0 || epoch == epochs - 1) {
- double test_loss = 0.0;
- double acc = evaluate_accuracy(test_set, test_idx, TEST_SUBSET, &test_loss);
- printf(" Test subset accuracy = %.4f, avg loss = %.6f\n", acc, test_loss);
- }
- printf("\n");
- }
- save_model_to_text_file("ppf_sqnn_model.txt");
- }
- static void print_menu(void) {
- printf("\n================ PPF-SQNN menu ================\n");
- printf("1 - Train model\n");
- printf("2 - Predict one image\n");
- printf("3 - Save model\n");
- printf("4 - Load model\n");
- printf("5 - Set time steps (current: %d)\n", g_time_steps);
- printf("0 - Exit\n");
- printf("===============================================\n");
- }
- int main(void) {
- rng_state = (unsigned int)time(NULL);
- printf("PPF-SQNN exact-replication project, menu build\n");
- printf("- Input: 28x28 -> 784\n");
- printf("- Reduced dimension: 128\n");
- printf("- Classes: 10\n");
- printf("- Qubits: 5\n");
- printf("- Train subset: 8000\n");
- printf("- Test subset: 10000\n");
- printf("- Batch size: 32\n");
- printf("- Default epochs: 140\n");
- printf("- xi: 0.8\n");
- printf("- Adam: lr=0.001 beta1=0.90 beta2=0.99 wd=0.0\n");
- printf("- Stage-2 time steps: runtime implementation parameter (default %d)\n\n", g_time_steps);
- char train_img[1024], train_lbl[1024], test_img[1024], test_lbl[1024];
- if (!find_mnist_files(train_img, sizeof(train_img), train_lbl, sizeof(train_lbl), test_img, sizeof(test_img), test_lbl, sizeof(test_lbl))) {
- printf("Could not locate all four MNIST IDX files.\n");
- return 1;
- }
- printf("Using MNIST files:\n");
- printf(" %s\n", train_img);
- printf(" %s\n", train_lbl);
- printf(" %s\n", test_img);
- printf(" %s\n\n", test_lbl);
- MnistSet train_set, test_set;
- if (!load_mnist_set(train_img, train_lbl, &train_set) || !load_mnist_set(test_img, test_lbl, &test_set)) {
- printf("Failed to load MNIST data.\n");
- return 1;
- }
- int *train_idx = (int *)malloc((size_t)train_set.num_images * sizeof(int));
- int *test_idx = (int *)malloc((size_t)test_set.num_images * sizeof(int));
- if (!train_idx || !test_idx) {
- printf("Allocation failure.\n");
- free_mnist_set(&train_set);
- free_mnist_set(&test_set);
- free(train_idx);
- free(test_idx);
- return 1;
- }
- for (int i = 0; i < train_set.num_images; ++i) train_idx[i] = i;
- for (int i = 0; i < test_set.num_images; ++i) test_idx[i] = i;
- shuffle_int(train_idx, train_set.num_images);
- shuffle_int(test_idx, test_set.num_images);
- if (file_exists("ppf_sqnn_model.txt")) {
- printf("Found existing model file ppf_sqnn_model.txt\n");
- printf("Trying to load it...\n");
- load_model_from_text_file("ppf_sqnn_model.txt");
- } else {
- init_weights();
- g_model_initialized = 1;
- }
- for (;;) {
- print_menu();
- int choice = read_int_prompt("Choose action: ", -1);
- if (choice == 0) {
- break;
- } else if (choice == 1) {
- train_model_menu(&train_set, &test_set, train_idx, test_idx);
- wait_enter();
- } else if (choice == 2) {
- predict_one_image_menu(&train_set, &test_set);
- wait_enter();
- } else if (choice == 3) {
- save_model_to_text_file("ppf_sqnn_model.txt");
- wait_enter();
- } else if (choice == 4) {
- load_model_from_text_file("ppf_sqnn_model.txt");
- wait_enter();
- } else if (choice == 5) {
- int new_steps = read_int_prompt("New time steps value: ", g_time_steps);
- if (new_steps > 0) {
- g_time_steps = new_steps;
- printf("Time steps updated to %d\n", g_time_steps);
- } else {
- printf("Time steps value must be positive.\n");
- }
- wait_enter();
- } else {
- printf("Unknown menu item.\n");
- wait_enter();
- }
- }
- free(train_idx);
- free(test_idx);
- free_mnist_set(&train_set);
- free_mnist_set(&test_set);
- return 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment