From 963065213610d164ff49e46cb25d274ca35b3bd7 Mon Sep 17 00:00:00 2001 From: miha-q <> Date: Fri, 8 Mar 2024 22:13:04 -0500 Subject: [PATCH] Fri Mar 8 10:13:04 PM EST 2024 --- Makefile | 12 +- bin/QAnsel | Bin 108096 -> 171624 bytes examples/bellstate.txt | 2 +- examples/decoherence.txt | 2 +- src/QAnsel.c | 1907 ++++++++++++-------------------------- src/core.c | 1103 ++++++++++++++++++++++ src/oldex.mm | 127 +++ 7 files changed, 1848 insertions(+), 1305 deletions(-) create mode 100644 src/core.c create mode 100644 src/oldex.mm diff --git a/Makefile b/Makefile index bcf674e..7406d5a 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,11 @@ all: - mv src/kernel.cl src/.kernel.cl - bash -c 'echo -ne "$$(cat src/.kernel.cl)\x00" > src/kernel.cl' - xxd -i src/kernel.cl | sed -e 's/gpu_gpu_/gpu_/g' > src/kernel.cl.c - mv src/.kernel.cl src/kernel.cl - cat src/kernel.cl | grep -vi '{gpu_only}' | sed -e 's/\/\/\(.*\){cpu_only}/\1/' -e 's/__global //' -e 's/__kernel //' > src/kernel_cpu.cl.c + + cat src/kernel.cl | grep -vi '{gpu_only}' | sed -e 's/__global //' -e 's/__kernel //' > src/kernel_cpu.cl + cat src/kernel.cl | grep -vi '{cpu_only}' > src/.kernel.tmp.1 + bash -c 'echo -ne "$$(cat src/.kernel.tmp.1)\x00" > src/.kernel.tmp.2' + xxd -i src/.kernel.tmp.2 | sed -e 's/src__kernel_tmp_2/kernel_gpu/' > src/kernel_gpu.cl gcc src/QAnsel.c -g -o bin/QAnsel -lm -I/usr/include/SDL2 -D_REENTRANT -lSDL2 -lOpenCL -pthread - #rm -f src/*.cl.c + #rm -f src/*.cl.c \ No newline at end of file diff --git a/bin/QAnsel b/bin/QAnsel index da2cdeb99265f700af4d9d4769626aa63894a13b..df10cea7c469d53137bdd117d6d6f409af83e528 100755 GIT binary patch literal 171624 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 literal 108096 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 diff --git a/examples/bellstate.txt b/examples/bellstate.txt index 31fe104..8ad1845 100644 --- a/examples/bellstate.txt +++ b/examples/bellstate.txt @@ -2,4 +2,4 @@ qreg q[2]; h q[0]; cx q[0], q[1]; -born; +born q; diff --git a/examples/decoherence.txt b/examples/decoherence.txt index c4ac0ed..2d59d95 100644 --- a/examples/decoherence.txt +++ b/examples/decoherence.txt @@ -9,4 +9,4 @@ h q[0]; cx q[0], q[1]; h q[0]; measure q[0] -> c[0]; -sample; +sample c; diff --git a/src/QAnsel.c b/src/QAnsel.c index 70b9a53..f232e4a 100644 --- a/src/QAnsel.c +++ b/src/QAnsel.c @@ -1,1428 +1,741 @@ -#include -#include -#include -#include -#include "complex.c" -#include "gates.c" -#include "display.c" -#include "chacha20.c" -#define QUBITS_MAX 14 -unsigned char HIDDEN_VARIABLE = 0; -FILE* RANDOM_FILE; -#define GPU_ENABLED -unsigned char USE_THREADS = 1; -#define MODE_BARE 1 -#define MODE_THREADED 2 -#define MODE_METAL 3 -#define MODE_METAL_THREADED 4 -unsigned char MODE = MODE_BARE; -//#define SPEED_TEST +#include +#include "core.c" -typedef struct +void qansel_read_script(char* script, char*** chunksReturn, int** chunkLinesReturn, int* countReturn) { - char n[128]; - unsigned char q0, q1, q2; - float arg0, arg1, arg2; -} QInstr; - -float qansel_rand_s(float s) -{ - unsigned int tmp; - memcpy(&tmp, &s, sizeof(unsigned int)); - srand(tmp); -} -float qansel_rand_h() -{ - return ((float)rand()) / ((float)RAND_MAX); -} -float qansel_rand_t() -{ - if (RANDOM_FILE) + size_t scriptSize = strlen(script); + char* line = malloc(0); + char** chunks = malloc(0); + int* chunksAssociatedLines = malloc(0); + int lineCount = 1; + int lineLen = 0; + int chunkCount = 0; + int inComment = 0; + for (int i = 0; i < scriptSize; i++) { - unsigned int num = 0; - for (unsigned char i = 0; i < 4; i++) + int charactersLeft = scriptSize - i; + if (script[i] == '\n') { - num = (num << 8) | fgetc(RANDOM_FILE); + lineCount++; + inComment = 0; + } + else if (charactersLeft >= 2 && script[i] == '/' && script[i + 1] == '/') + { + inComment = 1; + } + else if (!inComment && script[i] != ';' && script[i] != '{' && script[i] != '}') + { + line = realloc(line, lineLen + 1); + line[lineLen++] = script[i] == '\t' ? ' ' : script[i]; + if (script[i] == ')' && lineLen > 2) + { + for (int j = 0; j < lineLen - 1; j++) + { + if ( (line[j] == 'i' || line[j] == 'I') && (line[j + 1] == 'f' || line[j + 1] == 'F') ) + { + line = realloc(line, lineLen + 1); + line[lineLen++] = 0; + chunks = realloc(chunks, (chunkCount + 1) * sizeof(char*)); + chunksAssociatedLines = realloc(chunksAssociatedLines, (chunkCount + 1) * sizeof(int)); + chunks[chunkCount] = line; + chunksAssociatedLines[chunkCount++] = lineCount; + line = malloc(0); + lineLen = 0; + break; + } + } + } + } + else if (!inComment && (script[i] == '{' || script[i] == '}')) + { + if (lineLen > 0) + { + line = realloc(line, lineLen + 1); + line[lineLen++] = 0; + chunks = realloc(chunks, (chunkCount + 1) * sizeof(char*)); + chunksAssociatedLines = realloc(chunksAssociatedLines, (chunkCount + 1) * sizeof(int)); + chunks[chunkCount] = line; + chunksAssociatedLines[chunkCount++] = lineCount; + line = malloc(2); + } + else + { + line = realloc(line, 2); + } + line[0] = script[i]; + line[1] = 0; + chunks = realloc(chunks, (chunkCount + 1) * sizeof(char*)); + chunksAssociatedLines = realloc(chunksAssociatedLines, (chunkCount + 1) * sizeof(int)); + chunks[chunkCount] = line; + chunksAssociatedLines[chunkCount++] = lineCount; + line = malloc(0); + lineLen = 0; + } + else if (!inComment && script[i] == ';') + { + if (lineLen > 0) + { + line = realloc(line, lineLen + 1); + line[lineLen++] = 0; + chunks = realloc(chunks, (chunkCount + 1) * sizeof(char*)); + chunksAssociatedLines = realloc(chunksAssociatedLines, (chunkCount + 1) * sizeof(int)); + chunks[chunkCount] = line; + chunksAssociatedLines[chunkCount++] = lineCount; + line = malloc(0); + lineLen = 0; + } } - return ((float)num) / ((float)UINT32_MAX); - } - else - { - HIDDEN_VARIABLE = 1; - return qansel_rand_h(); - } -} - -float qansel_rand() -{ - return HIDDEN_VARIABLE ? qansel_rand_h() : qansel_rand_t(); -} - -void qansel_cnot(cpx_mtx_t* stateVector, unsigned char qubitCount, unsigned char bitA, unsigned char bitB) -{ - unsigned int retLen = (unsigned int)pow(2, qubitCount); - cpx_mtx_t ret; - cpx_mtx_init(&ret, 1, retLen); - cpx_t n; - for (unsigned int i = 0; i < retLen; i++) - { - unsigned char bitAVal = (i >> bitA) & 1; - unsigned char bitBVal = (i >> bitB) & 1; - unsigned char bitBNew = bitAVal ? !bitBVal : bitBVal; - unsigned int j = (i & ~(1 << bitB)) | (bitBNew << bitB); - cpx_mtx_get(stateVector, 0, i, &n); - cpx_mtx_set(&ret, 0, j, &n); } - cpx_mtx_free(stateVector); - stateVector->ptr = ret.ptr; - stateVector->rows = ret.rows; - stateVector->cols = ret.cols; -} - -void qansel_swap(cpx_mtx_t* stateVector, unsigned char qubitCount, unsigned char bitA, unsigned char bitB) -{ - unsigned int retLen = (unsigned int)pow(2, qubitCount); - cpx_mtx_t ret; - cpx_mtx_init(&ret, 1, retLen); - cpx_t n; - for (unsigned int i = 0; i < retLen; i++) + if (lineLen > 0) { - unsigned char bitAVal = (i >> bitA) & 1; - unsigned char bitBVal = (i >> bitB) & 1; - unsigned char bitANew = bitBVal; - unsigned char bitBNew = bitAVal; - unsigned int j = (i & ~((1 << bitA) | (1 << bitB))) | ((bitANew << bitA) | (bitBNew << bitB)); - cpx_mtx_get(stateVector, 0, i, &n); - cpx_mtx_set(&ret, 0, j, &n); + line = realloc(line, lineLen + 1); + line[lineLen++] = 0; + chunks = realloc(chunks, (chunkCount + 1) * sizeof(char*)); + chunksAssociatedLines = realloc(chunksAssociatedLines, (chunkCount + 1) * sizeof(int)); + chunks[chunkCount] = line; + chunksAssociatedLines[chunkCount++] = lineCount; } - cpx_mtx_free(stateVector); - stateVector->ptr = ret.ptr; - stateVector->rows = ret.rows; - stateVector->cols = ret.cols; -} - -void qansel_fredkin(cpx_mtx_t* stateVector, unsigned char qubitCount, unsigned char bitA, unsigned char bitB, unsigned char bitC) -{ - unsigned int retLen = (unsigned int)pow(2, qubitCount); - cpx_mtx_t ret; - cpx_mtx_init(&ret, 1, retLen); - cpx_t n; - for (unsigned int i = 0; i < retLen; i++) + else { - unsigned char bitAVal = (i >> bitA) & 1; - unsigned char bitBVal = (i >> bitB) & 1; - unsigned char bitCVal = (i >> bitC) & 1; - unsigned char bitBNew = bitAVal ? bitCVal : bitBVal; - unsigned char bitCNew = bitAVal ? bitBVal : bitCVal; - unsigned int j = (i & ~((1 << bitB) | (1 << bitC))) | ((bitBNew << bitB) | (bitCNew << bitC)); - cpx_mtx_get(stateVector, 0, i, &n); - cpx_mtx_set(&ret, 0, j, &n); + free(line); } - cpx_mtx_free(stateVector); - stateVector->ptr = ret.ptr; - stateVector->rows = ret.rows; - stateVector->cols = ret.cols; -} - - -void qansel_toffoli(cpx_mtx_t* stateVector, unsigned char qubitCount, unsigned char bitA, unsigned char bitB, unsigned char bitC) -{ - unsigned int retLen = (unsigned int)pow(2, qubitCount); - cpx_mtx_t ret; - cpx_mtx_init(&ret, 1, retLen); - cpx_t n; - for (unsigned int i = 0; i < retLen; i++) + for (int i = 0; i < chunkCount; i++) { - unsigned char bitAVal = (i >> bitA) & 1; - unsigned char bitBVal = (i >> bitB) & 1; - unsigned char bitCVal = (i >> bitC) & 1; - unsigned char bitCNew = (bitAVal && bitBVal) ? !bitCVal : bitCVal; - unsigned int j = (i & ~(1 << bitC)) | (bitCNew << bitC); - cpx_mtx_get(stateVector, 0, i, &n); - cpx_mtx_set(&ret, 0, j, &n); + int len = strlen(chunks[i]); + for (int j = 0; j < len; j++) + { + if (chunks[i][j] >= 'A' && chunks[i][j] <= 'Z') + { + chunks[i][j] += 'a' - 'A'; + } + } } - cpx_mtx_free(stateVector); - stateVector->ptr = ret.ptr; - stateVector->rows = ret.rows; - stateVector->cols = ret.cols; -} - -float* qansel_unitary(float theta, float phi, float lambda) -{ - cpx_mtx_t m; - cpx_t a, b, c, d; - a.real = cos(theta/2.0); - a.imaginary = 0; - b.real = -cos(lambda) * sin(theta/2.0); - b.imaginary = sin(lambda) * sin(theta/2.0); - c.real = cos(phi) * sin(theta/2.0); - c.imaginary = sin(phi) * sin(theta/2.0); - d.real = cos(phi + lambda) * cos(theta/2.0); - d.imaginary = sin(phi + lambda) * cos(theta/2.0); - cpx_mtx_init(&m, 2, 2); - cpx_mtx_set(&m, 0, 0, &a); - cpx_mtx_set(&m, 0, 1, &b); - cpx_mtx_set(&m, 1, 0, &c); - cpx_mtx_set(&m, 1, 1, &d); - return m.ptr; + *chunksReturn = chunks; + *chunkLinesReturn = chunksAssociatedLines; + *countReturn = chunkCount; } -void qansel_instruction(cpx_mtx_t* stateVector, unsigned char qubitCount, QInstr* instr) +float qansel_parse_float_part(char* neg, char* str) { - cpx_mtx_t tmp; - cpx_mtx_t gate; - gate.rows = 2; - gate.cols = 2; - float* gate_ptr; - switch (instr->n[0]) - { - case 'h': gate_ptr = Hadamard; break; - case 'x': gate_ptr = PauliX; break; - case 'y': gate_ptr = PauliY; break; - case 'z': gate_ptr = PauliZ; break; - case 's': gate_ptr = PhaseS; break; - case 't': gate_ptr = PhaseT; break; - case 'u': - gate_ptr = qansel_unitary(instr->arg0, instr->arg1, instr->arg2); - break; - default: gate_ptr = Identity; break; - } - - cpx_t n; - cpx_mtx_t filter; - cpx_mtx_init(&filter, 2, 2); - unsigned char qubit = qubitCount - (instr->q0) - 1; - if (qubit == 0) - { - memcpy(filter.ptr, gate_ptr, 8 * sizeof(float)); - } - else + float ret; + int len = strlen(str); + if (len > 2) { - memcpy(filter.ptr, Identity, 8 * sizeof(float)); - } - - for (unsigned char i = 1; i < qubitCount; i++) - { - if (qubit == i) - { - gate.ptr = gate_ptr; - } - else - { - gate.ptr = Identity; - } - - tmp.rows = filter.rows * gate.rows; - tmp.cols = filter.cols * gate.cols; - tmp.ptr = malloc(tmp.rows * (tmp.cols * 2) * sizeof(float)); - - #ifdef SPEED_TEST - printf("(%ix%i);(%ix%i) (knk)\n", tmp.rows, tmp.cols, gate.rows, gate.cols); - unsigned long int us1, us2; - us1 = get_time(); - cpx_mtx_knk_metal(tmp.ptr, filter.ptr, gate.ptr, filter.rows, filter.cols, gate.rows, gate.cols); - us2 = get_time(); - printf("\tMetal: %lu\n", us2 - us1); - us1 = get_time(); - cpx_mtx_knk_metal_2x2(tmp.ptr, filter.ptr, gate.ptr, filter.rows, filter.cols, gate.rows, gate.cols); - us2 = get_time(); - printf("\tMetal2x2: %lu\n", us2 - us1); - us1 = get_time(); - cpx_mtx_knk_threads(tmp.ptr, filter.ptr, gate.ptr, filter.rows, filter.cols, gate.rows, gate.cols); - us2 = get_time(); - printf("\tThreads: %lu\n", us2 - us1); - us1 = get_time(); - cpx_mtx_knk_threads_2x2(tmp.ptr, filter.ptr, gate.ptr, filter.rows, filter.cols, gate.rows, gate.cols); - us2 = get_time(); - printf("\tThreads2x2: %lu\n", us2 - us1); - us1 = get_time(); - cpx_mtx_knk(tmp.ptr, filter.ptr, gate.ptr, filter.rows, filter.cols, gate.rows, gate.cols); - us2 = get_time(); - printf("\tBare: %lu\n", us2 - us1); - us1 = get_time(); - cpx_mtx_knk_2x2(tmp.ptr, filter.ptr, gate.ptr, filter.rows, filter.cols, gate.rows, gate.cols); - us2 = get_time(); - printf("\tBare2x2: %lu\n", us2 - us1); - - //us1 = get_time(); - //cpx_mtx_knk(tmp.ptr, filter.ptr, gate.ptr, filter.rows, filter.cols, gate.rows, gate.cols); - //us2 = get_time(); - //printf("\tTranspose: %lu\n", us2 - us1); - #else - if (MODE == MODE_METAL && tmp.cols >= 64) + ret = atof(str); + if (str[len - 2] == 'p' && str[len - 1] == 'i') { - cpx_mtx_knk_metal_2x2(tmp.ptr, filter.ptr, gate.ptr, filter.rows, filter.cols, gate.rows, gate.cols); + ret *= M_PI; } - else if ((MODE == MODE_THREADED || MODE == MODE_METAL_THREADED) && tmp.cols >= 64) - { - cpx_mtx_knk_threads_2x2(tmp.ptr, filter.ptr, gate.ptr, filter.rows, filter.cols, gate.rows, gate.cols); - } - else - { - cpx_mtx_knk_2x2(tmp.ptr, filter.ptr, gate.ptr, filter.rows, filter.cols, gate.rows, gate.cols); - } - #endif - - free(filter.ptr); - filter.ptr = tmp.ptr; - filter.rows = tmp.rows; - filter.cols = tmp.cols; } - - cpx_mtx_init(&tmp, stateVector->rows, stateVector->cols); - - #ifdef SPEED_TEST - printf("%ix%i (dot)\n", tmp.rows, tmp.cols); - unsigned long int us1, us2; - us1 = get_time(); - cpx_mtx_dot_metal(tmp.ptr, stateVector->ptr, filter.ptr, stateVector->rows, stateVector->cols, filter.rows, filter.cols); - us2 = get_time(); - printf("\tMetal: %lu\n", us2 - us1); - us1 = get_time(); - cpx_mtx_dot_threads(tmp.ptr, stateVector->ptr, filter.ptr, stateVector->rows, stateVector->cols, filter.rows, filter.cols); - us2 = get_time(); - printf("\tThreads: %lu\n", us2 - us1); - us1 = get_time(); - cpx_mtx_dot(tmp.ptr, stateVector->ptr, filter.ptr, stateVector->rows, stateVector->cols, filter.rows, filter.cols); - us2 = get_time(); - printf("\tBare: %lu\n", us2 - us1); - #else - if ((MODE == MODE_METAL || MODE == MODE_METAL_THREADED) && tmp.cols >= 64) + else if (strcmp(str, "pi") == 0) { - cpx_mtx_dot_metal(tmp.ptr, stateVector->ptr, filter.ptr, stateVector->rows, stateVector->cols, filter.rows, filter.cols); + ret = M_PI; } - else if (MODE == MODE_THREADED && tmp.cols >= 64) + else { - cpx_mtx_dot_threads(tmp.ptr, stateVector->ptr, filter.ptr, stateVector->rows, stateVector->cols, filter.rows, filter.cols); + ret = atof(str); } - else + if (strcmp(neg, "-") == 0) { - cpx_mtx_dot(tmp.ptr, stateVector->ptr, filter.ptr, stateVector->rows, stateVector->cols, filter.rows, filter.cols); + ret *= -1; } - #endif - free(stateVector->ptr); - stateVector->ptr = tmp.ptr; - free(filter.ptr); - if (instr->n[0] == 'u') free(gate_ptr); + return ret; } -unsigned char qansel_measure(cpx_mtx_t* stateVector, unsigned char qubitCount, unsigned char qubit) +int qansel_parse_float(char* str, float* returnFloat) { - unsigned int qubitCountPow2 = (unsigned int)pow(2, qubitCount); - cpx_t n; - float prob0 = 0; - for (unsigned int i = 0; i < qubitCountPow2; i++) + *returnFloat = 0; + const char expr[] = "^(-|)([0-9][0-9]*\\.[0-9]*|[0-9][0-9]*|[0-9][0-9]*\\.[0-9]*pi|[0-9][0-9]*pi|pi)([/](-|)([0-9][0-9]*\\.[0-9]*|[0-9][0-9]*|[0-9][0-9]*\\.[0-9]*pi|[0-9][0-9]*pi|pi)|)$"; + regex_t regex; + regmatch_t regmatches[10]; + if (regcomp(®ex, expr, REG_EXTENDED | REG_ICASE)) return 0; + int ret = regexec(®ex, str, 10, regmatches, 0); + if (!ret) { - unsigned char bit = (i >> qubit) & 1; - cpx_mtx_get(stateVector, 0, i, &n); - if (bit == 0) prob0 += cpx_magsqr(&n); + int strbeg = regmatches[1].rm_so; + int strlen = regmatches[1].rm_eo - regmatches[1].rm_so; + char neg1[strlen + 1]; + memcpy(neg1, str + strbeg, strlen); + neg1[strlen] = 0; + + strbeg = regmatches[2].rm_so; + strlen = regmatches[2].rm_eo - regmatches[2].rm_so; + char numer[strlen + 1]; + memcpy(numer, str + strbeg, strlen); + numer[strlen] = 0; + *returnFloat = qansel_parse_float_part(neg1, numer); + + if (regmatches[3].rm_eo > regmatches[3].rm_so) + { + strbeg = regmatches[4].rm_so; + strlen = regmatches[4].rm_eo - regmatches[4].rm_so; + char neg2[strlen + 1]; + memcpy(neg2, str + strbeg, strlen); + neg2[strlen] = 0; + + strbeg = regmatches[5].rm_so; + strlen = regmatches[5].rm_eo - regmatches[5].rm_so; + char denom[strlen + 1]; + memcpy(denom, str + strbeg, strlen); + denom[strlen] = 0; + + float fdenom = qansel_parse_float_part(neg2, denom); + *returnFloat /= fdenom; + } } - - float r = qansel_rand(); - unsigned char newBit = r < prob0 ? 0 : 1; - float probTot = 0; - for (unsigned int i = 0; i < qubitCountPow2; i++) + else if (ret == REG_NOMATCH) { - unsigned char bit = (i >> qubit) & 1; - cpx_mtx_get(stateVector, 0, i, &n); - if (bit != newBit) - { - n.real = 0; - n.imaginary = 0; - } - else - { - probTot += cpx_magsqr(&n); - } - cpx_mtx_set(stateVector, 0, i, &n); + return 0; } - - float multiplier = sqrt(1 / probTot); - for (unsigned int i = 0; i < qubitCountPow2; i++) + else { - unsigned char bit = (i >> qubit) & 1; - cpx_mtx_get(stateVector, 0, i, &n); - if (bit == newBit) - { - n.real *= multiplier; - n.imaginary *= multiplier; - } - cpx_mtx_set(stateVector, 0, i, &n); + char errbuf[100]; + regerror(ret, ®ex, errbuf, sizeof(errbuf)); + fprintf(stderr, "QAnsel: %s.\n", errbuf); + exit(1); } + regfree(®ex); - return newBit; + return 1; } -void qansel_run(unsigned char qubitCount, unsigned char bitCount, QInstr* instr, unsigned char* retBitVect, unsigned int instrLen, unsigned char gfx) +int qansel_process_chunk(int index, char* chunk, int line, regmatch_t* regmatches, int* qubitCount, int* bitCount, unsigned char* binary, int* binarySize) { - unsigned int qubitCountPow2 = (unsigned int)pow(2, qubitCount); - - unsigned char bitVector[bitCount]; - cpx_mtx_t stateVector; - cpx_mtx_init(&stateVector, 1, qubitCountPow2); - cpx_mtx_set2(&stateVector, 0, 0, 1, 0); - if (gfx) display(&stateVector, qubitCount); - unsigned char flags; - - for (unsigned char i = 0; i < bitCount; i++) bitVector[i] = 0; - - unsigned long int ns, ps; - for (unsigned int i = 0; i < instrLen; i++) + unsigned short s0; + float d0, d1, d2; + unsigned char instr = 0; + unsigned char a0 = 0; + unsigned char a1 = 0; + unsigned char a2 = 0; + if (index == 0) //qreg { - //ns = get_time(); - //if (i > 0) - //{ - // printf("%s: %lu\n", instr[i].n, ns - ps); - //} - //ps = ns; - - cpx_t qqq; - cpx_mtx_get(&stateVector, 0, 0, &qqq); - if (strcmp(instr[i].n, "measure") == 0) - { - bitVector[instr[i].q1] = qansel_measure(&stateVector, qubitCount, instr[i].q0); - } - else if (strcmp(instr[i].n, "cswap") == 0) - { - qansel_fredkin(&stateVector, qubitCount, instr[i].q0, instr[i].q1, instr[i].q2); - } - else if (strcmp(instr[i].n, "ccx") == 0) + if (*qubitCount > 0) { - qansel_toffoli(&stateVector, qubitCount, instr[i].q0, instr[i].q1, instr[i].q2); + fprintf(stderr, "QAnsel on line %i: Qubits can only be initialized once.\n", line); + return 0; } - else if (strcmp(instr[i].n, "cx") == 0) + int strbeg = regmatches[1].rm_so; + int strlen = regmatches[1].rm_eo - regmatches[1].rm_so; + char tmp[strlen + 1]; + memcpy(tmp, chunk + strbeg, strlen); + tmp[strlen] = 0; + int nump = atoi(tmp); + if (nump > QUBITS_MAX) { - qansel_cnot(&stateVector, qubitCount, instr[i].q0, instr[i].q1); + fprintf(stderr, "QAnsel on line %i: Initialized qubits cannot exceed %i.\n", line, QUBITS_MAX); + return 0; } - else if (strcmp(instr[i].n, "swap") == 0) - { - qansel_swap(&stateVector, qubitCount, instr[i].q0, instr[i].q1); - } - else if (strcmp(instr[i].n, "if_all") == 0) - { - unsigned char val = 0; - for (int32_t j = bitCount - 1; j >= 0; j--) - { - val = (val << 1) | bitVector[j]; - } - if (val != instr[i].q0) i++; - } - else if (strcmp(instr[i].n, "if") == 0) - { - if (bitVector[instr[i].q0] != instr[i].q1) i++; - } - else if (strcmp(instr[i].n, "printq_all") == 0) - { - printf("[ "); cpx_mtx_print(&stateVector); printf(" ]\n"); - } - else if (strcmp(instr[i].n, "printc_all") == 0) + *qubitCount = nump; + } + else if (index == 1) //creg + { + if (*bitCount > 0) { - for (int32_t j = bitCount - 1; j >= 0; j--) - { - putchar('0' + bitVector[j]); - } - putchar('\n'); + fprintf(stderr, "QAnsel on line %i: Classical bits can only be initialized once.\n", line); + return 0; } - else if (strcmp(instr[i].n, "printc") == 0) + int strbeg = regmatches[1].rm_so; + int strlen = regmatches[1].rm_eo - regmatches[1].rm_so; + char tmp[strlen + 1]; + memcpy(tmp, chunk + strbeg, strlen); + tmp[strlen] = 0; + int nump = atoi(tmp); + if (nump > QUBITS_MAX) { - putchar('0' + bitVector[instr[i].q0]); - putchar('\n'); + fprintf(stderr, "QAnsel on line %i: Initialized classical bits cannot exceed %i.\n", line, QUBITS_MAX); + return 0; } - else if (strcmp(instr[i].n, "printq") == 0 || strcmp(instr[i].n, "density") == 0) - { - cpx_mtx_t tmp; - cpx_mtx_init(&tmp, 1, 2); - for (unsigned int j = 0; j < qubitCountPow2; j++) - { - if ((j >> instr[i].q0) & 1) - { - cpx_t a, b; - cpx_mtx_get(&tmp, 0, 1, &a); - cpx_mtx_get(&stateVector, 0, j, &b); - a.real += b.real; - a.imaginary += b.imaginary; - cpx_mtx_set(&tmp, 0, 1, &a); - } - else + *bitCount = nump; + } + else if (index == 2 || index == 3 || index == 4 || index == 5) //single qubit instructions + { + if (*qubitCount == 0) + { + fprintf(stderr, "QAnsel on line %i: Quantum bit instruction used prior to initialization.\n", line); + return 0; + } + int rmp = 1; + { + int strbeg = regmatches[rmp].rm_so; + int strlen = regmatches[rmp].rm_eo - regmatches[rmp].rm_so; + rmp++; + char tmp[strlen + 1]; + memcpy(tmp, chunk + strbeg, strlen); + tmp[strlen] = 0; + if (strcmp(tmp, "rx") == 0) instr = QANSEL_INSTRUCTION_RX; + else if (strcmp(tmp, "ry") == 0) instr = QANSEL_INSTRUCTION_RY; + else if (strcmp(tmp, "rz") == 0) instr = QANSEL_INSTRUCTION_RZ; + else if (strcmp(tmp, "u1") == 0) instr = QANSEL_INSTRUCTION_U1; + else if (strcmp(tmp, "u2") == 0) instr = QANSEL_INSTRUCTION_U2; + else if (strcmp(tmp, "u3") == 0) instr = QANSEL_INSTRUCTION_U3; + else if (strcmp(tmp, "u") == 0) instr = QANSEL_INSTRUCTION_U3; + else if (strcmp(tmp, "reset") == 0) instr = QANSEL_INSTRUCTION_RESET; + else if (strcmp(tmp, "barrier") == 0) instr = QANSEL_INSTRUCTION_BARRIER; + else if (strcmp(tmp, "born") == 0) instr = QANSEL_INSTRUCTION_BORN; + else if (strcmp(tmp, "density") == 0) instr = QANSEL_INSTRUCTION_DENSITY; + else if (strcmp(tmp, "print") == 0) instr = QANSEL_INSTRUCTION_PRINT; + else if (strcmp(tmp, "not") == 0) instr = QANSEL_INSTRUCTION_X; + else if (strcmp(tmp, "x") == 0) instr = QANSEL_INSTRUCTION_X; + else if (strcmp(tmp, "y") == 0) instr = QANSEL_INSTRUCTION_Y; + else if (strcmp(tmp, "z") == 0) instr = QANSEL_INSTRUCTION_Z; + else if (strcmp(tmp, "h") == 0) instr = QANSEL_INSTRUCTION_H; + else if (strcmp(tmp, "s") == 0) instr = QANSEL_INSTRUCTION_S; + else if (strcmp(tmp, "t") == 0) instr = QANSEL_INSTRUCTION_T; + } + for (int i = 0; i < (index == 2 ? 0 : (index == 3 ? 1 : (index == 4 ? 2 : 3))); i++) + { + int strbeg = regmatches[rmp].rm_so; + int strlen = regmatches[rmp].rm_eo - regmatches[rmp].rm_so; + rmp++; + char tmp[strlen + 1]; + memcpy(tmp, chunk + strbeg, strlen); + tmp[strlen] = 0; + if (!qansel_parse_float(tmp, (i == 0 ? &d0 : (i == 1 ? &d1 : &d2)))) + { + fprintf(stderr, "QAnsel on line %i: Invalid rotation value.\n", line); + return 0; + } + } + { + int strbeg = regmatches[rmp].rm_so; + int strlen = regmatches[rmp].rm_eo - regmatches[rmp].rm_so; + rmp++; + if (strlen > 0) + { + strbeg = regmatches[rmp].rm_so; + strlen = regmatches[rmp].rm_eo - regmatches[rmp].rm_so; + rmp++; + char tmp[strlen + 1]; + memcpy(tmp, chunk + strbeg, strlen); + tmp[strlen] = 0; + a0 = atoi(tmp); + if (a0 >= *qubitCount) { - cpx_t a, b; - cpx_mtx_get(&tmp, 0, 0, &a); - cpx_mtx_get(&stateVector, 0, j, &b); - a.real += b.real; - a.imaginary += b.imaginary; - cpx_mtx_set(&tmp, 0, 0, &a); + fprintf(stderr, "QAnsel on line %i: Index `%i` exceeds allocated quantum bits.\n", line, a0); + return 0; } } - float multiplier = 0; - cpx_t n; - cpx_mtx_get(&tmp, 0, 0, &n); - multiplier += cpx_magsqr(&n); - cpx_mtx_get(&tmp, 0, 1, &n); - multiplier += cpx_magsqr(&n); - multiplier = sqrt(1 / multiplier); - n.real *= multiplier; - n.imaginary *= multiplier; - cpx_mtx_set(&tmp, 0, 1, &n); - cpx_mtx_get(&tmp, 0, 0, &n); - n.real *= multiplier; - n.imaginary *= multiplier; - cpx_mtx_set(&tmp, 0, 0, &n); - - if (strcmp(instr[i].n, "density") == 0) - { - cpx_t a, b, c, d, x, y, z, w; - cpx_mtx_get(&tmp, 0, 0, &a); - cpx_mtx_get(&tmp, 0, 1, &b); - cpx_mtx_get(&tmp, 0, 0, &c); - cpx_mtx_get(&tmp, 0, 1, &d); - c.imaginary *= -1; - d.imaginary *= -1; - cpx_mul(&x, &a, &c); - cpx_mul(&y, &a, &d); - cpx_mul(&z, &b, &c); - cpx_mul(&w, &b, &d); - char* sx = cpx_str(&x); - char* sy = cpx_str(&y); - char* sz = cpx_str(&z); - char* sw = cpx_str(&w); - printf("[ %s, %s ]\n", sx, sy); - printf("[ %s, %s ]\n", sz, sw); - free(sx); - free(sy); - free(sz); - free(sw); - } else { - printf("[ "); cpx_mtx_print(&tmp); printf(" ]\n"); - } - cpx_mtx_free(&tmp); - } - else if (strcmp(instr[i].n, "born_all") == 0) - { - for (unsigned int j = 0; j < qubitCountPow2; j++) - { - unsigned int tmp = j; - for (unsigned char k = 0; k < qubitCount; k++) + a0 = 0x0F; + if (instr == QANSEL_INSTRUCTION_DENSITY) { - putchar('0' + (tmp >> (qubitCount - 1) & 1)); - tmp <<= 1; + fprintf(stderr, "QAnsel on line %i: Density matrices can only be produced for individual qubits.\n", line, a0); + return 0; } - cpx_t n; - cpx_mtx_get(&stateVector, 0, j, &n); - printf(": %.1f%%\n", cpx_magsqr(&n) * 100); - } - } - else if (strcmp(instr[i].n, "born") == 0) - { - float prob = 0; - for (unsigned int j = 0; j < qubitCountPow2; j++) - { - cpx_t n; - cpx_mtx_get(&stateVector, 0, j, &n); - if ((j >> instr[i].q0) & 1) - { - prob += cpx_magsqr(&n); - } - } - printf("0: %.1f%%\n", (1 - prob) * 100.0); - printf("1: %.1f%%\n", prob * 100.0); - } - else if (strcmp(instr[i].n, "hvar") == 0) - { - HIDDEN_VARIABLE = 1; - float tmp1 = (float)instr[i].arg0; - unsigned int tmp2; - memcpy(&tmp2, &tmp1, sizeof(unsigned int)); - srand(tmp2); - } - else if (strcmp(instr[i].n, "rand") == 0) - { - HIDDEN_VARIABLE = 0; - } - else if (strcmp(instr[i].n, "reset_all") == 0) - { - cpx_mtx_set2(&stateVector, 0, 0, 1, 0); - for (unsigned int j = 1; j < qubitCountPow2; j++) - { - cpx_mtx_set2(&stateVector, 0, j, 0, 0); - } - for (unsigned char j = 0; j < bitCount; j++) - { - bitVector[j] = 0; } } - else if (strcmp(instr[i].n, "resetq") == 0) + switch (index) { - unsigned char bit = qansel_measure(&stateVector, qubitCount, instr[i].q0); - if (bit) - { - instr[i].n[0] = 'x'; - instr[i].n[1] = 0; - qansel_instruction(&stateVector, qubitCount, instr + i); - } - } - else if (strcmp(instr[i].n, "resetc") == 0) - { - bitVector[instr[i].q0] = 0; - } - else - { - qansel_instruction(&stateVector, qubitCount, instr + i); - } - - if (gfx) display(&stateVector, qubitCount); - } - display(NULL, -1); - - cpx_mtx_free(&stateVector); - - if (retBitVect != NULL) - { - for (unsigned int i = 0; i < bitCount; i++) - { - retBitVect[i] = bitVector[i]; + case 2: + *binarySize += 2 + sizeof(float) * 0; + binary = realloc(binary, *binarySize); + binary[*binarySize - 2] = instr; + binary[*binarySize - 1] = a0; + break; + case 3: + *binarySize += 2 + sizeof(float) * 1; + binary = realloc(binary, *binarySize); + binary[*binarySize - 2 - sizeof(float) * 1] = instr; + binary[*binarySize - 1 - sizeof(float) * 1] = a0; + memcpy(binary + (*binarySize - sizeof(float)), &d0, sizeof(float)); + break; + case 4: + *binarySize += 2 + sizeof(float) * 2; + binary = realloc(binary, *binarySize); + binary[*binarySize - 2 - sizeof(float) * 2] = instr; + binary[*binarySize - 1 - sizeof(float) * 2] = a0; + memcpy(binary + (*binarySize - sizeof(float) * 2), &d0, sizeof(float)); + memcpy(binary + (*binarySize - sizeof(float) * 1), &d1, sizeof(float)); + break; + case 5: + *binarySize += 2 + sizeof(float) * 3; + binary = realloc(binary, *binarySize); + binary[*binarySize - 2 - sizeof(float) * 3] = instr; + binary[*binarySize - 1 - sizeof(float) * 3] = a0; + memcpy(binary + (*binarySize - sizeof(float) * 3), &d0, sizeof(float)); + memcpy(binary + (*binarySize - sizeof(float) * 2), &d1, sizeof(float)); + memcpy(binary + (*binarySize - sizeof(float) * 1), &d2, sizeof(float)); + break; } } - -} - -void process(int argc, char** argv) -{ - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - float seed = (float)((unsigned long)ts.tv_sec * 1000000000LL + ts.tv_nsec); - qansel_rand_s(seed); - char** lines = malloc(0); - unsigned int* lineIDs = malloc(0); - char* text = malloc(0); - unsigned int textLen = 0; - unsigned int linesLen = 0; - int c; - int pc = -1; - unsigned char comment = 0; - unsigned char commentM = 0; - unsigned int lineID = 1; - unsigned char skipSpaces = 1; - unsigned char inGate = 0; - unsigned char fullSample = 0; - while ( (c = getchar()) != EOF ) + else if (index == 6) //measure instruction { - if (c == '/' && commentM == 0 && comment == 0) - { - commentM = 1; - } - else if (c == '/' && commentM == 1 && comment == 0) - { - comment = 1; - commentM = 0; - } - else if (c == '\n') + if (*qubitCount == 0) { - comment = 0; - commentM = 0; - lineID += 1; + fprintf(stderr, "QAnsel on line %i: Quantum bit instruction used prior to initialization.\n", line); + return 0; } - else if (comment || (c == ' ' && skipSpaces)) {} - else if (c != '\n' && c != '\t' && c != ';' && (c != ')' || inGate)) + if (*bitCount == 0) { - if (commentM == 1) - { - text = realloc(text, textLen + 1); - text[textLen++] = '/'; - commentM = 0; - } - skipSpaces = 0; - if (c >= 'A' && c <= 'Z') c += 'a' - 'A'; - if (c == 'u') inGate = 1; - if (c == 'r') inGate = 1; - text = realloc(text, textLen + 1); - text[textLen++] = c; - pc = c; + fprintf(stderr, "QAnsel on line %i: Classical bit instruction used prior to initialization.\n", line); + return 0; } - else if (c == ';' || (c == ')' && !inGate)) + int rmp = 1; { - inGate = 0; - skipSpaces = 1; - if (c == ')') + int strbeg = regmatches[rmp].rm_so; + int strlen = regmatches[rmp].rm_eo - regmatches[rmp].rm_so; + rmp++; + if (strlen > 0) { - text = realloc(text, textLen + 1); - text[textLen++] = ')'; - } - text = realloc(text, textLen + 1); - text[textLen++] = 0; - lineIDs = realloc(lineIDs, (linesLen + 1) * sizeof(unsigned int)); - lineIDs[linesLen] = lineID; - lines = realloc(lines, (linesLen + 1) * sizeof(char*)); - lines[linesLen] = malloc(strlen(text) + 1); - strcpy(lines[linesLen++], text); - text = realloc(text, 0); - textLen = 0; - pc = ';'; - } - } - text = realloc(text, textLen + 1); - text[textLen++] = 0; - if (strlen(text) > 0) - { - free(text); - fprintf(stderr, "QAnsel: Invalid trailing text"); - exit(1); - } - free(text); - - unsigned char qubitCount = 0xFF; - unsigned char bitCount = 0xFF; - QInstr* instr = malloc(0); - unsigned int instrLen = 0; - unsigned char doDisplay = 0; - unsigned char errFound = 0; - for (unsigned int i = 0; i < linesLen; i++) - { - lineID = i; - - char g; - int q0, q1, q2; - float a0, a1, a2; - unsigned int matchedCount; - unsigned int lineLen = strlen(lines[i]); - if (lineLen > 1) - { - while (lineLen > 1 && (lines[i][lineLen - 1] == ' ' || lines[i][lineLen - 1] == '\t')) - { - lines[i][lineLen - 1] = 0; - lineLen--; - } - } - - if (sscanf(lines[i], "qreg q[%i]%n", &q0, &matchedCount) == 1) - { - if (qubitCount == 0xFF) - { - qubitCount = q0; - if (qubitCount < 1 || qubitCount > QUBITS_MAX) + strbeg = regmatches[rmp].rm_so; + strlen = regmatches[rmp].rm_eo - regmatches[rmp].rm_so; + rmp++; + char tmp[strlen + 1]; + memcpy(tmp, chunk + strbeg, strlen); + tmp[strlen] = 0; + a0 = atoi(tmp); + if (a0 >= *qubitCount) { - fprintf(stderr, "QAnsel: Invalid count"); - errFound = 1; - break; + fprintf(stderr, "QAnsel on line %i: Index `%i` exceeds allocated quantum bits.\n", line, a0); + return 0; } } else { - fprintf(stderr, "QAnsel: Repeated initialization"); - errFound = 1; - break; + a0 = 0x0F; } } - else if (sscanf(lines[i], "creg c[%i]%n", &q0, &matchedCount) == 1) { - if (bitCount == 0xFF) + int strbeg = regmatches[rmp].rm_so; + int strlen = regmatches[rmp].rm_eo - regmatches[rmp].rm_so; + rmp++; + if (strlen > 0) { - bitCount = q0; - if (bitCount < 1 || bitCount > QUBITS_MAX) + strbeg = regmatches[rmp].rm_so; + strlen = regmatches[rmp].rm_eo - regmatches[rmp].rm_so; + rmp++; + char tmp[strlen + 1]; + memcpy(tmp, chunk + strbeg, strlen); + tmp[strlen] = 0; + a1 = atoi(tmp); + if (a1 >= *bitCount) { - fprintf(stderr, "QAnsel: Invalid count"); - errFound = 1; - break; + fprintf(stderr, "QAnsel on line %i: Index `%i` exceeds allocated classical bits.\n", line, a1); + return 0; } + a1 += 0x10; } else { - fprintf(stderr, "QAnsel: Repeated initialization"); - errFound = 1; - break; - } - } - else if (sscanf(lines[i], "u(%f,%f,%f) q[%i]%n", &a0, &a1, &a2, &q0, &matchedCount) == 4) - { - if (qubitCount == 0xFF) - { - fprintf(stderr, "QAnsel: Instruction before initialization"); - errFound = 1; - break; + a1 = 0x1F; } - if (q0 < 0 || q0 >= qubitCount) - { - fprintf(stderr, "QAnsel: Invalid index"); - errFound = 1; - break; - } - instr = realloc(instr, (instrLen + 1) * sizeof(QInstr)); - instr[instrLen].n[0] = 'u'; - instr[instrLen].n[1] = 0; - instr[instrLen].q0 = q0; - instr[instrLen].arg0 = a0; - instr[instrLen].arg1 = a1; - instr[instrLen++].arg2 = a2; } - else if - ( - memcmp("rx(", lines[i], 3) == 0 - || memcmp("ry(", lines[i], 3) == 0 - || memcmp("rz(", lines[i], 3) == 0 - ) - { - float angle; - char ty; - if (sscanf(lines[i], "r%c(%f/%f) q[%i]%n", &ty, &a0, &a1, &q0, &matchedCount) == 4) - { - angle = a0 / a1; - } - else if (sscanf(lines[i], "r%c(%f/%fpi) q[%i]%n", &ty, &a0, &a1, &q0, &matchedCount) == 4) - { - angle = a0 / (a1 * M_PI); - } - else if (sscanf(lines[i], "r%c(%f/pi) q[%i]%n", &ty, &a0, &q0, &matchedCount) == 3) - { - angle = a0 / M_PI; - } - else if (sscanf(lines[i], "r%c(%f/-pi) q[%i]%n", &ty, &a0, &q0, &matchedCount) == 3) - { - angle = a0 / -M_PI; - } - else if (sscanf(lines[i], "r%c(%fpi/%f) q[%i]%n", &ty, &a0, &a1, &q0, &matchedCount) == 4) - { - angle = (a0 * M_PI) / a1; - } - else if (sscanf(lines[i], "r%c(pi/%f) q[%i]%n", &ty, &a0, &q0, &matchedCount) == 3) - { - angle = M_PI / a0; - } - else if (sscanf(lines[i], "r%c(-pi/%f) q[%i]%n", &ty, &a0, &q0, &matchedCount) == 3) - { - angle = -M_PI / a0; - } - else if (sscanf(lines[i], "r%c(%fpi/%fpi) q[%i]%n", &ty, &a0, &a1, &q0, &matchedCount) == 4) - { - angle = (a0 * M_PI) / (a1 * M_PI); - } - else if (sscanf(lines[i], "r%c(pi/pi) q[%i]%n", &ty, &q0, &matchedCount) == 2) - { - angle = 1; - } - else if (sscanf(lines[i], "r%c(-pi/pi) q[%i]%n", &ty, &q0, &matchedCount) == 2) - { - angle = -1; - } - else if (sscanf(lines[i], "r%c(pi/-pi) q[%i]%n", &ty, &q0, &matchedCount) == 2) - { - angle = -1; - } - else if (sscanf(lines[i], "r%c(-pi/-pi) q[%i]%n", &ty, &q0, &matchedCount) == 2) - { - angle = 1; - } - else if (sscanf(lines[i], "r%c(%fpi) q[%i]%n", &ty, &a0, &q0, &matchedCount) == 3) - { - angle = a0 * M_PI; - } - else if (sscanf(lines[i], "r%c(pi) q[%i]%n", &ty, &q0, &matchedCount) == 2) - { - angle = M_PI; - } - else if (sscanf(lines[i], "r%c(-pi) q[%i]%n", &ty, &q0, &matchedCount) == 2) - { - angle = -M_PI; - } - else if (sscanf(lines[i], "r%c(%f) q[%i]%n", &ty, &a0, &q0, &matchedCount) == 3) - { - angle = a0; + *binarySize += 3; + binary = realloc(binary, *binarySize); + binary[*binarySize - 3] = QANSEL_INSTRUCTION_MEASURE; + binary[*binarySize - 2] = a0; + binary[*binarySize - 1] = a1; + } + else if (index == 7) //classical bit instructions + { + if (*bitCount == 0) + { + fprintf(stderr, "QAnsel on line %i: Classical bit instruction used prior to initialization.\n", line); + return 0; + } + int rmp = 1; + { + int strbeg = regmatches[rmp].rm_so; + int strlen = regmatches[rmp].rm_eo - regmatches[rmp].rm_so; + rmp++; + char tmp[strlen + 1]; + memcpy(tmp, chunk + strbeg, strlen); + tmp[strlen] = 0; + if (strcmp(tmp, "sample") == 0) instr = QANSEL_INSTRUCTION_SAMPLE; + else if (strcmp(tmp, "reset") == 0) instr = QANSEL_INSTRUCTION_RESET; + else if (strcmp(tmp, "print") == 0) instr = QANSEL_INSTRUCTION_PRINT; + } + { + int strbeg = regmatches[rmp].rm_so; + int strlen = regmatches[rmp].rm_eo - regmatches[rmp].rm_so; + rmp++; + if (strlen > 0) + { + strbeg = regmatches[rmp].rm_so; + strlen = regmatches[rmp].rm_eo - regmatches[rmp].rm_so; + rmp++; + char tmp[strlen + 1]; + memcpy(tmp, chunk + strbeg, strlen); + tmp[strlen] = 0; + a0 = atoi(tmp); + if (a0 >= *bitCount) + { + fprintf(stderr, "QAnsel on line %i: Index `%i` exceeds allocated classical bits.\n", line, a0); + return 0; + } + a0 += 0x10; } else { - fprintf(stderr, "QAnsel: Syntax error"); - errFound = 1; - break; - } - if (qubitCount == 0xFF) - { - fprintf(stderr, "QAnsel: Instruction before initialization"); - errFound = 1; - break; - } - if (q0 < 0 || q0 >= qubitCount) - { - fprintf(stderr, "QAnsel: Invalid index"); - errFound = 1; - break; - } - - instr = realloc(instr, (instrLen + 1) * sizeof(QInstr)); - instr[instrLen].n[0] = 'u'; - instr[instrLen].n[1] = 0; - instr[instrLen].q0 = q0; - switch (ty) - { - case 'x': - instr[instrLen].arg0 = M_PI / 2; - instr[instrLen].arg1 = -M_PI / 2; - instr[instrLen].arg2 = angle - (M_PI / 2); - break; - case 'y': - instr[instrLen].arg0 = angle; - instr[instrLen].arg1 = 0; - instr[instrLen].arg2 = 0; - break; - case 'z': - instr[instrLen].arg0 = 0; - instr[instrLen].arg1 = 0; - instr[instrLen].arg2 = angle; - break; - } - instrLen++; - } - else if (sscanf(lines[i], "density q[%i]%n", &q0, &matchedCount) == 1) - { - g = lines[i][0]; - if (qubitCount == 0xFF) - { - fprintf(stderr, "QAnsel: Instruction before initialization"); - errFound = 1; - break; - } - if (q0 < 0 || q0 >= qubitCount) - { - fprintf(stderr, "QAnsel: Invalid index"); - errFound = 1; - break; - } - instr = realloc(instr, (instrLen + 1) * sizeof(QInstr)); - strcpy(instr[instrLen].n, "density"); - instr[instrLen++].q0 = (unsigned char)q0; - } - else if - ( - sscanf(lines[i], "h q[%i]%n", &q0, &matchedCount) == 1 - || sscanf(lines[i], "x q[%i]%n", &q0, &matchedCount) == 1 - || sscanf(lines[i], "y q[%i]%n", &q0, &matchedCount) == 1 - || sscanf(lines[i], "z q[%i]%n", &q0, &matchedCount) == 1 - || sscanf(lines[i], "t q[%i]%n", &q0, &matchedCount) == 1 - || sscanf(lines[i], "s q[%i]%n", &q0, &matchedCount) == 1 - ) - { - g = lines[i][0]; - if (qubitCount == 0xFF) - { - fprintf(stderr, "QAnsel: Instruction before initialization"); - errFound = 1; - break; - } - if (q0 < 0 || q0 >= qubitCount) - { - fprintf(stderr, "QAnsel: Invalid index"); - errFound = 1; - break; - } - instr = realloc(instr, (instrLen + 1) * sizeof(QInstr)); - instr[instrLen].n[0] = g; - instr[instrLen].n[1] = 0; - instr[instrLen++].q0 = (unsigned char)q0; - } - else if (sscanf(lines[i], "cx q[%i], q[%i]%n", &q0, &q1, &matchedCount) == 2) - { - if (qubitCount == 0xFF) - { - fprintf(stderr, "QAnsel: Instruction before initialization"); - errFound = 1; - break; - } - if (q0 >= qubitCount || q1 >= qubitCount | q0 < 0 || q1 < 0 || q0 == q1) - { - fprintf(stderr, "QAnsel: Invalid index"); - errFound = 1; - break; - } - instr = realloc(instr, (instrLen + 1) * sizeof(QInstr)); - strcpy(instr[instrLen].n, "cx"); - instr[instrLen].q0 = q0; - instr[instrLen++].q1 = q1; - } - else if (sscanf(lines[i], "swap q[%i], q[%i]%n", &q0, &q1, &matchedCount) == 2) - { - if (qubitCount == 0xFF) - { - fprintf(stderr, "QAnsel: Instruction before initialization"); - errFound = 1; - break; - } - if (q0 >= qubitCount || q1 >= qubitCount | q0 < 0 || q1 < 0 || q0 == q1) - { - fprintf(stderr, "QAnsel: Invalid index"); - errFound = 1; - break; - } - instr = realloc(instr, (instrLen + 1) * sizeof(QInstr)); - strcpy(instr[instrLen].n, "swap"); - instr[instrLen].q0 = q0; - instr[instrLen++].q1 = q1; - } - else if - ( - sscanf(lines[i], "cswap q[%i], q[%i], q[%i]%n", &q0, &q1, &q2, &matchedCount) == 3 - || sscanf(lines[i], "fredkin q[%i], q[%i], q[%i]%n", &q0, &q1, &q2, &matchedCount) == 3 - ) - { - if (qubitCount == 0xFF) - { - fprintf(stderr, "QAnsel: Instruction before initialization"); - errFound = 1; - break; - } - if (qubitCount < 3) - { - fprintf(stderr, "QAnsel: Three qubit gate used with insufficient qubits initialized"); - errFound = 1; - break; - } - if (q0 >= qubitCount || q1 >= qubitCount || q2 >= qubitCount || q0 < 0 || q1< 0 || q2 < 0 || q0 == q1 || q1 == q2 || q0 == q2) - { - fprintf(stderr, "QAnsel: Invalid index"); - errFound = 1; - break; + a0 = 0x1F; } - instr = realloc(instr, (instrLen + 1) * sizeof(QInstr)); - strcpy(instr[instrLen].n, "cswap"); - instr[instrLen].q0 = q0; - instr[instrLen].q1 = q1; - instr[instrLen++].q2 = q2; } - else if - ( - sscanf(lines[i], "ccx q[%i], q[%i], q[%i]%n", &q0, &q1, &q2, &matchedCount) == 3 - || sscanf(lines[i], "toffoli q[%i], q[%i], q[%i]%n", &q0, &q1, &q2, &matchedCount) == 3 - ) - { - if (qubitCount == 0xFF) - { - fprintf(stderr, "QAnsel: Instruction before initialization"); - errFound = 1; - break; - } - if (qubitCount < 3) - { - fprintf(stderr, "QAnsel: Three qubit gate used with insufficient qubits initialized"); - errFound = 1; - break; - } - if (q0 >= qubitCount || q1 >= qubitCount || q2 >= qubitCount || q0 < 0 || q1< 0 || q2 < 0 || q0 == q1 || q1 == q2 || q0 == q2) - { - fprintf(stderr, "QAnsel: Invalid index"); - errFound = 1; - break; - } - instr = realloc(instr, (instrLen + 1) * sizeof(QInstr)); - strcpy(instr[instrLen].n, "ccx"); - instr[instrLen].q0 = q0; - instr[instrLen].q1 = q1; - instr[instrLen++].q2 = q2; - } - else if (sscanf(lines[i], "measure q[%i] -> c[%i]%n", &q0, &q1, &matchedCount) == 2) - { - if (bitCount == 0xFF) - { - fprintf(stderr, "QAnsel: Measure instruction used before bit initialization"); - errFound = 1; - break; - } - if (q0 >= qubitCount || q1 >= bitCount || q0 < 0 || q1 < 0) - { - fprintf(stderr, "QAnsel: Invalid index"); - errFound = 1; - break; - } - instr = realloc(instr, (instrLen + 1) * sizeof(QInstr)); - strcpy(instr[instrLen].n, "measure"); - instr[instrLen].q0 = q0; - instr[instrLen++].q1 = q1; - } - else if (sscanf(lines[i], "if(c==%i)%n", &q0, &matchedCount) == 1) - { - if (bitCount == 0xFF) - { - fprintf(stderr, "QAnsel: If instruction used before bit initialization"); - errFound = 1; - break; - } - if (q0 < 0) - { - fprintf(stderr, "QAnsel: Invalid comparison"); - errFound = 1; - break; - } - instr = realloc(instr, (instrLen + 1) * sizeof(QInstr)); - strcpy(instr[instrLen].n, "if_all"); - instr[instrLen++].q0 = q0; - } - else if (sscanf(lines[i], "if(c[%i]==%i)%n", &q0, &q1, &matchedCount) == 2) - { - if (bitCount == 0xFF) - { - fprintf(stderr, "QAnsel: If instruction used before bit initialization"); - errFound = 1; - break; - } - if (q0 < 0 || q1 < 0 || q0 > bitCount) - { - fprintf(stderr, "QAnsel: Invalid comparison"); - errFound = 1; - break; - } - instr = realloc(instr, (instrLen + 1) * sizeof(QInstr)); - strcpy(instr[instrLen].n, "if"); - instr[instrLen].q0 = q0; - instr[instrLen++].q1 = q1; - } - /*else if (strcmp(lines[i], "display") == 0) - { - if (doDisplay) - { - fprintf(stderr, "QAnsel: Display re-initialized"); - errFound = 1; - break; - } - doDisplay = 1; - }*/ - else if (strcmp(lines[i], "print q") == 0) - { - matchedCount = lineLen; - if (qubitCount == 0xFF) - { - fprintf(stderr, "QAnsel: Qubit instruction used before initialization"); - errFound = 1; - break; - } - instr = realloc(instr, (instrLen + 1) * sizeof(QInstr)); - strcpy(instr[instrLen].n, "printq_all"); - instrLen++; - } - else if (strcmp(lines[i], "print c") == 0) - { - matchedCount = lineLen; - if (bitCount == 0xFF) - { - fprintf(stderr, "QAnsel: Bit instruction used before initialization"); - errFound = 1; - break; - } - instr = realloc(instr, (instrLen + 1) * sizeof(QInstr)); - strcpy(instr[instrLen].n, "printc_all"); - instrLen++; - } - else if (sscanf(lines[i], "print q[%i]%n", &q0, &matchedCount) == 1) - { - if (qubitCount == 0xFF) - { - fprintf(stderr, "QAnsel: Qubit instruction used before initialization"); - errFound = 1; - break; - } - if (q0 >= qubitCount || q0 < 0) - { - fprintf(stderr, "QAnsel: Invalid index"); - errFound = 1; - break; - } - instr = realloc(instr, (instrLen + 1) * sizeof(QInstr)); - strcpy(instr[instrLen].n, "printq"); - instr[instrLen++].q0 = q0; - } - else if (sscanf(lines[i], "print c[%i]%n", &q0, &matchedCount) == 1) - { - if (bitCount == 0xFF) - { - fprintf(stderr, "QAnsel: Bit instruction used before initialization"); - errFound = 1; - break; - } - if (q0 >= bitCount || q0 < 0) - { - fprintf(stderr, "QAnsel: Invalid index"); - errFound = 1; - break; - } - instr = realloc(instr, (instrLen + 1) * sizeof(QInstr)); - strcpy(instr[instrLen].n, "printc"); - instr[instrLen++].q0 = q0; - } - else if (strcmp(lines[i], "born") == 0 || strcmp(lines[i], "born q") == 0) - { - matchedCount = lineLen; - if (qubitCount == 0xFF) - { - fprintf(stderr, "QAnsel: Qubit instruction used before initialization"); - errFound = 1; - break; - } - instr = realloc(instr, (instrLen + 1) * sizeof(QInstr)); - strcpy(instr[instrLen].n, "born_all"); - instrLen++; - } - else if (sscanf(lines[i], "born q[%i]%n", &q0, &matchedCount) == 1) - { - if (qubitCount == 0xFF) - { - fprintf(stderr, "QAnsel: Qubit instruction used before initialization"); - errFound = 1; - break; - } - if (q0 >= qubitCount || q0 < 0) - { - fprintf(stderr, "QAnsel: Invalid index"); - errFound = 1; - break; - } - instr = realloc(instr, (instrLen + 1) * sizeof(QInstr)); - strcpy(instr[instrLen].n, "born"); - instr[instrLen++].q0 = q0; - } - else if (sscanf(lines[i], "reset q[%i]%n", &q0, &matchedCount) == 1) - { - if (qubitCount == 0xFF) - { - fprintf(stderr, "QAnsel: Qubit instruction used before initialization"); - errFound = 1; - break; - } - if (q0 >= qubitCount || q0 < 0) - { - fprintf(stderr, "QAnsel: Invalid index"); - errFound = 1; - break; - } - instr = realloc(instr, (instrLen + 1) * sizeof(QInstr)); - strcpy(instr[instrLen].n, "resetq"); - instr[instrLen++].q0 = q0; - } - else if (sscanf(lines[i], "reset c[%i]%n", &q0, &matchedCount) == 1) - { - if (bitCount == 0xFF) - { - fprintf(stderr, "QAnsel: Bit instruction used before initialization"); - errFound = 1; - break; - } - if (q0 >= bitCount || q0 < 0) - { - fprintf(stderr, "QAnsel: Invalid index"); - errFound = 1; - break; - } - instr = realloc(instr, (instrLen + 1) * sizeof(QInstr)); - strcpy(instr[instrLen].n, "resetc"); - instr[instrLen++].q0 = q0; - } - else if (strcmp(lines[i], "reset") == 0) + *binarySize += 2; + binary = realloc(binary, *binarySize); + binary[*binarySize - 2] = instr; + binary[*binarySize - 1] = a0; + } + else if (index == 8 || index == 9) //double qubit instructions + { + if (*qubitCount == 0) { - matchedCount = lineLen; - instr = realloc(instr, (instrLen + 1) * sizeof(QInstr)); - strcpy(instr[instrLen].n, "reset_all"); - instrLen++; + fprintf(stderr, "QAnsel on line %i: Quantum bit instruction used prior to initialization.\n", line); + return 0; } - else if (strcmp(lines[i], "barrier q") == 0) + int rmp = 1; { - matchedCount = lineLen; - if (qubitCount == 0xFF) - { - fprintf(stderr, "QAnsel: Qubit instruction used before initialization"); - errFound = 1; - break; - } - //do nothing as there are currently no - // optimizations that this instruction - // would prevent + int strbeg = regmatches[rmp].rm_so; + int strlen = regmatches[rmp].rm_eo - regmatches[rmp].rm_so; + rmp++; + char tmp[strlen + 1]; + memcpy(tmp, chunk + strbeg, strlen); + tmp[strlen] = 0; + if (strcmp(tmp, "cx") == 0) instr = QANSEL_INSTRUCTION_CX; + if (strcmp(tmp, "cnot") == 0) instr = QANSEL_INSTRUCTION_CCX; + else if (strcmp(tmp, "toffoli") == 0) instr = QANSEL_INSTRUCTION_CCX; + else if (strcmp(tmp, "cswap") == 0) instr = QANSEL_INSTRUCTION_CSWAP; + else if (strcmp(tmp, "fredkin") == 0) instr = QANSEL_INSTRUCTION_CSWAP; } - else if (sscanf(lines[i], "barrier q[%i]%n", &q0, &matchedCount) == 1) + for (int i = 0; i < (index == 8 ? 2 : 3); i++) { - if (qubitCount == 0xFF || bitCount == 0xFF) + int strbeg = regmatches[rmp].rm_so; + int strlen = regmatches[rmp].rm_eo - regmatches[rmp].rm_so; + rmp++; + char tmp[strlen + 1]; + memcpy(tmp, chunk + strbeg, strlen); + tmp[strlen] = 0; + int tmpi = atoi(tmp); + if (tmpi >= *qubitCount) { - fprintf(stderr, "QAnsel: Instruction used before initialization"); - errFound = 1; - break; + fprintf(stderr, "QAnsel on line %i: Index `%i` exceeds allocated quantum bits.\n", line, tmpi); + return 0; } - if (q0 >= qubitCount || q0 < 0) + switch (i) { - fprintf(stderr, "QAnsel: Invalid index"); - errFound = 1; - break; + case 0: a0 = tmpi; break; + case 1: a1 = tmpi; break; + case 2: a2 = tmpi; break; } - //do nothing as there are currently no - // optimizations that this instruction - // would prevent - } - else if (sscanf(lines[i], "hvar %f%n", &a0, &matchedCount) == 1) - { - instr = realloc(instr, (instrLen + 1) * sizeof(QInstr)); - strcpy(instr[instrLen].n, "hvar"); - instr[instrLen++].arg0 = a0; - } - else if (strcmp(lines[i], "rand") == 0) - { - matchedCount = lineLen; - instr = realloc(instr, (instrLen + 1) * sizeof(QInstr)); - strcpy(instr[instrLen].n, "rand"); - instrLen++; } - else if (strcmp(lines[i], "sample") == 0 || strcmp(lines[i], "sample c") == 0) + if (index == 8) { - matchedCount = lineLen; - if (i != linesLen - 1) - { - fprintf(stderr, "QAnsel: Sampling should be performed at the end of the program;"); - errFound = 1; - break; - } - if (bitCount == 0xFF) - { - fprintf(stderr, "QAnsel: Sampling cannot be used without initializing classical bits; "); - errFound = 1; - break; - } - fullSample = 255; - } - else if (sscanf(lines[i], "sample c[%i]%n", &q0, &matchedCount) == 1) - { - if (i != linesLen - 1) - { - fprintf(stderr, "QAnsel: Sampling should be performed at the end of the program;"); - errFound = 1; - break; - } - if (bitCount == 0xFF) - { - fprintf(stderr, "QAnsel: Sampling cannot be used without initializing classical bits; "); - errFound = 1; - break; - } - fullSample = q0 + 1; + *binarySize += 3; + binary = realloc(binary, *binarySize); + binary[*binarySize - 3] = instr; + binary[*binarySize - 2] = a0; + binary[*binarySize - 1] = a1; } else { - fprintf(stderr, "QAnsel: Syntax error"); - errFound = 1; - break; + *binarySize += 4; + binary = realloc(binary, *binarySize); + binary[*binarySize - 4] = instr; + binary[*binarySize - 3] = a0; + binary[*binarySize - 2] = a1; + binary[*binarySize - 1] = a2; } - if (lineLen != matchedCount) - { - fprintf(stderr, "QAnsel: Invalid trailing text"); - errFound = 1; - break; - } - } - - for (unsigned int i = 0; i < linesLen; i++) free(lines[i]); - free(lines); - - if (errFound) + else if (index == 10) //if instructions { - printf(" on line %i.\n", lineIDs[lineID]); - free(lineIDs); - free(instr); - exit(1); + if (*bitCount == 0) + { + fprintf(stderr, "QAnsel on line %i: Classical bit instruction used prior to initialization.\n", line); + return 0; + } + int rmp = 1; + { + int strbeg = regmatches[rmp].rm_so; + int strlen = regmatches[rmp].rm_eo - regmatches[rmp].rm_so; + rmp++; + if (strlen > 0) + { + strbeg = regmatches[rmp].rm_so; + strlen = regmatches[rmp].rm_eo - regmatches[rmp].rm_so; + rmp++; + char tmp[strlen + 1]; + memcpy(tmp, chunk + strbeg, strlen); + tmp[strlen] = 0; + a0 = atoi(tmp); + if (a0 >= *bitCount) + { + fprintf(stderr, "QAnsel on line %i: Index `%i` exceeds allocated classical bits.\n", line, a0); + return 0; + } + a0 += 0x10; + } + else + { + a0 = 0x1F; + } + } + { + int strbeg = regmatches[rmp].rm_so; + int strlen = regmatches[rmp].rm_eo - regmatches[rmp].rm_so; + rmp++; + char tmp[strlen + 1]; + memcpy(tmp, chunk + strbeg, strlen); + tmp[strlen] = 0; + if (strcmp(tmp, "==") == 0) instr = QANSEL_INSTRUCTION_IF_E; + else if (strcmp(tmp, "!=") == 0) instr = QANSEL_INSTRUCTION_IF_NE; + else if (strcmp(tmp, "<>") == 0) instr = QANSEL_INSTRUCTION_IF_NE; + else if (strcmp(tmp, ">=") == 0) instr = QANSEL_INSTRUCTION_IF_GE; + else if (strcmp(tmp, "<=") == 0) instr = QANSEL_INSTRUCTION_IF_LE; + else if (strcmp(tmp, ">") == 0) instr = QANSEL_INSTRUCTION_IF_G; + else if (strcmp(tmp, "<") == 0) instr = QANSEL_INSTRUCTION_IF_L; + } + { + int strbeg = regmatches[rmp].rm_so; + int strlen = regmatches[rmp].rm_eo - regmatches[rmp].rm_so; + rmp++; + char tmp[strlen + 1]; + memcpy(tmp, chunk + strbeg, strlen); + tmp[strlen] = 0; + s0 = (unsigned short)atoi(tmp); + } + *binarySize += 2 + sizeof(unsigned short); + binary = realloc(binary, *binarySize); + binary[*binarySize - 2 - sizeof(unsigned short)] = instr; + binary[*binarySize - 1 - sizeof(unsigned short)] = a0; + memcpy(binary + (*binarySize - sizeof(unsigned short)), &s0, sizeof(unsigned short)); } - - if (argc == 2) + else if (index == 11) //floating point settings { - if (strcmp(argv[1], "-d") == 0) + int rmp = 1; { - doDisplay = 1; + int strbeg = regmatches[rmp].rm_so; + int strlen = regmatches[rmp].rm_eo - regmatches[rmp].rm_so; + rmp++; + char tmp[strlen + 1]; + memcpy(tmp, chunk + strbeg, strlen); + tmp[strlen] = 0; + if (strcmp(tmp, "hvar") == 0) instr = QANSEL_INSTRUCTION_HVAR; } - } - else if (argc == 3) - { - if (strcmp(argv[1], "-h") == 0) { - seed = atof(argv[2]); - qansel_rand_s(seed); + int strbeg = regmatches[rmp].rm_so; + int strlen = regmatches[rmp].rm_eo - regmatches[rmp].rm_so; + rmp++; + char tmp[strlen + 1]; + memcpy(tmp, chunk + strbeg, strlen); + tmp[strlen] = 0; + if (!qansel_parse_float(tmp, &d0)) + { + fprintf(stderr, "QAnsel on line %i: Syntax error.\n", line); + return 0; + } } + *binarySize += 1 + sizeof(float); + binary = realloc(binary, *binarySize); + binary[*binarySize - 1 - sizeof(float)] = instr; + memcpy(binary + (*binarySize - sizeof(float)), &d0, sizeof(float)); } - else if (argc == 4) + else if (index == 12) //lone instructions { - if (strcmp(argv[1], "-d") == 0) - { - doDisplay = 1; - } - else if (strcmp(argv[3], "-d") == 0) - { - doDisplay = 1; - } - if (strcmp(argv[1], "-h") == 0) - { - seed = atof(argv[2]); - qansel_rand_s(seed); - } - else if (strcmp(argv[2], "-h") == 0) - { - seed = atof(argv[3]); - qansel_rand_s(seed); - } + int rmp = 1; + { + int strbeg = regmatches[rmp].rm_so; + int strlen = regmatches[rmp].rm_eo - regmatches[rmp].rm_so; + rmp++; + char tmp[strlen + 1]; + memcpy(tmp, chunk + strbeg, strlen); + tmp[strlen] = 0; + if (strcmp(tmp, "rand") == 0) instr = QANSEL_INSTRUCTION_RAND; + else if (strcmp(tmp, "exit") == 0) instr = QANSEL_INSTRUCTION_EXIT; + } + *binarySize += 1; + binary = realloc(binary, *binarySize); + binary[*binarySize - 1] = instr; } + return 1; +} - if (fullSample) +int qansel_process_chunks(char** chunks, int* associatedLines, int count) +{ + unsigned char* binary = malloc(0); + int binarySize = 0; + int qubitCount = 0; + int bitCount = 0; + char errbuf[100]; + regex_t regex; + regmatch_t regmatches[10]; + const char regexes[][1024] = { - unsigned short stats[65536]; - for (unsigned int i = 0; i < (1 << bitCount); i++) - { - stats[i] = 0; - } - unsigned char bitVect[bitCount]; - for (int i = 0; i < bitCount; i++) bitVect[i] = 0; - - unsigned char* dat = malloc(instrLen * sizeof(QInstr)); - memcpy(dat, instr, instrLen * sizeof(QInstr)); - unsigned int shots = 1000; - for (unsigned int i = 0; i < shots; i++) + "^\\s*qreg\\s*q\\s*\\[\\s*([0-9][0-9]*)\\s*\\]\\s*$", + "^\\s*creg\\s*c\\s*\\[\\s*([0-9][0-9]*)\\s*\\]\\s*$", + "^\\s*(x|y|z|h|s|t|reset|barrier|born|density|print)\\s*q\\s*(\\[\\s*([0-9][0-9]*)\\s*\\]\\s*|)$", + "^\\s*(rx|ry|rz|u1)\\(\\s*([-/0-9PI.]*)\\s*\\)\\s*q\\s*(\\[\\s*([0-9][0-9]*)\\s*\\]\\s*|)$", + "^\\s*(u2)\\(\\s*([-/0-9PI.]*)\\s*,\\s*([-/0-9PI.]*)\\s*\\)\\s*q\\s*(\\[\\s*([0-9][0-9]*)\\s*\\]\\s*|)$", + "^\\s*(u|u3)\\(\\s*([-/0-9PI.]*)\\s*,\\s*([-/0-9PI.]*)\\s*,\\s*([-/0-9PI.]*)\\s*\\)\\s*q\\s*(\\[\\s*([0-9][0-9]*)\\s*\\]\\s*|)$", + "^\\s*measure\\s*q\\s*(\\[\\s*([0-9]*)\\s*\\]\\s*|)\\s*->\\s*c\\c*(\\[\\s*([0-9][0-9]*)\\s*\\]\\s*|)$", + "^\\s*(sample|reset|print)\\s*c\\s*(\\[\\s*([0-9][0-9]*)\\s*\\]\\s*|)$", + "^\\s*(cx|cnot|swap)\\s*q\\s*\\[\\s*([0-9][0-9]*)\\s*\\]\\s*,\\s*q\\s*\\[\\s*([0-9]*)\\s*\\]\\s*$", + "^\\s*(ccx|toffoli|cswap|fredkin)\\s*q\\s*\\[\\s*([0-9]*)\\s*\\]\\s*,\\s*q\\s*\\[\\s*([0-9]*)\\s*\\]\\s*,\\s*q\\s*\\[\\s*([0-9][0-9]*)\\s*\\]\\s*$", + "^\\s*if\\s*\\(\\s*c\\s*(\\[\\s*([0-9][0-9]*)\\s*\\]|)\\s*(==|!=|<>|>=|<=|>|<)\\s*([0-9][0-9]*)\\s*\\).*\\s*$", + "^\\s*(hvar)\\s*([-/0-9PI.]*)\\s*$", + "^\\s*(rand|exit)\\s*$", + }; + int ret, status, found; + for (int i = 0; i < count; i++) + { + found = 0; + for (int j = 0; j < sizeof(regexes) / sizeof(regexes[0]); j++) { - qansel_run(qubitCount, bitCount, instr, bitVect, instrLen, doDisplay); - memcpy(instr, dat, instrLen * sizeof(QInstr)); - unsigned short stat = 0; - for (signed char j = bitCount - 1; j >= 0; j--) + if (regcomp(®ex, regexes[j], REG_EXTENDED | REG_ICASE)) { - stat = (stat << 1) | bitVect[j]; + printf("QAnsel: Regex fatal error.\n"); + exit(1); } - stats[stat]++; - } - free(dat); - unsigned int count = 0; - for (unsigned int i = 0; i < (1 << bitCount); i++) - { - unsigned int tmp = i; - for (unsigned char j = 0; j < bitCount; j++) + ret = regexec(®ex, chunks[i], 10, regmatches, 0); + if (!ret) { - unsigned char bit = (tmp >> (bitCount - 1) & 1); - if (j == (bitCount - (fullSample - 1) - 1) && bit) - { - count += stats[i]; - } - if (fullSample == 255) - { - putchar('0' + bit); - } - tmp <<= 1; + found = 1; + status = qansel_process_chunk(j, chunks[i], associatedLines[i], regmatches, &qubitCount, &bitCount, binary, &binarySize); + break; } - if (fullSample == 255) + else if (ret == REG_NOMATCH) {} + else { - printf(": %.1f%%\n", ((float)stats[i] / (float)shots) * (float)100); + regerror(ret, ®ex, errbuf, sizeof(errbuf)); + fprintf(stderr, "QAnsel: %s.\n", errbuf); + exit(1); } + regfree(®ex); } - if (fullSample != 255) - { - float prob = ((float)count / (float)shots) * (float)100; - printf("0: %.1f%%\n", ((float)100)-prob); - printf("1: %.1f%%\n", prob); + if (!status) break; + if (!found) + { + fprintf(stderr, "QAnsel on line %i: Invalid syntax.\n", associatedLines[i]); + return 0; } } - else - { - qansel_run(qubitCount, bitCount, instr, NULL, instrLen, doDisplay); - } - free(instr); - free(lineIDs); + qansel_execute(binary, binarySize); } void main(int argc, char** argv) { - MODE = MODE_METAL_THREADED; + char* script = malloc(0); + size_t scriptSize = 0; + int c; + while ( (c = getchar()) != EOF ) + { + script = realloc(script, scriptSize + 1); + script[scriptSize++] = c; + } + script = realloc(script, scriptSize + 1); + script[scriptSize++] = 0; + + char** chunks; + int* chunksAssociatedLines; + int chunksCount; + qansel_read_script(script, &chunks, &chunksAssociatedLines, &chunksCount); + qansel_process_chunks(chunks, chunksAssociatedLines, chunksCount); + for (int i = 0; i < chunksCount; i++) free(chunks[i]); + free(chunks); + free(chunksAssociatedLines); + free(script); + return; + + MODE = MODE_BARE; unsigned char err = cpx_mtx_begin(); if (err == 0 && (MODE == MODE_METAL_THREADED || MODE == MODE_METAL)) { @@ -1430,7 +743,7 @@ void main(int argc, char** argv) } RANDOM_FILE = fopen("/dev/TrueRNG0", "r"); if (!RANDOM_FILE) RANDOM_FILE = fopen("/dev/random", "r"); - process(argc, argv); + //qansel_execute fclose(RANDOM_FILE); if (MODE_METAL || MODE_METAL_THREADED) cpx_mtx_clean(); } \ No newline at end of file diff --git a/src/core.c b/src/core.c new file mode 100644 index 0000000..46c940f --- /dev/null +++ b/src/core.c @@ -0,0 +1,1103 @@ +#include +#include +#include +#include +#include "complex.c" +#include "gates.c" +#include "display.c" +#include "chacha20.c" +#define QUBITS_MAX 14 +unsigned char HIDDEN_VARIABLE = 0; +FILE* RANDOM_FILE; +#define GPU_ENABLED +unsigned char USE_THREADS = 1; +#define MODE_BARE 1 +#define MODE_THREADED 2 +#define MODE_METAL 3 +#define MODE_METAL_THREADED 4 +unsigned char MODE = MODE_BARE; +//#define SPEED_TEST + +#define QANSEL_INSTRUCTION_X 0x10 +#define QANSEL_INSTRUCTION_Y 0x11 +#define QANSEL_INSTRUCTION_Z 0x12 +#define QANSEL_INSTRUCTION_H 0x13 +#define QANSEL_INSTRUCTION_S 0x14 +#define QANSEL_INSTRUCTION_T 0x15 +#define QANSEL_INSTRUCTION_RX 0x20 +#define QANSEL_INSTRUCTION_RY 0x21 +#define QANSEL_INSTRUCTION_RZ 0x22 +#define QANSEL_INSTRUCTION_U1 0x23 +#define QANSEL_INSTRUCTION_U2 0x24 +#define QANSEL_INSTRUCTION_U3 0x25 +#define QANSEL_INSTRUCTION_CX 0x30 +#define QANSEL_INSTRUCTION_SWAP 0x31 +#define QANSEL_INSTRUCTION_CCX 0x40 +#define QANSEL_INSTRUCTION_CSWAP 0x41 +#define QANSEL_INSTRUCTION_MEASURE 0xD0 +#define QANSEL_INSTRUCTION_SAMPLE 0xD1 +#define QANSEL_INSTRUCTION_DENSITY 0xD2 +#define QANSEL_INSTRUCTION_BORN 0xD3 +#define QANSEL_INSTRUCTION_IF_E 0xE1 +#define QANSEL_INSTRUCTION_IF_NE 0xE2 +#define QANSEL_INSTRUCTION_IF_G 0xE3 +#define QANSEL_INSTRUCTION_IF_GE 0xE4 +#define QANSEL_INSTRUCTION_IF_L 0xE5 +#define QANSEL_INSTRUCTION_IF_LE 0xE6 +#define QANSEL_INSTRUCTION_RAND 0xF0 +#define QANSEL_INSTRUCTION_HVAR 0xF1 +#define QANSEL_INSTRUCTION_RESET 0xF2 +#define QANSEL_INSTRUCTION_PRINT 0xF3 +#define QANSEL_INSTRUCTION_BARRIER 0xF4 +#define QANSEL_INSTRUCTION_EXIT 0xF5 + +#define QANSEL_FLAGS_EQUAL 0b00001 +#define QANSEL_FLAGS_GREATER 0b00010 +#define QANSEL_FLAGS_LESSER 0b00100 +#define QANSEL_FLAGS_JUMPED 0b01000 +typedef struct +{ + char n[128]; + unsigned char q0, q1, q2; + float arg0, arg1, arg2; +} QInstr; + +const char* qansel_instruction_to_string(unsigned char instr) +{ + switch (instr) + { + case QANSEL_INSTRUCTION_X: return "QANSEL_INSTRUCTION_X"; + case QANSEL_INSTRUCTION_Y: return "QANSEL_INSTRUCTION_Y"; + case QANSEL_INSTRUCTION_Z: return "QANSEL_INSTRUCTION_Z"; + case QANSEL_INSTRUCTION_H: return "QANSEL_INSTRUCTION_H"; + case QANSEL_INSTRUCTION_S: return "QANSEL_INSTRUCTION_S"; + case QANSEL_INSTRUCTION_T: return "QANSEL_INSTRUCTION_T"; + case QANSEL_INSTRUCTION_RX: return "QANSEL_INSTRUCTION_RX"; + case QANSEL_INSTRUCTION_RY: return "QANSEL_INSTRUCTION_RY"; + case QANSEL_INSTRUCTION_RZ: return "QANSEL_INSTRUCTION_RZ"; + case QANSEL_INSTRUCTION_U1: return "QANSEL_INSTRUCTION_U1"; + case QANSEL_INSTRUCTION_U2: return "QANSEL_INSTRUCTION_U2"; + case QANSEL_INSTRUCTION_U3: return "QANSEL_INSTRUCTION_U3"; + case QANSEL_INSTRUCTION_CX: return "QANSEL_INSTRUCTION_CX"; + case QANSEL_INSTRUCTION_SWAP: return "QANSEL_INSTRUCTION_SWAP"; + case QANSEL_INSTRUCTION_CCX: return "QANSEL_INSTRUCTION_CCX"; + case QANSEL_INSTRUCTION_CSWAP: return "QANSEL_INSTRUCTION_CSWAP"; + case QANSEL_INSTRUCTION_MEASURE: return "QANSEL_INSTRUCTION_MEASURE"; + case QANSEL_INSTRUCTION_SAMPLE: return "QANSEL_INSTRUCTION_SAMPLE"; + case QANSEL_INSTRUCTION_DENSITY: return "QANSEL_INSTRUCTION_DENSITY"; + case QANSEL_INSTRUCTION_BORN: return "QANSEL_INSTRUCTION_BORN"; + case QANSEL_INSTRUCTION_IF_E: return "QANSEL_INSTRUCTION_JUMP_E"; + case QANSEL_INSTRUCTION_IF_NE: return "QANSEL_INSTRUCTION_JUMP_NE"; + case QANSEL_INSTRUCTION_IF_G: return "QANSEL_INSTRUCTION_JUMP_G"; + case QANSEL_INSTRUCTION_IF_GE: return "QANSEL_INSTRUCTION_JUMP_GE"; + case QANSEL_INSTRUCTION_IF_L: return "QANSEL_INSTRUCTION_JUMP_L"; + case QANSEL_INSTRUCTION_IF_LE: return "QANSEL_INSTRUCTION_JUMP_LE"; + case QANSEL_INSTRUCTION_RAND: return "QANSEL_INSTRUCTION_RAND"; + case QANSEL_INSTRUCTION_HVAR: return "QANSEL_INSTRUCTION_HVAR"; + case QANSEL_INSTRUCTION_RESET: return "QANSEL_INSTRUCTION_RESET"; + case QANSEL_INSTRUCTION_PRINT: return "QANSEL_INSTRUCTION_PRINT"; + case QANSEL_INSTRUCTION_BARRIER: return "QANSEL_INSTRUCTION_BARRIER"; + case QANSEL_INSTRUCTION_EXIT: return "QANSEL_INSTRUCTION_EXIT"; + } + return "Unknown"; +} +float qansel_rand_s(float s) +{ + unsigned int tmp; + memcpy(&tmp, &s, sizeof(unsigned int)); + srand(tmp); +} +float qansel_rand_h() +{ + return ((float)rand()) / ((float)RAND_MAX); +} +float qansel_rand_t() +{ + if (RANDOM_FILE) + { + unsigned int num = 0; + for (unsigned char i = 0; i < 4; i++) + { + num = (num << 8) | fgetc(RANDOM_FILE); + } + return ((float)num) / ((float)UINT32_MAX); + } + else + { + HIDDEN_VARIABLE = 1; + return qansel_rand_h(); + } +} + +float qansel_rand() +{ + return HIDDEN_VARIABLE ? qansel_rand_h() : qansel_rand_t(); +} + +void qansel_cnot(cpx_mtx_t* stateVector, unsigned char qubitCount, unsigned char bitA, unsigned char bitB) +{ + if (bitA >= qubitCount || bitB >= qubitCount) return; + unsigned int retLen = (unsigned int)pow(2, qubitCount); + cpx_mtx_t ret; + cpx_mtx_init(&ret, 1, retLen); + cpx_t n; + for (unsigned int i = 0; i < retLen; i++) + { + unsigned char bitAVal = (i >> bitA) & 1; + unsigned char bitBVal = (i >> bitB) & 1; + unsigned char bitBNew = bitAVal ? !bitBVal : bitBVal; + unsigned int j = (i & ~(1 << bitB)) | (bitBNew << bitB); + cpx_mtx_get(stateVector, 0, i, &n); + cpx_mtx_set(&ret, 0, j, &n); + } + cpx_mtx_free(stateVector); + stateVector->ptr = ret.ptr; + stateVector->rows = ret.rows; + stateVector->cols = ret.cols; +} + +void qansel_swap(cpx_mtx_t* stateVector, unsigned char qubitCount, unsigned char bitA, unsigned char bitB) +{ + if (bitA >= qubitCount || bitB >= qubitCount) return; + unsigned int retLen = (unsigned int)pow(2, qubitCount); + cpx_mtx_t ret; + cpx_mtx_init(&ret, 1, retLen); + cpx_t n; + for (unsigned int i = 0; i < retLen; i++) + { + unsigned char bitAVal = (i >> bitA) & 1; + unsigned char bitBVal = (i >> bitB) & 1; + unsigned char bitANew = bitBVal; + unsigned char bitBNew = bitAVal; + unsigned int j = (i & ~((1 << bitA) | (1 << bitB))) | ((bitANew << bitA) | (bitBNew << bitB)); + cpx_mtx_get(stateVector, 0, i, &n); + cpx_mtx_set(&ret, 0, j, &n); + } + cpx_mtx_free(stateVector); + stateVector->ptr = ret.ptr; + stateVector->rows = ret.rows; + stateVector->cols = ret.cols; +} + +void qansel_fredkin(cpx_mtx_t* stateVector, unsigned char qubitCount, unsigned char bitA, unsigned char bitB, unsigned char bitC) +{ + if (bitA >= qubitCount || bitB >= qubitCount) return; + unsigned int retLen = (unsigned int)pow(2, qubitCount); + cpx_mtx_t ret; + cpx_mtx_init(&ret, 1, retLen); + cpx_t n; + for (unsigned int i = 0; i < retLen; i++) + { + unsigned char bitAVal = (i >> bitA) & 1; + unsigned char bitBVal = (i >> bitB) & 1; + unsigned char bitCVal = (i >> bitC) & 1; + unsigned char bitBNew = bitAVal ? bitCVal : bitBVal; + unsigned char bitCNew = bitAVal ? bitBVal : bitCVal; + unsigned int j = (i & ~((1 << bitB) | (1 << bitC))) | ((bitBNew << bitB) | (bitCNew << bitC)); + cpx_mtx_get(stateVector, 0, i, &n); + cpx_mtx_set(&ret, 0, j, &n); + } + cpx_mtx_free(stateVector); + stateVector->ptr = ret.ptr; + stateVector->rows = ret.rows; + stateVector->cols = ret.cols; +} + + +void qansel_toffoli(cpx_mtx_t* stateVector, unsigned char qubitCount, unsigned char bitA, unsigned char bitB, unsigned char bitC) +{ + if (bitA >= qubitCount || bitB >= qubitCount) return; + unsigned int retLen = (unsigned int)pow(2, qubitCount); + cpx_mtx_t ret; + cpx_mtx_init(&ret, 1, retLen); + cpx_t n; + for (unsigned int i = 0; i < retLen; i++) + { + unsigned char bitAVal = (i >> bitA) & 1; + unsigned char bitBVal = (i >> bitB) & 1; + unsigned char bitCVal = (i >> bitC) & 1; + unsigned char bitCNew = (bitAVal && bitBVal) ? !bitCVal : bitCVal; + unsigned int j = (i & ~(1 << bitC)) | (bitCNew << bitC); + cpx_mtx_get(stateVector, 0, i, &n); + cpx_mtx_set(&ret, 0, j, &n); + } + cpx_mtx_free(stateVector); + stateVector->ptr = ret.ptr; + stateVector->rows = ret.rows; + stateVector->cols = ret.cols; +} + +float* qansel_unitary(float theta, float phi, float lambda) +{ + cpx_mtx_t m; + cpx_t a, b, c, d; + a.real = cos(theta/2.0); + a.imaginary = 0; + b.real = -cos(lambda) * sin(theta/2.0); + b.imaginary = sin(lambda) * sin(theta/2.0); + c.real = cos(phi) * sin(theta/2.0); + c.imaginary = sin(phi) * sin(theta/2.0); + d.real = cos(phi + lambda) * cos(theta/2.0); + d.imaginary = sin(phi + lambda) * cos(theta/2.0); + cpx_mtx_init(&m, 2, 2); + cpx_mtx_set(&m, 0, 0, &a); + cpx_mtx_set(&m, 0, 1, &b); + cpx_mtx_set(&m, 1, 0, &c); + cpx_mtx_set(&m, 1, 1, &d); + return m.ptr; +} + +void qansel_instruction +( + cpx_mtx_t* stateVector, + int qubitCount, + unsigned char instr, + unsigned char index, + float arg0, + float arg1, + float arg2 +) +{ + cpx_mtx_t tmp; + cpx_mtx_t gate; + gate.rows = 2; + gate.cols = 2; + float* gate_ptr; + switch (instr) + { + case QANSEL_INSTRUCTION_H: gate_ptr = Hadamard; break; + case QANSEL_INSTRUCTION_X: gate_ptr = PauliX; break; + case QANSEL_INSTRUCTION_Y: gate_ptr = PauliY; break; + case QANSEL_INSTRUCTION_Z: gate_ptr = PauliZ; break; + case QANSEL_INSTRUCTION_S: gate_ptr = PhaseS; break; + case QANSEL_INSTRUCTION_T: gate_ptr = PhaseT; break; + case QANSEL_INSTRUCTION_RX: + case QANSEL_INSTRUCTION_RY: + case QANSEL_INSTRUCTION_RZ: + case QANSEL_INSTRUCTION_U1: + case QANSEL_INSTRUCTION_U2: + case QANSEL_INSTRUCTION_U3: + gate_ptr = qansel_unitary(arg0, arg1, arg2); + break; + default: gate_ptr = Identity; break; + } + + cpx_t n; + cpx_mtx_t filter; + cpx_mtx_init(&filter, 2, 2); + unsigned char qubit = qubitCount - (index) - 1; + if (qubit == 0) + { + memcpy(filter.ptr, gate_ptr, 8 * sizeof(float)); + } + else + { + memcpy(filter.ptr, Identity, 8 * sizeof(float)); + } + + for (unsigned char i = 1; i < qubitCount; i++) + { + if (index != 0x0F) + { + if (qubit == i) + { + gate.ptr = gate_ptr; + } + else + { + gate.ptr = Identity; + } + } + else + { + gate.ptr = gate_ptr; + } + + tmp.rows = filter.rows * gate.rows; + tmp.cols = filter.cols * gate.cols; + tmp.ptr = malloc(tmp.rows * (tmp.cols * 2) * sizeof(float)); + + #ifdef SPEED_TEST + printf("(%ix%i);(%ix%i) (knk)\n", tmp.rows, tmp.cols, gate.rows, gate.cols); + unsigned long int us1, us2; + us1 = get_time(); + cpx_mtx_knk_metal(tmp.ptr, filter.ptr, gate.ptr, filter.rows, filter.cols, gate.rows, gate.cols); + us2 = get_time(); + printf("\tMetal: %lu\n", us2 - us1); + us1 = get_time(); + cpx_mtx_knk_metal_2x2(tmp.ptr, filter.ptr, gate.ptr, filter.rows, filter.cols, gate.rows, gate.cols); + us2 = get_time(); + printf("\tMetal2x2: %lu\n", us2 - us1); + us1 = get_time(); + cpx_mtx_knk_threads(tmp.ptr, filter.ptr, gate.ptr, filter.rows, filter.cols, gate.rows, gate.cols); + us2 = get_time(); + printf("\tThreads: %lu\n", us2 - us1); + us1 = get_time(); + cpx_mtx_knk_threads_2x2(tmp.ptr, filter.ptr, gate.ptr, filter.rows, filter.cols, gate.rows, gate.cols); + us2 = get_time(); + printf("\tThreads2x2: %lu\n", us2 - us1); + us1 = get_time(); + cpx_mtx_knk(tmp.ptr, filter.ptr, gate.ptr, filter.rows, filter.cols, gate.rows, gate.cols); + us2 = get_time(); + printf("\tBare: %lu\n", us2 - us1); + us1 = get_time(); + cpx_mtx_knk_2x2(tmp.ptr, filter.ptr, gate.ptr, filter.rows, filter.cols, gate.rows, gate.cols); + us2 = get_time(); + printf("\tBare2x2: %lu\n", us2 - us1); + + //us1 = get_time(); + //cpx_mtx_knk(tmp.ptr, filter.ptr, gate.ptr, filter.rows, filter.cols, gate.rows, gate.cols); + //us2 = get_time(); + //printf("\tTranspose: %lu\n", us2 - us1); + #else + if (MODE == MODE_METAL && tmp.cols >= 64) + { + cpx_mtx_knk_metal_2x2(tmp.ptr, filter.ptr, gate.ptr, filter.rows, filter.cols, gate.rows, gate.cols); + } + else if ((MODE == MODE_THREADED || MODE == MODE_METAL_THREADED) && tmp.cols >= 64) + { + cpx_mtx_knk_threads_2x2(tmp.ptr, filter.ptr, gate.ptr, filter.rows, filter.cols, gate.rows, gate.cols); + } + else + { + cpx_mtx_knk_2x2(tmp.ptr, filter.ptr, gate.ptr, filter.rows, filter.cols, gate.rows, gate.cols); + } + #endif + + free(filter.ptr); + filter.ptr = tmp.ptr; + filter.rows = tmp.rows; + filter.cols = tmp.cols; + } + + cpx_mtx_init(&tmp, stateVector->rows, stateVector->cols); + + #ifdef SPEED_TEST + printf("%ix%i (dot)\n", tmp.rows, tmp.cols); + unsigned long int us1, us2; + us1 = get_time(); + cpx_mtx_dot_metal(tmp.ptr, stateVector->ptr, filter.ptr, stateVector->rows, stateVector->cols, filter.rows, filter.cols); + us2 = get_time(); + printf("\tMetal: %lu\n", us2 - us1); + us1 = get_time(); + cpx_mtx_dot_threads(tmp.ptr, stateVector->ptr, filter.ptr, stateVector->rows, stateVector->cols, filter.rows, filter.cols); + us2 = get_time(); + printf("\tThreads: %lu\n", us2 - us1); + us1 = get_time(); + cpx_mtx_dot(tmp.ptr, stateVector->ptr, filter.ptr, stateVector->rows, stateVector->cols, filter.rows, filter.cols); + us2 = get_time(); + printf("\tBare: %lu\n", us2 - us1); + #else + if ((MODE == MODE_METAL || MODE == MODE_METAL_THREADED) && tmp.cols >= 64) + { + cpx_mtx_dot_metal(tmp.ptr, stateVector->ptr, filter.ptr, stateVector->rows, stateVector->cols, filter.rows, filter.cols); + } + else if (MODE == MODE_THREADED && tmp.cols >= 64) + { + cpx_mtx_dot_threads(tmp.ptr, stateVector->ptr, filter.ptr, stateVector->rows, stateVector->cols, filter.rows, filter.cols); + } + else + { + cpx_mtx_dot(tmp.ptr, stateVector->ptr, filter.ptr, stateVector->rows, stateVector->cols, filter.rows, filter.cols); + } + #endif + free(stateVector->ptr); + stateVector->ptr = tmp.ptr; + free(filter.ptr); + if (instr == QANSEL_INSTRUCTION_U1 || instr == QANSEL_INSTRUCTION_U2 || instr == QANSEL_INSTRUCTION_U3) + { + free(gate_ptr); + } +} + +unsigned char qansel_measure(cpx_mtx_t* stateVector, unsigned char qubitCount, unsigned char qubit) +{ + unsigned int qubitCountPow2 = (unsigned int)pow(2, qubitCount); + cpx_t n; + float prob0 = 0; + for (unsigned int i = 0; i < qubitCountPow2; i++) + { + unsigned char bit = (i >> qubit) & 1; + cpx_mtx_get(stateVector, 0, i, &n); + if (bit == 0) prob0 += cpx_magsqr(&n); + } + + float r = qansel_rand(); + unsigned char newBit = r < prob0 ? 0 : 1; + float probTot = 0; + for (unsigned int i = 0; i < qubitCountPow2; i++) + { + unsigned char bit = (i >> qubit) & 1; + cpx_mtx_get(stateVector, 0, i, &n); + if (bit != newBit) + { + n.real = 0; + n.imaginary = 0; + } + else + { + probTot += cpx_magsqr(&n); + } + cpx_mtx_set(stateVector, 0, i, &n); + } + + float multiplier = sqrt(1 / probTot); + for (unsigned int i = 0; i < qubitCountPow2; i++) + { + unsigned char bit = (i >> qubit) & 1; + cpx_mtx_get(stateVector, 0, i, &n); + if (bit == newBit) + { + n.real *= multiplier; + n.imaginary *= multiplier; + } + cpx_mtx_set(stateVector, 0, i, &n); + } + + return newBit; +} + +int qansel_get_instruction_bitmax(unsigned char* ptr, int offset, int* bitmax, int* qbitmax) +{ + unsigned char a0, a1, a2; + *bitmax = 0; + *qbitmax = 0; + switch (ptr[offset]) + { + case QANSEL_INSTRUCTION_X: + case QANSEL_INSTRUCTION_Y: + case QANSEL_INSTRUCTION_Z: + case QANSEL_INSTRUCTION_H: + case QANSEL_INSTRUCTION_S: + case QANSEL_INSTRUCTION_T: + case QANSEL_INSTRUCTION_RX: + case QANSEL_INSTRUCTION_RY: + case QANSEL_INSTRUCTION_RZ: + case QANSEL_INSTRUCTION_U1: + case QANSEL_INSTRUCTION_U2: + case QANSEL_INSTRUCTION_U3: + case QANSEL_INSTRUCTION_BARRIER: + case QANSEL_INSTRUCTION_DENSITY: + case QANSEL_INSTRUCTION_BORN: + a0 = ptr[offset + 1]; + if (a0 > 0x0D && a0 != 0x0F) return 0; + if (a0 != 0x0F) *qbitmax = a0 + 1; + return 1; + case QANSEL_INSTRUCTION_SAMPLE: + case QANSEL_INSTRUCTION_IF_E: + case QANSEL_INSTRUCTION_IF_NE: + case QANSEL_INSTRUCTION_IF_G: + case QANSEL_INSTRUCTION_IF_GE: + case QANSEL_INSTRUCTION_IF_L: + case QANSEL_INSTRUCTION_IF_LE: + a0 = ptr[offset + 1]; + if ((a0 > 0x1D || a0 < 0x10) && a0 != 0x1F) return 0; + if (a0 != 0x1F) *bitmax = (a0 - 0x10) + 1; + return 1; + case QANSEL_INSTRUCTION_RESET: + case QANSEL_INSTRUCTION_PRINT: + a0 = ptr[offset + 1]; + if (a0 == 0xFF) return 1; + if (a0 < 0x10) + { + if (a0 > 0x0D && a0 != 0x0F) return 0; + if (a0 != 0x0F) *qbitmax = a0 + 1; + return 1; + } + else + { + if ((a0 > 0x1D || a0 < 0x10) && a0 != 0x1F) return 0; + if (a0 != 0x1F) *bitmax = (a0 - 0x10) + 1; + return 1; + } + return 0; + case QANSEL_INSTRUCTION_CX: + case QANSEL_INSTRUCTION_SWAP: + a0 = ptr[offset + 1]; + a1 = ptr[offset + 2]; + if (a0 > 0x0D) return 0; + if (a1 > 0x0D) return 0; + *qbitmax = (a0 > a1 ? a0 : a1) + 1; + return 1; + case QANSEL_INSTRUCTION_CCX: + case QANSEL_INSTRUCTION_CSWAP: + a0 = ptr[offset + 1]; + a1 = ptr[offset + 2]; + a2 = ptr[offset + 3]; + if (a0 > 0x0D || a1 > 0x0D || a2 > 0x0D) return 0; + *qbitmax = ((a0 > a1) && (a0 > a2) ? a0 : ((a1 > a0) && (a1 > a2) ? a1 : a2)) + 1; + return 1; + case QANSEL_INSTRUCTION_MEASURE: + a0 = ptr[offset + 1]; + a1 = ptr[offset + 2]; + if (a0 > 0x0D) return 0; + if (a1 > 0x1D || a1 < 0x10) return 0; + *qbitmax = a0 + 1; + *bitmax = (a1 - 0x10) + 1; + return 1; + } + return 0; +} + +int qansel_get_instruction_size(unsigned char instr) +{ + switch (instr) + { + case QANSEL_INSTRUCTION_X: return 1 + 1; + case QANSEL_INSTRUCTION_Y: return 1 + 1; + case QANSEL_INSTRUCTION_Z: return 1 + 1; + case QANSEL_INSTRUCTION_H: return 1 + 1; + case QANSEL_INSTRUCTION_S: return 1 + 1; + case QANSEL_INSTRUCTION_T: return 1 + 1; + case QANSEL_INSTRUCTION_RX: return 1 + 1 + sizeof(float); + case QANSEL_INSTRUCTION_RY: return 1 + 1 + sizeof(float); + case QANSEL_INSTRUCTION_RZ: return 1 + 1 + sizeof(float); + case QANSEL_INSTRUCTION_U1: return 1 + 1 + sizeof(float); + case QANSEL_INSTRUCTION_U2: return 1 + 1 + sizeof(float) * 2; + case QANSEL_INSTRUCTION_U3: return 1 + 1 + sizeof(float) * 3; + case QANSEL_INSTRUCTION_CX: return 1 + 2; + case QANSEL_INSTRUCTION_SWAP: return 1 + 2; + case QANSEL_INSTRUCTION_CCX: return 1 + 3; + case QANSEL_INSTRUCTION_CSWAP: return 1 + 3; + case QANSEL_INSTRUCTION_MEASURE: return 1 + 2; + case QANSEL_INSTRUCTION_SAMPLE: return 1 + 1; + case QANSEL_INSTRUCTION_DENSITY: return 1 + 1; + case QANSEL_INSTRUCTION_BORN: return 1 + 1; + case QANSEL_INSTRUCTION_IF_E: return 1 + 1 + sizeof(unsigned short); + case QANSEL_INSTRUCTION_IF_NE: return 1 + 1 + sizeof(unsigned short); + case QANSEL_INSTRUCTION_IF_G: return 1 + 1 + sizeof(unsigned short); + case QANSEL_INSTRUCTION_IF_GE: return 1 + 1 + sizeof(unsigned short); + case QANSEL_INSTRUCTION_IF_L: return 1 + 1 + sizeof(unsigned short); + case QANSEL_INSTRUCTION_IF_LE: return 1 + 1 + sizeof(unsigned short); + case QANSEL_INSTRUCTION_RAND: return 1; + case QANSEL_INSTRUCTION_HVAR: return 1 + sizeof(float); + case QANSEL_INSTRUCTION_RESET: return 1 + 1; + case QANSEL_INSTRUCTION_PRINT: return 1 + 1; + case QANSEL_INSTRUCTION_BARRIER: return 1 + 1; + case QANSEL_INSTRUCTION_EXIT: return 1; + } + return 0; +} + +void qansel_born(cpx_mtx_t* stateVector, int PC, int qubitCount, unsigned char q0) +{ + unsigned int qubitCountPow2 = (unsigned int)pow(2, qubitCount); + if (q0 == 0x0F) + { + for (unsigned int j = 0; j < qubitCountPow2; j++) + { + unsigned int tmp = j; + for (unsigned char k = 0; k < qubitCount; k++) + { + putchar('0' + (tmp >> (qubitCount - 1) & 1)); + tmp <<= 1; + } + cpx_t n; + cpx_mtx_get(stateVector, 0, j, &n); + printf(": %.1f%%\n", cpx_magsqr(&n) * 100); + } + } + else if (q0 <= 0x0D) + { + float prob = 0; + for (unsigned int j = 0; j < qubitCountPow2; j++) + { + cpx_t n; + cpx_mtx_get(stateVector, 0, j, &n); + if ((j >> q0) & 1) + { + prob += cpx_magsqr(&n); + } + } + printf("0: %.1f%%\n", (1 - prob) * 100.0); + printf("1: %.1f%%\n", prob * 100.0); + } +} + +void qansel_density_or_print(cpx_mtx_t* stateVector, unsigned char* bitVector, unsigned char density, int bitCount, int qubitCount, unsigned char a0) +{ + unsigned int qubitCountPow2 = (unsigned int)pow(2, qubitCount); + if (a0 == 0x0F || a0 == 0x1F || a0 == 0xFF) + { + if (a0 == 0x0F || a0 == 0xFF) + { + printf("[ "); cpx_mtx_print(stateVector); printf(" ]\n"); + } + if (a0 == 0x1F || a0 == 0xFF) + { + for (int32_t j = bitCount - 1; j >= 0; j--) + { + putchar('0' + bitVector[j]); + } + putchar('\n'); + } + } + else if (a0 >= 0x10 && a0 <= 0x1D) + { + putchar('0' + bitVector[a0 - 0x10]); + putchar('\n'); + } + else + { + cpx_mtx_t tmp; + cpx_mtx_init(&tmp, 1, 2); + for (unsigned int j = 0; j < qubitCountPow2; j++) + { + if ((j >> a0) & 1) + { + cpx_t a, b; + cpx_mtx_get(&tmp, 0, 1, &a); + cpx_mtx_get(stateVector, 0, j, &b); + a.real += b.real; + a.imaginary += b.imaginary; + cpx_mtx_set(&tmp, 0, 1, &a); + } + else + { + cpx_t a, b; + cpx_mtx_get(&tmp, 0, 0, &a); + cpx_mtx_get(stateVector, 0, j, &b); + a.real += b.real; + a.imaginary += b.imaginary; + cpx_mtx_set(&tmp, 0, 0, &a); + } + } + float multiplier = 0; + cpx_t n; + cpx_mtx_get(&tmp, 0, 0, &n); + multiplier += cpx_magsqr(&n); + cpx_mtx_get(&tmp, 0, 1, &n); + multiplier += cpx_magsqr(&n); + multiplier = sqrt(1 / multiplier); + n.real *= multiplier; + n.imaginary *= multiplier; + cpx_mtx_set(&tmp, 0, 1, &n); + cpx_mtx_get(&tmp, 0, 0, &n); + n.real *= multiplier; + n.imaginary *= multiplier; + cpx_mtx_set(&tmp, 0, 0, &n); + + if (density) + { + cpx_t a, b, c, d, x, y, z, w; + cpx_mtx_get(&tmp, 0, 0, &a); + cpx_mtx_get(&tmp, 0, 1, &b); + cpx_mtx_get(&tmp, 0, 0, &c); + cpx_mtx_get(&tmp, 0, 1, &d); + c.imaginary *= -1; + d.imaginary *= -1; + cpx_mul(&x, &a, &c); + cpx_mul(&y, &a, &d); + cpx_mul(&z, &b, &c); + cpx_mul(&w, &b, &d); + char* sx = cpx_str(&x); + char* sy = cpx_str(&y); + char* sz = cpx_str(&z); + char* sw = cpx_str(&w); + printf("[ %s, %s ]\n", sx, sy); + printf("[ %s, %s ]\n", sz, sw); + free(sx); + free(sy); + free(sz); + free(sw); + } + else + { + printf("[ "); cpx_mtx_print(&tmp); printf(" ]\n"); + } + cpx_mtx_free(&tmp); + } +} + +float qansel_get_float(unsigned char* program, int offset) +{ + float ret; + memcpy(&ret, program + offset, sizeof(float)); + return ret; +} + +short qansel_get_short(unsigned char* program, int offset) +{ + short ret; + memcpy(&ret, program + offset, sizeof(short)); + return ret; +} + +int qansel_get_int(unsigned char* program, int offset) +{ + int ret; + memcpy(&ret, program + offset, sizeof(int)); + return ret; +} + +void qansel_reset(cpx_mtx_t* stateVector, unsigned char* bitVector, int qubitCount, int bitCount, unsigned char q0) +{ + unsigned int qubitCountPow2 = (unsigned int)pow(2, qubitCount); + if (q0 == 0xFF) + { + cpx_mtx_set2(stateVector, 0, 0, 1, 0); + for (unsigned int j = 1; j < qubitCountPow2; j++) + { + cpx_mtx_set2(stateVector, 0, j, 0, 0); + } + for (unsigned char j = 0; j < bitCount; j++) + { + bitVector[j] = 0; + } + } + else if (q0 == 0x0F) + { + cpx_mtx_set2(stateVector, 0, 0, 1, 0); + for (unsigned int j = 1; j < qubitCountPow2; j++) + { + cpx_mtx_set2(stateVector, 0, j, 0, 0); + } + } + else if (q0 == 0x1F) + { + for (unsigned char j = 0; j < bitCount; j++) + { + bitVector[j] = 0; + } + } + else if (q0 <= 0x0D) + { + unsigned char bit = qansel_measure(stateVector, qubitCount, q0); + if (bit) + { + qansel_instruction(stateVector, qubitCount, QANSEL_INSTRUCTION_X, q0, 0, 0, 0); + } + } + else if (q0 >= 0x10 && q0 <= 0x1D) + { + bitVector[q0 - 0x10] = 0; + } +} + +unsigned char qansel_compare(unsigned char* bitVector, int bitCount, int PC, unsigned char a0, short op) +{ + unsigned char ret = 0; + short val; + if (a0 == 0x1F) + { + val = 0; + for (int32_t j = bitCount - 1; j >= 0; j--) + { + val = (val << 1) | bitVector[j]; + } + if (val == op) ret |= QANSEL_FLAGS_EQUAL; + if (val > op) ret |= QANSEL_FLAGS_GREATER; + if (val < op) ret |= QANSEL_FLAGS_LESSER; + } + else if (a0 >= 0x10 && a0 <= 0x1D) + { + val = bitVector[a0 - 0x10]; + if (val == op) ret |= QANSEL_FLAGS_EQUAL; + if (val > op) ret |= QANSEL_FLAGS_GREATER; + if (val < op) ret |= QANSEL_FLAGS_LESSER; + } + return ret; +} + +void qansel_crawl(unsigned char* program, int programSize, int* qubitCount, int* bitCount, int* sample) +{ + printf("Crawling program . . .\n"); + int PC = 0; + *qubitCount = 0; + *bitCount = 0; + *sample = 0xFF; + while (PC < programSize) + { + int next = qansel_get_instruction_size(program[PC]); + printf("|%s|\n", qansel_instruction_to_string(program[PC])); + if (program[PC] == QANSEL_INSTRUCTION_SAMPLE) + { + if ((program[PC + 1] < 0x10 || program[PC + 1] > 0x1D) && program[PC + 1] != 0x1F) + { + fprintf(stderr, "QAnsel (%04X): Invalid index.\n", PC); + } + else + { + *sample = program[PC + 1] - 0x10; + } + } + if (next == 0) + { + printf("QAnsel (%04X): Invalid instruction 0x%02x.\n", PC, program[PC]); + exit(1); + } + int bitmax, qbitmax; + int success = qansel_get_instruction_bitmax(program, PC, &bitmax, &qbitmax); + if (!success) + { + fprintf(stderr, "QAnsel (%04X): Invalid index (%s).\n", PC); + exit(1); + } + if (bitmax > *bitCount) *bitCount = bitmax; + if (qbitmax > *qubitCount) *qubitCount = qbitmax; + PC += next; + } + printf("Quantum bits allocated: %i\n", *qubitCount); + printf("Classical bits allocated: %i\n", *bitCount); +} + +void qansel_run(unsigned char* program, int programSize, int qubitCount, int bitCount, unsigned char* outputBitVector) +{ + int PC = 0; + unsigned int qubitCountPow2 = (unsigned int)pow(2, qubitCount); + unsigned char bitVector[bitCount]; + memset(bitVector, 0, bitCount); + cpx_mtx_t stateVector; + cpx_mtx_init(&stateVector, 1, qubitCountPow2); + cpx_mtx_set2(&stateVector, 0, 0, 1, 0); + //if (gfx) display(&stateVector, qubitCount); + unsigned char skip = 0, a0 = 0, a1 = 0, a2 = 0; + unsigned char flags = 0; + unsigned short tmp = 0; + + while (PC < programSize) + { + //printf("%i;%i\n", PC, programSize); + int next = qansel_get_instruction_size(program[PC]); + if (skip) + { + skip = 0; + } + else + { + unsigned char instr = program[PC]; + //printf("-----------------------------------\n"); + //qansel_density_or_print(&stateVector, bitVector, 0, bitCount, qubitCount, 0x0F); + //qansel_density_or_print(&stateVector, bitVector, 0, bitCount, qubitCount, 0x1F); + //printf("%s(%i, %i, %i)(%f, %f, %f)\n", qansel_instruction_to_string(instr), program[PC+1], program[PC+2], program[PC], qansel_get_float(program, PC + 2), qansel_get_float(program, PC + 2 + sizeof(float)), qansel_get_float(program, PC + 2) + sizeof(float) * 2); + switch (instr) + { + case QANSEL_INSTRUCTION_X: + case QANSEL_INSTRUCTION_Y: + case QANSEL_INSTRUCTION_Z: + case QANSEL_INSTRUCTION_H: + case QANSEL_INSTRUCTION_S: + case QANSEL_INSTRUCTION_T: + a0 = program[PC + 1]; + qansel_instruction(&stateVector, qubitCount, instr, program[PC + 1], 0, 0, 0); + break; + case QANSEL_INSTRUCTION_RX: + a0 = program[PC + 1]; + qansel_instruction + ( + &stateVector, qubitCount, instr, a0, + M_PI / 2, -M_PI / 2, qansel_get_float(program, PC + 2) - (M_PI / 2) + ); + break; + case QANSEL_INSTRUCTION_RY: + case QANSEL_INSTRUCTION_U1: + a0 = program[PC + 1]; + qansel_instruction + ( + &stateVector, qubitCount, instr, a0, + qansel_get_float(program, PC + 2), 0, 0 + ); + break; + case QANSEL_INSTRUCTION_RZ: + a0 = program[PC + 1]; + qansel_instruction + ( + &stateVector, qubitCount, instr, a0, + 0, 0, qansel_get_float(program, PC + 2) + ); + break; + case QANSEL_INSTRUCTION_U2: + a0 = program[PC + 1]; + qansel_instruction + ( + &stateVector, qubitCount, instr, a0, + qansel_get_float(program, PC + 2), + qansel_get_float(program, PC + 2 + sizeof(float)), + 0 + ); + break; + case QANSEL_INSTRUCTION_U3: + a0 = program[PC + 1]; + qansel_instruction + ( + &stateVector, qubitCount, instr, a0, + qansel_get_float(program, PC + 2), + qansel_get_float(program, PC + 2 + sizeof(float)), + qansel_get_float(program, PC + 2 + sizeof(float) * 2) + ); + break; + case QANSEL_INSTRUCTION_CX: + a0 = program[PC + 1]; + a1 = program[PC + 2]; + qansel_cnot(&stateVector, qubitCount, a0, a1); + break; + case QANSEL_INSTRUCTION_SWAP: + a0 = program[PC + 1]; + a1 = program[PC + 2]; + qansel_swap(&stateVector, qubitCount, a0, a1); + break; + case QANSEL_INSTRUCTION_CCX: + a0 = program[PC + 1]; + a1 = program[PC + 2]; + a2 = program[PC + 3]; + qansel_toffoli(&stateVector, qubitCount, a0, a1, a2); + break; + case QANSEL_INSTRUCTION_CSWAP: + a0 = program[PC + 1]; + a1 = program[PC + 2]; + a2 = program[PC + 3]; + qansel_fredkin(&stateVector, qubitCount, a0, a1, a2); + break; + case QANSEL_INSTRUCTION_MEASURE: + a0 = program[PC + 1]; + a1 = program[PC + 2] - 0x10; + bitVector[a1] = qansel_measure(&stateVector, qubitCount, a0); + break; + case QANSEL_INSTRUCTION_BORN: + a0 = program[PC + 1]; + qansel_born(&stateVector, PC, qubitCount, a0); + break; + case QANSEL_INSTRUCTION_DENSITY: + a0 = program[PC + 1]; + qansel_density_or_print(&stateVector, bitVector, 1, bitCount, qubitCount, a0); + break; + case QANSEL_INSTRUCTION_PRINT: + a0 = program[PC + 1]; + qansel_density_or_print(&stateVector, bitVector, 0, bitCount, qubitCount, a0); + break; + case QANSEL_INSTRUCTION_BARRIER: + a0 = program[PC + 1]; + break; + case QANSEL_INSTRUCTION_RESET: + a0 = program[PC + 1]; + qansel_reset(&stateVector, bitVector, qubitCount, bitCount, a0); + break; + case QANSEL_INSTRUCTION_HVAR: + HIDDEN_VARIABLE = 1; + float tmp1 = qansel_get_float(program, PC + 1); + unsigned int tmp2; + memcpy(&tmp2, &tmp1, sizeof(unsigned int)); + srand(tmp2); + break; + case QANSEL_INSTRUCTION_RAND: + HIDDEN_VARIABLE = 0; + break; + case QANSEL_INSTRUCTION_IF_E: + a0 = program[PC + 1]; + tmp = qansel_get_short(program, PC + 2); + flags = qansel_compare(bitVector, bitCount, PC, a0, tmp); + skip = 1; if (flags & QANSEL_FLAGS_EQUAL) skip = 0; + break; + case QANSEL_INSTRUCTION_IF_NE: + a0 = program[PC + 1]; + tmp = qansel_get_short(program, PC + 2); + flags = qansel_compare(bitVector, bitCount, PC, a0, tmp); + skip = 1; if (!(flags & QANSEL_FLAGS_EQUAL)) skip = 0; + break; + case QANSEL_INSTRUCTION_IF_G: + a0 = program[PC + 1]; + tmp = qansel_get_short(program, PC + 2); + flags = qansel_compare(bitVector, bitCount, PC, a0, tmp); + skip = 1; if (flags & QANSEL_FLAGS_GREATER) skip = 0; + break; + case QANSEL_INSTRUCTION_IF_GE: + a0 = program[PC + 1]; + tmp = qansel_get_short(program, PC + 2); + flags = qansel_compare(bitVector, bitCount, PC, a0, tmp); + skip = 1; if ((flags & QANSEL_FLAGS_GREATER) && (flags & QANSEL_FLAGS_EQUAL)) skip = 0; + break; + case QANSEL_INSTRUCTION_IF_L: + a0 = program[PC + 1]; + tmp = qansel_get_short(program, PC + 2); + flags = qansel_compare(bitVector, bitCount, PC, a0, tmp); + skip = 1; if (flags & QANSEL_FLAGS_LESSER) skip = 0; + break; + case QANSEL_INSTRUCTION_IF_LE: + a0 = program[PC + 1]; + tmp = qansel_get_short(program, PC + 2); + flags = qansel_compare(bitVector, bitCount, PC, a0, tmp); + skip = 1; if ((flags & QANSEL_FLAGS_LESSER) && (flags & QANSEL_FLAGS_EQUAL)) skip = 0; + break; + case QANSEL_INSTRUCTION_SAMPLE: break; + + } + } + PC += next; + } + if (outputBitVector != NULL) + { + for (int i = 0; i < bitCount; i++) + { + outputBitVector[i] = bitVector[i]; + } + } +} + +void qansel_execute(unsigned char* buff, int sizeofbuff) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + float seed = (float)((unsigned long)ts.tv_sec * 1000000000LL + ts.tv_nsec); + qansel_rand_s(seed); + + unsigned short vals; + float valf; + int pos = 0; + + int qubitCount, bitCount, sample; + qansel_crawl(buff, sizeofbuff, &qubitCount, &bitCount, &sample); + if (sample != 0xFF) + { + unsigned short stats[65536]; + for (unsigned int i = 0; i < (1 << bitCount); i++) + { + stats[i] = 0; + } + unsigned char bitVect[bitCount]; + memset(bitVect, 0, bitCount); + for (int i = 0; i < bitCount; i++) bitVect[i] = 0; + unsigned int shots = 1000; + for (unsigned int i = 0; i < shots; i++) + { + qansel_run(buff, sizeofbuff, qubitCount, bitCount, bitVect); + unsigned short stat = 0; + for (signed char j = bitCount - 1; j >= 0; j--) + { + stat = (stat << 1) | bitVect[j]; + } + stats[stat]++; + } + unsigned int count = 0; + for (unsigned int i = 0; i < (1 << bitCount); i++) + { + unsigned int tmp = i; + for (unsigned char j = 0; j < bitCount; j++) + { + unsigned char bit = (tmp >> (bitCount - 1) & 1); + if (j == (bitCount - sample - 1) && bit) + { + count += stats[i]; + } + if (sample == 0x0F) + { + putchar('0' + bit); + } + tmp <<= 1; + } + if (sample == 0x0F) + { + printf(": %.1f%%\n", ((float)stats[i] / (float)shots) * (float)100); + } + } + if (sample != 0x0F) + { + float prob = ((float)count / (float)shots) * (float)100; + printf("0: %.1f%%\n", ((float)100)-prob); + printf("1: %.1f%%\n", prob); + } + } + else + { + qansel_run(buff, sizeofbuff, qubitCount, bitCount, NULL); + } +} diff --git a/src/oldex.mm b/src/oldex.mm new file mode 100644 index 0000000..bbb35ee --- /dev/null +++ b/src/oldex.mm @@ -0,0 +1,127 @@ + + //0+0 + QANSEL_INSTRUCTION_CCX, 0x00, 0x01, 0x02, + QANSEL_INSTRUCTION_CX, 0x00, 0x01, + QANSEL_INSTRUCTION_MEASURE, 0x01, 0x10, + QANSEL_INSTRUCTION_MEASURE, 0x02, 0x11, + QANSEL_INSTRUCTION_PRINT, 0x1F, + QANSEL_INSTRUCTION_RESET, 0xFF, + + //0+1 + QANSEL_INSTRUCTION_X, 0x00, + QANSEL_INSTRUCTION_CCX, 0x00, 0x01, 0x02, + QANSEL_INSTRUCTION_CX, 0x00, 0x01, + QANSEL_INSTRUCTION_MEASURE, 0x01, 0x10, + QANSEL_INSTRUCTION_MEASURE, 0x02, 0x11, + QANSEL_INSTRUCTION_PRINT, 0x1F, + QANSEL_INSTRUCTION_RESET, 0xFF, + + //1+0 + QANSEL_INSTRUCTION_X, 0x01, + QANSEL_INSTRUCTION_CCX, 0x00, 0x01, 0x02, + QANSEL_INSTRUCTION_CX, 0x00, 0x01, + QANSEL_INSTRUCTION_MEASURE, 0x01, 0x10, + QANSEL_INSTRUCTION_MEASURE, 0x02, 0x11, + QANSEL_INSTRUCTION_PRINT, 0x1F, + QANSEL_INSTRUCTION_RESET, 0xFF, + + //1+1 + QANSEL_INSTRUCTION_X, 0x00, + QANSEL_INSTRUCTION_X, 0x01, + QANSEL_INSTRUCTION_CCX, 0x00, 0x01, 0x02, + QANSEL_INSTRUCTION_CX, 0x00, 0x01, + QANSEL_INSTRUCTION_MEASURE, 0x01, 0x10, + QANSEL_INSTRUCTION_MEASURE, 0x02, 0x11, + QANSEL_INSTRUCTION_PRINT, 0x1F, + QANSEL_INSTRUCTION_RESET, 0xFF, + + + + + QANSEL_INSTRUCTION_U3, 0x00, 0xED, 00, 00, 00, 0xED, 00, 00, 00, 0xED, 00, 00, 00, + QANSEL_INSTRUCTION_PRINT, 0x00, + + QANSEL_INSTRUCTION_H, 0x01, + QANSEL_INSTRUCTION_CX, 0x01, 0x02, + + QANSEL_INSTRUCTION_CX, 0x00, 0x01, + QANSEL_INSTRUCTION_H, 0x00, + + QANSEL_INSTRUCTION_MEASURE, 0x00, 0x10, + QANSEL_INSTRUCTION_MEASURE, 0x01, 0x11, + + QANSEL_INSTRUCTION_IF_E, 0x1F, 0xED, 0x00, + QANSEL_INSTRUCTION_Z, 0x02, + QANSEL_INSTRUCTION_IF_E, 0x1F, 0xED, 0x00, + QANSEL_INSTRUCTION_X, 0x02, + QANSEL_INSTRUCTION_IF_E, 0x1F, 0xED, 0x00, + QANSEL_INSTRUCTION_X, 0x02, + QANSEL_INSTRUCTION_IF_E, 0x1F, 0xED, 0x00, + QANSEL_INSTRUCTION_Z, 0x02, + + QANSEL_INSTRUCTION_RESET, 0x00, + QANSEL_INSTRUCTION_RESET, 0x01, + QANSEL_INSTRUCTION_PRINT, 0x02, + + + + //Host generates random bits + // for the two players. + QANSEL_INSTRUCTION_H, 0x00, + QANSEL_INSTRUCTION_H, 0x01, + QANSEL_INSTRUCTION_MEASURE, 0x00, 0x10, + QANSEL_INSTRUCTION_MEASURE, 0x01, 0x11, + QANSEL_INSTRUCTION_RESET, 0x00, + QANSEL_INSTRUCTION_RESET, 0x01, + + //Two players are also provided + // an entangled qubit. + QANSEL_INSTRUCTION_H, 0x00, + QANSEL_INSTRUCTION_CX, 0x00, 0x01, + + //Player X strategy + //if(c[0]==0) do nothing + QANSEL_INSTRUCTION_IF_E, 0x10, 0xEE, 0x00, + QANSEL_INSTRUCTION_RY, 0x00, 0xEE, 0x00, 0x00, 0x00, + + //Player Y strategy + QANSEL_INSTRUCTION_IF_E, 0x11, 0xEE, 0x00, + QANSEL_INSTRUCTION_RY, 0x01, 0xEE, 0x00, 0x00, 0x00, + QANSEL_INSTRUCTION_IF_E, 0x11, 0xEE, 0x00, + QANSEL_INSTRUCTION_RY, 0x01, 0xEE, 0x00, 0x00, 0x00, + + //Transfer to host + QANSEL_INSTRUCTION_MEASURE, 0x00, 0x12, + QANSEL_INSTRUCTION_MEASURE, 0x01, 0x13, + + //a xor b + QANSEL_INSTRUCTION_CX, 0x00, 0x01, + QANSEL_INSTRUCTION_MEASURE, 0x01, 0x13, + + //Load x and y + QANSEL_INSTRUCTION_RESET, 0x00, + QANSEL_INSTRUCTION_IF_E, 0x10, 0xEE, 0x00, + QANSEL_INSTRUCTION_X, 0x00, + QANSEL_INSTRUCTION_RESET, 0x01, + QANSEL_INSTRUCTION_IF_E, 0x11, 0xEE, 0x00, + QANSEL_INSTRUCTION_X, 0x01, + + //x and y + QANSEL_INSTRUCTION_CCX, 0x00, 0x01, 0x02, + QANSEL_INSTRUCTION_MEASURE, 0x02, 0x12, + + //Load (a xor b) and (x and y) + QANSEL_INSTRUCTION_RESET, 0x00, + QANSEL_INSTRUCTION_IF_E, 0x12, 0xEE, 0x00, + QANSEL_INSTRUCTION_X, 0x00, + QANSEL_INSTRUCTION_RESET, 0x01, + QANSEL_INSTRUCTION_IF_E, 0x13, 0xEE, 0x00, + QANSEL_INSTRUCTION_X, 0x01, + + //(a xor b) = (x and y) + QANSEL_INSTRUCTION_CX, 0x00, 0x01, + QANSEL_INSTRUCTION_X, 0x01, + + //Store final results + QANSEL_INSTRUCTION_MEASURE, 0x01, 0x10, + QANSEL_INSTRUCTION_SAMPLE, 0x10 \ No newline at end of file -- 2.39.5