From d243568e9133afdebfa46d233475a2347cc5dda9 Mon Sep 17 00:00:00 2001 From: "Davide Gessa (dakk)" Date: Thu, 12 Oct 2023 12:53:54 +0200 Subject: [PATCH] refactor test.compute_and_compare_results --- test/test_compiler.py | 14 ++-- test/test_qlassf_bool.py | 28 +++---- test/test_qlassf_int.py | 34 ++++----- test/test_qlassf_tuple.py | 16 ++-- test/utils.py | 149 +++++++++++++++++++++----------------- 5 files changed, 129 insertions(+), 112 deletions(-) diff --git a/test/test_compiler.py b/test/test_compiler.py index 94a57ef9..5e7f7bec 100644 --- a/test/test_compiler.py +++ b/test/test_compiler.py @@ -16,36 +16,36 @@ from qlasskit import compiler, qlassf -from .utils import compare_circuit_truth_table +from .utils import compute_and_compare_results class TestCompiler(unittest.TestCase): def test_not_arg(self): f = "def test(a: bool) -> bool:\n\treturn not a" qf = qlassf(f, to_compile=True) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_and(self): f = "def test(a: bool, b: bool) -> bool:\n\treturn a and b" qf = qlassf(f, to_compile=True) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_and_long(self): f = "def test(a: bool, b: bool, c: bool, d: bool) -> bool:\n\treturn a and b and c and d" qf = qlassf(f, to_compile=True) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_and_long_with_not(self): f = "def test(a: bool, b: bool, c: bool, d: bool) -> bool:\n\treturn a and b and not c and d" qf = qlassf(f, to_compile=True) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_or(self): f = "def test(a: bool, b: bool) -> bool:\n\treturn a or b" qf = qlassf(f, to_compile=True) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_or_not(self): f = "def test(a: bool, b: bool) -> bool:\n\treturn not a or b" qf = qlassf(f, to_compile=True) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) diff --git a/test/test_qlassf_bool.py b/test/test_qlassf_bool.py index 1d8437b3..ad172f08 100644 --- a/test/test_qlassf_bool.py +++ b/test/test_qlassf_bool.py @@ -19,7 +19,7 @@ from qlasskit import QlassF, exceptions, qlassf -from .utils import COMPILATION_ENABLED, compare_circuit_truth_table +from .utils import COMPILATION_ENABLED, compute_and_compare_results a, b, c, d, e, g, h = symbols("a,b,c,d,e,g,h") _ret = Symbol("_ret") @@ -51,7 +51,7 @@ def test_arg_identity(self): self.assertEqual(len(qf.expressions), 1) self.assertEqual(qf.expressions[0][0], _ret) self.assertEqual(qf.expressions[0][1], ex) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_not_arg(self): ex = Not(a) @@ -60,7 +60,7 @@ def test_not_arg(self): self.assertEqual(len(qf.expressions), 1) self.assertEqual(qf.expressions[0][0], _ret) self.assertEqual(qf.expressions[0][1], ex) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_and(self): ex = And(Not(a), b) @@ -69,21 +69,21 @@ def test_and(self): self.assertEqual(len(qf.expressions), 1) self.assertEqual(qf.expressions[0][0], _ret) self.assertEqual(qf.expressions[0][1], ex) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_bool_eq(self): f = "def test(a: bool, b: bool) -> bool:\n\treturn a == b" qf = qlassf(f, to_compile=COMPILATION_ENABLED) self.assertEqual(len(qf.expressions), 1) self.assertEqual(qf.expressions[0][0], _ret) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_bool_neq(self): f = "def test(a: bool, b: bool) -> bool:\n\treturn a != b" qf = qlassf(f, to_compile=COMPILATION_ENABLED) self.assertEqual(len(qf.expressions), 1) self.assertEqual(qf.expressions[0][0], _ret) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_or_not(self): ex = Or(Not(a), b) @@ -92,7 +92,7 @@ def test_or_not(self): self.assertEqual(len(qf.expressions), 1) self.assertEqual(qf.expressions[0][0], _ret) self.assertEqual(qf.expressions[0][1], ex) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_multiple_arg(self): ex = And(a, And(Not(b), c)) @@ -101,7 +101,7 @@ def test_multiple_arg(self): self.assertEqual(len(qf.expressions), 1) self.assertEqual(qf.expressions[0][0], _ret) self.assertEqual(qf.expressions[0][1], ex) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_multiple_arg2(self): ex = And(a, And(Not(b), Or(a, c))) @@ -110,7 +110,7 @@ def test_multiple_arg2(self): self.assertEqual(len(qf.expressions), 1) self.assertEqual(qf.expressions[0][0], _ret) self.assertEqual(qf.expressions[0][1], ex) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_ifexp(self): ex = ITE(a, true, false) @@ -119,7 +119,7 @@ def test_ifexp(self): self.assertEqual(len(qf.expressions), 1) self.assertEqual(qf.expressions[0][0], _ret) self.assertEqual(qf.expressions[0][1], ex) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_ifexp2(self): ex = ITE(And(a, And(Not(b), c)), true, false) @@ -128,7 +128,7 @@ def test_ifexp2(self): self.assertEqual(len(qf.expressions), 1) self.assertEqual(qf.expressions[0][0], _ret) self.assertEqual(qf.expressions[0][1], ex) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_ifexp3(self): exp = ITE( @@ -144,7 +144,7 @@ def test_ifexp3(self): self.assertEqual(len(qf.expressions), 1) self.assertEqual(qf.expressions[0][0], _ret) self.assertEqual(qf.expressions[0][1], exp) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_assign(self): f = "def test(a: bool, b: bool) -> bool:\n\tc = a and b\n\treturn c" @@ -154,7 +154,7 @@ def test_assign(self): self.assertEqual(qf.expressions[0][1], And(a, b)) self.assertEqual(qf.expressions[1][0], _ret) self.assertEqual(qf.expressions[1][1], c) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_assign2(self): f = ( @@ -168,7 +168,7 @@ def test_assign2(self): self.assertEqual(qf.expressions[0][1], And(a, And(Not(b), c))) self.assertEqual(qf.expressions[1][0], _ret) self.assertEqual(qf.expressions[1][1], d) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_assign3(self): f = ( diff --git a/test/test_qlassf_int.py b/test/test_qlassf_int.py index 1a292455..f13b9386 100644 --- a/test/test_qlassf_int.py +++ b/test/test_qlassf_int.py @@ -19,7 +19,7 @@ from qlasskit import QlassF, exceptions, qlassf # Qint2 -from .utils import COMPILATION_ENABLED, compare_circuit_truth_table +from .utils import COMPILATION_ENABLED, compute_and_compare_results a, b, c, d = symbols("a,b,c,d") _ret = Symbol("_ret") @@ -43,7 +43,7 @@ def test_int_arg2(self): qf.expressions[0][1], ITE(And(Symbol("a.0"), b), Symbol("a.1"), Symbol("a.0")), ) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_const_return(self): f = "def test(a: Qint2) -> Qint2:\n\treturn 1" @@ -61,7 +61,7 @@ def test_int_return_tuple(self): f = "def test(a: Qint2) -> Tuple[Qint2, bool]:\n\tb = a[0] and a[1]\n\treturn (a, b)" qf = qlassf(f, to_compile=COMPILATION_ENABLED) self.assertEqual(len(qf.expressions), 4) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_int_tuple(self): f = "def test(a: Tuple[Qint2, Qint2]) -> bool:\n\treturn a[0][0] and a[1][1]" @@ -69,7 +69,7 @@ def test_int_tuple(self): self.assertEqual(len(qf.expressions), 1) self.assertEqual(qf.expressions[0][0], _ret) self.assertEqual(qf.expressions[0][1], And(Symbol("a.0.0"), Symbol("a.1.1"))) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_int_identity(self): f = "def test(a: Qint2) -> Qint2:\n\treturn a" @@ -79,7 +79,7 @@ def test_int_identity(self): self.assertEqual(qf.expressions[0][1], Symbol("a.0")) self.assertEqual(qf.expressions[1][0], Symbol("_ret.1")) self.assertEqual(qf.expressions[1][1], Symbol("a.1")) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) # TODO: need consts # def test_int_const(self): @@ -92,7 +92,7 @@ def test_int_const_compare_eq(self): self.assertEqual(len(qf.expressions), 1) self.assertEqual(qf.expressions[0][0], _ret) self.assertEqual(qf.expressions[0][1], And(Symbol("a.1"), Not(Symbol("a.0")))) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_int_const_compare_eq_different_type(self): f = "def test(a: Qint4) -> bool:\n\treturn a == 2" @@ -108,7 +108,7 @@ def test_int_const_compare_eq_different_type(self): Not(Symbol("a.3")), ), ) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_const_int_compare_eq_different_type(self): f = "def test(a: Qint4) -> bool:\n\treturn 2 == a" @@ -124,7 +124,7 @@ def test_const_int_compare_eq_different_type(self): Not(Symbol("a.3")), ), ) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_const_int_compare_neq_different_type(self): f = "def test(a: Qint4) -> bool:\n\treturn 2 != a" @@ -140,7 +140,7 @@ def test_const_int_compare_neq_different_type(self): Symbol("a.3"), ), ) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_int_int_compare_eq(self): f = "def test(a: Qint2, b: Qint2) -> bool:\n\treturn a == b" @@ -154,7 +154,7 @@ def test_int_int_compare_eq(self): Not(Xor(Symbol("a.1"), Symbol("b.1"))), ), ) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_int_int_compare_neq(self): f = "def test(a: Qint2, b: Qint2) -> bool:\n\treturn a != b" @@ -168,21 +168,21 @@ def test_int_int_compare_neq(self): Xor(Symbol("a.1"), Symbol("b.1")), ), ) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_const_int_compare_gt(self): f = "def test(a: Qint2) -> bool:\n\treturn a > 1" qf = qlassf(f, to_compile=COMPILATION_ENABLED) self.assertEqual(len(qf.expressions), 1) self.assertEqual(qf.expressions[0][0], _ret) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_const_int4_compare_gt(self): f = "def test(a: Qint4) -> bool:\n\treturn a > 6" qf = qlassf(f, to_compile=COMPILATION_ENABLED) self.assertEqual(len(qf.expressions), 1) self.assertEqual(qf.expressions[0][0], _ret) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) # def test_int4_int4_compare_gt(self): # f = "def test(a: Qint4, b: Qint4) -> bool:\n\treturn a > b" @@ -196,28 +196,28 @@ def test_const_int_compare_lt(self): qf = qlassf(f, to_compile=COMPILATION_ENABLED) self.assertEqual(len(qf.expressions), 1) self.assertEqual(qf.expressions[0][0], _ret) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_const_int4_compare_lt(self): f = "def test(a: Qint4) -> bool:\n\treturn a < 6" qf = qlassf(f, to_compile=COMPILATION_ENABLED) self.assertEqual(len(qf.expressions), 1) self.assertEqual(qf.expressions[0][0], _ret) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_int_int_compare_gt(self): f = "def test(a: Qint2, b: Qint2) -> bool:\n\treturn a > b" qf = qlassf(f, to_compile=COMPILATION_ENABLED) self.assertEqual(len(qf.expressions), 1) self.assertEqual(qf.expressions[0][0], _ret) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_int_int_compare_lt(self): f = "def test(a: Qint2, b: Qint2) -> bool:\n\treturn a < b" qf = qlassf(f, to_compile=COMPILATION_ENABLED) self.assertEqual(len(qf.expressions), 1) self.assertEqual(qf.expressions[0][0], _ret) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) # def test_const_int_compare_gte(self): # f = "def test(a: Qint2, b: Qint2) -> bool:\n\treturn a >= b" diff --git a/test/test_qlassf_tuple.py b/test/test_qlassf_tuple.py index cca95928..0e884a6c 100644 --- a/test/test_qlassf_tuple.py +++ b/test/test_qlassf_tuple.py @@ -20,7 +20,7 @@ from qlasskit import QlassF, exceptions, qlassf -from .utils import COMPILATION_ENABLED, compare_circuit_truth_table +from .utils import COMPILATION_ENABLED, compute_and_compare_results a, b, c, d = symbols("a,b,c,d") _ret = Symbol("_ret") @@ -37,7 +37,7 @@ def test_tuple_arg(self): self.assertEqual(len(qf.expressions), 1) self.assertEqual(qf.expressions[0][0], _ret) self.assertEqual(qf.expressions[0][1], And(a_0, a_1)) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_tuple_ite(self): f = "def test(b: bool, a: Tuple[bool, bool]) -> Tuple[bool,bool]:\n\treturn (a[1],a[0]) if b else a" @@ -58,7 +58,7 @@ def test_tuple_arg_assign(self): self.assertEqual(len(qf.expressions), 3) self.assertEqual(qf.expressions[-1][0], _ret) self.assertEqual(qf.expressions[-1][1], And(b, c)) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_tuple_of_tuple_arg(self): f = "def test(a: Tuple[Tuple[bool, bool], bool]) -> bool:\n\treturn a[0][0] and a[0][1] and a[1]" @@ -68,7 +68,7 @@ def test_tuple_of_tuple_arg(self): self.assertEqual( qf.expressions[0][1], And(Symbol("a.0.0"), And(Symbol("a.0.1"), a_1)) ) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_tuple_of_tuple_of_tuple_arg(self): f = ( @@ -82,7 +82,7 @@ def test_tuple_of_tuple_of_tuple_arg(self): qf.expressions[0][1], And(Symbol("a.0.0.0"), And(Symbol("a.0.0.1"), And(Symbol("a.0.1"), a_1))), ) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_tuple_assign(self): f = "def test(a: Tuple[bool, bool]) -> bool:\n\tb = (a[1],a[0])\n\treturn b[0] and b[1]" @@ -90,7 +90,7 @@ def test_tuple_assign(self): self.assertEqual(len(qf.expressions), 3) self.assertEqual(qf.expressions[-1][0], _ret) self.assertEqual(qf.expressions[-1][1], And(b_0, b_1)) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_tuple_assign2(self): f = ( @@ -102,7 +102,7 @@ def test_tuple_assign2(self): self.assertEqual(len(qf.expressions), 4) self.assertEqual(qf.expressions[-1][0], _ret) self.assertEqual(qf.expressions[-1][1], And(b_0, And(b_1, Symbol("b.2")))) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_tuple_assign3(self): f = ( @@ -116,7 +116,7 @@ def test_tuple_assign3(self): self.assertEqual( qf.expressions[-1][1], And(b_0, And(Symbol("b.1.0"), Symbol("b.1.1"))) ) - compare_circuit_truth_table(self, qf) + compute_and_compare_results(self, qf) def test_tuple_result(self): f = "def test(a: bool, b: bool) -> Tuple[bool,bool]:\n\treturn a,b" diff --git a/test/utils.py b/test/utils.py index 6457b96d..75982bed 100644 --- a/test/utils.py +++ b/test/utils.py @@ -50,79 +50,30 @@ def qiskit_measure_and_count(circ, shots=1): return counts -def compare_circuit_truth_table(cls, qf): - if not COMPILATION_ENABLED: - return - - truth_table = qf.truth_table() - gate = qf.gate() +def compute_result_of_qcircuit(cls, qf, truth_line): + """Simulate the quantum circuit for a given truth_line containing inputs""" circ = qf.circuit() - circ_qi = circ.export("circuit", "qiskit") + gate = qf.gate() + qc = QuantumCircuit(gate.num_qubits) + + # circ_qi = circ.export("circuit", "qiskit") # print(circ_qi.draw("text")) # print(qf.expressions) - for truth_line in truth_table: - qc = QuantumCircuit(gate.num_qubits) - - # Prepare inputs - [qc.initialize(1 if truth_line[i] else 0, i) for i in range(qf.input_size)] + # Prepare inputs + [qc.initialize(1 if truth_line[i] else 0, i) for i in range(qf.input_size)] - qc.append(gate, list(range(qf.num_qubits))) + qc.append(gate, list(range(qf.num_qubits))) - # Measure - counts = qiskit_measure_and_count(qc) + # Measure + counts = qiskit_measure_and_count(qc) - # Extract str of truthtable and result - truth_str = "".join( - map(lambda x: "1" if x else "0", truth_line[-qf.ret_size :]) - ) + res = list(counts.keys())[0][::-1] + res_str = "" + for qname in qf.truth_table_header()[-qf.ret_size :]: + res_str += res[circ.qubit_map[qname]] - res = list(counts.keys())[0][::-1] - res_str = "" - for qname in qf.truth_table_header()[-qf.ret_size :]: - res_str += res[circ.qubit_map[qname]] - - cls.assertEqual(len(counts), 1) - cls.assertEqual(truth_str, res_str) - - # Calculate original result from python function - def truth_to_arg(truth, i, argtt): - if argtt == bool: - return truth[i], i + 1 - elif inspect.isclass(argtt) and issubclass(argtt, Qtype): - return ( - argtt.from_bool(truth[i : i + argtt.BIT_SIZE]), - i + argtt.BIT_SIZE, - ) - else: # A tuple - al = [] - for x in get_args(argtt): - a, i = truth_to_arg(truth, i, x) - al.append(a) - return tuple(al), i - - args = [] - i = 0 - for x in qf.args: - arg, i = truth_to_arg(truth_line, i, x.ttype) - args.append(arg) - - cls.assertEqual(i, qf.input_size) - - res_original = qf.original_f(*args) - - def res_to_str(res): - if type(res) == bool: - return "1" if res else "0" - elif type(res) == tuple: - return "".join([res_to_str(x) for x in res]) - else: - return res.to_bool_str() - - res_original_str = res_to_str(res_original) - - cls.assertEqual(len(res_original_str), qf.ret_size) - cls.assertEqual(res_str, res_original_str) + cls.assertEqual(len(counts), 1) max_qubits = ( qf.input_size @@ -130,4 +81,70 @@ def res_to_str(res): + sum([gateinputcount(compiler.optimizer(e[1])) for e in qf.expressions]) ) - cls.assertLessEqual(gate.num_qubits, max_qubits) + cls.assertLessEqual(qf.gate().num_qubits, max_qubits) + + return res_str + + +def compute_result_of_originalf(cls, qf, truth_line): + """Compute the result of originalf for a given truth_line containing inputs""" + + def truth_to_arg(truth, i, argtt): + """Translate a bin string truth starting from i, to type argtt""" + if argtt == bool: + return truth[i], i + 1 + elif inspect.isclass(argtt) and issubclass(argtt, Qtype): + return ( + argtt.from_bool(truth[i : i + argtt.BIT_SIZE]), + i + argtt.BIT_SIZE, + ) + else: # A tuple + al = [] + for x in get_args(argtt): + a, i = truth_to_arg(truth, i, x) + al.append(a) + return tuple(al), i + + def res_to_str(res): + """Translate res to a binary string""" + if type(res) == bool: + return "1" if res else "0" + elif type(res) == tuple: + return "".join([res_to_str(x) for x in res]) + else: + return res.to_bool_str() + + args = [] + i = 0 + for x in qf.args: + arg, i = truth_to_arg(truth_line, i, x.ttype) + args.append(arg) + + cls.assertEqual(i, qf.input_size) + + res_original = qf.original_f(*args) + res_original_str = res_to_str(res_original) + + cls.assertEqual(len(res_original_str), qf.ret_size) + return res_original_str + + +def compute_and_compare_results(cls, qf): + """Create and simulate the qcircuit, and compare the result with the + truthtable and with the original_f""" + truth_table = qf.truth_table() + + for truth_line in truth_table: + # Extract str of truthtable and result + truth_str = "".join( + map(lambda x: "1" if x else "0", truth_line[-qf.ret_size :]) + ) + + # Calculate and compare the gate result + if COMPILATION_ENABLED: + res_qc = compute_result_of_qcircuit(cls, qf, truth_line) + cls.assertEqual(truth_str, res_qc) + + # Calculate and compare the originalf result + res_original = compute_result_of_originalf(cls, qf, truth_line) + cls.assertEqual(truth_str, res_original)