Skip to content

Commit 111ac31

Browse files
committed
optimize qlassf.truth_table, introduce cnotsim
1 parent cfd4138 commit 111ac31

File tree

6 files changed

+118
-20
lines changed

6 files changed

+118
-20
lines changed

qlasskit/boolquant.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818

1919
class QuantumBooleanGate(Function, Boolean):
20+
# TODO: add an eval method to be able to simplify H(H(a)) => a
21+
2022
def build(name: str):
2123
return type(name, (QuantumBooleanGate,), {})
2224

qlasskit/qcircuit/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@
2222
from .qcircuit import QCircuit # noqa: F401, E402
2323
from .qcircuitenhanced import QCircuitEnhanced # noqa: F401, E402
2424
from .qcircuitwrapper import QCircuitWrapper, reindex # noqa: F401, E402
25+
from .cnotsim import CNotSim, GateNotSimulableException # noqa: F401, E402

qlasskit/qcircuit/cnotsim.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Copyright 2023 Davide Gessa
2+
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from typing import List, Union
16+
17+
from .gates import CCX, CX, MCX, MCtrl, X
18+
from .qcircuit import QCircuit
19+
20+
21+
class GateNotSimulableException(Exception):
22+
def __init__(self, g):
23+
super(GateNotSimulableException, self).__init__(
24+
f"Gate not simulable by CNotSim: {g}"
25+
)
26+
27+
28+
class CNotSim:
29+
"""A dummy simulator for X, CX, CCX, MCX circuits"""
30+
31+
def simulate( # noqa: C901
32+
self,
33+
qc: QCircuit,
34+
initialize: List[bool] = [],
35+
to_measure: List[Union[int, str]] = [],
36+
) -> List[bool]:
37+
"""Simulate a quantum circuit qc
38+
39+
Args:
40+
initialize (List[bool], optional): list of initializer for the qubits
41+
to_measure (List[int], optional): list of qubits to measure; measure all
42+
if None.
43+
"""
44+
qubits = [False] * qc.num_qubits
45+
46+
for i, x in enumerate(initialize):
47+
qubits[i] = x
48+
49+
for g, w, p in qc.gates:
50+
if isinstance(g, X):
51+
qubits[w[0]] = not qubits[w[0]]
52+
elif isinstance(g, CX):
53+
if qubits[w[0]]:
54+
qubits[w[1]] = not qubits[w[1]]
55+
elif isinstance(g, CCX):
56+
if qubits[w[0]] and qubits[w[1]]:
57+
qubits[w[2]] = not qubits[w[2]]
58+
elif isinstance(g, MCX):
59+
if all([qubits[x] for x in w[0:-1]]):
60+
qubits[w[-1]] = not qubits[w[-1]]
61+
elif isinstance(g, MCtrl) and isinstance(g.gate, X):
62+
if all([qubits[x] for x in w[0:-1]]):
63+
qubits[w[-1]] = not qubits[w[-1]]
64+
else:
65+
raise GateNotSimulableException(g)
66+
67+
if len(to_measure) == 0:
68+
return qubits
69+
70+
measured = []
71+
for q in to_measure:
72+
measured.append(qubits[qc[q]])
73+
return measured

qlasskit/qlassfun.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from .ast2ast import ast2ast
2424
from .ast2logic import Arg, Args, BoolExpList, LogicFun, flatten, translate_ast
2525
from .boolopt import BoolOptimizerProfile, bestWorkingOptimizer
26+
from .boolopt.bool_optimizer import merge_expressions
2627
from .boolquant import Q # noqa: F403, F401
2728
from .compiler import SupportedCompiler, to_quantum
2829
from .qcircuit import QCircuitWrapper
@@ -134,6 +135,8 @@ def truth_table(self, max=None) -> List[List[bool]]:
134135
f"Max truth table size reached: {bits + self.output_size} > {MAX_TRUTH_TABLE_SIZE}"
135136
)
136137

138+
exps = merge_expressions(self.expressions)
139+
137140
for i in range(
138141
0, 2**bits, int(2**bits / max) if max and max < 2**bits else 1
139142
):
@@ -142,7 +145,7 @@ def truth_table(self, max=None) -> List[List[bool]]:
142145
bin_arr = list(map(lambda c: c == "1", bin_str))
143146
known = list(zip(arg_bits, bin_arr))
144147

145-
for ename, exp in self.expressions:
148+
for ename, exp in exps:
146149
exp_sub = exp.subs(known)
147150

148151
known = list(filter(lambda x: x[0] != ename.name, known))

test/test_qlassf_int.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -392,11 +392,6 @@ def test_mul(self):
392392
qf = qlassf(f, to_compile=COMPILATION_ENABLED, compiler=self.compiler)
393393
compute_and_compare_results(self, qf)
394394

