-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Micro-simulator in C #37
base: main
Are you sure you want to change the base?
Changes from all commits
6f3f500
26dc2be
5dd4ba8
9d37786
8c8ee5b
8d56e15
79d9586
49686bd
0cf6bb1
50d66aa
1c47f0f
16ec432
f98f11c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
prefix := `realpath prefix` | ||
|
||
export LD_LIBRARY_PATH := env("LD_LIBRARY_PATH", "") + ":" + prefix + "/lib/" | ||
export LD_DYLD_PATH:= env("LD_DYLD_PATH", "") + ":" + prefix + "/lib/" | ||
|
||
install: | ||
cargo cinstall --prefix {{prefix}} | ||
|
||
example-build name: | ||
cd examples/ && make {{name}} | ||
|
||
example name: (example-build name) | ||
cd examples/ && ./{{name}} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,4 @@ | ||
circuit | ||
draw | ||
|
||
libmicrosim.so | ||
execute |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,22 @@ | ||
PREFIX=$(shell realpath ../prefix/) | ||
export PKG_CONFIG_PATH := $(PREFIX)/lib/pkgconfig | ||
|
||
CFLAGS=-g -Wall -Wextra $(shell pkg-config --cflags qibo_core_c) | ||
LDFLAGS=$(shell pkg-config --libs qibo_core_c) | ||
|
||
SOURCES=$(wildcard *.c) | ||
EXECUTABLES=$(patsubst %.c,%,$(SOURCES)) | ||
|
||
|
||
libmicrosim.so: microsimulator/microsim.c microsimulator/microsim.h | ||
@cp ./microsimulator/microsim.h $(PREFIX)/include/ | ||
$(CC) $(CFLAGS) -I$(PREFIX)/include $(LDFLAGS) microsimulator/microsim.c -shared -o libmicrosim.so | ||
cp libmicrosim.so $(PREFIX)/lib/ | ||
|
||
CMICROSIM=-I$(PREFIX)/include | ||
LDMICROSIM=-L$(PREFIX)/lib -lmicrosim | ||
|
||
execute: libmicrosim.so execute.c | ||
$(CC) $(CFLAGS) $(CMICROSIM) $(LDFLAGS) $(LDMICROSIM) execute.c -o execute | ||
|
||
all: $(EXECUTABLES) |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
#include <stdio.h> | ||
#include <complex.h> | ||
#include "qibo_core_c.h" | ||
|
||
|
||
int main(int, char *[]) | ||
{ | ||
qibo_core_circuit *c = qibo_core_circuit_new(5); | ||
qibo_core_circuit_add(c, "H", (size_t[]) {0}, 1); | ||
qibo_core_circuit_add(c, "X", (size_t[]) {2}, 1); | ||
qibo_core_circuit_add(c, "H", (size_t[]) {2}, 1); | ||
qibo_core_circuit_add(c, "X", (size_t[]) {3}, 1); | ||
qibo_core_circuit_add(c, "CNOT", (size_t[]) {0, 3}, 2); | ||
|
||
printf("%s\n", qibo_core_circuit_draw(c)); | ||
|
||
return 0; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
#include <stdio.h> | ||
#include <complex.h> | ||
#include "qibo_core_c.h" | ||
#include "microsim.h" | ||
|
||
|
||
void print_state(complex double *state, const size_t size) { | ||
//const size_t size = sizeof(state) / sizeof(state[0]); | ||
for (size_t i=0; i < size; i++) { | ||
printf("%ld: %.4f, %.4f\n", i, creal(state[i]), cimag(state[i])); | ||
} | ||
} | ||
|
||
|
||
int main(int, char *[]) | ||
{ | ||
qibo_core_circuit *c = qibo_core_circuit_new(5); | ||
qibo_core_circuit_add(c, "H", (size_t[]) {0}, 1); | ||
qibo_core_circuit_add(c, "X", (size_t[]) {2}, 1); | ||
qibo_core_circuit_add(c, "H", (size_t[]) {2}, 1); | ||
qibo_core_circuit_add(c, "X", (size_t[]) {3}, 1); | ||
qibo_core_circuit_add(c, "CNOT", (size_t[]) {0, 3}, 2); | ||
|
||
printf("%s\n\n", qibo_core_circuit_draw(c)); | ||
|
||
// Initialize 5-qubit state vector | ||
complex double state[32] = { 0 }; | ||
state[0] = 1; | ||
|
||
// Execute circuit | ||
execute_circuit(c, state); | ||
|
||
// Print state vector | ||
print_state(state, 32); | ||
|
||
return 0; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
#include <stdio.h> | ||
#include <complex.h> | ||
#include <math.h> | ||
#include <string.h> | ||
#include <stdlib.h> | ||
#include "qibo_core_c.h" | ||
#include "microsim.h" | ||
|
||
// normalization factor | ||
double const H = 1.0 / sqrt(2); | ||
// matrices implementing gates | ||
struct { | ||
complex double h[4]; | ||
complex double x[4]; | ||
complex double y[4]; | ||
complex double z[4]; | ||
} const MATRICES = { | ||
{H, H, H, -H}, | ||
{0, 1, 1, 0}, | ||
{0, -I, I, 0}, | ||
{1, 0, 0, -1} | ||
}; | ||
|
||
// get matrix corresponding to the gate | ||
complex double const* matrix(const char* gate) { | ||
if (strcmp(gate, "H") == 0) { | ||
return MATRICES.h; | ||
} | ||
if (strcmp(gate, "Y") == 0) { | ||
return MATRICES.y; | ||
} | ||
if (strcmp(gate, "Z") == 0) { | ||
return MATRICES.z; | ||
} | ||
return MATRICES.x; | ||
} | ||
|
||
// integer comparison, compatible with qsort | ||
int compare(const void* a, const void* b) { | ||
return (*(int*)a - *(int*)b); | ||
} | ||
|
||
// TODO: description missing | ||
size_t control_index(size_t const g, size_t const* qubits, size_t const nqubits) { | ||
size_t i = g; | ||
for (size_t j = 0; j < nqubits; j++) { | ||
size_t const n = qubits[j]; | ||
size_t const k = 1 << n; | ||
i = ((i >> n) << (n + 1)) + (i & (k - 1)) + k; | ||
} | ||
return i; | ||
} | ||
|
||
// TODO: description missing | ||
void apply_controlled_gate( | ||
complex double* state, | ||
complex double const* gate, | ||
size_t const* qubits, | ||
size_t const ncontrols, | ||
size_t const nqubits | ||
) { | ||
size_t target = qubits[0]; | ||
|
||
size_t sorted_qubits[ncontrols + 1]; | ||
memcpy(sorted_qubits, qubits, (ncontrols + 1) * sizeof(size_t)); | ||
for (size_t i = 0; i < ncontrols + 1; i++) { | ||
sorted_qubits[i] = nqubits - sorted_qubits[i] - 1; | ||
} | ||
qsort(sorted_qubits, ncontrols + 1, sizeof(size_t), compare); | ||
|
||
size_t const nstates = 1 << (nqubits - ncontrols - 1); | ||
size_t const tk = 1 << (nqubits - target - 1); | ||
// TODO: This can be parallelized for large number of qubits | ||
for (size_t g = 0; g < nstates; g++) { | ||
size_t const i2 = control_index(g, sorted_qubits, ncontrols + 1); | ||
size_t const i1 = i2 - tk; | ||
complex double const state1 = state[i1]; | ||
complex double const state2 = state[i2]; | ||
state[i1] = gate[0] * state1 + gate[1] * state2; | ||
state[i2] = gate[2] * state1 + gate[3] * state2; | ||
} | ||
} | ||
|
||
// calculate number of control qubits | ||
// maybe can be improved if we expose gate n_elements to C API | ||
// but this will change anyway when we implement ``controlled_by`` | ||
size_t n_controls(const char* gate) { | ||
if (strcmp(gate, "CNOT") == 0) { | ||
return 1; | ||
} | ||
return 0; | ||
} | ||
|
||
// execute a qibo-core circuit, mutating the state in place | ||
void execute_circuit(qibo_core_circuit* circuit, complex double* state) { | ||
size_t const n_elements = qibo_core_circuit_n_elements(circuit); | ||
size_t const n_gates = qibo_core_circuit_n_gates(circuit); | ||
|
||
for (size_t gid = 0; gid < n_gates; gid++) { | ||
char const* gate = qibo_core_circuit_gate(circuit, gid); | ||
|
||
size_t const* elements; | ||
size_t n_gate_elements; | ||
qibo_core_circuit_elements(circuit, gid, &elements, &n_gate_elements); | ||
|
||
complex double const* matrix_ = matrix(gate); | ||
size_t n_controls_ = n_controls(gate); | ||
apply_controlled_gate(state, matrix_, elements, n_controls_, n_elements); | ||
|
||
qibo_core_circuit_free_elements(elements, n_gate_elements); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
#ifndef MICROSIM_H | ||
#define MICROSIM_H | ||
|
||
#include "qibo_core_c.h" | ||
|
||
void execute_circuit(qibo_core_circuit *circuit, complex double *state); | ||
|
||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ use std::slice; | |
|
||
use qibo_core::prelude::*; | ||
|
||
|
||
#[no_mangle] | ||
pub extern "C" fn qibo_core_circuit_new(n_elements: usize) -> Box<Circuit> { | ||
Box::new(Circuit::new(n_elements)) | ||
|
@@ -36,6 +37,47 @@ pub extern "C" fn qibo_core_circuit_n_elements(circuit: &Circuit) -> usize { | |
circuit.n_elements() | ||
} | ||
|
||
#[no_mangle] | ||
pub extern "C" fn qibo_core_circuit_n_gates(circuit: &Circuit) -> usize { | ||
circuit.n_gates() | ||
} | ||
|
||
#[no_mangle] | ||
pub extern "C" fn qibo_core_circuit_gate(circuit: &Circuit, gid: usize) -> *const c_char { | ||
let gate = circuit.gate(gid); | ||
let name = match gate { | ||
Gate::One(One::H) => "H", | ||
Gate::One(One::X) => "X", | ||
Gate::One(One::Y) => "Y", | ||
Gate::One(One::Z) => "Z", | ||
Gate::Two(Two::CNOT) => "CNOT", | ||
_ => todo!() | ||
}; | ||
|
||
CString::new(name).unwrap().into_raw() | ||
} | ||
|
||
#[no_mangle] | ||
pub extern "C" fn qibo_core_circuit_elements(circuit: &Circuit, gid: usize, ptr: *mut *const usize, len: *mut usize) { | ||
let elements = circuit.elements(gid); | ||
// Complaints about what follows are to be directed to ChatGPT | ||
let boxed_slice = elements.clone().into_boxed_slice(); | ||
unsafe { | ||
*ptr = boxed_slice.as_ptr(); | ||
*len = elements.len(); | ||
} | ||
std::mem::forget(boxed_slice); | ||
} | ||
Comment on lines
+60
to
+70
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, you're avoiding returning in this function, resorting to a more complex solution (with mutable inputs, used to effectively return). I will fix it, since this should be a very simple function. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, did not want to avoid returning and I would prefer something simpler. I was just not sure how to return a Rust vector to C, so I used chatGPT (as written in the comment), probably not with the best prompt, to get something working. Same for your next comment (about If you are planning to fix, great! Otherwise I can also have another look. Note, that C does not need to manipulate the vector of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm planning to improve over this. I'd like to return a structured object (i.e. essentially a smart pointer, like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree, and most likely, we should focus on the C++ API rather than the pure C, encapsulating objects in std::array, std::vector and smart pointers. I don't believe we will see interest from collaborators to have a pure C backend soon. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I perfectly agree. That's why I was also interested in direct usage of I will try to have another go at packaging it properly, with a more advanced build tool, since now we'll need the C++ applications (including examples) to depend on the C++ library, that in turn will depend on the C library (whose dependency on |
||
|
||
#[no_mangle] | ||
pub extern "C" fn qibo_core_circuit_free_elements(ptr: *const usize, len: usize) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a possible way for freeing the elements. However, once you passed the pointer down to C, you should be able to call However, the proposal would be to directly use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
if !ptr.is_null() { | ||
unsafe { | ||
let _ = Vec::from_raw_parts(ptr as *mut usize, len, len); | ||
} | ||
} | ||
} | ||
alecandido marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#[no_mangle] | ||
pub extern "C" fn qibo_core_circuit_draw(circuit: &Circuit) -> *mut c_char { | ||
let repr = circuit.draw(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be moved in the
qibo_core
library (even the main Rust one), start making astate
object (that could simply wrap an array ofdouble
).