diff --git a/3sat.py b/3sat.py new file mode 100644 index 0000000..2d03d1b --- /dev/null +++ b/3sat.py @@ -0,0 +1,101 @@ +import inspect +import itertools +import random + +from grover import classical_func_search + + +class X(object): + """""" + + def __init__(self, i): + self.i = i + + +class SAT(object): + def __init__(self, predicates=3): + self.and_clauses = [] + self.and_clauses_funcs = [] + self.predicates = predicates + + def or_(self, or_clauses): + def inner_or_(params): + for or_clause in or_clauses: + yes_no, var = or_clause[0], or_clause[1].i - 1 + if yes_no(params[var]): + return True + return False + + return inner_or_ + + def and_(self, and_clauses): + def inner_and_(params): + for and_clause in and_clauses: + if and_clause(params): + return False + return True + + return inner_and_ + + def yes(self, x): + return x + + def no(self, x): + return not x + + def generate(self, ands=1): + params = [X(i) for i in range(1, self.predicates + 1)] + + def or_clause(or_clauses): + def inner_or_clause(params): + return self.or_(or_clauses)(params) + + return inner_or_clause + + for _ in range(ands): + or_clauses = list(zip(random.choices([self.yes, self.no], k=3), random.sample(params, k=3))) + self.and_clauses.append(or_clauses) + self.and_clauses_funcs.append(or_clause(or_clauses)) + + def inner_generate(args): + return self.and_(self.and_clauses_funcs)(args) + + self.inner = inner_generate + + def __call__(self, args): + self.inner(args) + + def show(self): + for j, _or_clause in enumerate(self.and_clauses): + for i, predicate in enumerate(_or_clause): + if i == 0: + print("( ", end='') + trueness, var = predicate + if trueness == self.yes: + print("x_{}".format(var.i), end='') + else: + print("¬x_{}".format(var.i), end='') + if i + 1 != len(_or_clause): + print(" ∨ ", end='') + if j + 1 != len(self.and_clauses): + print(" ) ∧ ") + else: + print(" )") + + +def classical_3sat(func): + # Generate all possible true/false tupples for the 3-sat problem + input_range = list(itertools.product([True, False], repeat=func.predicates)) + random.shuffle(input_range) + return classical_func_search(func, input_range) + + +def main(): + gen_3sat = SAT(predicates=6) + gen_3sat.generate(ands=1) + gen_3sat.show() + print(classical_3sat(gen_3sat)) + + +if __name__ == "__main__": + main() diff --git a/compiler.py b/compiler.py new file mode 100644 index 0000000..4fc9e2a --- /dev/null +++ b/compiler.py @@ -0,0 +1,9 @@ +from lib_q_computer_math import s + + +def int_to_state(i): + return s("|{}>".format(bin(i))) + + +if __name__ == "__main__": + print(int_to_state(42)) diff --git a/grover.py b/grover.py new file mode 100644 index 0000000..b8c7ed4 --- /dev/null +++ b/grover.py @@ -0,0 +1,45 @@ +# http://twistedoakstudios.com/blog/Post2644_grovers-quantum-search-algorithm +import inspect +import itertools +import random +from pprint import pprint + + +def classical_func_search(func, input_range): + """Grover’s algorithm takes a function, searches through + the implicit list of possible inputs to that function, and + returns inputs that causes the function to return true. + """ + rv = [] + for i, params in enumerate(input_range): + result = func(params) + if result: + rv.append(params) + return rv + + +def _3sat(x1, x2, x3): + # https://cstheory.stackexchange.com/questions/38538/oracle-construction-for-grovers-algorithm + # 3-SAT from here: https://qiskit.org/textbook/ch-applications/satisfiability-grover.html + return (not x1 or not x2 or not x3) and \ + (x1 or not x2 or x3) and \ + (x1 or x2 or not x3) and \ + (x1 or not x2 or not x3) and \ + (not x1 or x2 or x3) + + +def classical_3sat(func): + # Generate all possible true/false tupples for the 3-sat problem + sig = inspect.signature(func) + params = sig.parameters + input_range = list(itertools.product([True, False], repeat=len(params))) + random.shuffle(input_range) + return classical_func_search(func, input_range) + + +def main(): + pprint(classical_3sat(_3sat)) + + +if __name__ == "__main__": + main() diff --git a/lib_q_computer_math.py b/lib_q_computer_math.py index 6e31508..2ea8b79 100644 --- a/lib_q_computer_math.py +++ b/lib_q_computer_math.py @@ -48,7 +48,7 @@ class Matrix(object): def __eq__(self, other): if isinstance(other, Complex): return bool(np.allclose(self.m, other)) - elif isinstance(other, TwoQubitPartial): + elif isinstance(other, PartialQubit): return False return np.allclose(self.m, other.m) @@ -291,16 +291,16 @@ class State(Vector): # [0, 0, 1, 1, 0, 0, 1, 1] partial_measurement_of_qbit = [int(b[qubit_n - 1]) for b in bin_repr] # [0, 1, 4, 5] - indexes_for_p_0 = [i for i, index in enumerate(partial_measurement_of_qbit) if index==0] + indexes_for_p_0 = [i for i, index in enumerate(partial_measurement_of_qbit) if index == 0] weights_0 = [self.get_prob(j) for j in indexes_for_p_0] choices_0 = [format_str.format(i) for i in indexes_for_p_0] - measurement_result = random.choices(choices_0, weights_0)[0][qubit_n-1] + measurement_result = random.choices(choices_0, weights_0)[0][qubit_n - 1] # TODO: Verify if this is the correct collapse to lower dimension after partial measurement # https://www.youtube.com/watch?v=MG_9JWsrKtM&list=PL1826E60FD05B44E4&index=16 normalization_factor = np.sqrt(np.sum([self.m[i][0] for i in indexes_for_p_0])) # TODO: This can be 0... self.m = [ - [(self.m[i][0]**2)/normalization_factor] for i in indexes_for_p_0 + [(self.m[i][0] ** 2) / normalization_factor] for i in indexes_for_p_0 ] return measurement_result @@ -555,7 +555,7 @@ class HermitianOperator(LinearTransformation, HermitianMatrix): # np.kron(np.outer(_0.m, _0.m), np.eye(2)) + np.kron(np.outer(_1.m, _1.m), X.m) # _0.x(_0) * Matrix(I.m) + _1.x(_1) * Matrix(X.m) -class TwoQubitPartial(object): +class PartialQubit(object): def __init__(self, rpr): self.rpr = rpr self.operator = None @@ -564,12 +564,12 @@ class TwoQubitPartial(object): return str("-{}-".format(self.rpr)) -C_partial = TwoQubitPartial("C") -x_partial = TwoQubitPartial("x") +C_partial = PartialQubit("C") +x_partial = PartialQubit("x") class TwoQubitOperator(UnitaryOperator): - def __init__(self, m: ListOrNdarray, A: TwoQubitPartial, B: TwoQubitPartial, + def __init__(self, m: ListOrNdarray, A: PartialQubit, B: PartialQubit, A_p: UnitaryOperator, B_p: UnitaryOperator, *args, **kwargs): super().__init__(m, *args, **kwargs) A.operator, B.operator = self, self @@ -697,11 +697,23 @@ H = UnitaryOperator([[1 / np.sqrt(2), 1 / np.sqrt(2)], [1 / np.sqrt(2), -1 / np.sqrt(2)], ], name="H") -CNOT = TwoQubitOperator([[1, 0, 0, 0], - [0, 1, 0, 0], - [0, 0, 0, 1], - [0, 0, 1, 0], ], - C_partial, x_partial, I, X) +CNOT = TwoQubitOperator([ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 0, 1], + [0, 0, 1, 0], +], C_partial, x_partial, I, X) + +# TOFFOLLI_GATE = ThreeQubitOperator([ +# [1, 0, 0, 0, 0, 0, 0, 0], +# [0, 1, 0, 0, 0, 0, 0, 0], +# [0, 0, 1, 0, 0, 0, 0, 0], +# [0, 0, 0, 1, 0, 0, 0, 0], +# [0, 0, 0, 0, 1, 0, 0, 0], +# [0, 0, 0, 0, 0, 1, 0, 0], +# [0, 0, 0, 0, 0, 0, 0, 1], +# [0, 0, 0, 0, 0, 0, 1, 0], +# ], C_partial, C_partial, x_partial, I, I, X) def assert_raises(exception, msg, callable, *args, **kwargs): @@ -1037,7 +1049,7 @@ class QuantumCircuit(object): self.add_row(row_data) def compose_quantum_state(self, step): - partials = [op for op in step if isinstance(op, TwoQubitPartial)] + partials = [op for op in step if isinstance(op, PartialQubit)] # TODO: No more than 1 TwoQubitGate **OR** UnitaryOperator can be used in a step for partial in partials: two_qubit_op = partial.operator @@ -1096,8 +1108,8 @@ class QuantumProcessor(object): self.reset() def compose_quantum_state(self, step): - if any([type(s) is TwoQubitPartial for s in step]): - two_qubit_gates = filter(lambda s: type(s) is TwoQubitPartial, step) + if any([type(s) is PartialQubit for s in step]): + two_qubit_gates = filter(lambda s: type(s) is PartialQubit, step) state = [] for two_qubit_gate in two_qubit_gates: two_qubit_gate.operator.verify_step(step)