395-
# def test_mul2(self):
396-
# f = "def test(a: Qint4, b: Qint4) -> Qint8: return a * b"
397-
# qf = qlassf(f, to_compile=COMPILATION_ENABLED, compiler=self.compiler)
398-
# compute_and_compare_results(self, qf)
399-
400395
def test_mul_and_sum(self):
401396
f = "def test(a: Qint2, b: Qint2, c: Qint2) -> Qint2: return a * b + c"
402397
qf = qlassf(f, to_compile=COMPILATION_ENABLED, compiler=self.compiler)
@@ -412,8 +407,7 @@ def test_mul_const(self):
412407
qf = qlassf(f, compiler=self.compiler, to_compile=COMPILATION_ENABLED)
413408
compute_and_compare_results(self, qf)
414409

415-
# def test_mul4(self):
416-
# f = "def test(a: Qint4, b: Qint4) -> Qint4: return a * b"
417-
# qf = qlassf(f, to_compile=COMPILATION_ENABLED, compiler=self.compiler)
418-
# print(qf.expressions)
419-
# compute_and_compare_results(self, qf)
410+
def test_mul4(self):
411+
f = "def test(a: Qint4, b: Qint4) -> Qint8: return a * b"
412+
qf = qlassf(f, to_compile=COMPILATION_ENABLED, compiler=self.compiler)
413+
compute_and_compare_results(self, qf)

test/utils.py

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import inspect
1616
import json
17+
import os
1718
import random
1819
import threading
1920
from typing import Tuple, get_args
@@ -23,6 +24,7 @@
2324
from sympy.logic.boolalg import gateinputcount
2425

2526
from qlasskit import Qint, QlassF, Qtype, compiler, const_to_qtype
27+
from qlasskit.qcircuit import CNotSim, GateNotSimulableException
2628

2729
COMPILATION_ENABLED = True
2830

@@ -122,13 +124,19 @@ def compute_result_of_qcircuit(cls, qf, truth_line):
122124

123125
cls.assertEqual(len(counts), 1)
124126

125-
max_qubits = (
126-
qf.input_size
127-
+ len(qf.expressions)
128-
+ sum([gateinputcount(e[1]) for e in qf.expressions])
129-
)
127+
return res_str
128+
129+
130+
def compute_result_of_qcircuit_using_cnotsim(cls, qf, truth_line):
131+
qc = qf.circuit()
132+
133+
qinit = [True if truth_line[i] else False for i in range(qf.input_size)]
130134

131-
cls.assertLessEqual(qf.gate().num_qubits, max_qubits)
135+
res = CNotSim().simulate(qc, initialize=qinit)
136+
137+
res_str = ""
138+
for qname in qf.truth_table_header()[-qf.output_size :]:
139+
res_str += "1" if res[qc[qname]] else "0"
132140

133141
return res_str
134142

@@ -202,7 +210,7 @@ def compute_and_compare_results(cls, qf, test_original_f=True):
202210
elif COMPILATION_ENABLED:
203211
qc_truth = truth_table
204212

205-
circ_qi = qf.circuit().export("circuit", "qiskit")
213+
# circ_qi = qf.circuit().export("circuit", "qiskit")
206214

207215
# update_statistics(qf.circuit().num_qubits, qf.circuit().num_gates)
208216

@@ -223,5 +231,22 @@ def compute_and_compare_results(cls, qf, test_original_f=True):
223231

224232
# Calculate and compare the gate result
225233
if qc_truth and truth_line in qc_truth and COMPILATION_ENABLED:
226-
res_qc = compute_result_of_qcircuit(cls, qf, truth_line)
227-
cls.assertEqual(truth_str, res_qc)
234+
max_qubits = (
235+
qf.input_size
236+
+ len(qf.expressions)
237+
+ sum([gateinputcount(e[1]) for e in qf.expressions])
238+
)
239+
cls.assertLessEqual(qf.num_qubits, max_qubits)
240+
241+
if os.getenv("GITHUB_ACTIONS"):
242+
res_qc = compute_result_of_qcircuit(cls, qf, truth_line)
243+
cls.assertEqual(truth_str, res_qc)
244+
else:
245+
try:
246+
res_qc2 = compute_result_of_qcircuit_using_cnotsim(
247+
cls, qf, truth_line
248+
)
249+
cls.assertEqual(truth_str, res_qc2)
250+
except GateNotSimulableException:
251+
res_qc = compute_result_of_qcircuit(cls, qf, truth_line)
252+
cls.assertEqual(truth_str, res_qc)

0 commit comments

Comments
 (0)