Skip to content

Commit

Permalink
Merge pull request #19 from sebastiaanbrand/qasm-file-renaming
Browse files Browse the repository at this point in the history
Merge master and filename changes in interface-to-mtbdd
  • Loading branch information
MarinusVanDijk authored Jul 18, 2024
2 parents 7436338 + 2709241 commit ae759da
Show file tree
Hide file tree
Showing 41 changed files with 2,870 additions and 270 deletions.
16 changes: 16 additions & 0 deletions .github/workflows/cmake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ jobs:
sudo apt update
sudo apt install cmake cmake-data build-essential
sudo apt install libgmp-dev
sudo apt install libmpfr-dev
sudo apt install libmpc-dev
- name: Configure CMake
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
Expand All @@ -46,3 +48,17 @@ jobs:
# Execute tests defined by the CMake configuration.
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
run: ctest -C ${{env.BUILD_TYPE}}

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.10"

- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
pip install numpy
pip install pytest
- name: Test CLI
run: pytest
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ CMakeFiles/
CMakeCache.txt
qsylvan.pc
*.swp

# Python stuff
__pycache__/
2 changes: 1 addition & 1 deletion docs/documentation/c_interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ NOTE: In the code we are refering to the QMDDs as QMDDs, but our QMDDs are reall
* `qmdd_create_single_qubit_gate(int n, int t, gate_id_t gateid)` : Creates an n-qubit matrix QMDD which applies gate `gateid` to qubit t and I to all others.
* `qmdd_create_single_qubit_gates(int n, gate_id_t *gateid)` : Creates an n-qubit matrix QMDD which applies the given list of n `gatesid`'s to n qubits.
* `qmdd_create_single_qubit_gates_same(int n, gate_id_t gateid)` : Creates an n-qubit matrix QMDD which applies single qubit gate `gateid` to all qubits.
* `qmdd_create_controlled_gate(int n, int c, int t, gate_id_t gateid)` : Creates an n-qubit matrix QMDD which acts as a controlled-`gateid` gate on target qubit t with control qubit c, and I on all others. (Requires c < t.)
* `qmdd_create_cgate(int n, int c, int t, gate_id_t gateid)` : Creates an n-qubit matrix QMDD which acts as a controlled-`gateid` gate on target qubit t with control qubit c, and I on all others. (Requires c < t.)
* `qmdd_create_multi_cgate(int n, int *c_options, gate_id_t gateid)` : Creates a controlled-`gateid` gate which acts on the qubits as specified in by the `c_options` array. `c_options[k]` can contain `-1` = ignore qubit k (apply I), `0` = control qubit k on |0>, `1` = control qubit k on |1>, and `2` = the target qubit. (Requires the index of the target qubit to be greater than the indices of the controls.)
* `qmdd_create_all_control_phase(int n, bool *x)` : Creates an n-qubit CZ gate, controlled on all qubits. The array x specifies whether each qubit k is controlled on |0> (when x[k]=0) or on |1> (when x[k]=1).

Expand Down
25 changes: 13 additions & 12 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
set(SOURCES
grover_cnf.c
grover.c
random_circuit.c
shor.c
supremacy.c
)

if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
# add argp library for OSX
find_package(Argp REQUIRED)
Expand All @@ -26,12 +18,21 @@ macro(add_example NAME SOURCE)
endmacro(add_example)

add_example(bell_state bell_state.c)
# target_sources(bell_state PRIVATE getrss.c getrss.h)

add_example(vqc vqc.c)

set(ALGORITHM_EXAMPLES
grover_cnf.c
grover.c
random_circuit.c
shor.c
supremacy.c
)

add_example(alg_run alg_run.c)
target_sources(alg_run PRIVATE ${SOURCES})
target_sources(alg_run PRIVATE ${ALGORITHM_EXAMPLES})

add_example(test_algs test_algs.c)
target_sources(test_algs PRIVATE ${SOURCES})
target_sources(test_algs PRIVATE ${ALGORITHM_EXAMPLES})

add_executable(circuit_equivalence circuit_equivalence.c)
target_link_libraries(circuit_equivalence qsylvan qsylvan_qasm_parser)
7 changes: 4 additions & 3 deletions examples/alg_run.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ static size_t max_cachesize = 1LL<<20;
static size_t wgt_tab_size = 1LL<<23;
static double tolerance = 1e-14;
static int wgt_table_type = COMP_HASHMAP;
static int wgt_norm_strat = NORM_LARGEST;
static int wgt_norm_strat = NORM_MAX;
static int wgt_inv_caching = 1;

