-
Notifications
You must be signed in to change notification settings - Fork 2.4k
/
classicalfunction.py
173 lines (140 loc) · 5.61 KB
/
classicalfunction.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# This code is part of Qiskit.
#
# (C) Copyright IBM 2020.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""ClassicalFunction class"""
import ast
from typing import Callable, Optional
from qiskit.circuit import QuantumCircuit, QuantumRegister
from qiskit.exceptions import QiskitError
from qiskit.utils.optionals import HAS_TWEEDLEDUM
from .classical_element import ClassicalElement
from .classical_function_visitor import ClassicalFunctionVisitor
from .utils import tweedledum2qiskit
@HAS_TWEEDLEDUM.require_in_instance
class ClassicalFunction(ClassicalElement):
"""Represent a classical function and its logic network."""
def __init__(self, source, name=None):
"""Creates a ``ClassicalFunction`` from Python source code in ``source``.
The code should be a single function with types.
Args:
source (str): Python code with type hints.
name (str): Optional. Default: "*classicalfunction*". ClassicalFunction name.
Raises:
QiskitError: If source is not a string.
"""
if not isinstance(source, str):
raise QiskitError("ClassicalFunction needs a source code as a string.")
self._ast = ast.parse(source)
self._network = None
self._scopes = None
self._args = None
self._truth_table = None
super().__init__(
name or "*classicalfunction*",
num_qubits=sum(qreg.size for qreg in self.qregs),
params=[],
)
def compile(self):
"""Parses and creates the logical circuit"""
_classical_function_visitor = ClassicalFunctionVisitor()
_classical_function_visitor.visit(self._ast)
self._network = _classical_function_visitor._network
self._scopes = _classical_function_visitor.scopes
self._args = _classical_function_visitor.args
self.name = _classical_function_visitor.name
@property
def network(self):
"""Returns the logical network"""
if self._network is None:
self.compile()
return self._network
@property
def scopes(self):
"""Returns the scope dict"""
if self._scopes is None:
self.compile()
return self._scopes
@property
def args(self):
"""Returns the classicalfunction arguments"""
if self._args is None:
self.compile()
return self._args
@property
def types(self):
"""Dumps a list of scopes with their variables and types.
Returns:
list(dict): A list of scopes as dicts, where key is the variable name and
value is its type.
"""
ret = []
for scope in self.scopes:
ret.append({k: v[0] for k, v in scope.items()})
return ret
def simulate(self, bitstring: str) -> bool:
"""Evaluate the expression on a bitstring.
This evaluation is done classically.
Args:
bitstring: The bitstring for which to evaluate.
Returns:
bool: result of the evaluation.
"""
from tweedledum.classical import simulate # pylint: disable=import-error
return simulate(self._network, bitstring)
def simulate_all(self):
"""
Returns a truth table.
Returns:
str: a bitstring with a truth table
"""
result = []
for position in range(2 ** self._network.num_pis()):
sim_result = "".join([str(int(tt[position])) for tt in self.truth_table])
result.append(sim_result)
return "".join(reversed(result))
@property
def truth_table(self):
"""Returns (and computes) the truth table"""
from tweedledum.classical import simulate # pylint: disable=import-error
if self._truth_table is None:
self._truth_table = simulate(self._network)
return self._truth_table
def synth(
self,
registerless: bool = True,
synthesizer: Optional[Callable[[ClassicalElement], QuantumCircuit]] = None,
) -> QuantumCircuit:
"""Synthesis the logic network into a :class:`~qiskit.circuit.QuantumCircuit`.
Args:
registerless: Default ``True``. If ``False`` uses the parameter names to create
registers with those names. Otherwise, creates a circuit with a flat quantum register.
synthesizer: Optional. If None tweedledum's pkrm_synth is used.
Returns:
QuantumCircuit: A circuit implementing the logic network.
"""
if registerless:
qregs = None
else:
qregs = self.qregs
if synthesizer:
return synthesizer(self)
from tweedledum.synthesis import pkrm_synth # pylint: disable=import-error
return tweedledum2qiskit(pkrm_synth(self.truth_table[0]), name=self.name, qregs=qregs)
def _define(self):
"""The definition of the classical function is its synthesis"""
self.definition = self.synth()
@property
def qregs(self):
"""The list of qregs used by the classicalfunction"""
qregs = [QuantumRegister(1, name=arg) for arg in self.args if self.types[0][arg] == "Int1"]
if self.types[0]["return"] == "Int1":
qregs.append(QuantumRegister(1, name="return"))
return qregs