QuPRS 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
QuPRS/__init__.py ADDED
@@ -0,0 +1,12 @@
1
+ # __init__.py
2
+
3
+ try:
4
+ from ._version import *
5
+ except ImportError:
6
+ # Fallback for development mode or when setuptools_scm hasn't generated the file yet
7
+ import warnings
8
+ warnings.warn("Version not found in _version.py, likely in development mode or during sdist build.", stacklevel=2)
9
+ __version__ = "0.0.0+unknown"
10
+ __version_tuple__ = (0, 0, 0, "unknown")
11
+ version = __version__
12
+ version_tuple = __version_tuple__
QuPRS/_version.py ADDED
@@ -0,0 +1,21 @@
1
+ # file generated by setuptools-scm
2
+ # don't change, don't track in version control
3
+
4
+ __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
5
+
6
+ TYPE_CHECKING = False
7
+ if TYPE_CHECKING:
8
+ from typing import Tuple
9
+ from typing import Union
10
+
11
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
12
+ else:
13
+ VERSION_TUPLE = object
14
+
15
+ version: str
16
+ __version__: str
17
+ __version_tuple__: VERSION_TUPLE
18
+ version_tuple: VERSION_TUPLE
19
+
20
+ __version__ = version = '0.1.0'
21
+ __version_tuple__ = version_tuple = (0, 1, 0)
@@ -0,0 +1,5 @@
1
+ # cache_manager/__init__.py
2
+ from .global_cache_manager import GlobalCacheManager
3
+ from .cache_decorator import tracked_lru_cache
4
+
5
+ cache_manager = GlobalCacheManager()
@@ -0,0 +1,18 @@
1
+ # cache_manager/cache_decorator.py
2
+
3
+ from functools import lru_cache, wraps
4
+
5
+
6
+ def tracked_lru_cache(maxsize=128):
7
+
8
+ def decorator(func):
9
+ from . import cache_manager
10
+ cached_func = lru_cache(maxsize=maxsize)(func)
11
+ cache_manager.register(cached_func)
12
+
13
+ @wraps(func)
14
+ def wrapper(*args, **kwargs):
15
+ return cached_func(*args, **kwargs)
16
+
17
+ return wrapper
18
+ return decorator
@@ -0,0 +1,24 @@
1
+ # cache_manager/global_cache_manager.py
2
+
3
+ class GlobalCacheManager:
4
+ def __init__(self):
5
+ self._caches = []
6
+
7
+ def register(self, func):
8
+ self._caches.append(func)
9
+ def __repr__(self) -> str:
10
+ return str(self.cache_info)
11
+ @property
12
+ def cache(self):
13
+ return self._caches
14
+ @property
15
+ def cache_info(self):
16
+ return {f"{item.__name__}": item.cache_info() for item in self._caches}
17
+ @property
18
+ def hit_rate(self):
19
+ return {f"{item.__name__}": (item.cache_info().hits/(item.cache_info().misses+item.cache_info().hits) if item.cache_info().misses+item.cache_info().hits>0 else 0) for item in self._caches}
20
+ def clear_all_caches(self):
21
+ for func in self._caches:
22
+ func.cache_clear()
23
+ def clear_cache(self, func):
24
+ func.cache_clear()
QuPRS/config.py ADDED
@@ -0,0 +1,14 @@
1
+ # config.py
2
+ TOLERANCE = 1e-10
3
+
4
+ def set_tolerance(new_tolerance: float):
5
+ global TOLERANCE
6
+ if not isinstance(new_tolerance, (int, float)):
7
+ raise ValueError("Tolerance must be a numeric type")
8
+ if new_tolerance <= 0:
9
+ raise ValueError("Tolerance must be a positive number")
10
+ TOLERANCE = new_tolerance
11
+
12
+ def get_tolerance() -> float:
13
+
14
+ return TOLERANCE
File without changes
@@ -0,0 +1,26 @@
1
+ from QuPRS.pathsum import PathSum
2
+
3
+ def support_gate_set():
4
+ return ('h','x','y','z', 'p', 's','sdg', 't','tdg','sx','sxdg',
5
+ 'rx', 'ry','rz', 'u','u1','u2','u3',
6
+ 'cx','ch','cz','cp','ccx','mcx',
7
+ 'crx','cry','crz','cu1','cu3','cu',
8
+ 'swap','barrier','measure' )
9
+
10
+ def gate_map(circuit:PathSum, gate_name:str, qubit, gate_params = [], is_bra: bool =False, debug = False):
11
+ if debug:
12
+ print('add gate: %s, %s, %s, %s'%(gate_name, qubit, gate_params, is_bra))
13
+ print('circuit in',circuit)
14
+
15
+ assert gate_name in support_gate_set(), 'Not support %s gate yet.'%gate_name
16
+ if gate_name in ['barrier','measure']:
17
+ return circuit
18
+ func = getattr(circuit, gate_name)
19
+ if gate_params == []:
20
+ circuit = func(*qubit, is_bra)
21
+ else:
22
+ circuit = func(*gate_params, *qubit, is_bra)
23
+
24
+ if debug:
25
+ print('circuit out', circuit)
26
+ return circuit
@@ -0,0 +1,225 @@
1
+ from qiskit import QuantumCircuit, qasm2, qasm3
2
+ import numpy as np
3
+ import symengine as se
4
+ import signal,time, os
5
+
6
+ from QuPRS import config
7
+ from QuPRS.pathsum import PathSum, Register
8
+ from QuPRS.interface.gate_library import support_gate_set, gate_map
9
+
10
+ # global support_gate_set
11
+ support_gate_set = support_gate_set()
12
+
13
+ def timeout_handler(signum, frame):
14
+ raise TimeoutError("Operation timed out")
15
+ signal.signal(signal.SIGALRM, timeout_handler)
16
+
17
+ def build_circuit(circuit: QuantumCircuit, initial_state: bool|list|tuple = None):
18
+ pathsum_circuit = initialize(circuit, initial_state)
19
+ gate_list = get_gates(circuit)
20
+ # support_gate_set = support_gate_set()
21
+ for gate in gate_list:
22
+ assert gate[0] in support_gate_set, 'Not support %s gate yet.'%gate[0]
23
+ # print(pathsum_circuit, gate)
24
+ pathsum_circuit= gate_map(pathsum_circuit, gate[0], [f'{item[0]}_{item[1]}' for item in gate[1]], gate[2])
25
+
26
+ return pathsum_circuit
27
+
28
+ def initialize(circuit: QuantumCircuit, initial_state: bool|list|tuple = None):
29
+ '''
30
+ Construct initial PathSum and the name mapping.
31
+ '''
32
+ qiskit_regs = circuit.qregs
33
+ regs =[]
34
+ for reg in qiskit_regs:
35
+ regs.append(Register(reg.size, reg.name ))
36
+
37
+ return PathSum.QuantumCircuit(*regs, initial_state = initial_state)
38
+
39
+ def get_gate(circuit: QuantumCircuit, gate):
40
+ '''Get a gate properties.'''
41
+ gate_name = gate.operation.name
42
+ gate_params = gate.operation.params
43
+ qubits_old = gate.qubits
44
+ qubits = []
45
+ for qubit in qubits_old:
46
+ QuantumRegister = circuit.find_bit(qubit).registers
47
+ qubits.append((QuantumRegister[0][0].name, QuantumRegister[0][1]))
48
+ return gate_name, tuple(qubits), tuple(gate_params)
49
+
50
+ def get_gates(circuit: QuantumCircuit):
51
+ gates = []
52
+ for gate in circuit.data:
53
+ gates.append(get_gate(circuit, gate))
54
+ return gates
55
+
56
+ def add_gate(pathsum_circuit: PathSum.QuantumCircuit, gate, is_bra = False, count = 0, debug = False) -> tuple[PathSum, int]:
57
+ assert gate[0] in support_gate_set, 'Not support %s gate yet.'%gate[0]
58
+ if debug:
59
+ print(f'gate:{gate}, is_bra: {is_bra}')
60
+ pathsum_circuit= gate_map(pathsum_circuit, gate[0], [f'{item[0]}_{item[1]}' for item in gate[1]], gate[2], is_bra)
61
+
62
+ count += 1
63
+ if debug:
64
+ print(count, pathsum_circuit)
65
+ return pathsum_circuit, count
66
+
67
+ def load_circuit(circuit: str) -> QuantumCircuit:
68
+ """
69
+ Load a Qiskit circuit from a file or qasm string.
70
+
71
+ Args:
72
+ circuit (str): The path to the QASM file or a QASM string.
73
+
74
+ Returns:
75
+ QuantumCircuit: The loaded Qiskit circuit.
76
+ """
77
+ if os.path.isfile(circuit):
78
+ with open(circuit, 'r') as f:
79
+ data = f.read()
80
+ else:
81
+ data = circuit
82
+ flag = True if 'OPENQASM 3.0' in data else False
83
+ if flag:
84
+ qiskit_circuit = qasm3.loads(data)
85
+ else:
86
+ qiskit_circuit = qasm2.loads(data)
87
+ return qiskit_circuit
88
+
89
+ def qasm_eq_check(circuit1: str|QuantumCircuit, circuit2:str|QuantumCircuit, strategy ='Difference' , Benchmark_Name=None, timeout= 600):
90
+ tolerance = config.TOLERANCE
91
+ def load_circuit(circuit):
92
+ if isinstance(circuit, str):
93
+ if circuit.endswith('.qasm'):
94
+ f = open(circuit, 'r')
95
+ data = f.read()
96
+ if 'OPENQASM 3.0' in data:
97
+ return qasm3.load(circuit)
98
+ else:
99
+ return qasm2.load(circuit)
100
+ elif 'OPENQASM' in circuit:
101
+ if 'OPENQASM 3.0' in circuit:
102
+ return qasm3.loads(circuit)
103
+ else:
104
+ return qasm2.loads(circuit)
105
+ else:
106
+ raise ValueError("Invalid circuit format")
107
+ elif isinstance(circuit, QuantumCircuit):
108
+ return circuit
109
+
110
+ qiskit_circuit = load_circuit(circuit1)
111
+ qiskit_circuit2 = load_circuit(circuit2)
112
+
113
+ start_time = time.time()
114
+
115
+ initial_state = initialize(qiskit_circuit)
116
+ pathsum_circuit = initial_state
117
+ qubit_num = initial_state.num_qubits
118
+
119
+ l1 = len(qiskit_circuit.data)
120
+ l2 = len(qiskit_circuit2.data)
121
+
122
+ output_dict = {"Benchmark_Name": Benchmark_Name,
123
+ "qubit_num": qubit_num,
124
+ "gate_num": l1,
125
+ "gate_num2": l2}
126
+
127
+ if strategy == 'Proportional':
128
+ from QuPRS.utils.strategy import proportional
129
+ output_dict, pathsum_circuit = proportional(pathsum_circuit, qiskit_circuit, qiskit_circuit2, l1, l2, output_dict, timeout)
130
+ elif strategy == 'Naive':
131
+ from QuPRS.utils.strategy import naive
132
+ output_dict, pathsum_circuit = naive(pathsum_circuit, qiskit_circuit, qiskit_circuit2, l1, l2, output_dict, timeout)
133
+ elif strategy == "Straightforward":
134
+ from QuPRS.utils.strategy import straightforward
135
+ output_dict, pathsum_circuit = straightforward(pathsum_circuit, qiskit_circuit, qiskit_circuit2, l1, l2, output_dict, timeout)
136
+ elif strategy == "Difference":
137
+ from QuPRS.utils.strategy import difference
138
+ output_dict, pathsum_circuit = difference(pathsum_circuit, qiskit_circuit, qiskit_circuit2, l1, l2, output_dict, timeout)
139
+ else:
140
+ raise ValueError("Invalid strategy")
141
+ if "equivalent" in output_dict:
142
+ return output_dict, pathsum_circuit
143
+ else:
144
+ total_time = time.time() - start_time
145
+ output_dict["Time"] = round(total_time, 3)
146
+
147
+ # if pathsum_circuit.f == initial_state.f and pathsum_circuit.pathvar == initial_state.pathvar:
148
+ if pathsum_circuit.f == initial_state.f:
149
+ if pathsum_circuit.P == initial_state.P:
150
+ output_dict["equivalent"] = 'equivalent'
151
+ P_free_symbols = pathsum_circuit.P.free_symbols
152
+ check_P_symbol = tuple(filter(lambda x: x.name in pathsum_circuit.f.bits, P_free_symbols))
153
+ if len(P_free_symbols) == 0:
154
+ if pathsum_circuit.P < tolerance:
155
+ output_dict["equivalent"] = 'equivalent'
156
+ else:
157
+ output_dict["equivalent"] = 'equivalent*'
158
+ elif len(check_P_symbol) == 0:
159
+ output_dict["equivalent"] = 'equivalent*'
160
+ else:
161
+ output_dict["equivalent"] = 'not_equivalent'
162
+ return output_dict, pathsum_circuit
163
+ else:
164
+ output_dict["equivalent"] = 'unknown'
165
+ return output_dict, pathsum_circuit
166
+
167
+ def qasm_eq_check_with_wmc(circuit1: str|QuantumCircuit, circuit2:str|QuantumCircuit, strategy ='Difference' , Benchmark_Name=None, timeout= 600):
168
+ tolerance = config.TOLERANCE
169
+
170
+ output_dict, circuit = qasm_eq_check(circuit1=circuit1, circuit2=circuit2, strategy =strategy , Benchmark_Name=Benchmark_Name, timeout= timeout)
171
+ wmc_time = 0
172
+ log_wmc = None
173
+ expect = None
174
+ theta = None
175
+ to_DIMACS_time = 0
176
+ if output_dict["equivalent"] == "unknown":
177
+ from QuPRS.interface.ps2wmc import to_DIMACS, run_wmc
178
+ from QuPRS.utils.util import get_theta
179
+
180
+ expect = circuit.num_qubits+circuit.num_pathvar/2
181
+ signal.alarm(timeout)
182
+ start_time = time.time()
183
+ try:
184
+ to_DIMACS(circuit)
185
+ to_DIMACS_time = round(time.time() - start_time, 3)
186
+ except TimeoutError:
187
+ to_DIMACS_time = f'>{timeout}'
188
+ finally:
189
+ signal.alarm(0)
190
+
191
+ if to_DIMACS_time != f'>{timeout}':
192
+ signal.alarm(timeout)
193
+ start_time = time.time()
194
+ try:
195
+ complex_number = run_wmc()
196
+ wmc_time = round(time.time() - start_time, 3)
197
+ abs_num = np.sqrt(complex_number[0]**2 + complex_number[1]**2)
198
+ log_wmc = round(np.log2(abs_num), 3)
199
+ theta = get_theta(complex_number[1]/abs_num, complex_number[0]/abs_num)
200
+ if abs(log_wmc - expect) < tolerance:
201
+ if abs(theta) < tolerance * 2 * np.pi:
202
+ output_dict["equivalent"] = "equivalent"
203
+ else:
204
+ output_dict["equivalent"] = "equivalent*"
205
+ else:
206
+ output_dict["equivalent"] = "not_equivalent"
207
+ except TimeoutError:
208
+ wmc_time = f'>{timeout}'
209
+ finally:
210
+ signal.alarm(0)
211
+
212
+ elif output_dict["equivalent"] == "equivalent*":
213
+ theta = str((circuit.P* 2 * se.pi).evalf())
214
+ elif output_dict["equivalent"] == "equivalent":
215
+ theta = 0
216
+
217
+ output_dict["PathSum_time"] = output_dict["Time"]
218
+ output_dict["to_DIMACS_time"] = to_DIMACS_time
219
+ output_dict["wmc_time"] = wmc_time
220
+ if output_dict["equivalent"] == "Timeout" or to_DIMACS_time == f'>{timeout}' or wmc_time == f'>{timeout}':
221
+ output_dict["equivalent"] = "Timeout"
222
+ output_dict["Time"] = f'>{timeout}'
223
+ else:
224
+ output_dict["Time"] = round(wmc_time + to_DIMACS_time + output_dict["PathSum_time"],3)
225
+ return output_dict
@@ -0,0 +1,252 @@
1
+ import symengine as se
2
+ import sympy as sp
3
+ from sympy.logic.boolalg import Xor, And, Or, Not
4
+
5
+ import subprocess, re
6
+
7
+ from QuPRS.utils import WMC
8
+ from QuPRS.utils.util import algebraic_to_logical
9
+
10
+ counter = {'idx': 0}
11
+ def fresh_var():
12
+ counter['idx'] += 1
13
+ return sp.Symbol(f'e_{counter["idx"]-1}')
14
+ def extend_symbols_map(symbols_map, new_symbol):
15
+ if new_symbol not in symbols_map:
16
+ symbols_map[new_symbol] = len(symbols_map) + 1
17
+ return symbols_map
18
+ # Convert a logical equivalence to CNF
19
+ def a_eq_b_cnf(a, b):
20
+ if a == b:
21
+ return sp.true
22
+ return sp.to_cnf(Or(And(a, b), And(Not(a), Not(b))), simplify=True, force=True)
23
+ def encode_equivalence(c, cnf):
24
+ if c == cnf:
25
+ return sp.true
26
+ cnf_clauses = []
27
+ # cnf = and(a1, a2, ...) or or(o1, o2, ...), a1 = or(o1, o2, ...)
28
+ if isinstance(cnf, And):
29
+ args = cnf.args
30
+ new_args = list(args)
31
+ for i, arg in enumerate(args):
32
+ if isinstance(arg, Or):
33
+ args2 = arg.args
34
+ new_args[i] = fresh_var()
35
+ cnf_clauses.append(Or(Not(new_args[i]), *args2)) # e -> or(o1, o2, ...)
36
+ cnf_clauses.extend([Or(Not(arg2), new_args[i]) for arg2 in args2]) # or(o1, o2, ...) -> e
37
+ elif isinstance(arg, sp.Symbol) or isinstance(arg, Not):
38
+ continue
39
+ else:
40
+ raise ValueError(f"Unsupported CNF clause {arg} type {type(arg)}")
41
+
42
+ cnf_clauses.append(Or(c, *[Not(arg) for arg in new_args])) # and(a1, a2, ...) -> c
43
+ cnf_clauses.extend([Or(Not(c), arg) for arg in new_args]) # c-> and(a1, a2, ...)
44
+ elif isinstance(cnf, Or):
45
+ args = cnf.args
46
+ cnf_clauses.append(Or(Not(c), *args)) # c -> or(o1, o2, ...)
47
+ cnf_clauses.extend([Or(Not(arg), c) for arg in args]) # or(o1, o2, ...) -> c
48
+ elif isinstance(cnf, Not) or isinstance(cnf, sp.Symbol) or isinstance(cnf, se.Symbol):
49
+ cnf_clauses.append(Or(Not(c), cnf)) # c -> o1
50
+ cnf_clauses.append(Or(Not(cnf), c)) # o1 -> c
51
+ elif cnf == sp.true:
52
+ cnf_clauses.append(c) # c -> True
53
+ elif cnf == sp.false:
54
+ cnf_clauses.append(Not(c)) # c -> False
55
+ else:
56
+ raise ValueError(f"Unsupported CNF clause {cnf} type {type(cnf)}")
57
+ return And(*cnf_clauses)
58
+ def anf_to_cnf(anf):
59
+ """
60
+ Convert ANF to CNF using Tseitin transformation.
61
+ """
62
+ if anf == sp.true or anf == sp.false or \
63
+ isinstance(anf, sp.Symbol) or \
64
+ isinstance(anf, se.Symbol) or \
65
+ isinstance(anf, And):
66
+ return (anf,)
67
+
68
+ clauses = []
69
+ def tseitin(c, anf): # c <-> anf
70
+ # print(f"tseitin: {c} <-> {anf}")
71
+ if anf == sp.true:
72
+ clauses.append(c)
73
+ elif anf == sp.false:
74
+ clauses.append(Not(c))
75
+ elif isinstance(anf, Not):
76
+ args = anf.args
77
+ if isinstance(args[0], And):
78
+ a = fresh_var()
79
+ tseitin(a, args[0])
80
+ elif isinstance(args[0], Xor):
81
+ a = fresh_var()
82
+ tseitin(a, Xor(*args[0].args))
83
+ else:
84
+ a = args[0]
85
+ clauses.append(Or(Not(c), Not(a))) # c -> Not(a)
86
+ clauses.append(Or(c, a)) # Not(a) -> c
87
+ elif isinstance(anf, Xor):
88
+ args = anf.args
89
+ if isinstance(args[0], And):
90
+ a = fresh_var()
91
+ tseitin(a, args[0])
92
+ else:
93
+ a = args[0]
94
+
95
+ if len(args) > 2:
96
+ b = fresh_var()
97
+ tseitin(b, Xor(*args[1:]))
98
+ elif isinstance(args[1], And):
99
+ b = fresh_var()
100
+ tseitin(b, args[1])
101
+ else:
102
+ b = args[1]
103
+
104
+ clauses.extend([
105
+ Or(Not(a), Not(b), Not(c)),
106
+ Or(a, b, Not(c)),
107
+ Or(a, Not(b), c),
108
+ Or(Not(a), b, c),
109
+ ]) # c <-> a xor b
110
+
111
+ elif isinstance(anf, And):
112
+ args = anf.args
113
+ clauses.extend([Or(Not(c), arg) for arg in args]) # c -> anf
114
+ clauses.append(Or(c, *[Not(arg) for arg in args])) # anf -> c
115
+ elif isinstance(anf, sp.Symbol) or isinstance(anf, se.Symbol):
116
+ clauses.append(Or(Not(c), anf)) # c -> anf
117
+ clauses.append(Or(c, Not(anf))) # anf -> c
118
+ else:
119
+ raise ValueError(f"Unsupported ANF clause {anf} type {type(anf)}")
120
+
121
+ new_var = fresh_var()
122
+ tseitin(new_var, anf)
123
+ # print(f"clauses: {clauses}")
124
+ return (new_var, And(*clauses))
125
+
126
+ # Convert polynomial P to weighted logical expressions
127
+ def P_to_wlogi(P):
128
+ if P == 0:
129
+ return []
130
+ args = P.as_coefficients_dict()
131
+ output = [(v, algebraic_to_logical(k)) if v != 0 and v != 1 else (k, algebraic_to_logical(v)) for k, v in args.items() if v != 0]
132
+ return output
133
+
134
+ # Convert CNF to clauses
135
+ def cnf_to_clauses(cnf, symbols_map, flag=False):
136
+ if isinstance(cnf, And):
137
+ return [cnf_to_clauses(arg, symbols_map, flag) for arg in cnf.args]
138
+ elif isinstance(cnf, Or):
139
+ clause = ' '.join(cnf_to_clauses(arg, symbols_map, flag=True) for arg in cnf.args) + ' 0'
140
+ return clause
141
+ elif isinstance(cnf, Not):
142
+ return f'-{cnf_to_clauses(cnf.args[0], symbols_map, flag)}'
143
+ elif isinstance(cnf, sp.Symbol):
144
+ clause = f'{symbols_map[cnf] }'
145
+ return clause if flag else [clause + ' 0']
146
+ elif cnf == sp.true:
147
+ return []
148
+ else:
149
+ raise ValueError(f'Invalid CNF: {cnf}')
150
+
151
+ # Convert weighted logical expressions to clauses
152
+ def wlogi_to_clauses(wlogi_list, symbols_map):
153
+ clauses = []
154
+ weights = []
155
+ z = sp.symbols(f'z_:{len(wlogi_list)}')
156
+ l = len(symbols_map)
157
+ for symbol in z:
158
+ extend_symbols_map(symbols_map, symbol)
159
+ for i, (w, logi) in enumerate(wlogi_list):
160
+ weights.append(f'c p weight {l + i + 1} {se.cos(2*se.pi*w).evalf()} {se.sin(2*se.pi*w).evalf()} 0')
161
+ idx = counter["idx"]
162
+ temp = encode_equivalence(z[i], logi)
163
+ for i in range(idx, counter["idx"]):
164
+ extend_symbols_map(symbols_map, sp.Symbol(f'e_{i}'))
165
+ clauses.extend(cnf_to_clauses(temp, symbols_map))
166
+ return weights, clauses, symbols_map
167
+
168
+ # Convert PathSum object to DIMACS format
169
+ def to_DIMACS(pathsum, filename='wmc.cnf'):
170
+ counter["idx"] = 0
171
+ free_symbols_list = [sp.Symbol(item) for item in pathsum.bits]
172
+ free_symbols_list.extend(sp.sympify(free_symbol) for free_symbol in pathsum.pathvar)
173
+ symbols_map ={}
174
+ for symbol in free_symbols_list:
175
+ extend_symbols_map(symbols_map, symbol)
176
+
177
+ weights, clauses, symbols_map = wlogi_to_clauses(P_to_wlogi(pathsum.P), symbols_map)
178
+ for qubit in range(pathsum.num_qubits):
179
+ a = sp.Symbol(pathsum.bits[qubit])
180
+ idx = counter["idx"]
181
+ b = anf_to_cnf(pathsum.f[qubit])
182
+ if len(b) == 1:
183
+ b = b[0]
184
+ else:
185
+ for i in range(idx, counter["idx"]):
186
+ extend_symbols_map(symbols_map, sp.Symbol(f'e_{i}'))
187
+ clauses.extend(cnf_to_clauses(b[1], symbols_map))
188
+ b = b[0]
189
+ temp = encode_equivalence(a, b)
190
+ clauses.extend(cnf_to_clauses(temp, symbols_map))
191
+
192
+ DIMACS_text = f'p cnf {len(symbols_map)} {len(clauses)}\nc t wmc'
193
+ DIMACS_text += '\n' + '\n'.join(weights + clauses)
194
+ with open(filename, 'w') as file:
195
+ file.write(DIMACS_text.strip())
196
+ return DIMACS_text
197
+
198
+ def run_wmc(file='wmc.cnf'):
199
+ result = subprocess.run([str(WMC()), "-mode=1", file], capture_output=True, text=True)
200
+
201
+ output_lines = result.stdout.split("\n")
202
+
203
+ for line in output_lines:
204
+ if line.startswith("c s exact double prec-sci"):
205
+ match = re.search(r"([-+]?\d+(?:\.\d+)?(?:[eE][-+]?\d+)?)([-+]\d+(?:\.\d+)?(?:[eE][-+]?\d+)?)i", line)
206
+ if match:
207
+ real_part = float(match.group(1))
208
+ imag_part = float(match.group(2))
209
+ return real_part, imag_part
210
+ match = re.search(r"([-+]?\d+(?:\.\d+)?(?:[eE][-+]?\d+)?)", line)
211
+ if match:
212
+ real_part = float(match.group(1))
213
+ imag_part = 0
214
+ return real_part, imag_part
215
+ match = re.search(r"([-+]\d+(?:\.\d+)?(?:[eE][-+]?\d+)?)i", line)
216
+ if match:
217
+ real_part = 0
218
+ imag_part = float(match.group(1))
219
+ return real_part, imag_part
220
+ return None
221
+
222
+ if __name__ == "__main__":
223
+ # from pathsum.pathsum import PathSum, F, Register
224
+ # y = se.symbols('y_:2')
225
+ # x = se.symbols('x_:2')
226
+ # P = se.Rational(1, 2) * x[0] * y[0] + se.Rational(1, 2) * y[0] * y[1]
227
+
228
+ # reg = Register(2)
229
+ # data = {reg[0].name: y[1]}
230
+ # bits = tuple(data.keys())
231
+ # f = F(reg, data=data, bits=bits)
232
+
233
+ # pathvar = frozenset(y)
234
+ # circuit = PathSum(P, f, pathvar)
235
+
236
+ # print(to_DIMACS(circuit, filename='wmc.cnf'))
237
+ # print("result:",run_wmc(file='wmc.cnf'))
238
+
239
+ x = sp.symbols('x_:10')
240
+ # A = sp.Symbol('A')
241
+ # B = And(Or(x,y),z)
242
+ A = x[0]
243
+ B = (x[3] | x[4] | ~x[1]) & (x[3] | x[1] | ~x[4]) & (x[4] | x[1] | ~x[3])
244
+ # & (x[4] | x[2] | ~x[5]) & (x[4] | x[5] | ~x[2]) & (x[2] | x[5] | ~x[4]) & (~x[3] | ~x[4] | ~x[1]) & (~x[4] | ~x[2] | ~x[5])
245
+
246
+ eq_cnf = encode_equivalence(A, B)
247
+ print(eq_cnf)
248
+
249
+ # anf = Xor(*x)
250
+ # print('anf:', anf)
251
+ # cnf = anf_to_cnf(anf)
252
+ # print('cnf:', cnf)
@@ -0,0 +1,68 @@
1
+ import re
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ def convert_qasm_to_qc(oldfile, output_dir=None):
6
+ # Determine the output directory
7
+ if output_dir:
8
+ output_dir = Path(output_dir)
9
+ else:
10
+ output_dir = Path(oldfile).parent
11
+
12
+ # Define the new file name in the specified or default directory
13
+ newfile = output_dir / f"{Path(oldfile).stem}.qc"
14
+
15
+ # Copy the content from the old file to the new file
16
+ with open(oldfile, 'r') as infile, open(newfile, 'w') as outfile:
17
+ for line in infile:
18
+ # Ignore specific lines
19
+ if 'OPENQASM 2.0;' in line or 'include "qelib1.inc";' in line:
20
+ continue
21
+ # Write the line to the new file
22
+ outfile.write(line)
23
+
24
+ # Read the contents of the new file
25
+ with open(newfile, 'r') as file:
26
+ content = file.read()
27
+
28
+ # Replacement patterns
29
+ replacements = [
30
+ (r'qreg [^\d]*\[(\d+)\];', lambda m: f".v {' '.join(map(str, range(int(m.group(1)))))}\nBEGIN"),
31
+ (r'h qubits\[(\d+)\];', r'H \1'),
32
+ (r'x qubits\[(\d+)\];', r'X \1'),
33
+ (r'y qubits\[(\d+)\];', r'Y \1'),
34
+ (r'z qubits\[(\d+)\];', r'Z \1'),
35
+ (r'cx qubits\[(\d+)\][^a-z]+qubits\[(\d+)\];', r'tof \1 \2'),
36
+ (r'cz qubits\[(\d+)\][^a-z]+qubits\[(\d+)\];', r'CZ \1 \2'),
37
+ (r's qubits\[(\d+)\];', r'S \1'),
38
+ (r'sdg qubits\[(\d+)\];', r'S* \1'),
39
+ (r't qubits\[(\d+)\];', r'T \1'),
40
+ (r'tdg qubits\[(\d+)\];', r'T* \1'),
41
+ (r'swap qubits\[(\d+)\][^a-z]+qubits\[(\d+)\];', r'Swap \1 \2'),
42
+ (r'rz\((.+)\) qubits\[(\d+)\];', r'Rz \1 \2'),
43
+ (r'rx\((.+)\) qubits\[(\d+)\];', r'Rx \1 \2'),
44
+ (r'ry\((.+)\) qubits\[(\d+)\];', r'Ry \1 \2'),
45
+ (r'ccx qubits\[(\d+)\][^a-z]+qubits\[(\d+)\][^a-z]+qubits\[(\d+)\];', r'tof \1 \2 \3'),
46
+ ]
47
+
48
+
49
+ # Apply the replacements
50
+ for pattern, replacement in replacements:
51
+ content = re.sub(pattern, replacement, content)
52
+ # Append "END" to the file
53
+ content += "END"
54
+
55
+ # Write the modified content back to the file
56
+ with open(newfile, 'w') as file:
57
+ file.write(content)
58
+
59
+ print(f"Conversion completed. Output saved to: {newfile}")
60
+
61
+
62
+ if __name__ == "__main__":
63
+ if len(sys.argv) < 2 or len(sys.argv) > 3:
64
+ print("Usage: python script.py <oldfile.qasm> [output_dir]")
65
+ else:
66
+ oldfile = sys.argv[1]
67
+ output_dir = sys.argv[2] if len(sys.argv) == 3 else None
68
+ convert_qasm_to_qc(oldfile, output_dir)