static int grover_flag = 1; // 0 = random, 1 = 11..1
Expand All @@ -46,7 +46,7 @@ static struct argp_option options[] =
{"qubits", 'q', "<nqubits>", 0, "Number of qubits (must be set for Grover)", 0},
{"rseed", 'r', "<random-seed>", 0, "Set random seed", 0},
{"depth", 'd', "<depth>", 0, "Depth of circuits with arbitrary depth (e.g. supremacy)", 0},
{"norm-strat", 's', "<low|largest|l2>", 0, "Edge weight normalization strategy", 0},
{"norm-strat", 's', "<low|max|min|l2>", 0, "Edge weight normalization strategy", 0},
{"tol", 1, "<tolerance>", 0, "Tolerance for deciding edge weights equal (default=1e-14)", 0},
{"inv-caching", 2, "<0|1>", 0, "Turn inverse chaching of edge weight computations on/off (default=on)", 0},
{"grover-flag", 20, "<random|ones>", 0, "Grover flag (default=11..1)", 0},
Expand All @@ -73,7 +73,8 @@ parse_opt(int key, char *arg, struct argp_state *state)
break;
case 's':
if (strcmp(arg, "low")==0) wgt_norm_strat = NORM_LOW;
else if (strcmp(arg, "largest")==0) wgt_norm_strat = NORM_LARGEST;
else if (strcmp(arg, "max")==0) wgt_norm_strat = NORM_MAX;
else if (strcmp(arg, "min")==0) wgt_norm_strat = NORM_MIN;
else if (strcasecmp(arg, "l2")==0) wgt_norm_strat = NORM_L2;
else argp_usage(state);
break;
Expand Down
281 changes: 281 additions & 0 deletions examples/circuit_equivalence.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
#include <inttypes.h>
#include <stdio.h>
#include <math.h>
#include <argp.h>
#include <time.h>
#include <sys/time.h>
#include <qsylvan.h>
#include "../qasm/simple_parser.h"

#define max(a,b) ((a > b) ? a : b)


/**********************<Arguments (configured via argp)>***********************/

static int workers = 1;
static size_t min_tablesize = 1LL<<20;
static size_t max_tablesize = 1LL<<25;
static size_t min_cachesize = 1LL<<16;
static size_t max_cachesize = 1LL<<18;
static size_t wgt_tab_size = 1LL<<23;
static double tolerance = 1e-14;
static int wgt_table_type = COMP_HASHMAP;
static int wgt_norm_strat = NORM_MAX;
static bool count_nodes = false;
static quantum_circuit_t* circuit_U;
static quantum_circuit_t* circuit_V;

