diff --git a/lib_q_computer.py b/lib_q_computer.py index fc6c2a6..819753f 100644 --- a/lib_q_computer.py +++ b/lib_q_computer.py @@ -1,12 +1,23 @@ +import random + import numpy as np # Raw matrixes to be used for initialization of qubits and gates + +# |0> and |1> __0 = [[1], [0]] __1 = [[0], [1]] +# |+> and |-> +__p = [[1 / np.sqrt(2)], + [1 / np.sqrt(2)]] +__m = [[1 / np.sqrt(2)], + [-1 / np.sqrt(2)]] + +# Gate/Operator raws _I = [ [1, 0], [0, 1], @@ -42,6 +53,7 @@ _CNOT = [ class State(object): """Represents a quantum state""" + def __init__(self, matrix_state): """ Can be initialized with a matrix, e.g. for |0> this is [[0],[1]] @@ -51,8 +63,10 @@ class State(object): def __getitem__(self, item): """ - Kind of hacky way to store a substate of an item for use in Gate operations - so that one can use state[0] and the encompassing operator can access this. + Kind of hacky way to store a substate of an item for use in Gate + operations + so that one can use state[0] and the encompassing operator can access + this. :param item: :return: """ @@ -67,6 +81,31 @@ class State(object): def __repr__(self): return str(self.matrix_state) + def __eq__(self, other): + return np.array_equal(self.matrix_state, other.matrix_state) + + def density_matrix(self): + return self.matrix_state.dot(np.transpose(self.matrix_state)) + + def measure_probability(self): + """ + In a qbit [a, b] normalized: |a|^2 + |b|^2 = 1 + Probability of 0 is |a|^2 and 1 with prob |b|^2 + + :returns: tuple of probabilities to measure 0 or 1""" + return [np.abs(qbit[0]) ** 2 for qbit in self.matrix_state] + + def measure(self): + """ + This gets a random choice of either 0 and 1 with weights + based on the probabilities of the qbit + + :returns: classical bit based on qbit probabilities""" + weights = self.measure_probability() + format_str = "{:0" + str(int(np.ceil(np.log2(len(weights))))) + "b}" + choices = [format_str.format(i) for i in range(len(weights))] + return random.choices(choices, weights)[0] + def kron(_list): """Calculates a Kronicker product of a list of matrices @@ -83,9 +122,11 @@ class Gate(State): def on(self, other_state): """ Applies a gate operation on a state. - If applying to a substate, use the index of the substate, e.g. `H.on(bell[0])` will apply the Hadamard gate + If applying to a substate, use the index of the substate, e.g. `H.on( + bell[0])` will apply the Hadamard gate on the 0th qubit of the `bell` state. - :param other_state: another state (e.g. `H.on(q1)` or a list of states (e.g. for `CNOT.on([q1, q2])`) + :param other_state: another state (e.g. `H.on(q1)` or a list of + states (e.g. for `CNOT.on([q1, q2])`) :return: the state after the application of the Gate """ this_state_m = self.matrix_state @@ -97,7 +138,8 @@ class Gate(State): # Use the Kronicker product of Identity with the state.item where # state.item is the substate larger_side = max(len(self), len(other_state)) - _list = [this_state_m if i == other_state.item else _I for i in range(larger_side)] + _list = [this_state_m if i == other_state.item else _I + for i in range(larger_side)] this_state_m = kron(_list) return State(this_state_m.dot(other_state_m)) @@ -113,3 +155,38 @@ Z = Gate(_Z) H = Gate(_H) CNOT = Gate(_CNOT) + + +def run_qbit_tests(): + # asserts are sets of tests to check if mathz workz + # initialize qubits + _0 = Qubit(__0) + _1 = Qubit(__1) + _p = Qubit(__p) + _m = Qubit(__m) + + # Identity: verify that I|0> == |0> and I|1> == |0> + assert I.on(_0) == _0 + assert I.on(_1) == _1 + + # Pauli X: verify that X|0> == |1> and X|1> == |0> + assert X.on(_0) == _1 + assert X.on(_1) == _0 + + # measure probabilities in sigma_x of |0> and |1> + # using allclose since dealing with floats + assert np.allclose(_0.measure_probability(), (1.0, 0.0)) + assert np.allclose(_1.measure_probability(), (0.0, 1.0)) + + # applying Hadamard puts the qbit in orthogonal +/- basis + assert np.array_equal(H.on(_0), _p) + assert np.array_equal(H.on(_1), _m) + + # measure probabilities in sigma_x of |+> and |-> + # using allclose since dealing with floats + assert np.allclose(_p.measure_probability(), (0.5, 0.5)) + assert np.allclose(_m.measure_probability(), (0.5, 0.5)) + + +if __name__ == "__main__": + run_qbit_tests()