static struct argp_option options[] =
{
{"workers", 'w', "<workers>", 0, "Number of workers/threads (default=1)", 0},
{"norm-strat", 's', "<low|max|min|l2>", 0, "Edge weight normalization strategy (default=max)", 0},
{"tol", 't', "<tolerance>", 0, "Tolerance for deciding edge weights equal (default=1e-14)", 0},
{"count-nodes", 'c', 0, 0, "Track maximum number of nodes", 0},
{0, 0, 0, 0, 0, 0}
};
static error_t
parse_opt(int key, char *arg, struct argp_state *state)
{
switch (key) {
case 'w':
workers = atoi(arg);
break;
case 's':
if (strcmp(arg, "low")==0) wgt_norm_strat = NORM_LOW;
else if (strcmp(arg, "max")==0) wgt_norm_strat = NORM_MAX;
else if (strcmp(arg, "min")==0) wgt_norm_strat = NORM_MIN;
else if (strcasecmp(arg, "l2")==0) wgt_norm_strat = NORM_L2;
else argp_usage(state);
break;
case 't':
tolerance = atof(arg);
break;
case 'c':
count_nodes = true;
break;
case ARGP_KEY_ARG:
if (state->arg_num == 0)
circuit_U = parse_qasm_file(arg);
else if (state->arg_num == 1)
circuit_V = parse_qasm_file(arg);
else
argp_usage(state);
break;
case ARGP_KEY_END:
if (state->arg_num < 1) argp_usage(state);
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
static struct argp argp = { options, parse_opt, "<U.qasm> <V.qasm>", 0, 0, 0, 0 };

/*********************</Arguments (configured via argp)>***********************/


typedef struct stats_s {
bool equivalent;
char counterexample[100];
double cpu_time;
double wall_time;
size_t max_nodes_total;
size_t max_nodes_U;
size_t max_nodes_V;
} stats_t;
stats_t stats = {0};


void print_stats() {
// print stats in JSON format
printf("{\n");
printf(" \"statistics\": {\n");
printf(" \"circuit_U\": \"%s\",\n", circuit_U->name);
printf(" \"circuit_V\": \"%s\",\n", circuit_V->name);
printf(" \"counterexample\": \"%s\",\n", stats.counterexample);
printf(" \"equivalent\" : %d,\n", (int)stats.equivalent);
printf(" \"max_nodes_total\": %" PRIu64 ",\n", stats.max_nodes_total);
printf(" \"max_nodes_U\": %" PRIu64 ",\n", stats.max_nodes_U);
printf(" \"max_nodes_V\": %" PRIu64 ",\n", stats.max_nodes_V);
printf(" \"n_qubits\": %d,\n", circuit_U->qreg_size);
printf(" \"time_cpu\": %lf,\n", stats.cpu_time);
printf(" \"time_wall\": %lf,\n", stats.wall_time);
printf(" \"tolerance\": %.5e,\n", tolerance);
printf(" \"wgt_norm_strat\": %d,\n", wgt_norm_strat);
printf(" \"workers\": %d\n", workers);
printf(" }\n");
printf("}\n");
}

/**
* Obtain current wallclock time
*/
static double
wctime()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (tv.tv_sec + 1E-6 * tv.tv_usec);
}


QMDD get_gate_matrix(quantum_op_t* gate, BDDVAR nqubits, bool dag) {
// TODO: move this relation between parsed quantum_op and internal gate
// somewhere else?
if (strcmp(gate->name, "id") == 0) {
return qmdd_create_all_identity_matrix(nqubits);
}
else if (strcmp(gate->name, "x") == 0) {
return qmdd_create_single_qubit_gate(nqubits, gate->targets[0], GATEID_X);
}
else if (strcmp(gate->name, "y") == 0) {
return qmdd_create_single_qubit_gate(nqubits, gate->targets[0], GATEID_Y);
}
else if (strcmp(gate->name, "z") == 0) {
return qmdd_create_single_qubit_gate(nqubits, gate->targets[0], GATEID_Z);
}
else if (strcmp(gate->name, "h") == 0) {
return qmdd_create_single_qubit_gate(nqubits, gate->targets[0], GATEID_H);
}
else if (strcmp(gate->name, "s") == 0) {
if (dag) return qmdd_create_single_qubit_gate(nqubits, gate->targets[0], GATEID_Sdag);
else return qmdd_create_single_qubit_gate(nqubits, gate->targets[0], GATEID_S);
}
else if (strcmp(gate->name, "sdg") == 0) {
if (dag) return qmdd_create_single_qubit_gate(nqubits, gate->targets[0], GATEID_S);
else return qmdd_create_single_qubit_gate(nqubits, gate->targets[0], GATEID_Sdag);
}
else if (strcmp(gate->name, "t") == 0) {
if (dag) return qmdd_create_single_qubit_gate(nqubits, gate->targets[0], GATEID_Tdag);
else return qmdd_create_single_qubit_gate(nqubits, gate->targets[0], GATEID_T);
}
else if (strcmp(gate->name, "tdg") == 0) {
if (dag) return qmdd_create_single_qubit_gate(nqubits, gate->targets[0], GATEID_T);
else return qmdd_create_single_qubit_gate(nqubits, gate->targets[0], GATEID_Tdag);
}
else if (strcmp(gate->name, "rx") == 0) {
if (dag) return qmdd_create_single_qubit_gate(nqubits, gate->targets[0], GATEID_Rx(-gate->angle[0]));
else return qmdd_create_single_qubit_gate(nqubits, gate->targets[0], GATEID_Rx(gate->angle[0]));
}
else if (strcmp(gate->name, "ry") == 0) {
if (dag) return qmdd_create_single_qubit_gate(nqubits, gate->targets[0], GATEID_Ry(-gate->angle[0]));
else return qmdd_create_single_qubit_gate(nqubits, gate->targets[0], GATEID_Ry(gate->angle[0]));
}
else if (strcmp(gate->name, "rz") == 0) {
if (dag) return qmdd_create_single_qubit_gate(nqubits, gate->targets[0], GATEID_Rz(-gate->angle[0]));
else return qmdd_create_single_qubit_gate(nqubits, gate->targets[0], GATEID_Rz(gate->angle[0]));
}
else if (strcmp(gate->name, "cx") == 0) {
return qmdd_create_cgate(nqubits, gate->ctrls[0], gate->targets[0], GATEID_X);
}
else if (strcmp(gate->name, "cz") == 0) {
return qmdd_create_cgate(nqubits, gate->ctrls[0], gate->targets[0], GATEID_Z);
}
else {
fprintf(stderr, "Gate '%s' currently unsupported\n", gate->name);
exit(EXIT_FAILURE);
}
}

QMDD compute_UPUdag(quantum_circuit_t *circuit, gate_id_t P, BDDVAR k) {
BDDVAR nqubits = circuit->qreg_size;
QMDD circ_matrix = qmdd_create_single_qubit_gate(nqubits, k, P);
QMDD tmp = AADD_ZERO;
aadd_protect(&circ_matrix);
aadd_protect(&tmp);

quantum_op_t *op = circuit->operations;
while (op != NULL) {
switch (op->type) {
case op_gate:
tmp = get_gate_matrix(op, nqubits, false);
circ_matrix = aadd_matmat_mult(tmp, circ_matrix, nqubits);
tmp = get_gate_matrix(op, nqubits, true);
circ_matrix = aadd_matmat_mult(circ_matrix, tmp, nqubits);
break;
case op_measurement:
fprintf(stderr, "ERROR: Measurments not supported\n");
exit(EXIT_FAILURE);
break;
default:
break;
}
op = op->next;
}

aadd_unprotect(&circ_matrix);
aadd_unprotect(&tmp);

return circ_matrix;
}

void circuit_equivalence_check(quantum_circuit_t *U, quantum_circuit_t *V) {
if (U->qreg_size != V->qreg_size) {
snprintf(stats.counterexample, sizeof(stats.counterexample),
"Circuits have different number of qubits");
return;
}

BDDVAR nqubits = U->qreg_size;

stats.equivalent = true;

uint64_t m = 0;
uint32_t XZ[2] = {GATEID_X, GATEID_Z};
char XZ_str[2] = {'X', 'Z'};
for (BDDVAR k = 0; k < nqubits; k++) {
for (int i = 0; i < 2; i++) {
QMDD qmdd_U = compute_UPUdag(U, XZ[i], k);
aadd_protect(&qmdd_U);
QMDD qmdd_V = compute_UPUdag(V, XZ[i], k);
aadd_unprotect(&qmdd_U);
// TODO: ^ remove global phase?

if (count_nodes) {
size_t nodes_U = aadd_countnodes(qmdd_U);
size_t nodes_V = aadd_countnodes(qmdd_V);
stats.max_nodes_total = max(stats.max_nodes_total,nodes_U+nodes_V);
stats.max_nodes_U = max(stats.max_nodes_U, nodes_U);
stats.max_nodes_V = max(stats.max_nodes_V, nodes_V);
}

if (qmdd_U != qmdd_V) {
stats.equivalent = false;
snprintf(stats.counterexample, sizeof(stats.counterexample),
"U*%c_%d*U^dag != V*%c_%d*V^dag", XZ_str[i], k, XZ_str[i], k);
return;
}
}
}
}

int main(int argc, char *argv[])
{
argp_parse(&argp, argc, argv, 0, 0, 0);

// Init
lace_start(workers, 0);
sylvan_set_sizes(min_tablesize, max_tablesize, min_cachesize, max_cachesize);
sylvan_init_package();
qsylvan_init_simulator(wgt_tab_size, wgt_tab_size, tolerance, wgt_table_type, wgt_norm_strat);

clock_t cpu_t1 = clock();
double wall_t1 = wctime();

circuit_equivalence_check(circuit_U, circuit_V);

clock_t cpu_t2 = clock();
double wall_t2 = wctime();

stats.cpu_time = (double)(cpu_t2 - cpu_t1) / CLOCKS_PER_SEC;
stats.wall_time = wall_t2 - wall_t1;
print_stats();

// Cleanup
sylvan_quit();
lace_stop();
free_quantum_circuit(circuit_U);
free_quantum_circuit(circuit_V);

return 0;
}
2 changes: 1 addition & 1 deletion examples/test_algs.c
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ int main()
// Simple Sylvan initialization
sylvan_set_sizes(1LL<<25, 1LL<<25, 1LL<<16, 1LL<<16);
sylvan_init_package();
qsylvan_init_simulator(1LL<<16, 1LL<<16, TOLERANCE, COMP_HASHMAP, NORM_LARGEST);
qsylvan_init_simulator(1LL<<16, 1LL<<16, TOLERANCE, COMP_HASHMAP, NORM_MAX);
qmdd_set_testing_mode(true); // turn on internal sanity tests

int res = runtests();
Expand Down
Loading

0 comments on commit ae759da

Please sign in to comment.