diff --git a/config/.gitignore b/config/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..ffc1da3f3eab889b6702e8536d9d600c76cb511f --- /dev/null +++ b/config/.gitignore @@ -0,0 +1 @@ +*.tokens \ No newline at end of file diff --git a/config/README.md b/config/README.md new file mode 100644 index 0000000000000000000000000000000000000000..54973958f92505491f6b5cac7958fbcb879b7c4b --- /dev/null +++ b/config/README.md @@ -0,0 +1,8 @@ +请在此文件下保存在对接各云平台时使用的token,这些token可用于测试。此文件夹中`*.tokens`文件不会被git同步。 + +云平台与文件名的对应关系如下: + +| 云平台 | token文件名 | +|---|---| +| 中电信天衍量子云平台 | `tianyan.tokens` | +| 国盾量子云平台 | `quantumctek.tokens` | diff --git a/examples/bell_state/host.py b/examples/bell_state/host.py index 18b9d2369f3bd8292c54b6b67b252f448ac03ad6..c0665b24d7533481fc512eabd502a2606cb2752b 100644 --- a/examples/bell_state/host.py +++ b/examples/bell_state/host.py @@ -6,7 +6,7 @@ qu_file = Path(__file__).parent / "kernel.qu" def routine(circ_name, num_shots=1): - task = Quingo_task(qu_file, circ_name) + task = Quingo_task(qu_file, circ_name, debug_mode=True) cfg = ExeConfig(ExeMode.SimFinalResult, num_shots=num_shots) qasm_fn = compile(task, params=(), config_file="") sim_result = execute(qasm_fn, BackendType.QUALESIM_QUANTUMSIM, cfg) diff --git a/examples/bell_state/kernel.qu b/examples/bell_state/kernel.qu index 77206da72e53bd882163e86f2e939e6d0a5eb906..b4eee97770292933c3dad1f8480afff0aadec346 100644 --- a/examples/bell_state/kernel.qu +++ b/examples/bell_state/kernel.qu @@ -1,10 +1,13 @@ import std_ops operation bell_state() : unit { - using(q0: qubit, q1: qubit) { - H(q0); - CNOT(q0, q1); - measure(q0); - measure(q1); + using(q : qubit[2] = {42,48}) { + H(q[0]); + CNOT(q[0], q[1]); + measure(q[0]); + measure(q[1]); } -} \ No newline at end of file +} +// operation main(): unit{ +// return bell_state(); +// } \ No newline at end of file diff --git a/examples/bell_state/test.json b/examples/bell_state/test.json new file mode 100644 index 0000000000000000000000000000000000000000..7a54b89f1628f92c885d53f9518486a1346cb79f --- /dev/null +++ b/examples/bell_state/test.json @@ -0,0 +1,635 @@ +{ + "qubits": [ + "Q00", + "Q01", + "Q02", + "Q03", + "Q04", + "Q05", + "Q06", + "Q07", + "Q08", + "Q09", + "Q10", + "Q11", + "Q12", + "Q13", + "Q14", + "Q15", + "Q16", + "Q17", + "Q18", + "Q19", + "Q20", + "Q21", + "Q22", + "Q23", + "Q24", + "Q25", + "Q26", + "Q27", + "Q28", + "Q29", + "Q30", + "Q31", + "Q32", + "Q33", + "Q34", + "Q35", + "Q36", + "Q37", + "Q38", + "Q39", + "Q40", + "Q41", + "Q42", + "Q43", + "Q44", + "Q45", + "Q46", + "Q47", + "Q48", + "Q49", + "Q50", + "Q51", + "Q52", + "Q53", + "Q54", + "Q55", + "Q56", + "Q57", + "Q58", + "Q59", + "Q60", + "Q61", + "Q62", + "Q63", + "Q64", + "Q65" + ], + "czs": [ + [ + "Q00", + "Q06" + ], + [ + "Q00", + "Q07" + ], + [ + "Q01", + "Q07" + ], + [ + "Q01", + "Q08" + ], + [ + "Q02", + "Q08" + ], + [ + "Q02", + "Q09" + ], + [ + "Q03", + "Q09" + ], + [ + "Q03", + "Q10" + ], + [ + "Q04", + "Q10" + ], + [ + "Q04", + "Q11" + ], + [ + "Q05", + "Q11" + ], + [ + "Q06", + "Q12" + ], + [ + "Q07", + "Q12" + ], + [ + "Q07", + "Q13" + ], + [ + "Q08", + "Q13" + ], + [ + "Q08", + "Q14" + ], + [ + "Q09", + "Q14" + ], + [ + "Q09", + "Q15" + ], + [ + "Q10", + "Q15" + ], + [ + "Q10", + "Q16" + ], + [ + "Q11", + "Q16" + ], + [ + "Q11", + "Q17" + ], + [ + "Q12", + "Q18" + ], + [ + "Q12", + "Q19" + ], + [ + "Q13", + "Q19" + ], + [ + "Q13", + "Q20" + ], + [ + "Q14", + "Q20" + ], + [ + "Q14", + "Q21" + ], + [ + "Q15", + "Q21" + ], + [ + "Q15", + "Q22" + ], + [ + "Q16", + "Q22" + ], + [ + "Q16", + "Q23" + ], + [ + "Q17", + "Q23" + ], + [ + "Q18", + "Q24" + ], + [ + "Q19", + "Q24" + ], + [ + "Q19", + "Q25" + ], + [ + "Q20", + "Q25" + ], + [ + "Q20", + "Q26" + ], + [ + "Q21", + "Q26" + ], + [ + "Q21", + "Q27" + ], + [ + "Q22", + "Q27" + ], + [ + "Q22", + "Q28" + ], + [ + "Q23", + "Q28" + ], + [ + "Q23", + "Q29" + ], + [ + "Q24", + "Q30" + ], + [ + "Q24", + "Q31" + ], + [ + "Q25", + "Q31" + ], + [ + "Q25", + "Q32" + ], + [ + "Q26", + "Q32" + ], + [ + "Q26", + "Q33" + ], + [ + "Q27", + "Q33" + ], + [ + "Q27", + "Q34" + ], + [ + "Q28", + "Q34" + ], + [ + "Q28", + "Q35" + ], + [ + "Q29", + "Q35" + ], + [ + "Q30", + "Q36" + ], + [ + "Q31", + "Q36" + ], + [ + "Q31", + "Q37" + ], + [ + "Q32", + "Q37" + ], + [ + "Q32", + "Q38" + ], + [ + "Q33", + "Q38" + ], + [ + "Q33", + "Q39" + ], + [ + "Q34", + "Q39" + ], + [ + "Q34", + "Q40" + ], + [ + "Q35", + "Q40" + ], + [ + "Q35", + "Q41" + ], + [ + "Q36", + "Q42" + ], + [ + "Q36", + "Q43" + ], + [ + "Q37", + "Q43" + ], + [ + "Q37", + "Q44" + ], + [ + "Q38", + "Q44" + ], + [ + "Q38", + "Q45" + ], + [ + "Q39", + "Q45" + ], + [ + "Q39", + "Q46" + ], + [ + "Q40", + "Q46" + ], + [ + "Q40", + "Q47" + ], + [ + "Q41", + "Q47" + ], + [ + "Q42", + "Q48" + ], + [ + "Q43", + "Q48" + ], + [ + "Q43", + "Q49" + ], + [ + "Q44", + "Q49" + ], + [ + "Q44", + "Q50" + ], + [ + "Q45", + "Q50" + ], + [ + "Q45", + "Q51" + ], + [ + "Q46", + "Q51" + ], + [ + "Q46", + "Q52" + ], + [ + "Q47", + "Q52" + ], + [ + "Q47", + "Q53" + ], + [ + "Q48", + "Q54" + ], + [ + "Q48", + "Q55" + ], + [ + "Q49", + "Q55" + ], + [ + "Q49", + "Q56" + ], + [ + "Q50", + "Q56" + ], + [ + "Q50", + "Q57" + ], + [ + "Q51", + "Q57" + ], + [ + "Q51", + "Q58" + ], + [ + "Q52", + "Q58" + ], + [ + "Q52", + "Q59" + ], + [ + "Q53", + "Q59" + ], + [ + "Q54", + "Q60" + ], + [ + "Q55", + "Q60" + ], + [ + "Q55", + "Q61" + ], + [ + "Q56", + "Q61" + ], + [ + "Q56", + "Q62" + ], + [ + "Q57", + "Q62" + ], + [ + "Q57", + "Q63" + ], + [ + "Q58", + "Q63" + ], + [ + "Q58", + "Q64" + ], + [ + "Q59", + "Q64" + ], + [ + "Q59", + "Q65" + ] + ], + "readout": [ + { + "channel": "01", + "qubits": [ + "Q00", + "Q01", + "Q02", + "Q03", + "Q04", + "Q05" + ] + }, + { + "channel": "02", + "qubits": [ + "Q06", + "Q07", + "Q08", + "Q09", + "Q10", + "Q11" + ] + }, + { + "channel": "03", + "qubits": [ + "Q12", + "Q13", + "Q14", + "Q15", + "Q16", + "Q17" + ] + }, + { + "channel": "04", + "qubits": [ + "Q18", + "Q19", + "Q20", + "Q21", + "Q22", + "Q23" + ] + }, + { + "channel": "05", + "qubits": [ + "Q24", + "Q25", + "Q26", + "Q27", + "Q28", + "Q29" + ] + }, + { + "channel": "06", + "qubits": [ + "Q30", + "Q31", + "Q32", + "Q33", + "Q34", + "Q35" + ] + }, + { + "channel": "07", + "qubits": [ + "Q36", + "Q37", + "Q38", + "Q39", + "Q40", + "Q41" + ] + }, + { + "channel": "08", + "qubits": [ + "Q42", + "Q43", + "Q44", + "Q45", + "Q46", + "Q47" + ] + }, + { + "channel": "09", + "qubits": [ + "Q48", + "Q49", + "Q50", + "Q51", + "Q52", + "Q53" + ] + }, + { + "channel": "10", + "qubits": [ + "Q54", + "Q55", + "Q56", + "Q57", + "Q58", + "Q59" + ] + }, + { + "channel": "11", + "qubits": [ + "Q60", + "Q61", + "Q62", + "Q63", + "Q64", + "Q65" + ] + } + ] +} \ No newline at end of file diff --git a/examples/cim/coupling_graph.txt b/examples/cim/coupling_graph.txt new file mode 100644 index 0000000000000000000000000000000000000000..1637300fdc8139ba5b03612cf237505a047049b6 --- /dev/null +++ b/examples/cim/coupling_graph.txt @@ -0,0 +1,7 @@ +6 6 +1 2 1 +2 3 1 +3 4 1 +4 5 1 +5 6 1 +6 1 1 diff --git a/examples/cim/demo.py b/examples/cim/demo.py new file mode 100644 index 0000000000000000000000000000000000000000..99986265b31e7269706811b1d42a9a85b645db31 --- /dev/null +++ b/examples/cim/demo.py @@ -0,0 +1,73 @@ +from pathlib import Path + +import numpy as np + +from quingo.cim import cim_envolve, read_coupling_graph + + +def cim_simulate_matrix(couple_matrix: np.ndarray, num_repetitions: int = 3) -> None: + """ + 执行CIM模拟并打印结果 + + 参数: + couple_matrix (np.ndarray): 耦合矩阵 + num_repetitions (int): 模拟重复次数,默认3 + """ + print("input matrix: \n", couple_matrix) + print(f"Simulation result ({num_repetitions} times):") + for _ in range(num_repetitions): + sol = cim_envolve(couple_matrix) + print(sol) + print("--------------------------------------") + + +def cim_simulate_coupling_graph(file_path: str, num_repetitions: int = 3) -> None: + """ + 从文件读取耦合图并执行CIM模拟 + + 参数: + file_path (str): 耦合图文件路径 + num_repetitions (int): 模拟重复次数,默认3 + """ + couple_matrix = read_coupling_graph(file_path) + cim_simulate_matrix(couple_matrix, num_repetitions) + + +if __name__ == "__main__": + np.set_printoptions(linewidth=200, suppress=True) + + print("========================================================================") + print("CIM Simulation Example, direct matrix input:") + print("========================================================================") + # 输入 + couple_matrix = np.array( + [ + [0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4], + [4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0], + [0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0], + [0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0], + [0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0], + [0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4], + [4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0], + ] + ) + cim_simulate_matrix(couple_matrix) + + couple_matrix = np.array( + [[0, 1, 0, 1], [1, 0, 1, 0], [0, 1, 0, 1], [1, 0, 1, 0]] + ) + cim_simulate_matrix(couple_matrix) + + print("========================================================================") + print("CIM Simulation Example, read coupling graph from file:") + print("========================================================================") + graph_fn = Path(__file__).parent / "coupling_graph.txt" + cim_simulate_coupling_graph(graph_fn) diff --git a/examples/cim/sk_128.txt b/examples/cim/sk_128.txt new file mode 100644 index 0000000000000000000000000000000000000000..a02bf9f3a531df4bae8becd4a381b667f0d8d1d8 --- /dev/null +++ b/examples/cim/sk_128.txt @@ -0,0 +1,128 @@ +0 0 0 0 0 1 1 1 1 0 1 1 1 1 0 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1 1 0 0 1 1 1 0 1 1 0 1 1 0 0 1 0 0 1 0 0 0 1 1 0 0 0 1 1 0 0 0 1 1 1 1 1 0 1 1 1 0 1 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 0 1 1 0 1 1 0 1 1 0 1 1 1 0 0 1 1 0 0 1 1 1 0 1 0 1 1 0 1 1 1 0 1 +0 0 1 0 1 0 0 1 0 0 1 1 0 1 1 0 1 1 0 1 1 1 1 0 1 0 0 1 0 1 0 1 1 1 1 1 0 1 0 0 0 1 1 0 1 1 0 1 0 1 0 1 1 1 0 0 1 1 1 0 1 0 1 0 0 0 1 1 0 1 0 0 1 0 0 1 0 0 0 1 1 1 0 1 0 1 1 0 1 1 1 0 1 1 0 0 0 1 0 1 1 0 1 0 1 1 1 1 0 0 0 0 0 0 1 0 1 0 1 0 1 1 0 0 0 1 1 1 +0 1 0 0 1 1 0 0 0 1 0 1 1 1 0 0 0 0 0 1 0 0 0 1 0 1 0 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 1 1 0 1 0 1 1 0 1 1 1 0 0 0 1 0 1 0 0 0 0 0 1 1 1 0 1 0 1 1 0 0 1 0 0 1 0 1 0 1 1 0 0 0 0 1 1 1 1 1 1 0 0 0 1 1 0 1 1 0 0 0 0 1 1 1 0 1 0 1 0 1 1 0 1 1 0 0 1 1 0 +0 0 0 0 1 1 1 1 1 1 0 1 0 0 0 1 0 1 0 0 0 1 0 1 1 1 1 0 0 1 1 0 0 1 1 0 1 1 1 0 0 1 1 1 1 1 1 1 0 0 1 1 0 0 0 0 1 1 1 1 0 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 0 0 1 1 0 0 0 0 0 1 0 0 0 1 0 0 0 0 1 1 1 1 0 1 0 0 1 0 1 0 0 1 1 1 0 0 0 1 0 1 0 1 1 0 1 1 1 0 0 1 0 0 +0 1 1 1 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 1 1 1 1 1 0 1 1 0 0 0 0 1 0 0 1 0 0 1 1 0 0 1 1 1 1 0 0 0 1 0 0 0 0 1 0 1 0 1 1 1 1 1 0 1 0 1 1 0 0 1 0 1 1 1 0 1 1 1 0 0 0 0 1 1 0 0 1 1 0 0 0 0 1 0 0 1 1 0 1 0 0 1 0 1 0 1 1 1 1 1 0 1 1 0 0 0 0 0 0 0 0 1 +1 0 1 1 1 0 1 0 0 0 0 1 1 0 1 1 1 0 1 0 1 0 1 1 0 1 0 1 1 1 1 0 0 0 1 0 0 1 1 0 0 1 1 1 0 1 0 0 0 0 1 1 1 1 1 1 0 1 1 1 0 1 1 0 1 0 0 1 1 1 0 0 1 1 1 0 0 1 0 0 0 1 1 1 0 0 0 0 1 1 1 0 1 0 0 0 0 1 0 0 1 0 0 1 1 1 1 1 0 0 1 0 1 1 1 1 0 1 0 0 0 1 0 1 1 1 1 0 +1 0 0 1 1 1 0 1 0 0 0 0 0 1 1 1 1 1 1 1 0 0 1 1 1 0 0 0 0 1 0 1 0 0 1 1 1 0 1 1 0 1 1 0 1 0 1 1 0 1 0 1 0 0 1 0 0 1 0 1 1 1 0 0 0 0 0 1 0 0 0 1 0 1 0 1 0 1 0 1 0 0 1 1 0 1 0 0 1 0 1 0 0 0 0 1 1 1 1 0 0 1 1 1 0 0 0 0 1 1 1 0 1 1 0 1 1 0 0 0 1 0 0 0 0 0 0 0 +1 1 0 1 0 0 1 0 1 1 1 0 0 0 1 1 0 1 0 0 1 1 0 1 1 0 0 0 1 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 0 0 0 0 1 1 1 0 0 1 0 0 0 1 1 0 1 0 1 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 1 0 0 1 1 0 1 0 0 1 0 1 0 0 0 0 0 0 0 1 0 1 1 1 0 0 0 0 1 1 1 0 0 1 1 0 1 1 0 1 0 0 0 1 1 0 +1 0 0 1 0 0 0 1 0 0 1 1 0 1 0 1 1 1 0 0 0 1 0 1 0 1 1 0 0 0 0 0 0 0 0 1 1 0 1 0 1 0 0 1 1 0 1 1 1 0 1 0 1 0 0 1 0 1 1 0 1 0 1 1 0 1 1 0 1 1 1 1 1 0 0 1 1 0 1 1 0 0 0 1 1 0 0 1 0 1 1 1 0 0 0 0 1 1 0 0 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 0 1 0 1 1 1 1 0 0 1 0 1 0 +0 0 1 1 1 0 0 1 0 0 0 1 0 0 1 1 1 0 0 1 1 1 0 1 1 0 0 1 1 0 0 0 1 0 1 1 0 1 0 1 0 0 0 1 0 0 1 1 0 0 0 0 0 0 0 1 1 1 1 0 0 0 1 1 1 0 1 1 1 0 0 1 0 0 0 1 1 0 0 1 1 0 1 1 0 0 1 1 1 1 0 1 0 0 0 1 0 0 1 0 0 1 1 1 0 1 1 0 1 1 0 1 0 0 0 0 0 1 0 0 1 1 1 1 0 0 1 0 +1 1 0 0 0 0 0 1 1 0 0 0 1 1 0 1 1 0 1 0 1 1 0 1 1 0 1 1 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 1 1 0 1 0 1 0 0 0 0 0 1 1 0 1 0 0 0 1 0 0 1 0 0 0 1 1 0 0 1 1 1 0 0 1 1 0 0 0 0 0 1 0 1 0 0 1 0 0 1 1 0 1 0 0 0 0 1 0 0 0 0 1 1 1 1 1 0 1 0 1 1 0 1 1 0 1 1 0 0 1 1 +1 1 1 1 0 1 0 0 1 1 0 0 0 1 0 1 0 0 1 0 1 0 1 1 1 0 1 1 1 0 0 0 1 0 0 1 0 1 1 1 0 1 0 1 1 1 0 0 1 0 0 1 0 0 0 1 1 0 0 0 1 1 0 1 1 1 0 1 1 0 1 1 0 1 1 1 1 0 1 0 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 1 1 0 0 0 1 0 1 0 0 0 0 0 0 1 1 0 1 1 0 0 0 1 0 1 0 0 0 +1 0 1 0 0 1 0 0 0 0 1 0 0 0 1 1 0 0 0 1 1 1 1 0 1 1 0 1 0 1 1 1 1 1 1 1 0 0 1 1 0 1 1 1 1 0 1 1 1 0 0 0 0 0 0 1 1 1 0 0 1 0 1 1 1 0 0 0 1 1 0 1 1 0 1 1 1 1 0 1 0 0 0 1 0 0 1 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 1 0 0 0 0 1 1 0 1 1 1 1 1 1 0 1 1 1 0 1 0 0 0 1 0 0 +1 1 1 0 0 0 1 0 1 0 1 1 0 0 1 0 1 0 1 1 1 1 1 0 0 0 0 1 0 0 1 0 0 1 1 0 0 0 0 1 0 1 0 1 0 0 1 0 1 1 0 1 0 1 0 0 0 1 1 1 1 0 1 1 1 0 1 1 0 0 1 1 0 1 1 1 1 1 1 1 0 0 1 0 1 0 1 1 1 1 0 0 1 1 0 1 1 1 1 0 1 0 0 0 0 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 0 0 1 0 0 0 1 0 +0 1 0 0 0 1 1 1 0 1 0 0 1 1 0 1 1 0 1 1 0 1 1 1 1 1 0 1 1 0 0 0 0 0 0 1 1 1 0 1 0 0 0 1 1 0 0 0 1 1 1 1 0 0 0 1 1 1 1 1 0 0 1 1 0 0 0 1 1 0 1 1 1 1 1 0 1 1 0 1 0 1 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 1 0 1 1 1 0 0 1 1 0 0 0 1 1 0 1 0 1 0 0 1 1 1 1 0 0 1 0 1 0 0 +0 0 0 1 0 1 1 1 1 1 1 1 1 0 1 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 1 0 1 1 0 1 1 1 0 0 0 0 1 0 0 1 0 0 0 1 0 0 0 0 1 0 1 1 1 0 0 1 0 0 0 0 0 0 1 1 1 1 0 0 0 0 1 0 1 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 1 0 0 0 1 0 1 1 0 1 1 0 1 0 1 0 0 0 +1 1 0 0 0 1 1 0 1 1 1 0 0 1 1 0 0 0 0 1 1 1 0 0 1 1 1 0 0 0 1 0 0 0 0 1 0 0 1 1 0 0 1 1 0 0 1 0 1 1 1 1 0 0 1 1 0 0 0 0 1 0 0 1 0 0 0 1 0 1 1 0 1 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 0 1 1 0 0 0 0 1 0 1 0 0 0 1 1 0 0 1 0 1 1 1 0 0 1 1 1 0 1 0 1 0 0 0 1 +1 1 0 1 0 0 1 1 1 0 0 0 0 0 0 1 0 0 1 0 1 1 1 0 1 0 0 1 1 0 0 0 1 0 1 0 1 0 0 1 1 1 0 1 0 1 1 1 1 0 1 0 1 0 0 1 0 1 0 1 0 1 1 0 0 0 1 1 0 0 1 0 0 1 1 1 0 0 0 0 1 1 1 1 1 1 0 1 1 1 1 0 0 0 1 1 0 1 1 0 1 0 1 0 0 0 1 1 1 1 0 1 0 0 0 1 0 1 1 1 1 0 0 0 1 0 0 1 +1 0 0 0 0 1 1 0 0 0 1 1 0 1 1 1 0 1 0 1 1 0 0 0 1 0 1 0 1 1 1 0 0 1 1 1 1 1 0 1 1 1 1 1 0 0 1 1 1 1 0 1 1 1 0 1 0 1 0 1 0 1 1 0 1 1 1 1 0 1 0 0 1 1 1 1 0 1 0 0 1 1 0 1 0 1 0 1 0 1 0 1 0 0 1 0 1 1 0 1 1 0 0 1 0 1 1 1 1 1 1 1 0 0 1 0 0 1 0 1 0 0 1 0 1 1 0 0 +0 1 1 0 1 0 1 0 0 1 0 0 1 1 1 0 1 0 1 0 0 0 1 1 1 1 1 0 1 0 0 0 1 1 1 1 0 1 1 0 0 0 0 0 0 1 1 0 1 0 0 0 0 1 1 0 1 1 1 0 1 0 1 1 0 1 0 1 1 1 0 1 1 0 1 0 1 1 0 1 1 0 0 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 0 1 0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 0 0 0 0 0 1 1 1 1 0 1 +1 1 0 0 1 1 0 1 0 1 1 1 1 1 0 0 1 1 1 0 0 0 1 1 0 0 1 1 1 1 0 1 0 1 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 0 1 1 1 0 0 1 1 0 0 0 0 1 0 1 0 0 1 1 0 1 1 1 0 1 1 1 1 0 1 0 0 0 1 0 1 0 1 1 1 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 1 1 0 1 1 1 0 1 1 1 0 0 1 0 1 0 1 1 1 1 1 1 1 +0 1 0 1 0 0 0 1 1 1 1 0 1 1 1 0 1 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 1 1 0 0 1 1 0 1 1 0 1 0 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 1 1 0 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 1 0 1 1 1 0 0 0 1 1 1 1 1 0 1 1 0 0 1 1 0 0 0 1 1 0 0 0 1 0 0 0 0 1 1 0 1 1 0 0 0 0 0 0 0 1 1 0 +0 1 0 0 0 1 1 0 0 0 0 1 1 1 1 0 0 1 0 1 1 1 0 1 1 0 1 0 1 1 0 0 1 1 0 0 0 1 1 0 1 1 1 0 1 0 1 0 0 0 1 0 1 1 0 0 1 1 1 1 1 0 1 0 1 1 0 1 1 1 1 0 1 1 1 0 0 1 1 0 0 1 1 1 0 1 0 0 0 0 0 1 0 0 1 1 1 0 0 1 1 1 0 1 1 0 0 0 0 0 0 1 0 1 0 0 0 1 0 1 0 1 0 0 1 1 1 1 +0 0 1 1 1 1 1 1 1 1 1 1 0 0 1 0 0 0 0 1 1 0 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 1 0 1 0 1 1 0 0 0 0 0 1 0 0 1 0 1 0 1 1 1 0 0 0 0 0 0 0 0 1 1 0 1 1 0 0 1 0 1 0 1 0 1 0 1 1 1 1 1 0 1 0 0 0 0 1 0 1 1 1 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 0 0 0 0 0 0 1 1 1 0 0 1 1 1 1 +0 1 0 1 0 0 1 1 0 1 1 1 1 0 1 1 1 1 1 1 0 0 1 0 0 1 0 0 1 0 1 0 0 1 1 0 0 1 1 0 1 0 0 1 0 1 0 1 0 0 1 0 1 1 1 0 1 1 1 1 1 1 0 0 0 0 1 0 1 1 0 0 1 1 0 0 0 1 1 1 0 0 0 1 1 0 0 0 0 0 1 1 0 0 1 0 0 1 1 1 1 0 0 1 1 0 0 1 1 0 1 0 1 1 1 0 1 1 1 0 1 1 0 1 1 1 1 1 +0 0 1 1 1 1 0 0 1 0 0 0 1 0 1 1 1 0 0 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 1 1 1 1 0 1 1 1 1 1 1 0 0 0 1 0 0 1 1 1 0 1 0 1 1 0 0 1 1 1 0 0 0 1 1 1 1 0 1 0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 1 1 0 1 0 1 0 1 1 1 0 1 1 1 1 0 0 0 1 0 0 1 0 1 1 1 0 1 0 0 0 1 +0 0 0 1 1 0 0 0 1 0 1 1 0 0 0 0 1 0 1 1 1 0 1 1 0 0 0 1 1 0 1 1 1 0 0 1 0 0 1 0 0 1 1 0 0 0 1 0 0 1 1 1 1 1 1 0 1 1 1 0 1 1 0 0 0 0 0 1 0 1 1 1 1 0 0 0 0 1 1 0 0 0 0 1 0 0 1 1 1 1 0 1 0 1 0 0 0 0 0 0 1 1 1 1 0 1 0 1 0 0 1 1 0 1 1 1 0 1 1 1 1 0 0 1 0 0 1 1 +0 1 1 0 1 1 0 0 0 1 1 1 1 1 1 0 0 1 0 0 1 1 0 0 0 0 1 0 1 0 1 0 0 1 1 0 0 1 1 0 0 1 1 0 1 0 1 0 1 0 0 1 0 1 1 0 0 0 0 0 1 1 0 1 1 0 0 1 1 0 1 1 1 0 1 1 0 1 1 0 0 1 0 0 1 0 1 0 0 1 0 1 1 1 1 1 0 0 0 1 1 0 0 1 1 0 0 0 0 1 0 0 0 1 1 0 1 0 0 1 1 0 1 0 0 0 1 1 +0 0 0 0 1 1 0 1 0 1 0 1 0 0 1 0 0 1 1 1 1 0 1 0 1 0 1 1 0 0 1 0 0 1 0 0 0 0 1 1 1 0 1 1 0 0 1 0 0 0 0 1 1 0 0 0 1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 1 0 1 0 0 0 0 1 0 1 1 0 1 1 0 0 0 0 1 0 1 0 1 1 1 1 0 0 0 1 1 1 1 1 0 1 1 1 0 1 0 0 1 0 0 1 1 1 0 0 1 0 0 1 1 0 0 +0 1 1 1 1 1 1 0 0 0 0 0 1 0 0 0 0 0 1 0 1 0 1 0 0 0 0 0 0 0 0 1 1 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 1 0 1 0 0 1 0 0 0 1 0 1 1 0 0 0 1 1 1 1 1 0 0 0 1 1 1 0 1 0 0 0 1 1 1 0 1 0 0 0 0 1 1 0 0 1 0 1 0 0 1 1 0 1 0 0 0 0 1 0 1 0 1 1 0 0 0 0 1 1 1 1 0 0 0 1 1 0 1 1 +1 0 1 1 0 1 0 1 0 0 0 0 1 1 0 0 1 0 1 0 0 0 0 1 1 1 1 1 1 0 0 0 1 0 1 1 1 1 0 1 0 1 0 0 0 0 0 0 0 1 1 0 1 1 0 1 1 1 0 0 1 0 0 0 0 0 0 0 1 0 1 1 1 0 0 1 0 0 1 1 1 0 0 0 0 0 1 0 0 0 1 0 0 1 1 0 0 1 0 1 1 1 0 1 0 1 1 1 0 0 0 1 0 1 0 0 1 1 1 0 1 1 1 0 1 0 1 1 +0 1 1 0 1 0 1 1 0 0 0 0 1 0 0 1 0 0 0 0 1 1 0 1 0 0 1 0 0 1 0 0 0 1 0 1 1 0 1 1 1 1 1 1 1 1 1 0 0 0 1 1 0 0 1 0 1 0 1 1 1 1 1 1 1 1 1 1 0 1 1 0 1 1 1 1 0 0 0 0 0 1 1 0 0 1 0 0 0 1 0 0 1 1 1 1 0 1 1 0 1 1 0 1 0 1 0 0 0 0 1 0 0 0 1 1 0 0 1 0 0 0 0 1 1 1 1 0 +0 1 0 0 1 0 0 1 0 1 0 1 1 0 0 1 0 1 0 1 0 1 1 0 0 0 1 0 0 1 1 0 0 0 0 1 0 1 1 1 0 0 0 1 1 1 1 1 0 0 0 0 1 0 1 1 0 0 0 1 0 0 1 1 0 1 0 1 1 0 1 0 0 1 0 1 1 0 1 1 1 1 1 1 0 1 0 0 1 0 0 0 0 1 0 1 0 1 1 1 0 0 1 0 1 1 1 1 0 0 0 1 1 1 0 1 1 0 1 0 0 0 1 1 0 0 1 1 +0 1 0 1 0 0 0 1 0 0 0 0 1 1 0 1 0 0 1 1 1 1 1 0 1 0 0 1 1 0 0 1 0 0 1 1 1 0 1 1 1 1 0 1 1 1 0 0 0 0 1 1 1 1 1 0 1 1 0 1 0 0 1 1 1 0 1 1 1 0 0 0 1 1 0 1 0 1 1 1 0 0 0 0 0 0 0 1 1 0 0 0 1 0 1 0 0 0 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 0 1 0 1 0 0 0 1 +1 1 0 1 0 1 1 1 0 1 0 0 1 1 0 0 0 1 1 1 0 0 0 0 1 0 0 1 0 1 1 0 0 1 0 1 1 1 1 0 1 1 0 0 1 0 0 0 1 0 1 1 0 0 1 1 1 0 1 1 0 0 0 0 0 1 1 0 1 0 1 1 1 0 1 1 1 0 1 0 1 1 1 0 0 1 1 0 1 0 0 1 0 1 1 1 0 0 1 1 0 0 1 1 0 0 1 0 0 1 1 0 0 1 0 1 0 0 0 0 0 0 0 1 0 1 1 0 +1 1 0 0 0 0 1 1 1 1 1 1 1 0 1 1 1 0 1 1 0 0 0 1 0 0 1 0 0 1 1 1 1 1 1 0 0 1 0 0 1 0 1 1 0 0 0 0 0 1 1 1 0 1 1 0 0 0 0 0 1 0 0 1 0 1 0 0 1 0 1 0 1 1 1 1 0 0 1 1 1 1 1 0 0 0 0 1 1 1 0 0 1 0 0 0 0 0 1 0 1 1 1 1 1 1 1 1 0 0 0 1 1 0 0 0 1 0 1 1 1 1 1 0 1 1 1 0 +0 0 0 1 0 0 1 1 1 0 1 0 0 0 1 1 0 1 1 0 0 1 0 0 0 0 0 0 0 0 1 1 0 1 1 0 0 0 1 0 0 0 1 0 1 0 0 1 0 0 0 1 1 1 0 1 1 1 1 1 1 0 0 0 0 1 0 0 0 0 1 1 0 0 1 0 0 1 0 1 0 1 1 0 1 0 1 1 0 0 0 1 0 1 0 1 0 0 0 0 0 0 1 0 1 1 1 1 1 0 1 1 0 0 0 1 1 1 1 0 1 0 0 1 1 0 1 1 +1 1 0 1 1 1 0 1 0 1 0 1 0 0 1 0 0 0 1 1 0 1 1 1 1 0 0 1 0 0 1 0 1 0 1 1 0 0 1 0 1 1 1 1 0 1 1 1 0 1 0 1 1 1 1 0 0 1 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 0 1 1 0 1 1 1 0 1 1 1 1 1 1 1 0 1 1 0 0 0 0 0 0 0 1 0 1 1 0 1 1 1 0 0 1 0 0 1 0 0 1 1 1 1 0 1 0 1 0 0 0 0 +1 0 0 1 0 1 1 1 1 0 0 1 1 0 0 1 1 0 0 1 0 0 1 0 1 1 1 1 1 0 0 1 1 1 1 0 1 1 0 1 1 0 0 1 0 0 1 0 1 0 0 0 0 0 0 1 1 0 0 0 0 1 0 1 1 1 0 1 1 1 1 0 1 1 1 0 1 0 1 0 0 0 1 1 0 1 1 0 0 1 1 0 1 1 1 0 1 0 0 1 0 1 0 0 1 0 1 0 0 1 1 1 0 1 1 1 0 1 0 1 1 1 1 1 1 0 1 0 +0 0 0 0 0 0 1 1 0 1 0 1 1 1 1 1 1 1 1 0 0 1 0 1 0 0 0 0 1 0 1 1 1 1 0 0 0 0 1 0 1 1 0 1 1 0 0 0 1 1 0 1 0 1 0 1 1 0 0 1 0 1 0 1 1 0 0 0 1 0 1 1 0 1 1 0 0 0 1 1 1 1 1 1 0 1 1 0 1 0 1 0 0 0 0 1 0 0 1 0 0 1 0 1 1 0 1 1 0 0 1 0 0 1 1 0 0 1 1 0 0 0 1 1 0 1 1 0 +0 0 0 0 1 0 0 1 1 0 0 0 0 0 0 1 0 1 1 0 1 1 1 0 1 0 0 0 1 0 0 1 0 1 1 1 0 1 1 1 0 1 0 1 1 1 1 1 1 1 1 0 0 1 0 1 1 1 0 0 0 1 0 1 1 0 0 1 1 0 0 1 0 1 0 0 0 0 1 0 0 0 0 1 0 1 0 1 0 1 1 0 1 1 0 0 0 1 1 0 1 0 0 0 1 0 0 0 0 0 1 1 1 0 0 1 0 0 1 1 1 1 0 0 0 0 0 0 +1 1 0 1 0 1 1 0 0 0 0 1 1 1 0 0 0 1 1 0 0 0 1 1 0 0 1 1 0 0 1 1 0 1 1 0 0 1 0 1 1 0 1 0 1 1 0 0 1 1 0 1 0 0 1 1 0 0 0 1 0 0 1 1 1 1 0 1 1 1 1 0 0 1 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 1 0 0 1 1 1 1 0 0 0 1 0 1 1 1 1 1 1 1 0 1 1 1 0 0 0 0 1 1 0 1 0 0 0 1 1 0 0 0 +1 1 0 1 0 1 1 0 0 0 0 0 1 0 0 0 1 0 1 0 1 1 1 1 0 1 1 1 1 0 0 1 0 0 0 1 1 1 0 0 0 1 0 0 1 1 0 1 1 0 0 1 0 1 0 0 0 1 1 0 0 1 0 1 1 1 1 1 0 1 0 0 0 0 1 1 1 0 0 0 0 1 0 0 1 1 0 1 0 1 1 0 1 0 0 1 0 0 0 1 0 0 0 1 1 0 1 0 1 1 0 1 1 1 0 1 0 1 1 0 0 0 1 1 0 0 0 0 +1 0 1 1 1 1 0 0 1 1 1 1 1 1 1 0 1 1 1 0 1 0 0 0 1 1 0 0 1 0 0 1 1 1 0 1 0 1 1 1 1 0 0 0 0 1 1 1 1 1 0 1 1 0 0 0 1 1 1 0 1 1 1 0 1 0 0 0 1 1 0 1 1 1 1 0 1 0 1 0 1 0 0 0 1 0 1 1 1 1 0 0 0 1 0 1 0 0 0 0 0 0 1 1 0 1 1 1 0 1 1 0 0 0 0 1 1 0 0 1 1 1 1 1 0 1 0 1 +0 1 1 1 1 0 1 1 1 0 0 1 1 0 1 0 0 0 0 0 0 1 1 0 0 1 0 1 0 1 0 1 1 1 1 0 1 0 0 1 1 1 1 0 0 1 0 1 0 0 0 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 1 1 0 0 1 1 1 0 1 1 1 0 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 1 1 0 1 1 1 0 0 0 0 1 1 0 1 0 1 1 0 0 0 1 0 0 1 1 1 1 1 0 0 1 0 +1 1 0 1 0 1 0 1 0 0 0 1 0 0 0 1 0 1 0 1 0 1 0 0 1 1 0 0 0 0 0 1 1 1 0 0 0 1 0 0 1 1 1 1 1 0 0 1 1 1 0 0 0 1 1 0 1 0 0 1 1 1 1 1 1 0 0 1 0 0 1 0 1 1 1 0 1 1 0 1 0 0 0 0 1 1 1 1 1 0 0 1 0 0 1 1 1 1 0 0 1 0 0 0 0 0 0 0 1 1 1 1 0 1 0 0 0 1 0 0 1 0 1 0 0 1 1 0 +1 0 1 1 0 0 1 0 1 1 1 0 1 1 0 0 1 1 1 1 0 1 1 0 0 0 1 1 1 0 0 1 1 0 0 0 0 1 1 0 1 0 0 1 0 0 0 1 1 0 0 1 1 1 1 0 0 0 0 1 0 0 1 0 0 0 1 0 1 1 1 1 0 1 1 1 0 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 0 0 1 0 1 1 0 0 1 0 1 1 1 1 0 0 0 1 1 0 0 0 1 0 1 1 1 1 1 1 0 1 1 1 1 1 +0 1 1 1 1 0 1 0 1 1 1 0 1 0 0 0 0 1 1 0 0 1 0 0 1 1 0 0 0 0 0 0 1 0 0 0 1 1 0 0 1 0 1 1 1 1 1 0 1 1 1 0 1 0 0 0 1 0 1 0 0 1 0 1 1 0 1 1 0 1 1 1 0 1 1 0 0 1 1 1 0 1 0 1 0 1 1 1 0 0 1 1 0 0 0 1 1 1 0 0 1 1 0 0 0 1 1 0 0 0 1 0 1 0 0 1 1 0 1 1 0 0 0 1 1 1 0 1 +1 0 1 0 1 0 0 0 1 0 0 1 1 1 1 1 1 1 1 1 0 0 0 1 0 1 0 1 0 1 0 0 0 0 1 0 0 0 1 1 1 1 1 1 0 1 1 1 0 1 1 0 0 1 0 1 0 0 0 1 1 1 0 1 1 1 0 1 1 0 0 1 0 0 1 1 0 0 1 0 0 1 0 1 1 1 0 1 1 1 0 0 0 0 0 0 1 0 1 1 0 1 0 1 1 1 1 0 1 1 1 1 1 1 1 0 0 0 0 1 0 1 0 1 0 0 0 0 +1 1 0 0 1 0 1 0 0 0 1 0 0 1 1 0 1 0 1 0 0 0 0 0 0 1 1 0 0 0 1 0 0 0 0 1 0 1 0 1 1 1 0 1 0 1 0 1 1 0 1 0 0 0 1 0 0 0 0 1 1 0 0 0 1 0 1 0 1 0 1 0 0 0 0 1 1 0 0 0 1 0 1 1 1 0 0 0 0 1 0 0 1 1 1 0 0 1 1 0 1 0 0 1 1 1 1 0 0 0 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 0 1 1 +0 0 1 1 1 1 0 1 1 0 0 0 0 0 1 0 1 1 0 0 1 0 1 0 1 1 1 0 0 1 1 1 0 1 1 1 0 0 0 0 1 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 0 1 0 0 0 1 1 0 0 1 1 0 0 1 0 0 1 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 1 1 0 1 0 0 0 1 0 0 1 0 1 0 1 1 0 0 0 1 1 1 1 1 1 1 0 0 0 0 1 0 1 0 +0 1 0 1 0 1 1 1 0 0 1 1 0 1 1 0 1 0 1 0 1 1 0 1 0 1 1 1 1 0 0 1 0 1 1 1 1 1 0 1 0 1 1 1 0 0 1 0 0 0 0 0 0 1 1 1 0 1 1 0 1 0 0 1 0 0 1 1 1 0 0 0 1 1 0 1 1 0 1 0 1 0 1 0 1 0 1 0 1 0 0 1 0 1 1 1 0 0 1 0 1 0 0 1 1 1 1 0 0 0 1 1 0 0 0 0 0 0 1 1 0 1 0 0 0 0 1 1 +1 1 1 0 0 1 0 1 1 0 0 0 0 0 0 1 0 1 1 0 1 0 1 0 1 1 1 0 1 0 1 0 1 1 0 0 1 1 0 0 0 0 0 1 0 0 1 1 0 0 0 0 0 0 1 1 0 1 1 0 0 0 1 0 1 1 0 0 0 1 0 0 0 0 1 1 0 0 1 0 0 1 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 1 0 0 1 1 0 1 0 1 1 1 1 0 0 0 0 1 1 1 1 +0 1 1 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 1 1 0 0 1 1 1 0 1 1 0 1 1 0 0 1 0 1 1 1 0 1 1 0 1 0 0 1 1 0 1 0 0 1 0 0 1 0 1 1 1 1 0 0 0 1 0 0 1 0 0 0 1 1 1 1 0 1 0 0 0 1 0 1 1 1 1 0 1 1 0 1 1 0 1 1 1 1 1 0 1 0 0 0 0 0 0 0 1 1 0 1 0 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1 0 1 +0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 1 0 1 1 0 0 0 1 1 1 1 1 0 1 0 0 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1 1 1 1 1 0 0 1 1 0 1 1 0 0 1 1 0 0 1 1 0 1 0 1 0 1 0 1 0 0 0 0 0 0 1 0 1 1 1 1 1 1 0 1 1 0 0 0 1 1 0 1 1 1 0 0 1 0 0 1 0 0 0 1 0 0 0 1 1 1 0 1 1 +1 0 1 0 0 1 0 1 1 1 0 1 1 0 1 0 1 1 1 0 1 0 0 1 0 0 0 0 0 0 1 0 1 0 1 0 1 0 1 1 1 1 0 0 1 0 0 0 1 0 0 1 1 0 1 0 1 1 1 0 1 0 1 0 1 0 1 0 0 0 1 1 0 0 0 1 0 0 0 0 0 0 1 1 0 0 1 1 1 0 1 1 1 0 1 0 0 1 0 1 1 1 1 0 1 0 0 1 0 0 0 1 1 1 0 0 1 0 1 0 0 1 1 1 0 0 0 1 +0 1 1 1 0 0 0 0 0 1 0 1 1 0 1 0 0 0 0 1 1 0 1 1 1 1 1 0 1 0 1 1 0 1 1 0 1 0 1 1 1 0 0 1 0 1 0 1 0 0 0 0 0 1 1 1 0 1 0 0 1 1 0 0 0 1 1 0 0 1 1 1 1 1 1 0 1 1 1 0 0 1 0 1 0 1 0 0 0 0 1 1 0 0 0 1 0 1 1 0 1 0 1 0 1 0 1 0 0 1 0 1 0 1 0 1 1 0 0 1 1 1 0 0 0 1 1 1 +0 1 1 1 0 1 1 0 1 1 1 0 1 1 1 1 0 1 1 1 0 0 1 1 1 0 1 0 0 1 1 0 0 1 0 0 1 1 0 0 1 0 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 1 0 0 0 1 0 1 1 1 1 0 0 1 0 1 0 1 1 1 1 0 1 1 0 1 1 1 1 0 1 1 0 1 0 0 1 0 1 1 0 0 0 0 0 0 0 1 1 1 0 1 1 1 1 0 1 1 1 1 1 1 0 1 0 1 1 1 0 0 +0 1 0 1 0 1 0 0 1 1 1 0 0 1 1 0 0 0 0 1 0 0 1 0 1 0 1 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 0 0 0 1 0 0 1 1 1 1 1 1 0 0 0 1 0 1 0 1 0 1 1 1 1 0 1 0 0 1 0 0 0 1 0 1 0 0 1 0 1 0 1 0 1 0 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 0 1 1 0 1 0 1 1 0 1 0 1 0 1 0 1 1 1 0 1 0 1 1 +1 0 0 1 1 1 1 1 0 0 0 0 0 1 1 1 0 1 1 0 0 1 1 0 1 1 0 0 0 1 0 1 1 1 1 0 1 1 0 1 0 1 0 0 0 1 1 0 1 1 1 0 0 1 1 0 0 1 1 0 0 1 1 1 1 1 0 1 0 1 1 0 1 1 0 1 1 1 0 1 1 1 0 1 1 0 1 0 1 1 0 1 1 0 0 1 0 0 0 0 0 1 1 1 0 0 0 0 1 1 0 1 0 1 1 1 0 0 1 0 0 1 1 0 1 0 1 0 +1 1 0 0 0 0 1 1 1 0 1 1 1 1 0 1 1 0 0 1 0 1 1 0 1 1 1 1 1 1 1 1 0 0 0 1 1 0 0 0 0 0 0 1 1 1 0 0 1 1 1 1 0 0 0 1 1 0 0 0 0 0 0 1 0 0 1 0 0 0 0 1 0 1 1 1 0 0 1 1 1 0 1 0 1 0 1 1 1 0 0 1 1 0 1 1 0 1 1 1 1 0 0 0 0 0 1 0 1 1 0 0 1 0 1 1 1 0 0 0 0 1 1 1 0 0 1 1 +0 0 1 0 1 1 1 0 0 0 0 1 0 0 0 1 0 1 1 0 1 0 0 0 1 1 1 1 1 0 0 1 0 0 0 0 0 0 1 1 1 0 1 1 1 1 0 1 1 0 1 0 0 0 0 0 1 0 1 1 0 0 1 0 0 1 1 0 1 1 0 0 1 1 1 1 1 1 0 1 0 0 0 0 1 1 0 1 0 1 0 0 0 1 0 0 1 1 1 0 0 1 0 0 0 0 1 0 0 0 0 1 1 1 1 0 1 0 1 1 1 1 0 1 0 0 0 1 +0 1 0 1 0 1 0 1 1 1 0 0 1 1 1 0 0 1 1 1 0 1 1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 1 0 1 1 1 1 0 0 0 0 0 1 0 1 1 0 0 0 1 0 1 0 1 0 1 1 1 1 0 1 0 1 0 1 1 0 0 0 0 1 0 0 1 0 1 1 1 0 1 0 0 0 1 1 0 1 0 1 0 1 1 1 0 1 1 1 1 0 1 1 0 1 1 1 1 1 1 0 1 1 0 0 0 1 1 1 0 +0 0 1 1 1 0 0 0 1 1 0 1 1 1 1 0 1 0 0 1 1 0 0 0 0 1 0 1 0 0 0 1 1 1 0 1 0 0 1 1 1 1 1 0 0 1 0 1 1 0 1 1 0 1 1 0 0 1 1 1 1 0 1 0 0 0 0 1 0 0 1 1 1 0 1 0 1 1 1 0 1 1 0 1 0 1 0 1 1 0 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 1 1 1 1 1 0 1 0 0 1 1 1 0 0 1 +1 0 0 0 1 1 0 1 0 1 1 1 1 1 0 1 0 0 1 0 0 1 1 0 0 0 0 1 1 1 0 1 0 1 0 0 0 1 1 1 1 1 1 1 0 1 0 1 1 1 0 0 1 0 0 1 0 0 0 1 0 0 0 0 0 0 0 1 1 0 0 0 1 0 0 1 1 1 0 1 1 0 1 1 0 0 1 1 0 1 0 1 1 1 0 1 1 1 0 0 0 1 1 0 1 1 0 1 1 1 0 0 1 0 1 0 0 0 0 0 0 0 0 1 0 1 0 1 +1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 0 0 0 1 1 0 0 1 0 0 1 0 0 0 1 0 1 1 0 1 1 1 0 1 0 0 1 1 0 0 0 0 0 1 0 0 0 1 0 1 0 1 1 1 1 0 1 1 0 0 0 1 0 1 0 0 1 1 1 1 1 0 1 0 1 0 0 0 1 0 0 1 1 1 0 0 0 0 0 1 1 0 0 1 1 0 0 1 0 1 1 0 0 0 0 0 1 1 1 0 1 1 0 1 0 1 0 1 1 0 1 1 0 +0 1 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1 1 0 1 0 0 1 1 1 0 0 1 1 0 1 0 1 1 0 0 0 0 0 0 0 1 0 1 0 1 1 0 1 0 1 0 1 1 1 1 1 1 0 1 1 1 0 0 1 0 1 1 0 0 1 0 1 0 1 1 0 0 0 1 1 1 1 0 0 1 0 1 0 1 1 0 1 0 1 0 1 0 0 1 0 1 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1 1 0 1 0 0 0 0 0 1 +0 1 0 0 1 1 1 0 0 1 1 1 0 1 1 0 1 1 1 1 1 0 1 1 0 0 1 1 0 1 0 1 1 1 0 0 0 0 1 0 1 1 1 0 1 1 0 1 1 0 1 1 0 0 0 0 0 1 1 1 0 0 1 1 1 0 1 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 1 1 0 1 0 0 1 0 0 0 1 0 1 0 0 1 1 0 1 1 1 0 1 1 1 1 0 1 1 0 1 0 0 0 0 1 +0 0 0 1 0 1 0 0 1 1 0 1 1 0 1 0 0 0 0 1 0 0 1 0 1 0 0 1 1 1 1 0 1 1 1 1 0 0 1 1 1 1 0 1 0 0 1 0 1 1 1 1 0 0 0 0 0 1 1 0 0 1 1 0 1 1 1 0 0 1 1 1 1 1 0 1 0 0 1 1 0 0 0 1 1 0 0 1 1 1 1 0 1 1 0 0 0 0 1 1 0 0 1 0 1 0 1 0 1 1 1 0 0 0 1 0 1 1 0 1 0 0 1 0 0 0 1 0 +1 1 1 1 1 1 0 0 1 0 0 0 1 0 0 0 1 0 1 1 1 0 1 1 1 1 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 1 1 0 0 1 1 0 0 0 0 1 0 1 0 1 0 0 1 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 1 0 0 1 0 1 1 1 0 1 1 1 1 1 1 0 0 0 0 1 1 1 0 1 0 1 1 1 1 1 0 0 1 0 0 1 0 1 0 0 0 0 1 0 1 0 0 0 0 0 0 1 1 +1 0 1 1 0 0 0 0 1 0 0 1 0 1 1 0 1 1 0 0 1 0 1 1 0 1 1 1 0 0 1 1 1 0 1 1 1 0 1 1 0 1 0 0 1 1 1 1 0 1 0 0 0 1 1 1 1 0 1 1 0 0 1 1 0 0 0 0 1 0 0 1 1 1 1 0 1 0 0 1 1 1 1 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 1 1 0 0 0 1 0 0 1 1 0 1 1 0 1 1 1 0 0 1 1 0 0 1 0 1 0 0 1 0 +1 0 1 1 1 0 1 0 1 1 1 1 1 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 1 0 1 0 0 0 1 0 1 0 0 1 1 0 0 1 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 0 1 0 0 1 0 1 1 0 1 0 1 0 0 1 0 0 0 0 1 1 0 1 0 1 1 1 0 0 0 0 0 1 0 1 0 0 1 0 1 1 1 1 0 0 0 0 1 0 0 1 0 0 0 1 0 0 0 0 0 0 0 1 0 1 0 0 1 0 +1 1 0 0 1 1 0 0 1 0 1 0 1 0 1 1 1 0 1 1 0 0 1 0 1 0 1 1 0 1 1 1 0 1 1 1 0 0 1 0 0 0 0 1 1 1 0 0 0 0 1 1 0 1 0 0 1 0 0 1 0 1 1 1 1 1 0 1 1 1 1 0 0 1 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 1 1 1 1 1 0 0 0 1 1 0 0 0 1 0 1 0 1 0 1 0 1 1 0 1 0 0 1 1 0 1 0 0 +1 0 1 1 0 1 1 0 0 0 0 1 0 1 1 1 0 1 1 0 1 0 1 1 1 0 0 0 1 1 0 1 1 1 0 1 0 1 1 1 1 1 0 1 0 1 1 1 0 0 0 1 0 1 1 0 1 1 1 1 1 1 0 0 0 1 1 1 1 0 1 1 1 0 0 0 1 1 1 0 1 0 0 1 0 0 1 0 0 1 1 1 1 1 0 1 1 1 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 1 0 0 1 1 1 0 1 1 0 1 0 1 0 0 +0 0 0 0 0 1 0 1 0 0 0 1 1 1 1 1 0 1 1 1 1 0 1 0 0 0 0 1 0 1 0 1 0 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 0 0 0 1 0 1 0 1 0 0 0 1 1 1 1 0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 1 0 0 1 1 1 1 0 0 0 0 1 1 1 0 0 0 1 1 1 0 0 1 0 1 1 1 0 1 1 0 1 1 0 0 1 +1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 0 0 1 1 0 1 0 0 1 0 1 0 1 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 0 1 0 1 0 1 1 1 1 1 1 0 1 0 1 0 1 1 1 1 0 1 1 1 0 1 1 0 0 0 0 1 0 0 0 0 1 0 0 1 1 0 0 1 1 1 0 1 1 0 1 1 1 1 1 0 0 1 0 1 0 1 0 1 1 0 1 1 1 1 1 0 1 0 0 0 1 0 1 1 0 1 0 1 1 +1 0 1 0 0 0 0 1 1 1 1 1 1 1 1 0 1 0 0 1 1 0 0 0 0 1 0 0 0 1 0 0 1 0 1 0 0 1 1 0 0 0 1 1 1 1 0 0 0 1 0 1 0 0 1 0 1 1 0 1 0 1 0 1 1 0 1 1 0 0 1 0 1 1 1 0 0 1 1 1 1 0 0 1 0 1 0 1 1 1 1 0 0 1 0 1 0 1 1 0 0 1 1 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1 0 1 0 1 1 0 0 +1 0 0 0 1 1 1 0 0 0 1 0 1 1 1 0 1 0 1 1 0 0 1 1 1 1 1 1 0 0 0 0 0 1 0 0 1 1 0 0 0 1 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 0 1 0 1 1 1 0 0 0 0 0 0 0 1 0 0 1 0 1 0 0 0 0 1 0 1 1 1 0 1 1 1 0 0 1 1 1 0 1 1 1 0 1 0 0 1 1 0 0 0 1 0 1 1 1 0 1 0 0 1 1 1 1 1 1 1 1 1 +0 0 0 1 1 0 0 0 1 0 0 1 0 1 0 0 1 0 0 0 1 1 1 0 1 1 1 1 1 0 1 0 1 1 1 1 0 0 1 1 1 1 0 1 1 0 0 1 1 0 1 1 1 0 1 0 1 1 0 0 1 0 0 1 0 0 0 0 1 1 0 1 1 1 0 0 1 1 0 1 1 0 0 1 0 1 0 1 0 1 1 1 1 1 0 1 0 1 0 0 0 0 0 0 1 0 0 1 0 0 1 1 0 0 1 1 0 1 1 0 0 1 0 0 1 1 1 0 +1 1 1 1 1 0 1 0 1 1 0 0 1 1 1 1 1 0 0 1 0 0 0 1 1 0 0 0 0 0 1 0 1 1 0 1 1 1 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 0 1 1 1 1 0 0 1 1 0 0 1 0 1 1 0 0 0 1 1 0 1 0 1 0 1 1 0 0 0 0 1 0 1 0 1 1 1 0 0 1 0 1 0 1 0 1 1 0 0 0 1 0 0 1 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 +0 1 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 1 1 1 0 1 0 0 0 1 0 0 1 1 1 0 1 0 1 1 0 1 0 1 0 0 0 1 1 0 0 0 0 1 0 1 0 0 1 0 0 1 0 1 1 0 1 1 1 0 1 0 0 1 1 0 0 1 0 0 1 0 1 1 0 1 0 1 0 1 0 1 1 0 1 1 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 1 1 1 1 0 0 1 1 1 0 0 0 +0 1 0 0 1 1 0 0 0 0 1 0 0 0 1 1 0 1 1 0 0 0 1 1 0 0 0 1 1 1 0 1 1 0 1 1 1 1 0 1 0 0 1 0 0 0 1 1 1 0 0 0 1 1 0 0 1 1 0 1 0 0 0 1 0 0 1 0 0 1 1 1 0 0 0 0 0 0 0 0 1 0 1 0 1 1 1 0 1 1 1 1 1 1 0 1 0 0 0 1 1 1 1 1 0 0 1 0 1 1 0 0 1 0 1 0 1 0 0 1 0 0 0 1 0 1 1 1 +0 0 1 0 1 1 1 1 0 1 0 1 0 1 0 0 0 1 0 0 1 1 1 1 0 0 0 0 0 1 0 1 1 0 1 1 1 0 1 1 0 0 0 0 0 0 0 0 0 1 0 1 1 1 1 1 0 0 1 0 1 0 0 0 1 0 1 0 0 1 1 0 0 0 0 1 0 0 0 1 0 1 0 1 1 1 1 0 0 0 1 1 0 0 1 1 1 0 1 0 0 0 1 1 0 0 0 0 0 0 1 0 1 1 1 1 0 1 0 0 1 1 1 1 1 0 0 0 +0 1 0 0 1 1 1 0 1 1 0 0 1 0 1 1 0 1 1 0 0 1 1 1 1 1 1 0 1 0 0 0 1 0 0 0 0 1 1 1 1 0 0 0 0 0 1 1 1 1 0 0 1 1 0 1 1 1 0 1 0 0 1 1 1 1 1 0 1 0 0 1 0 1 0 1 1 1 1 1 1 0 1 0 1 1 1 0 0 1 1 1 0 1 0 1 1 0 0 0 0 1 1 1 0 0 0 1 1 0 1 1 1 0 1 0 1 1 1 0 1 0 1 0 0 0 0 0 +1 0 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 1 1 1 0 1 1 1 0 1 1 1 0 0 0 0 0 0 1 1 0 0 0 0 1 1 1 1 1 0 1 1 0 1 0 1 0 0 0 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 0 0 1 1 1 1 1 1 0 0 1 0 0 0 0 1 0 0 1 0 0 0 1 1 0 1 1 1 1 1 0 0 1 1 1 1 0 0 1 1 1 1 1 0 +0 1 0 1 0 0 1 1 0 0 0 0 0 0 0 0 0 1 1 1 0 0 1 1 0 1 0 0 0 0 0 1 1 0 1 0 0 1 1 1 1 1 1 0 0 1 1 1 1 0 0 0 0 0 0 0 1 1 0 0 0 1 1 1 0 0 0 1 0 1 0 1 0 0 0 0 1 1 1 0 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 0 1 0 0 1 0 1 1 0 0 1 0 0 1 0 1 1 0 0 1 0 1 0 0 +0 1 1 0 0 0 0 1 0 1 0 1 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 1 0 0 0 1 0 1 1 1 1 0 0 0 1 0 1 0 1 0 0 0 1 0 1 0 1 0 1 1 1 1 0 1 0 1 1 1 0 0 1 0 0 0 1 1 1 0 1 0 0 0 1 1 1 1 0 0 1 1 0 0 1 0 1 1 0 0 1 1 1 1 1 1 1 0 1 1 0 0 0 0 0 1 1 1 1 1 1 1 0 1 0 1 0 0 0 0 1 +0 0 1 0 0 0 0 0 1 1 1 0 0 1 1 1 0 1 1 1 1 0 0 1 0 0 1 0 0 0 0 0 0 1 0 1 1 1 0 0 1 0 1 1 0 1 0 1 1 0 1 0 0 1 0 1 0 0 0 0 1 1 1 1 1 1 0 1 1 1 0 0 0 0 0 1 1 1 1 0 1 0 0 0 1 1 1 0 1 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 1 1 1 1 0 1 1 1 0 0 0 0 0 0 1 0 1 0 0 0 0 0 +1 1 0 0 1 1 1 1 0 1 0 0 0 1 0 0 0 1 0 1 1 1 0 0 0 0 1 0 0 0 0 0 1 1 1 1 0 1 0 1 0 0 0 1 0 1 0 0 1 0 0 1 1 0 0 1 0 1 1 1 1 0 0 1 0 1 1 0 1 1 0 0 0 0 0 1 1 0 0 1 1 1 0 0 1 1 1 1 0 1 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 0 1 1 1 1 1 0 1 1 1 1 0 0 1 0 1 0 1 0 0 0 +1 1 0 1 1 1 0 0 1 1 1 0 1 1 0 0 0 1 1 1 0 1 0 0 0 0 1 1 1 1 0 1 0 0 0 1 0 1 1 0 1 1 1 1 0 0 1 0 1 1 0 0 0 1 1 0 0 1 0 1 0 1 1 0 1 0 0 0 1 1 0 0 1 1 1 0 1 1 1 0 0 1 0 1 1 1 0 0 1 0 1 0 1 0 0 1 1 1 0 0 0 1 0 0 0 1 1 1 0 0 1 0 0 1 1 1 0 1 1 1 1 0 0 1 0 0 0 1 +0 1 0 0 0 1 1 0 1 0 0 0 0 0 0 1 1 1 0 0 0 1 0 0 1 1 0 0 0 1 1 0 0 0 0 0 0 0 1 1 1 0 1 0 0 0 0 1 0 0 1 0 0 1 0 1 1 0 1 0 0 0 0 0 0 0 1 0 1 0 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 0 0 1 1 1 1 0 1 1 0 0 1 1 1 0 1 0 1 0 0 1 1 1 1 1 0 1 0 0 0 1 1 1 0 0 1 1 0 +0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0 1 0 1 0 1 1 1 0 1 0 1 1 1 0 0 0 0 0 1 0 1 1 0 0 0 0 0 0 0 1 1 1 0 0 0 1 0 0 1 1 1 1 1 1 1 0 0 1 1 0 1 1 0 0 0 1 0 1 0 1 0 1 1 0 1 1 1 1 1 1 1 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 1 1 0 0 0 0 1 1 1 1 0 0 1 0 1 0 0 0 1 1 0 1 1 1 1 1 +0 1 1 0 1 1 0 0 0 0 1 0 0 1 0 0 1 0 0 1 1 1 0 1 0 0 0 1 0 0 0 1 0 1 0 1 0 1 1 0 1 1 1 0 0 0 0 0 0 1 0 0 0 1 1 1 0 0 0 1 1 0 0 1 1 0 0 1 1 0 0 0 1 1 1 0 0 0 1 1 0 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 0 1 1 0 0 1 1 0 1 0 0 1 0 1 1 0 0 1 0 1 0 1 1 1 1 0 0 0 0 0 +0 1 1 0 1 0 0 1 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 0 1 0 0 0 0 0 1 0 1 0 1 1 0 0 0 1 0 0 1 1 0 1 0 1 1 1 0 0 1 0 1 0 1 1 0 1 1 0 1 0 1 0 1 1 0 0 0 1 1 0 0 1 0 1 0 0 1 1 1 0 1 0 0 0 1 1 1 0 0 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 +0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 1 1 1 1 1 0 1 1 0 1 1 0 1 1 0 0 0 1 0 0 1 0 0 1 1 1 0 0 1 1 1 0 1 1 1 0 1 1 0 1 0 1 0 0 1 0 0 0 1 1 0 1 0 0 1 0 1 0 1 1 0 1 0 1 1 1 0 0 0 1 0 1 1 0 1 0 1 1 0 1 0 0 0 0 0 1 0 1 0 0 1 0 1 1 1 1 1 0 0 0 0 0 0 1 1 1 0 +0 0 1 1 0 0 1 0 0 1 1 0 0 1 0 0 1 1 0 1 0 1 1 1 0 0 0 1 1 1 0 1 1 0 1 0 1 0 0 1 0 1 1 1 1 1 0 1 0 0 1 1 0 1 1 0 1 0 1 1 1 0 0 0 1 1 1 1 0 1 0 0 1 1 1 1 1 1 1 0 0 1 1 1 0 1 0 0 0 1 1 0 0 0 1 0 1 0 0 0 0 0 1 0 1 1 1 1 0 0 0 0 1 0 0 1 1 1 1 1 0 0 0 1 0 1 1 0 +1 0 1 1 0 0 1 0 1 0 1 0 1 1 0 0 0 0 1 1 0 0 1 1 0 1 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 1 1 1 0 0 0 0 1 1 0 0 1 1 0 0 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 0 1 0 0 0 0 1 1 0 1 0 1 0 1 0 0 0 1 0 1 0 1 0 0 1 0 1 0 1 1 0 1 1 0 1 1 1 0 1 0 1 1 1 0 0 0 0 1 1 0 0 1 +1 1 1 1 0 1 1 0 1 0 0 1 0 1 1 0 0 1 1 1 0 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 0 0 0 0 1 0 0 0 1 1 1 1 0 1 1 0 0 0 0 1 1 1 1 0 1 1 0 0 1 0 1 0 0 0 0 0 1 1 1 1 1 0 1 1 1 0 0 0 0 1 1 1 1 1 1 0 0 0 1 0 1 0 1 1 1 0 1 1 0 1 1 0 0 1 1 1 1 1 0 1 0 0 1 0 0 1 0 1 1 0 1 1 +0 0 0 0 1 0 1 0 0 1 1 0 0 1 0 0 0 1 0 1 0 1 0 1 1 0 0 0 0 1 0 1 1 1 1 1 0 0 0 1 1 0 0 0 0 0 0 0 1 1 0 1 0 1 1 0 1 0 0 0 1 1 1 0 0 1 0 1 1 1 1 1 1 0 1 0 1 1 0 0 0 0 1 0 0 0 1 1 1 0 1 0 0 0 1 0 0 1 0 1 1 0 0 1 1 0 1 0 1 0 1 1 1 0 0 1 1 0 0 0 1 1 1 1 0 1 1 1 +1 1 0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 0 1 1 1 1 1 0 1 1 0 1 0 1 1 0 1 1 1 0 0 0 1 0 0 1 1 0 1 0 0 0 1 0 0 0 0 0 1 1 0 0 1 0 1 0 0 0 0 1 0 0 1 0 1 1 1 0 0 0 0 1 0 1 0 1 0 0 1 0 1 1 1 0 0 1 1 1 0 0 0 1 1 0 1 0 0 0 1 0 0 1 0 0 0 0 1 1 0 0 1 1 0 1 0 0 1 0 1 1 1 0 +1 1 0 0 0 1 0 0 1 0 0 1 1 1 1 1 1 1 1 0 1 0 1 1 1 0 1 1 1 0 1 1 0 1 0 1 0 1 0 0 1 0 0 0 1 1 1 1 0 1 0 1 0 0 0 1 1 0 1 0 1 0 1 0 0 0 1 0 0 1 0 1 0 0 0 1 0 1 0 0 0 1 0 0 0 0 1 1 0 0 0 1 1 1 1 0 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 1 0 0 1 0 0 1 0 0 0 0 1 1 1 1 +0 0 1 0 1 0 1 1 1 1 0 1 0 0 1 1 0 0 0 1 1 0 1 1 0 1 1 0 1 1 1 1 0 1 0 1 0 0 1 1 0 1 0 0 1 0 0 1 1 0 1 0 0 0 0 1 0 0 1 1 0 1 1 0 1 0 0 0 0 1 0 1 0 1 0 0 1 0 0 1 0 1 0 1 0 0 1 1 0 1 1 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 1 1 0 1 1 0 1 1 1 +1 1 1 1 1 0 1 0 1 1 0 0 1 0 0 1 1 1 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 1 1 1 1 0 0 0 1 0 1 0 0 1 0 0 0 0 0 0 0 0 1 1 0 1 1 0 0 1 0 1 1 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 1 1 1 1 1 1 0 0 0 1 1 0 0 0 1 1 1 0 0 0 1 0 0 0 1 0 0 0 1 1 1 0 0 0 0 1 0 0 1 1 0 1 0 1 1 0 0 +1 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 1 0 0 1 1 0 1 1 1 1 1 0 1 1 0 1 1 1 0 1 0 1 0 1 1 1 0 0 1 0 1 1 0 1 1 0 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 1 0 1 0 1 0 0 0 0 1 0 1 1 1 0 1 1 0 0 0 1 1 1 1 0 0 0 1 1 0 0 0 0 0 1 0 1 0 1 1 1 1 0 1 1 0 1 1 0 1 0 0 0 1 0 0 1 1 +0 1 1 1 1 1 0 1 1 0 0 0 0 0 1 0 0 0 0 0 0 1 1 0 1 1 0 1 1 0 0 0 1 1 0 1 1 0 1 1 1 1 1 0 0 0 1 0 1 1 1 1 0 0 1 1 1 0 0 0 0 0 1 0 1 1 1 1 1 1 0 0 1 1 1 1 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 1 1 0 0 0 1 0 1 1 1 1 1 0 0 0 1 0 0 1 1 0 0 1 0 0 1 0 0 0 1 +1 1 1 0 0 1 0 1 1 1 0 1 0 0 1 0 0 0 1 0 1 0 0 0 0 1 1 0 0 0 1 1 1 1 0 1 1 1 0 0 0 1 0 1 0 0 1 1 1 1 0 1 1 0 0 0 0 1 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1 1 0 1 1 1 0 0 0 0 1 1 1 0 0 1 0 1 0 1 0 1 0 0 1 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0 1 0 1 +1 1 0 0 0 1 0 0 1 1 0 0 0 1 0 0 1 1 1 0 1 0 0 1 0 0 0 0 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 0 0 1 1 1 1 1 1 1 1 0 1 1 1 0 1 1 1 0 0 0 0 0 1 0 1 1 0 0 0 1 0 1 0 0 0 1 0 0 1 0 1 1 1 1 0 0 1 0 1 1 0 1 1 0 1 0 0 1 1 1 0 1 0 1 1 0 1 1 0 0 0 1 0 1 1 0 1 0 1 1 0 0 +1 1 0 1 1 1 0 0 1 0 0 1 1 1 0 1 1 1 1 0 0 0 0 1 1 1 1 0 1 0 1 0 1 1 0 1 1 1 0 1 0 1 0 1 1 0 0 0 0 0 0 0 0 1 1 1 0 1 1 0 0 0 1 0 1 0 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0 0 0 1 1 0 0 1 0 1 1 0 0 1 0 1 1 0 0 1 1 0 0 0 1 0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 1 0 0 1 1 0 1 0 +0 0 0 1 0 0 1 0 0 1 1 0 1 1 0 0 0 1 1 0 1 1 0 1 1 1 0 0 1 1 0 0 0 1 0 0 1 0 0 0 0 0 1 0 0 1 0 0 1 0 1 0 1 0 1 0 0 0 0 1 1 0 0 0 1 0 0 1 1 0 0 0 1 0 0 0 0 0 0 1 0 1 0 1 0 1 0 1 1 0 0 1 0 1 1 0 1 0 1 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 1 0 1 1 1 1 1 0 1 0 0 0 0 +0 0 0 1 1 0 1 0 0 1 1 0 0 0 1 0 0 1 1 1 1 0 0 1 0 1 0 1 0 0 0 0 0 1 1 0 0 0 1 0 0 1 1 1 1 1 1 0 1 0 1 0 0 1 0 0 1 1 1 1 1 0 1 0 1 0 0 0 1 0 1 1 0 1 1 1 0 0 0 0 0 1 0 0 1 0 0 1 1 0 0 1 1 1 0 0 0 1 0 0 0 0 1 1 1 1 1 0 1 0 0 0 1 0 1 1 0 0 1 0 1 0 0 0 0 1 1 0 +1 0 1 0 0 1 1 1 0 0 1 0 1 0 1 1 1 0 1 0 1 0 0 0 1 1 1 0 1 1 0 1 0 1 1 0 1 1 1 1 1 1 0 1 0 1 1 1 1 1 0 1 0 0 0 0 0 1 0 0 0 0 1 1 0 0 1 1 1 1 1 0 1 1 1 1 0 1 1 0 0 0 1 1 1 1 0 0 1 1 1 1 0 0 0 0 1 1 1 0 0 1 1 1 0 0 1 0 1 0 0 0 0 1 1 1 0 1 1 1 1 1 1 1 1 0 0 1 +1 0 1 0 1 0 0 1 0 1 1 0 1 1 0 0 0 1 1 0 0 0 1 1 0 0 1 0 0 1 1 0 1 1 0 1 1 0 1 0 1 1 1 0 1 1 0 0 1 1 0 1 1 0 1 1 1 1 1 1 0 1 0 0 0 1 0 1 0 0 0 0 0 0 1 1 0 0 1 1 1 0 0 1 1 1 0 1 1 0 1 1 1 0 1 0 1 1 1 0 0 1 1 1 0 0 0 1 0 0 0 0 0 1 0 1 0 0 1 0 1 0 1 1 0 1 0 0 +0 0 1 0 1 1 1 1 1 0 1 0 1 1 1 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 1 0 1 0 1 0 0 1 1 1 0 0 1 1 0 1 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 0 1 0 0 1 1 1 0 1 0 1 1 1 1 0 1 1 1 0 1 0 1 1 0 1 1 1 1 1 0 1 0 0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 1 1 1 0 1 0 0 1 0 1 +0 0 0 1 1 1 1 0 1 0 0 0 1 0 0 0 1 0 0 0 1 1 1 0 1 0 1 1 1 0 1 0 1 0 1 0 0 1 1 1 0 0 1 0 0 1 0 0 1 1 1 0 0 1 0 1 1 0 0 1 0 1 1 1 0 1 0 0 0 0 1 1 0 1 0 1 0 1 0 1 1 0 1 0 1 0 1 1 0 1 1 0 0 1 1 0 0 1 0 1 1 1 0 1 1 0 1 0 0 0 1 1 0 0 1 0 1 0 1 0 0 1 0 0 1 1 0 1 +1 1 1 0 1 1 0 0 1 0 1 1 1 1 1 1 1 0 1 1 1 1 0 0 1 1 1 1 0 0 0 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 1 1 1 0 1 1 1 0 0 1 1 1 1 1 1 1 1 0 0 1 1 0 1 0 1 0 1 0 0 1 1 0 0 1 1 1 0 1 1 0 1 1 1 1 0 0 1 0 1 0 0 0 0 1 0 1 0 0 0 0 0 1 1 0 1 1 0 1 0 0 0 0 1 0 0 0 0 0 1 0 +1 0 0 1 1 1 1 1 0 0 0 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 0 1 0 1 0 1 0 1 0 1 1 0 0 0 1 0 0 1 0 0 1 0 0 1 1 0 1 1 0 1 1 0 1 0 1 0 0 0 0 0 0 0 1 0 0 1 0 1 0 1 0 0 0 1 0 1 1 0 0 1 0 1 1 0 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 1 0 1 0 0 0 0 1 0 1 0 0 0 1 +1 1 1 0 0 0 1 1 1 0 1 0 0 1 0 1 0 0 0 1 0 1 0 0 1 0 0 1 1 1 1 0 1 0 0 1 1 1 0 0 0 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 1 1 0 1 1 1 1 0 1 0 1 1 0 0 0 1 1 1 0 0 1 0 0 1 1 0 1 1 0 1 0 1 0 1 1 0 0 1 1 1 0 1 1 1 0 1 1 1 1 0 1 0 0 0 0 0 1 0 1 0 0 1 0 0 0 0 0 0 1 0 1 +0 0 0 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0 0 1 0 0 1 1 1 1 0 1 1 0 0 1 1 0 0 1 1 0 1 1 0 0 0 1 0 0 0 0 1 1 0 0 0 1 1 1 1 0 1 1 1 0 0 0 1 0 1 0 1 1 1 1 1 0 1 1 0 0 1 0 1 1 1 0 0 1 0 0 0 1 1 1 1 1 1 0 1 0 0 0 0 0 0 0 0 1 1 1 0 1 0 1 1 1 +1 1 1 1 1 0 0 1 1 0 0 1 1 1 1 0 1 1 0 0 0 0 0 0 1 0 1 0 1 1 1 1 1 1 0 1 1 1 0 1 1 0 1 0 0 0 1 1 0 1 1 1 1 0 1 1 0 1 1 1 0 1 0 0 0 1 1 0 0 0 1 0 0 1 1 0 0 0 1 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 0 1 1 1 0 0 0 0 0 0 0 1 0 0 1 1 1 1 1 1 0 0 1 0 0 1 0 1 1 1 1 0 1 1 +0 0 1 0 0 0 0 1 1 0 1 0 1 1 1 1 1 1 1 0 1 0 1 1 0 1 1 1 0 1 0 0 0 1 0 1 0 1 1 0 1 1 0 1 1 0 1 1 1 1 1 1 1 0 0 0 1 1 0 0 0 1 1 1 0 0 1 1 1 1 0 0 1 0 0 1 1 1 0 0 1 1 0 0 1 1 0 0 0 1 0 0 1 0 0 1 0 0 0 1 1 1 1 1 0 0 1 0 1 0 1 0 1 0 0 0 0 1 1 0 0 0 1 1 1 0 0 0 +1 1 0 1 0 0 1 0 1 1 1 0 0 0 1 1 0 1 0 0 0 0 0 1 1 1 1 1 0 0 1 0 0 0 0 1 1 0 1 0 1 0 0 1 1 1 1 0 0 1 0 0 0 1 0 0 1 0 1 0 0 1 1 0 0 1 0 1 0 0 0 0 0 1 1 0 1 1 0 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 0 0 0 0 1 0 0 1 1 0 1 0 1 1 1 1 1 1 1 0 1 0 0 1 0 0 0 0 1 1 1 1 1 1 +1 1 1 1 0 1 0 1 1 1 0 0 1 0 0 0 1 0 0 0 1 0 1 1 1 1 0 0 1 0 1 0 0 1 0 1 0 1 1 0 1 0 0 1 1 0 1 0 1 1 0 1 0 1 0 1 1 1 1 1 1 1 0 0 0 0 1 0 0 0 1 1 0 1 1 1 0 1 1 0 0 0 1 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0 1 0 1 0 1 1 0 0 0 0 1 0 0 1 1 +0 0 1 1 0 0 0 0 0 1 1 1 0 1 0 1 0 0 1 1 1 0 0 0 0 0 0 1 0 0 1 0 1 0 0 1 0 0 1 1 0 0 1 1 1 1 0 0 0 1 0 0 0 1 1 1 0 0 1 1 1 0 0 1 0 1 0 1 1 0 0 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 1 0 1 1 0 0 0 1 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 1 1 0 0 1 +1 0 0 0 0 1 0 0 0 1 1 0 0 0 1 0 1 0 0 1 1 0 0 0 1 1 1 0 0 1 0 1 1 1 1 0 1 1 1 1 0 1 1 1 1 0 1 1 1 1 0 0 0 1 1 1 0 1 0 0 1 1 0 1 1 1 0 0 0 0 1 1 1 1 1 0 0 1 0 0 1 1 1 0 1 1 0 0 0 1 0 1 0 0 0 1 1 1 1 0 0 1 0 1 1 1 0 1 1 0 1 1 0 0 0 1 0 1 1 1 1 1 1 0 0 0 0 1 +1 0 0 0 0 1 0 0 1 0 0 1 0 0 0 1 0 1 1 1 1 0 1 1 1 0 0 0 1 1 1 1 0 0 0 1 1 0 1 0 0 1 0 0 0 0 1 1 0 1 1 0 1 1 1 0 0 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 1 0 1 0 1 0 0 0 1 0 0 1 0 0 1 0 1 1 0 1 1 0 1 0 0 0 1 1 0 0 1 0 0 1 0 0 0 0 1 1 1 0 1 0 0 0 0 0 +1 1 1 1 0 1 0 1 0 0 0 0 1 0 1 0 0 0 1 1 1 1 1 1 1 0 0 0 1 0 0 1 0 0 1 1 0 0 0 1 0 0 0 1 0 1 1 1 0 0 0 0 1 1 0 0 1 1 0 0 0 0 1 0 1 1 0 0 0 0 0 0 1 1 0 0 1 1 1 0 0 1 0 0 1 1 0 0 0 0 1 1 0 0 1 1 0 0 1 1 1 1 1 0 0 1 1 0 0 1 0 1 1 1 0 0 1 1 0 0 1 0 0 0 0 0 0 1 +0 1 1 0 0 1 0 1 1 1 1 0 0 1 0 0 0 0 0 0 1 1 1 1 1 0 1 1 0 1 1 1 1 0 1 1 1 0 1 1 0 0 0 0 1 1 1 0 0 1 1 1 1 0 1 0 1 0 1 1 1 0 1 0 0 1 0 0 1 1 1 1 0 0 0 1 0 1 1 0 0 1 0 0 1 0 0 0 0 0 1 1 0 0 1 1 0 1 1 1 1 1 0 1 0 0 0 1 0 1 0 0 0 0 1 0 0 1 1 0 1 1 0 0 0 0 0 1 +1 1 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 1 0 1 1 0 1 1 1 1 1 1 0 1 1 0 1 1 0 0 1 0 0 0 0 0 0 1 0 0 1 1 0 1 0 1 1 1 1 1 1 0 1 0 1 1 0 1 1 0 1 1 0 1 0 0 0 0 1 1 0 1 0 0 0 1 0 0 0 0 1 0 0 1 0 1 0 0 0 0 1 1 1 0 1 1 0 1 1 1 0 0 0 0 1 0 1 1 0 1 1 1 1 0 1 1 1 1 0 1 1 0 diff --git a/examples/rem_noise/calibration.qu b/examples/rem_noise/calibration.qu new file mode 100644 index 0000000000000000000000000000000000000000..d2989cabfa9df8129189b4017303ea52c908cd95 --- /dev/null +++ b/examples/rem_noise/calibration.qu @@ -0,0 +1,24 @@ +import std_ops +// Z for 0, X for 1, Y for 2 +operation calibration_circuits(num_qubits: int, observable: int[], state_labels:int[]) : unit { + using(qs: qubit[num_qubits]) { + for(int i = 0; i < num_qubits; i += 1) { + if (state_labels[i] == 1){ + // X + X(qs[i]); + } + if (observable[i] == 1) { + H(qs[i]); + H(qs[i]); + } + if (observable[i] == 2) { + // Y + H(qs[i]); + S(qs[i]); + Sdag(qs[i]); + H(qs[i]); + } + measure(qs[i]); + } + } +} \ No newline at end of file diff --git a/examples/rem_noise/rem.ipynb b/examples/rem_noise/rem.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..66426816c91b947fcb106cff92b9c7d021be620e --- /dev/null +++ b/examples/rem_noise/rem.ipynb @@ -0,0 +1,183 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# How to use\n", + "***\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "以下举例子说明rem的使用流程和接口格式,考虑以下可观测量期望值求解问题。" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from quingo import *\n", + "from pathlib import Path\n", + "from quingo.lib.rem import get_corr_exp_value_for_full_matrix_model\n", + "n_qubits = 2\n", + "# observable = 'ZZ'\n", + "observable = [0, 0]\n", + "\n", + "qu_file = Path(\"test.qu\")\n", + "\n", + "from quingo.lib.utils import get_prob_noisy\n", + "\n", + "noise_config = [0, 1], [0.1, 0.1], ['bflip', 'bflip']\n", + "task = Quingo_task(qu_file, \"bell_state\")\n", + "\n", + "noisy_circ_prob = get_prob_noisy(task,noise_config,n_qubits,shots=50)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{0: 0.42, 1: 0.14, 2: 0, 3: 0.44}" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "noisy_circ_prob" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "qu_calibration= Path(\"calibration.qu\")\n", + "calibration_circ = Quingo_task(qu_calibration, \"calibration_circuits\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "from quingo.lib.utils import calibration_matrix" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[(2, [0, 0], [0, 0]), (2, [0, 0], [0, 1]), (2, [0, 0], [1, 0]), (2, [0, 0], [1, 1])]\n" + ] + } + ], + "source": [ + "cali_matrix_fm = calibration_matrix(calibration_circ,2,[0,0],noise_config)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[0.74, 0.07, 0.1 , 0. ],\n", + " [0.12, 0. , 0.82, 0.09],\n", + " [0.13, 0.84, 0.02, 0.12],\n", + " [0.01, 0.09, 0.06, 0.79]])" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cali_matrix_fm" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0.42 0.14 0. 0.44]\n", + "[ 0.5806 -0.1715 0.0235 0.5674]\n" + ] + } + ], + "source": [ + "corr_exp_value_fp = get_corr_exp_value_for_full_matrix_model(n_qubits, noisy_circ_prob, cali_matrix_fm)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{0: 0.49623028762904114,\n", + " 1: -7.704358491026306e-18,\n", + " 2: 0.014963835514714814,\n", + " 3: 0.488805876856244}" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "corr_exp_value_fp" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tmp", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/rem_noise/test.qu b/examples/rem_noise/test.qu new file mode 100644 index 0000000000000000000000000000000000000000..3d9cbf4afb00b4e95d1e66e2d254886f2700578a --- /dev/null +++ b/examples/rem_noise/test.qu @@ -0,0 +1,10 @@ +import std_ops + +operation bell_state() : unit { + using(q : qubit[2]) { + H(q[0]); + CNOT(q[0], q[1]); + measure(q[0]); + measure(q[1]); + } +} \ No newline at end of file diff --git a/examples/xiaohong/host.py b/examples/xiaohong/host.py index 051118ccf8eca9de12af0bcd00ed26884aea302f..d14c2e02b9617101d79d048c817181ae1d0755cd 100644 --- a/examples/xiaohong/host.py +++ b/examples/xiaohong/host.py @@ -1,6 +1,6 @@ -from quingo import * from pathlib import Path +from quingo import * qu_file = Path(__file__).parent / "kernel.qu" @@ -10,10 +10,10 @@ def routine(circ_name, num_shots=1): cfg = ExeConfig( ExeMode.RealMachine, num_shots, - xh_login_key="7e6999bab11453428b8ded1fac00b3ea", - xh_machine_name="Transponder", + qcloud_platform_login_key="7e6999bab11453428b8ded1fac00b3ea", + qcloud_machine_name="Transpose", ) - qasm_fn = compile(task, params=(), config_file="") + qasm_fn = compile(task, params=(), config_file="", target="qcloud_sh") res = execute(qasm_fn, BackendType.XIAOHONG, cfg) print("result: ", res) diff --git a/setup.cfg b/setup.cfg index cb1dc12d498056d99ba5cb4593d85c963aa5f803..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,36 +0,0 @@ -[metadata] -name = quingo -version = 0.3.1 -author = Xiang Fu -author_email = gtaifu@gmail.com -use_2to3 = False -description = Quingo Runtime System -long_description = file: README.md -long_description_content_type = text/markdown -url = https://gitee.com/quingo/quingo-runtime -project_urls = - Bug Tracker = https://gitee.com/quingo/quingo-runtime/issues -classifiers = - Programming Language :: Python :: 3 :: Only - License :: OSI Approved :: Apache Software License - Operating System :: OS Independent - -[options] -package_dir = - = src -packages =find: -python_requires = >=3.7 -install_requires = - numpy - pyqcisim >= 1.3.1 - symqc >= 1.1.1 - colorama - termcolor - requests - tqdm - qualesim >= 1.0.2 - qualesim-tequila >= 1.0.2 - pyquiet >= 0.0.4 - -[options.packages.find] -where = src \ No newline at end of file diff --git a/setup.py b/setup.py index b908cbe55cb344569d32de1dfc10ca7323828dc5..747ca1be0e544e0022fca725b327dd0e9698057b 100755 --- a/setup.py +++ b/setup.py @@ -1,3 +1,44 @@ -import setuptools +from setuptools import setup, find_packages -setuptools.setup() +with open("README.md", "r", encoding="utf-8") as fh: + long_description = fh.read() + +setup( + name="quingo", + version="0.4.0", + author="Xiang Fu", + author_email="gtaifu@gmail.com", + description="Quingo Runtime System", + long_description=long_description, + long_description_content_type="text/markdown", + url="https://gitee.com/quingo/quingo-runtime", + project_urls={ + "Bug Tracker": "https://gitee.com/quingo/quingo-runtime/issues", + }, + classifiers=[ + "Programming Language :: Python :: 3 :: Only", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + ], + package_dir={"": "src"}, + packages=find_packages(where="src"), + python_requires=">=3.7", + install_requires=[ + "numpy", + "scipy", + "pyqcisim >= 1.3.1", + "symqc >= 1.1.1", + "colorama", + "termcolor", + "requests", + "tqdm", + "pyquiet >= 0.0.4", + "pycim-simulator" + ], + extras_require={ + ':sys_platform == "linux"': [ + "qualesim >= 1.0.2", + "qualesim-tequila >= 1.0.2", + ], + }, +) diff --git a/src/quingo/backend/__init__.py b/src/quingo/backend/__init__.py index 3a7fb7a538c8990627c711473e27cb56e2cdd3b3..1208a16d6ede238e1f63da24a4882859e7c95060 100755 --- a/src/quingo/backend/__init__.py +++ b/src/quingo/backend/__init__.py @@ -1,6 +1,6 @@ from quingo.backend.backend_hub import BackendType from quingo.backend.pyqcisim_tequila import PyQCISim_tequila from quingo.backend.pyqcisim_quantumsim import PyQCISim_quantumsim -from quingo.backend.qualesim import QuaLeSim +from quingo.backend.qualesim import QuaLeSim_tequila, QuaLeSim_quantumsim from quingo.backend.symqc import IfSymQC from quingo.backend.qisa import Qisa diff --git a/src/quingo/backend/backend_hub.py b/src/quingo/backend/backend_hub.py index a576e44079639c26d1d5b9ef89cbf954ab501a5c..87fe27fccca7a406694837c3a5e865ab75c5460b 100755 --- a/src/quingo/backend/backend_hub.py +++ b/src/quingo/backend/backend_hub.py @@ -10,8 +10,10 @@ class BackendType(enum.Enum): SYMQC = enum.auto() QUANTIFY = enum.auto() XIAOHONG = enum.auto() + TIANYAN = enum.auto() QUALESIM_TEQUILA = enum.auto() QUALESIM_QUANTUMSIM = enum.auto() + QOS = enum.auto() @singleton @@ -48,18 +50,30 @@ class Backend_hub: False, Qisa.QCIS, ), + BackendType.TIANYAN: ( + "ZDXLZ_Tianyan", + "tianyan", + False, + Qisa.QCIS, + ), BackendType.QUALESIM_TEQUILA: ( - "QuaLeSim", + "QuaLeSim_tequila", "qualesim", True, Qisa.QCIS, ), BackendType.QUALESIM_QUANTUMSIM: ( - "QuaLeSim", + "QuaLeSim_quantumsim", "qualesim", True, Qisa.QCIS, ), + BackendType.QOS: ( + "QOS", + "qos", + False, + Qisa.QCIS, + ), } def support(self, backend_type): diff --git a/src/quingo/backend/if_backend.py b/src/quingo/backend/if_backend.py index 35543fc0741b14843e192d894e60e68c0cd0bc47..039014b80ae7dee89ea563b2171ec88b7076986e 100755 --- a/src/quingo/backend/if_backend.py +++ b/src/quingo/backend/if_backend.py @@ -23,5 +23,8 @@ class If_backend: def upload_program(self, program): raise NotImplementedError + def upload_program_str(self, program: str): + raise NotImplementedError + def execute(self, exe_config: ExeConfig): raise NotImplementedError diff --git a/src/quingo/backend/pyqcisim_quantumsim.py b/src/quingo/backend/pyqcisim_quantumsim.py index 5f3c7e8fc1cb5e1a39cbd44b345e8178c469e90c..6271008da7f15276cb30d7517f4ace0acc30dcdb 100755 --- a/src/quingo/backend/pyqcisim_quantumsim.py +++ b/src/quingo/backend/pyqcisim_quantumsim.py @@ -29,6 +29,10 @@ class PyQCISim_quantumsim(If_backend): program = prog_fn.open("r").read() self.sim.compile(program) + def upload_program_str(self, program: str): + """upload the program string to the simulator.""" + self.sim.compile(program) + def execute(self, exe_config: ExeConfig) -> Union[List | NDArray]: """Execute the given quantum circuit. Args: diff --git a/src/quingo/backend/pyqcisim_tequila.py b/src/quingo/backend/pyqcisim_tequila.py index 42a20052ff41e3172a9bab78d63c8ad9dc78299d..8e535699e5bf26981b3767f6da6e931b2a18a741 100644 --- a/src/quingo/backend/pyqcisim_tequila.py +++ b/src/quingo/backend/pyqcisim_tequila.py @@ -21,6 +21,10 @@ class PyQCISim_tequila(If_backend): program = prog_fn.open("r").read() self.sim.compile(program) + def upload_program_str(self, program: str): + """upload the program string to the simulator.""" + self.sim.compile(program) + def execute(self, exe_config: ExeConfig): """Execute the given quantum circuit. Args: @@ -31,15 +35,26 @@ class PyQCISim_tequila(If_backend): """ if exe_config.mode == ExeMode.SimShots: - return self.sim.simulate("one_shot", exe_config.num_shots) + return self.sim.simulate( + "one_shot", exe_config.num_shots, noise_config=exe_config.noise_config + ) if exe_config.mode == ExeMode.SimFinalResult: - return self.sim.simulate("final_result") + return self.sim.simulate( + "final_result", noise_config=exe_config.noise_config + ) if exe_config.mode == ExeMode.SimStateVector: - names, nd_array_values = self.sim.simulate("state_vector") + names, nd_array_values = self.sim.simulate( + "state_vector", noise_config=exe_config.noise_config + ) return (names, nd_array_values) + if exe_config.mode == ExeMode.SimProbability: + return self.sim.simulate( + "probability", noise_config=exe_config.noise_config + ) + raise ValueError( "Unsupported execution mode ({}) for TEQUILA.".format(exe_config.mode) ) diff --git a/src/quingo/backend/qisa.py b/src/quingo/backend/qisa.py index b98e392303083473468e763bfd1f34a7e95638df..2a801ee63aeadd714ffef7ecbb5027b975402e0c 100644 --- a/src/quingo/backend/qisa.py +++ b/src/quingo/backend/qisa.py @@ -4,6 +4,7 @@ import enum class Qisa(enum.Enum): QCIS = enum.auto() QUIET = enum.auto() + BRANCH_QUIET = enum.auto() eQASM = enum.auto() Quantify = enum.auto() @@ -16,6 +17,8 @@ def get_qisa_name(qisa: Qisa): return "qcis" if qisa == Qisa.QUIET: return "quiets" + if qisa == Qisa.BRANCH_QUIET: + return "branch-quiets" if qisa == Qisa.eQASM: return "eqasm" if qisa == Qisa.Quantify: @@ -30,7 +33,7 @@ def get_suffix(qisa: Qisa): if qisa == Qisa.QCIS: return ".qcis" - if qisa == Qisa.QUIET: + if qisa == Qisa.QUIET or qisa == Qisa.BRANCH_QUIET: return ".qi" if qisa == Qisa.eQASM: return ".eqasm" diff --git a/src/quingo/backend/qos.py b/src/quingo/backend/qos.py new file mode 100755 index 0000000000000000000000000000000000000000..80b04e70dad58945fc688350032cc51598d1ad29 --- /dev/null +++ b/src/quingo/backend/qos.py @@ -0,0 +1,57 @@ +from __future__ import annotations +from numpy.typing import NDArray +from typing import List, Union +import json + +from quingo.utils import ensure_path +from .backend_hub import BackendType +from .if_backend import If_backend +from quingo.core.exe_config import ExeMode, ExeConfig +from pyqos.experiment.data_taking.scan_circuits import RunCircuits + + +class QOS(If_backend): + """A functional QCIS simulation backend using PyQCISim and QuantumSim.""" + + def __init__(self): + super().__init__(BackendType.QOS) + + def upload_program(self, prog_fn): + """ + Upload assembly or binary program to the simulator. + + Args: + prog_fn: the name of the assembly or binary file. + """ + prog_fn = ensure_path(prog_fn) + self.qcis_circuit = prog_fn.open("r").read() + qubits_fn = prog_fn.parent / (prog_fn.stem + ".json") + self.qubits = json.load(qubits_fn.open("r")) + + def execute(self, exe_config: ExeConfig) -> Union[List | NDArray]: + """Execute the given quantum circuit. + Args: + - mode (str): the simulation mode to use: + + The number of shots is specified in exe_config.num_shots, which is only valid for + ExeMode.SimShots. + """ + if exe_config.mode == ExeMode.RealMachine: + # print("qubits = ", self.qubits) + # print("qcis_circuit = ", self.qcis_circuit) + # return "qqqqq" + raw_res = RunCircuits( + qubits=[self.qubits], + use_template=False, + circuits=([self.qcis_circuit] * exe_config.qos_circuit_times), + data_type="P01", + sampling_interval=200e-6, + num_shots=exe_config.num_shots, + ) + raw_res.wait() + frams = raw_res.dataset.get_stream_data() + return frams + + raise ValueError( + "Unsupported execution mode ({}) for QOS.".format(exe_config.mode) + ) diff --git a/src/quingo/backend/qualesim.py b/src/quingo/backend/qualesim.py index 7abcf00d2543ccd5641bd164df03e1922147ff5c..bc54bf24967b5fbf9e687a0b76e201321f1a3466 100644 --- a/src/quingo/backend/qualesim.py +++ b/src/quingo/backend/qualesim.py @@ -5,14 +5,16 @@ from pathlib import Path from quingo.backend.backend_hub import BackendType from quingo.backend.if_backend import If_backend from quingo.core.exe_config import ExeConfig, ExeMode -from qualesim.plugin import Loglevel -from qualesim.host import Simulator +import numpy as np -class QuaLeSim(If_backend): +class QuaLeSim_base(If_backend): def __init__(self, backend_type=BackendType.QUALESIM_QUANTUMSIM): super().__init__(backend_type) + from qualesim.plugin import Loglevel + from qualesim.host import Simulator + self.sim = Simulator(stderr_verbosity=Loglevel.OFF) if backend_type == BackendType.QUALESIM_QUANTUMSIM: self.sim.with_backend("quantumsim", verbosity=Loglevel.OFF) @@ -29,6 +31,8 @@ class QuaLeSim(If_backend): prog_fn = ensure_path(prog_fn) if prog_fn.suffix in [".qcis", ".qi"]: + from qualesim.plugin import Loglevel + self.sim.with_frontend(str(prog_fn), verbosity=Loglevel.OFF) else: raise TypeError( @@ -36,6 +40,9 @@ class QuaLeSim(If_backend): "'.qcis' (for QCIS) and '.qi' (for QUIET-s)".format(prog_fn.suffix) ) + def upload_program_str(self, program: str): + pass + def execute(self, exe_config: ExeConfig): if exe_config.mode == ExeMode.SimShots: measure_mod = "one_shot" @@ -60,16 +67,18 @@ class QuaLeSim(If_backend): ) if exe_config.mode == ExeMode.SimFinalResult: - measure_mod = "one_shot" + measure_mod = "state_vector" try: self.sim.simulate() - res = self.sim.run( - measure_mod=measure_mod, num_shots=exe_config.num_shots - ) + res = self.sim.run(measure_mod=measure_mod) self.sim.stop() - final_state = res["res"] - final_state["quantum"] = tuple(final_state["quantum"]) - return final_state["quantum"] + final_state = eval(res["res"]) + result = dict() + result["classical"] = final_state["classical"] + result["quantum"] = tuple( + (final_state["quantum"][0], np.array(final_state["quantum"][1])) + ) + return result except Exception as e: raise ValueError( @@ -99,3 +108,13 @@ class QuaLeSim(If_backend): exe_config.mode ) ) + + +class QuaLeSim_tequila(QuaLeSim_base): + def __init__(self): + super().__init__(BackendType.QUALESIM_TEQUILA) + + +class QuaLeSim_quantumsim(QuaLeSim_base): + def __init__(self): + super().__init__(BackendType.QUALESIM_QUANTUMSIM) diff --git a/src/quingo/backend/quingo_result_format_spec.md b/src/quingo/backend/quingo_result_format_spec.md index 1df899487f2e7a36db1aba00e87b765119d9ba66..d0b4ddd991bc27b459ed4bdf24408273cb9c3ce3 100644 --- a/src/quingo/backend/quingo_result_format_spec.md +++ b/src/quingo/backend/quingo_result_format_spec.md @@ -42,11 +42,10 @@ end The result shall be: ```python -{ - 'classical': {}, - 'quantum': (['q1', 'q2'], - [(0.7071067811865474+0j), 0j, 0j, (0.7071067811865476+0j)]) -} + ( + ['q1', 'q2'], + [(0.7071067811865474+0j), 0j, 0j, (0.7071067811865476+0j)] + ) ``` ## SymbolicStateVector diff --git a/src/quingo/backend/symqc.py b/src/quingo/backend/symqc.py index 8e39be100d42b96ae12e8635a26fbff6ce893ffd..ab7ab0e90c061dc9ca92133623b2abcbbea5c631 100644 --- a/src/quingo/backend/symqc.py +++ b/src/quingo/backend/symqc.py @@ -3,6 +3,7 @@ from .backend_hub import BackendType from .if_backend import If_backend from quingo.core.exe_config import ExeConfig, ExeMode from symqc.simulator import SymQC +import numpy as np class IfSymQC(If_backend): @@ -25,6 +26,10 @@ class IfSymQC(If_backend): else: raise TypeError("The SymQC simulator can only accept QCIS instructions.") + def upload_program_str(self, program: str): + """upload the program string to the simulator.""" + self.sim.compile(program) + def execute(self, exe_config: ExeConfig): """Execute the given quantum circuit. Args: @@ -38,6 +43,12 @@ class IfSymQC(If_backend): if exe_config.mode == ExeMode.SimFinalResult: raw_res = self.sim.simulate("final_state") + raw_res["quantum"] = ( + raw_res["quantum"][0], + np.array(raw_res["quantum"][1]).reshape( + -1, + ), + ) return raw_res if exe_config.mode == ExeMode.SimStateVector: diff --git a/src/quingo/backend/tianyan.py b/src/quingo/backend/tianyan.py new file mode 100644 index 0000000000000000000000000000000000000000..e97bfe12ad5f55cdbab279fc5fcde77f7b761a7c --- /dev/null +++ b/src/quingo/backend/tianyan.py @@ -0,0 +1,112 @@ +from datetime import datetime + +from cqlib import TianYanPlatform + +from quingo.core.exe_config import ExeConfig, ExeMode +from quingo.utils import ensure_path + +from .backend_hub import BackendType +from .if_backend import If_backend + + +class ZDXLZ_Tianyan(If_backend): + def __init__(self): + super().__init__(BackendType.TIANYAN) + self.ty_platform = None + self.qcis_circuit = None + + def upload_program(self, prog_fn): + """ + Upload assembly or binary program to the simulator. + + Args: + prog_fn: the name of the assembly or binary file. + """ + prog_fn = ensure_path(prog_fn) + lines = [] + with prog_fn.open("r") as f: + lines = f.readlines() + + if len(lines) == 0: + raise ValueError("The program file is empty.") + + trimed_lines = [] + for line in lines: + trimed_lines.append(" ".join(line.split())) + + self.qcis_circuit = "\n".join( + [ + line.strip() + for line in trimed_lines + if line.strip() and not line.startswith("#") + ] + ) + # self.qcis_circuit = prog_fn.open("r").read() + + def upload_program_str(self, program: str): + """upload the program string to the simulator.""" + self.qcis_circuit = program + + def execute(self, exe_config: ExeConfig): + """Execute the given quantum circuit. + Args: + - mode (str): the simulation mode to use: + - "one_shot": the simulation result is a dictionary with each key being a qubit + measured, and the value is the outcome of measuring this qubit. + - num_shots (int): the number of iterations performed in `one_shot` mode. + - xh_login_key (str): login key to connect XiaoHong + - xh_machine_name (str): name of machine name to execute qcis + """ + if not exe_config.mode == ExeMode.RealMachine: + raise ValueError( + f"Unsupported execution mode ({exe_config.mode}) for Tianyan." + ) + + # connect Tianyan + self.configure_platform( + exe_config.qcloud_platform_login_key, exe_config.qcloud_machine_name + ) + + # submit job + print(f"\n===================== Start execute: ===================== ") + + print("circuit:\n", self.qcis_circuit, sep="") + print( + "type of circuit:", + type(self.qcis_circuit), + " length:", + len(self.qcis_circuit), + ) + print("machine name:", exe_config.qcloud_machine_name) + print(f"num shots = {exe_config.num_shots}") + + print("") + + query_id = self.ty_platform.submit_job( + circuit=self.qcis_circuit, + exp_name=f'quingo_exp.{datetime.now().strftime("%Y%m%d%H%M%S")}', + num_shots=exe_config.num_shots, + ) + + # invalid query + if not query_id: + raise EnvironmentError("Fail to connect Tianyan!") + + # read result + result = self.ty_platform.query_experiment( + query_id=query_id, max_wait_time=3600, sleep_time=5 + ) + result = self.format_result(result) + return result + + def configure_platform(self, login_key, machine_name): + self.ty_platform = TianYanPlatform(login_key=login_key) + print(f"Set account successfully:") + print(f" login key = {login_key[0:5]}" + "*" * (len(login_key) - 5)) + self.ty_platform.set_machine(machine_name) + print(f" machine name = {machine_name}") + + def format_result(self, result): + print("result:", result) + # origin_result = result[0]["results"] + # return {"qubits": origin_result[0], "results": origin_result[1:]} diff --git a/src/quingo/backend/xiaohong.py b/src/quingo/backend/xiaohong.py index d170d6368fb8028c1b4381d5cecb2f6f5866c371..33dd627f065fad2e69bcdf4e476021024f6a1ce2 100644 --- a/src/quingo/backend/xiaohong.py +++ b/src/quingo/backend/xiaohong.py @@ -1,8 +1,9 @@ +from quingo.core.exe_config import ExeConfig, ExeMode +from quingo.lib.pyezQ import Account from quingo.utils import ensure_path + from .backend_hub import BackendType from .if_backend import If_backend -from quingo.core.exe_config import ExeConfig, ExeMode -import pyezQ class XiaoHong(If_backend): @@ -21,6 +22,10 @@ class XiaoHong(If_backend): prog_fn = ensure_path(prog_fn) self.qcis_circuit = prog_fn.open("r").read() + def upload_program_str(self, program: str): + """upload the program string to the simulator.""" + self.qcis_circuit = program + def execute(self, exe_config: ExeConfig): """Execute the given quantum circuit. Args: @@ -37,7 +42,9 @@ class XiaoHong(If_backend): ) # connect XiaoHong - self.set_account(exe_config.xh_login_key, exe_config.xh_machine_name) + self.set_account( + exe_config.qcloud_platform_login_key, exe_config.qcloud_machine_name + ) # submit job print(f"Start execute:") @@ -56,7 +63,7 @@ class XiaoHong(If_backend): return result def set_account(self, login_key, machine_name): - self.account = pyezQ.Account(login_key=login_key, machine_name=machine_name) + self.account = Account(login_key=login_key, machine_name=machine_name) print(f"Set account successfully:") print(f" login key = {login_key[0:5]}" + "*" * (len(login_key) - 5)) print(f" machine name = {machine_name}") diff --git a/src/quingo/cim/__init__.py b/src/quingo/cim/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..304a84b5d786a04d7e1ac7e5d59e6fe4162646c1 --- /dev/null +++ b/src/quingo/cim/__init__.py @@ -0,0 +1,4 @@ +from .cim_sim import cim_envolve +from .parser import read_coupling_graph + +__all__ = ["cim_envolve", "read_coupling_graph"] diff --git a/src/quingo/cim/cim_sim.py b/src/quingo/cim/cim_sim.py new file mode 100644 index 0000000000000000000000000000000000000000..22fd5832d667a7580f6a9c278c8486a4d3115480 --- /dev/null +++ b/src/quingo/cim/cim_sim.py @@ -0,0 +1,57 @@ +import numpy as np +from typing import Callable, Optional +import pycim as cim + +def cim_envolve( + couple_matrix: np.ndarray, + round_number: int = 300, + pump_coeff: float = 5e-3, + intensity_value: float = 0.1, + custom_pump_schedule: Optional[Callable] = None, + custom_intensity: Optional[Callable] = None, +) -> np.ndarray: + """ + 执行CIM动力学模拟并返回演化结果 + + 参数: + couple_matrix (np.ndarray): 耦合矩阵,描述系统相互作用 + round_number (int): 模拟轮数,默认300 + pump_coeff (float): 泵浦系数,默认0.005 + intensity_value (float): 耦合强度值,默认0.1 + custom_pump_schedule (Callable): 自定义泵浦函数,需接受时间参数t + custom_intensity (Callable): 自定义耦合强度函数,需接受时间参数t + + 返回: + np.ndarray: 模拟结果数组,形状为(round_number, n_nodes) + + 异常: + ValueError: 耦合矩阵非方阵或泵浦系数为负 + RuntimeError: 未知错误 + """ + # 输入校验 + if couple_matrix.ndim != 2 or couple_matrix.shape[0] != couple_matrix.shape[1]: + raise ValueError("耦合矩阵必须是方阵") + if pump_coeff < 0: + raise ValueError("泵浦系数不能为负") + + + try: + # 初始化设备与配置 + device = cim.simulation.device() + setup = cim.simulation.setup() + setup.round_number = round_number + # times -1 according to match the simulator behavior + setup.couple_matrix = -1 * couple_matrix + + # 使用自定义函数或默认实现 + setup.p = custom_pump_schedule or (lambda t: np.sqrt(pump_coeff * t)) + setup.intensity = custom_intensity or (lambda t: intensity_value) + + # 执行模拟 + sol_info = cim.simulation.singleSimulation( + device, setup, model="meanField", solver="RK45" + ) + return np.sign(sol_info.y[:, -1]) + + except Exception as e: + raise RuntimeError(f"未知错误: {str(e)}") from e \ No newline at end of file diff --git a/src/quingo/cim/parser.py b/src/quingo/cim/parser.py new file mode 100644 index 0000000000000000000000000000000000000000..9f5c38ee98c8d6c7bce10852ed0f71754a8908a4 --- /dev/null +++ b/src/quingo/cim/parser.py @@ -0,0 +1,161 @@ +from pathlib import Path +from typing import Union + +import numpy as np + + +def read_J(coupling_graph_fn: Union[Path, str]) -> np.ndarray: + """ + 从耦合图文件中读取并构建对称的邻接矩阵。 + + 该函数解析耦合图文件格式,构建一个对称的numpy数组表示图的邻接矩阵。 + 文件第一行包含节点数和边数,后续行描述边的连接关系和权重。 + + Parameters + ---------- + coupling_graph_fn : Path or str + 耦合图文件的路径。可以是字符串路径或pathlib.Path对象。 + + Returns + ------- + np.ndarray + 形状为 (num_nodes, num_nodes) 的对称邻接矩阵,其中 J[i,j] 表示 + 节点 i 和节点 j 之间的耦合强度。 + + Raises + ------ + ValueError + 如果文件为空、格式错误或边数与声明的边数不匹配。 + 具体错误情况包括: + - 文件内容为空 + - 文件只有一行 + - 第一行格式不正确(不是两个整数) + - 边的数量与声明的边数不匹配 + - 边的格式不正确(不是三个值:node1 node2 weight) + + Notes + ----- + 文件格式: + - 第一行:num_nodes num_edges(空格分隔的两个整数) + - 后续行:node1 node2 weight(空格分隔,其中节点编号从1开始) + + 示例文件内容: + 5 4 + 1 2 1.0 + 2 3 1.0 + 3 4 1.0 + 4 5 1.0 + + 该函数会自动将节点编号从1-based转换为0-based,并确保生成的矩阵是对称的。 + + Examples + -------- + >>> J = read_J("coupling_graph.txt") + >>> print(J.shape) + (5, 5) + >>> print(J[0, 1]) # 节点1和节点2之间的耦合 + 1.0 + """ + + if isinstance(coupling_graph_fn, str): + coupling_graph_fn = Path(coupling_graph_fn) + + with coupling_graph_fn.open("r") as coupling_graph_file: + + lines = coupling_graph_file.readlines() + lines = [line.strip() for line in lines if line.strip()] + + if len(lines) == 0: + raise ValueError("文件内容为空") + + if len(lines) == 1: + raise ValueError("文件内容格式错误") + + + header_info = lines[0].split(' ') + + if len(header_info) != 2: + raise ValueError("文件内容格式错误") + + num_nodes, num_edges = map(int, header_info) + + if num_edges != len(lines) - 1: + raise ValueError("文件内容格式错误") + + J = np.zeros((num_nodes, num_nodes)) + + edges = lines[1:] + for edge in edges: + edge = edge.split(" ") + if len(edge) != 3: + raise ValueError("文件内容格式错误") + + node1 = int(edge[0]) - 1 + node2 = int(edge[1]) - 1 + + value = float(edge[2]) + + J[node1][node2] = value + J[node2][node1] = value + + return J + +def read_coupling_graph(file_path: Union[Path, str]) -> np.ndarray: + """ + 从指定文件路径读取耦合图并返回耦合矩阵 + + 参数: + file_path (Path | str): 文件路径,支持字符串或Path对象 + + 返回: + np.ndarray: 耦合矩阵,形状为(n_nodes, n_nodes) + + 异常: + FileNotFoundError: 文件不存在 + ValueError: 文件内容格式错误 + + + 说明: + 文件中每一行包含三个元素,分别是 第一个节点、第二个节点、耦合强度 + """ + try: + return read_J(file_path) + except FileNotFoundError as e: + raise FileNotFoundError(f"文件未找到: {file_path}") from e + except ValueError as e: + raise ValueError(f"文件内容格式错误: {str(e)}") from e + +def read_plain_matrix(file_path: Union[Path, str]) -> np.ndarray: + """ + 从指定文件路径读取耦合矩阵并返回 + + 参数: + file_path (Path | str): 文件路径,支持字符串或Path对象 + + 返回: + np.ndarray: 耦合矩阵,形状为(n_nodes, n_nodes) + + 异常: + FileNotFoundError: 文件不存在 + ValueError: 文件内容格式错误 + + 文件格式说明: + 文件中包含一个 n x n 的矩阵,每一行包含 n 个元素,表示矩阵的每一行 + 例如: + 0 1 0 0 0 + 1 0 1 0 0 + 0 1 0 1 0 + 0 0 1 0 1 + 0 0 0 1 0 + """ + if isinstance(file_path, str): + file_path = Path(file_path) + + if not file_path.exists(): + raise FileNotFoundError(f"文件未找到: {file_path}") + + with file_path.open("r") as file: + lines = file.readlines() + matrix = np.array([line.split() for line in lines], dtype=float) + return matrix + diff --git "a/src/quingo/core/build\346\226\207\344\273\266\345\244\271\347\256\241\347\220\206\351\234\200\346\261\202\345\210\206\346\236\220.md" "b/src/quingo/core/build\346\226\207\344\273\266\345\244\271\347\256\241\347\220\206\351\234\200\346\261\202\345\210\206\346\236\220.md" new file mode 100644 index 0000000000000000000000000000000000000000..53d929bf1846da2ec3deb11eadf5bb0d6e989922 --- /dev/null +++ "b/src/quingo/core/build\346\226\207\344\273\266\345\244\271\347\256\241\347\220\206\351\234\200\346\261\202\345\210\206\346\236\220.md" @@ -0,0 +1,8 @@ +文件夹管理的需求是什么? + +- 每个task对应一个build文件夹,还是每个task的每次执行对应一个文件夹? + - 每个task对应一个文件夹。 +- 用户应当可以指定build文件夹的位置。 + - 使用特定的位置:在指定文件夹下创建临时文件夹 + - 若没有指定,则使用/tmp/。使用tmp时,该文件夹被使用完之后,会被删除 +- 用户可以指定,临时文件夹用完之后是否被删除 \ No newline at end of file diff --git a/src/quingo/core/compile.py b/src/quingo/core/compile.py index 07d6c41d30016ed2f9a1c851b8d86eee928936a0..d194c7c502966a66543e95f5c289502b1a17f7e5 100644 --- a/src/quingo/core/compile.py +++ b/src/quingo/core/compile.py @@ -11,22 +11,40 @@ from quingo.backend.qisa import Qisa, get_qisa_name, get_suffix logger = get_logger((__name__).split(".")[-1]) -def compile(task: Quingo_task, params: tuple, qasm_fn: Path = None, config_file=""): +def compile(task: Quingo_task, params: tuple, qasm_fn: Path = None, **kwargs): """Compile the quingo file with given parameters and return the path of the generated qasm file. """ + if "config_file" in kwargs: + config_file = kwargs["config_file"] + else: + config_file = "" + if "target" in kwargs: + target = kwargs["target"] + else: + target = "" + if "chip_path" in kwargs: + chip_path = kwargs["chip_path"] + else: + chip_path = "" + logger.setLevel(logging.INFO) gen_main_file(task.called_qu_fn, task.called_func, task.cl_entry_fn, params) if qasm_fn is None: suffix = get_suffix(task.qisa_type) qasm_fn = task.cl_entry_fn.with_suffix(suffix) + mq_fn = task.cl_entry_fn.with_suffix(".json") else: qasm_fn = ensure_path(qasm_fn) + mq_fn = qasm_fn.stem + ".json" + mq_fn = ensure_path(mq_fn) quingoc_path = Path(get_mlir_path()) - compile_cmd = compose_cl_cmd(task, qasm_fn, quingoc_path, config_file) + compile_cmd = compose_cl_cmd( + task, qasm_fn, quingoc_path, config_file, target, chip_path, mq_fn + ) if task.debug_mode: logger.info(compile_cmd) ret_value = subprocess.run( @@ -51,7 +69,15 @@ def compile(task: Quingo_task, params: tuple, qasm_fn: Path = None, config_file= return qasm_fn -def compose_cl_cmd(task: Quingo_task, qasm_fn: Path, quingoc_path: Path, configfile=""): +def compose_cl_cmd( + task: Quingo_task, + qasm_fn: Path, + quingoc_path: Path, + configfile="", + target="", + chip_path="", + mq_fn="", +): qasm_fn = ensure_path(qasm_fn) quingoc_path = ensure_path(quingoc_path) @@ -70,16 +96,26 @@ def compose_cl_cmd(task: Quingo_task, qasm_fn: Path, quingoc_path: Path, configf config_fn = '--config-fn="{}"'.format(str(configfile)) + chip_path_ = '--chip-config="{}"'.format(str(chip_path)) + + target_ = '--target="{}"'.format(str(target)) + # mq_path = '--mq-path="{}"'.format(str(mq_fn)) + cmd_eles = [ cl_path, + "-u", cl_entry_fn, opt_inc_dirs, config_fn, + chip_path_, + target_, + # mq_path, opt_isa, opt_qubit_map, opt_out_fn, ] + compile_cmd = " ".join([ele for ele in cmd_eles if ele.strip() != ""]) return compile_cmd diff --git a/src/quingo/core/compiler_config.py b/src/quingo/core/compiler_config.py index 6be46f83ecaf44aec92f271fc7d7113d303b87f4..417dd73bb56afba8c5bc1da28a63850628e19427 100644 --- a/src/quingo/core/compiler_config.py +++ b/src/quingo/core/compiler_config.py @@ -97,7 +97,7 @@ def get_mlir_path(): "Cannot find the compiler.\n" " To resolve this problem, you can install quingoc with two ways:\n" ' 1. run the following command "python -m quingo.install_quingoc"\n' - " 2. Dowload quingoc from https://gitee.com/quingo/quingoc-release/" + " 2. Download quingoc from https://gitee.com/quingo/quingoc-release/" "releases and save it at a directory in the system path \n" "or configure its path by calling this method inside python:\n" " `quingo.set_mlir_compiler_path()`" diff --git a/src/quingo/core/exe_config.py b/src/quingo/core/exe_config.py index 0c84518aa5ff1f495a0f6057307390022dc58163..e31477d28c5085c85d26989369e0d64c83a1a6d8 100644 --- a/src/quingo/core/exe_config.py +++ b/src/quingo/core/exe_config.py @@ -16,6 +16,7 @@ class ExeMode(enum.Enum): SimFinalResult = enum.auto() SimStateVector = enum.auto() SymbolicStateVector = enum.auto() + SimProbability = enum.auto() RealMachine = enum.auto() @@ -24,13 +25,17 @@ class ExeConfig: self, mode: ExeMode = ExeMode.SimShots, num_shots: int = 1, - xh_login_key: str = None, # use for connecting XIAOHONG - xh_machine_name: str = None, # use for connecting XIAOHONG + qcloud_platform_login_key: str = None, # use for connecting XIAOHONG + qcloud_machine_name: str = None, # use for connecting XIAOHONG + qos_circuit_times: int = 100, # use for connecting QOS + noise_config=None, ): self.mode = mode self.num_shots = num_shots - self.xh_login_key = xh_login_key - self.xh_machine_name = xh_machine_name + self.qcloud_platform_login_key = qcloud_platform_login_key + self.qcloud_machine_name = qcloud_machine_name + self.qos_circuit_times = qos_circuit_times + self.noise_config = noise_config def __str__(self) -> str: return str(self.mode) diff --git a/src/quingo/core/manager.py b/src/quingo/core/manager.py index 994550e450a40b0a56cc53f3095b6b754d68038b..2a5fffbea62f9027fca9d69063d676014abcfa79 100755 --- a/src/quingo/core/manager.py +++ b/src/quingo/core/manager.py @@ -4,6 +4,7 @@ from typing import List, Union import numpy as np import array import sympy as sp +import logging from pathlib import Path @@ -11,6 +12,10 @@ from quingo.core.exe_config import ExeConfig, ExeMode from quingo.core.quingo_task import Quingo_task from quingo.core.compile import compile from quingo.backend.backend_hub import BackendType, Backend_hub +from quingo.core.quingo_logger import get_logger +from quingo.utils import validate_path + +logger = get_logger((__name__).split(".")[-1]) def verify_backend_config(backend: BackendType, exe_config: ExeConfig) -> bool: @@ -24,17 +29,33 @@ def verify_backend_config(backend: BackendType, exe_config: ExeConfig) -> bool: def execute( - qasm_fn: Path, be_type: BackendType, exe_config: ExeConfig = ExeConfig() + qasm_fn_or_str: Path, + be_type: BackendType, + exe_config: ExeConfig = ExeConfig(), + debug_mode=False, ) -> Union[List | NDArray]: """Execute the quingo task on the specified backend and return the result.""" + logger.setLevel(logging.INFO) if not verify_backend_config(be_type, exe_config): raise ValueError( "Error configuration {} on the backend {}".format(str(exe_config), backend) ) + if debug_mode: + execute_info = "execute the following program with backend {}: \n {}".format( + str(be_type.name), str(qasm_fn_or_str) + ) + logger.info(execute_info) + backend = Backend_hub().get_instance(be_type) - backend.upload_program(qasm_fn) + + qasm_fn = validate_path(qasm_fn_or_str) + if qasm_fn is None: + backend.upload_program_str(qasm_fn_or_str) + else: + backend.upload_program(qasm_fn) + result = backend.execute(exe_config) if exe_config.mode == ExeMode.SimStateVector: names, array_values = result @@ -67,9 +88,9 @@ def call( params: tuple, be_type: BackendType = BackendType.QUANTUM_SIM, exe_config: ExeConfig = ExeConfig(), - config_fn="", + **kwargs, ): """Execute the quingo task on the specified backend and return the result.""" - qasm_fn = compile(task, params, config_file=config_fn) - return execute(qasm_fn, be_type, exe_config) + qasm_fn = compile(task, params, **kwargs) + return execute(qasm_fn, be_type, exe_config, debug_mode=task.debug_mode) diff --git a/src/quingo/core/quingo_task.py b/src/quingo/core/quingo_task.py index c662ae4f7c06ab4e19bf75efb6f07b2d5b0e33d7..7519b5564c564786760baf658375e749e3436788 100644 --- a/src/quingo/core/quingo_task.py +++ b/src/quingo/core/quingo_task.py @@ -5,6 +5,8 @@ import tempfile from quingo.backend.backend_hub import BackendType from quingo.backend.qisa import Qisa from quingo.utils import ensure_path +import time +import os DEBUG_MODE = False @@ -15,41 +17,111 @@ def create_empty_dir(dir_path: Path): dir_path.mkdir() +def get_cur_time_as_str(): + cur_time = time.localtime() + return "{:04}{:02}{:02}_{:02}{:02}{:02}".format( + cur_time.tm_year, + cur_time.tm_mon, + cur_time.tm_mday, + cur_time.tm_hour, + cur_time.tm_min, + cur_time.tm_sec, + ) + + class Quingo_task: - def __init__(self, called_qu_fn: Path, called_func: str, **kwargs) -> None: + def __init__( + self, + called_qu_fn: Path, + called_func: str, + build_under=None, + debug_mode=False, + delete_build_dir=True, + qisa=None, + backend=BackendType.QUANTUM_SIM, + qubits_info=None, + ) -> None: """ - Define a quingo task by specifying the quingo file and the entry function. - - Note, this task only stores the following five elements: - - the path of the called Quingo file (called_qu_fn) - - the name of the entry function (called_func) - - the path of the build directory (build_dir) - - debug_mode - - the target qisa (qisa) if specified upon initialization - - the target backend (backend) if specified upon initialization - - If `qisa` and `backend` are not specified, then the value of them will be infered - when required. - - The `build_dir` is calculated at the first time it is called. If the debug mode is - on, then the build_dir is under the same directory as the called Quingo file. - Otherwise, the build_dir is a temporary directory. - - Except them, all other properties are calculated when they are called. By doing so, - only a minimal amount of information is stored in the task object, which reduces the - risk of inconsistency when using this object. + Initializes a Quingo task object. + + This constructor defines a Quingo task by specifying the Quingo file and the entry function. It stores essential information required for executing the Quingo task, including paths, debug mode, target qisa, and backend. Other properties are calculated on-demand to minimize stored information and reduce inconsistency risks. + + Parameters: + - called_qu_fn (Path): The path of the Quingo file to be called. + - called_func (str): The name of the entry function within the Quingo file. + - build_under (Optional[Path]): The path under which the build directory should be created. + If None, the build directory's location is determined based on the debug mode. + - debug_mode (bool): If True, the task runs in debug mode, affecting the location of the + build directory and possibly other behaviors. + - delete_build_dir (bool): delete the build directory after task is completed. + Defaults to True + - qisa (Optional[Any]): The target assembly specification. + - backend (BackendType): The target backend for the Quingo task. Defaults to QuantumSim. + - qubits_info (Optional[Any]): Information about the qubits used in the task. + + Note: + The `build_dir` is calculated the first time it is needed. + In debug mode, it is located in the `build` dir under the current working directory. + Otherwise, it is a temporary directory (like `/tmp/`). """ # file name and function name called_qu_fn = ensure_path(called_qu_fn) self._called_qu_fn = called_qu_fn self._called_func = called_func - self._build_dir = None - self._qubits_info = kwargs.get("qubits_info", None) - self.debug_mode = kwargs.get("debug_mode", False) + self.debug_mode = debug_mode + # qisa and backend - self._qisa = kwargs.get("qisa", None) - self._backend = kwargs.get("backend", BackendType.QUANTUM_SIM) + self._qisa = qisa + self._backend = backend + self._qubits_info = qubits_info + + self.create_build_dir(build_under, delete_build_dir, debug_mode) + + def create_build_dir( + self, build_under=None, delete_build_dir=True, debug_mode=False + ): + """It specifies a temporary build directory for this task. + By default, this build directory will be deleted when the task is destroyed. + + When `build_under` is None, the build directory is under system temporary directory. + Otherwise, a new build directory will be created under this given directory. + + When `debug_mode` is True, and `build_under` is None, a build directory will be created under `/build/`. + + 中文版需求说明: + - 每个task对应一个build文件夹,还是每个task的每次执行对应一个文件夹? + - 每个task对应一个文件夹 + - 如果一个task要进行多次执行,目前直接覆盖 + + - 用户应当可以指定build文件夹的位置。 + - 使用特定的位置:在指定文件夹下创建临时文件夹 + - 若没有指定,则使用/tmp/。使用tmp时,该文件夹被使用完之后,会被删除 + + - 文件夹用完之后默认被删除,但在用户指定下,临时文件夹用完之后可以被保留 + """ + if debug_mode and build_under is None: + self.parent_work_dir = Path.cwd() / gc.build_dirname + else: + self.parent_work_dir = build_under + + self.delete_build_dir = False if debug_mode else delete_build_dir + build_dir_prefix = "qg-" + get_cur_time_as_str() + "-" + + if self.parent_work_dir is not None: + if not self.parent_work_dir.exists(): + self.parent_work_dir.mkdir() + + self.working_dir = tempfile.mkdtemp( + dir=self.parent_work_dir, + prefix=build_dir_prefix, + ) + + assert Path(self.working_dir).exists() + + def __del__(self): + if self.delete_build_dir: + shutil.rmtree(str(self.working_dir)) @property def qubits_info(self): @@ -75,24 +147,8 @@ class Quingo_task: @property def build_dir(self): - """The path of the build directory. - - - In Debug mode, the build directory is under the current directory. - - Otherwise, the build directory is a temporary directory. - """ - # since build_dir can be a temporary directory, we need to record it instead of - # calculating it every time. - if self._build_dir is not None: - return self._build_dir - - if self.debug_mode: # debug mode create `build` dir under the current dir - self._build_dir = Path.cwd() / gc.build_dirname - create_empty_dir(self._build_dir) - - else: - self._build_dir = Path(tempfile.mkdtemp(prefix="quingo-")) - - return self._build_dir + """The path of the build directory. see `create_build_dir` for more details.""" + return Path(self.working_dir).absolute() @property def qisa_type(self): diff --git a/src/quingo/global_config.py b/src/quingo/global_config.py index af5db04ce7a81df5c4ecdca42e1f606936757da9..a535d34752f5356d06ad2aa4b85a4c7b9ae749a8 100755 --- a/src/quingo/global_config.py +++ b/src/quingo/global_config.py @@ -1,6 +1,8 @@ from pathlib import Path -qgrtsys_root_dir = Path(__file__).absolute().parent +qgrtsys_root_dir = Path(__file__).absolute().parent # corresponds to `/src/quingo` +qgrtsys_repo_dir = qgrtsys_root_dir.parent.parent # corresponds to `` + mlir_compiler_config_path = qgrtsys_root_dir / "core" / "mlir_compiler_path.txt" default_mlir_compiler_path = Path.home() / ".quingo" / "quingoc" diff --git a/src/quingo/lib/calibration.qu b/src/quingo/lib/calibration.qu new file mode 100644 index 0000000000000000000000000000000000000000..d2989cabfa9df8129189b4017303ea52c908cd95 --- /dev/null +++ b/src/quingo/lib/calibration.qu @@ -0,0 +1,24 @@ +import std_ops +// Z for 0, X for 1, Y for 2 +operation calibration_circuits(num_qubits: int, observable: int[], state_labels:int[]) : unit { + using(qs: qubit[num_qubits]) { + for(int i = 0; i < num_qubits; i += 1) { + if (state_labels[i] == 1){ + // X + X(qs[i]); + } + if (observable[i] == 1) { + H(qs[i]); + H(qs[i]); + } + if (observable[i] == 2) { + // Y + H(qs[i]); + S(qs[i]); + Sdag(qs[i]); + H(qs[i]); + } + measure(qs[i]); + } + } +} \ No newline at end of file diff --git a/src/quingo/lib/pyezQ.py b/src/quingo/lib/pyezQ.py new file mode 100644 index 0000000000000000000000000000000000000000..20466847c861b03ebc41044a3fc888e0ef952a6b --- /dev/null +++ b/src/quingo/lib/pyezQ.py @@ -0,0 +1,1130 @@ +# -*- coding: utf-8 -*- +import json +import requests +import re + +# import os +# import shutil +from time import time, sleep +import random +import numpy as np + +# import traceback +import datetime +from functools import wraps + +# from isqmap import transpile +# from qcis_to_qasm.qcis_to_qasm import QcisToQasm +# from qasm_to_qcis.qasm_to_qcis import QasmToQcis +# from sabreMapper.sabre_mapper import SabreMapper +from typing import List, Optional, Dict, Union + +# from simplify import QCIS_Simplify, QASM_Simplify + + +class Account: + + def __init__( + self, login_key: Optional[str] = None, machine_name: Optional[str] = None + ): + """accout initialization + + Args: + login_key: + API Token under personal center on the web. Defaults to None. Defaults to None. + machine_name: + name of quantum computer. Defaults to None. + + Raises: + Exception: throw an exception when login fails + """ + # self.qasmtoqcis = QasmToQcis() + # self.qcistoqasm = QcisToQasm() + # self.qcis_simplify = QCIS_Simplify() + # self.qasm_simplify = QASM_Simplify() + self.login_key = login_key + self.token = None + self.machine_name = machine_name + cloud_url = "https://quantumcomputer.ac.cn/" + self.base_url = f"{cloud_url}" + self.login = self.log_in() + if self.machine_name: + self.set_machine(self.machine_name) + if self.login == 0: + raise Exception("登录失败") + self.computer_selection_mark = 1 # 0--原来老的12比特 1--新的66比特 + + def reconnect_on_failuer(func, max_retries=2, retry_delay=1): + @wraps(func) + def wrapper(self, *args, **kwargs): + retries = 0 + while retries < max_retries: + try: + result = func(self, *args, **kwargs) + return result + except Exception as e: + print( + f"{func.__name__} execution failed, " + f"try count:{retries + 1} error info:{e}" + ) + if retries == max_retries: + break + if hasattr(e, "code") and e.code == 10100101: + # 说明是token未设置,需要重新登录 + print("user token loss or not set, log in again.") + self.log_in() + sleep(retry_delay) + retries = retries + 1 + raise Exception( + f"function:[{func.__name__}] Max retries exceeded. Attempt {max_retries} times failed. " + ) + + return wrapper + + @reconnect_on_failuer + def log_in(self): + """Authenticate username and password and return user credit + + Returns: + int: log in state, 1 means pass authentication, 0 means failed + + """ + url = f"{self.base_url}/sdk/api/user/login" + data = {"loginToken": self.login_key} + res = requests.post(url, json=data) + status_code = res.status_code + if status_code != 200: + raise Exception(f"登录失败, 请求接口失败, status_code:{status_code}") + result = json.loads(res.text) + code = result.get("code", -1) + msg = result.get("msg", "登录失败") + if code != 0: + print(f"登录失败:{msg}") + raise Exception(f"登录失败:{msg}") + token = result.get("data").get("token") + self.token = token + return 1 + + @reconnect_on_failuer + def create_experiment(self, exp_name: str): + """create a new experiment, the new one is the experiment set ID. + + Args: + exp_name: new experiment collection Name + + Returns: + Union[int, str]: 0 failed, not 0 successful, success returns the experimental set id + """ + if self.computer_selection_mark: + url = f"{self.base_url}/sdk/api/multiple/experiment/save" + else: + url = f"{self.base_url}/sdk/api/experiment/save" + print("当前创建实验使用的机器名:", self.machine_name) + data = { + "experimentClipId": "", + "name": exp_name, + "machineName": self.machine_name, + "source": "SDK", + } + headers = {"sdk_token": self.token} + res = requests.post(url, json=data, headers=headers) + status_code = res.status_code + if status_code != 200: + raise Exception(f"创建实验失败, 请求接口失败, status_code:{status_code}") + result = json.loads(res.text) + code = result.get("code", -1) + if code == -10: + raise TokenNotSetException() + msg = result.get("msg", "创建实验失败") + if code != 0: + print(f"创建实验失败:{msg}") + return 0 + lab_id = result.get("data").get("lab_id") + return lab_id + + @reconnect_on_failuer + def save_experiment( + self, lab_id: str, exp_data: str, version: str, is_verify: Optional[bool] = True + ): + """save the experiment and return the experiment ID. + + Args: + lab_id: + the result returned by the create_experiment interface, experimental set id + exp_data: + experimental content, qics + version: + version description + is_verify: + Is the circuit verified.True verify, False do not verify. Defaults to True. + + Examples: + the input parameter can be the following value: + + lab_id: XXX + exp_data: + X Q1 + Y Q12 + S Q3 + SD Q15 + T Q12 + TD Q3 + Z Q12 + H Q1 + RX Q3 2.78 + RY Q9 1.97 + RXY Q15 1.23 3.04 + X2P Q1 + X2M Q1 + Y2P Q3 + Y2M Q12 + X Q19 + CZ Q1 Q7 + RZ Q8 2.16 + I Q1 100 + B Q1 Q12 Q3 + M Q15 Q12 Q5 Q6 + + Returns: + Union[int, str]: 0 failed, not 0 successful, success returns the experiment id + """ + exp_data = exp_data.upper() + exp_data = self.get_experiment_data(exp_data) + if self.computer_selection_mark: + url = f"{self.base_url}/sdk/api/multiple/experiment/detail/save" + else: + url = f"{self.base_url}/sdk/api/experiment/detail/save" + data = { + "circuit": exp_data, + "lab_id": lab_id, + "language": "qcis", + "version": version, + "machineName": self.machine_name, + "is_verify": is_verify, + } + headers = {"sdk_token": self.token} + res = requests.post(url, json=data, headers=headers) + status_code = res.status_code + if status_code != 200: + raise Exception(f"保存实验失败, 请求接口失败, status_code:{status_code}") + result = json.loads(res.text) + code = result.get("code", -1) + if code == -10: + raise TokenNotSetException() + msg = result.get("msg", "保存实验失败") + if code != 0: + print(f"保存实验失败:{msg}") + return 0 + save_result = result.get("data").get("exp_id") + + return save_result + + @reconnect_on_failuer + def run_experiment(self, exp_id: str, num_shots: Optional[int] = 12000): + """running the experiment returns the query result id. + + Args: + exp_id: + the result returned by the save_experiment interface, experimental id + num_shots: + number of repetitions per experiment. Defaults to 12000. + + Returns: + Union[int, str]: 0 failed, not 0 successful, success returns the query id. + """ + if self.computer_selection_mark: + url = f"{self.base_url}/sdk/api/multiple/experiment/temporary/save" + else: + url = f"{self.base_url}/sdk/api/experiment/temporary/save" + data = { + "circuit": [""], + "exp_id": exp_id, + "lab_id": "", + "query_id": [""], + "shots": num_shots, + "version": "", + "machineName": self.machine_name, + "source": "SDK", + } + headers = {"sdk_token": self.token} + res = requests.post(url, json=data, headers=headers) + status_code = res.status_code + if status_code != 200: + raise Exception(f"运行实验失败, 请求接口失败, status_code:{status_code}") + result = json.loads(res.text) + code = result.get("code", -1) + msg = result.get("msg", "运行实验失败") + if code == -10: + raise TokenNotSetException() + if code != 0: + print(f"运行实验失败:{msg}, {result}") + return 0 + run_result = result.get("data").get("query_id") + return run_result + + @reconnect_on_failuer + def query_experiment( + self, + query_id: Union[str, List[str]], + max_wait_time: Optional[int] = 60, + result_type=2, + ): + """query experimental results + + Args: + query_id: + the result returned by the run_experiment interface, experimental set id + max_wait_time: + maximum waiting time for querying experiments. Defaults to 60. + result_type: + election of return value type of other quantum computer except oneD12, + only probability is returned by oneD12, + result_type value of 0 represents the raw data, + and a value of 1 represents the probability valueDefaults to 2. + + The maximum number of experimental result queries supported by the server is 50. + If there are more than 50, an error message will be displayed. + + Raises: + Exception: query experiment result type error + + Returns: + Union[int, str]: 0 failed, not 0 successful, success returns the experimental result + """ + if isinstance(query_id, str): + query_id = [query_id] + t0 = time() + while time() - t0 < max_wait_time: + try: + if self.computer_selection_mark: + url = f"{self.base_url}/sdk/api/multiple/experiment/find/results" + if result_type not in [0, 1, 2]: + print("查询实验结果类型错误") + return 0 + else: + url = f"{self.base_url}/sdk/api/experiment/find/results" + result_type = 1 + data = {"query_id": query_id, "type": result_type} + headers = {"sdk_token": self.token} + res = requests.post(url, json=data, headers=headers) + status_code = res.status_code + if status_code != 200: + raise Exception( + f"查询实验失败, 请求接口失败, status_code:{status_code}" + ) + result = json.loads(res.text) + code = result.get("code", -1) + msg = result.get("msg", "查询实验失败") + if code == -10: + raise TokenNotSetException() + if code != 0: + print(f"查询实验失败:{msg}") + return 0 + query_exp = result.get("data", None) + if query_exp: + return query_exp + except: + import traceback + + print(traceback.format_exc()) + continue + sleep_time = random.randint(0, 15) * 0.3 + round(random.uniform(0, 1.5), 2) + print(f"查询实验结果请等待: {{:.2f}}秒".format(sleep_time)) + sleep(sleep_time) + raise Exception("查询实验结果失败, 实验结果为空") + + @reconnect_on_failuer + def submit_job( + self, + circuit: Optional[Union[List, str]] = None, + exp_name: Optional[str] = "exp0", + parameters: Optional[List[List]] = None, + values: Optional[List[List]] = None, + num_shots: Optional[int] = 12000, + lab_id: Optional[str] = None, + exp_id: Optional[str] = None, + version: Optional[str] = "version01", + is_verify: Optional[bool] = True, + ): + """submit experimental tasks + There are some parameter range limitations when using batch submission circiuts. + 1. circuits length less than 50 + numshots maximum 100000 + the number of measurement qubits is less than 15 + 2. circuits length greater than 50 but less than 100 + numshots maximum 50000 + the number of measurement qubits is less than 30 + 3. circuits length greater than 100 but less than 600 + numshots maximum 10000 + the number of measurement bits is less than the number of all available qubits + + Args: + circuit: + experimental content, qics. Defaults to None. + exp_name: + new experiment collection Name. Defaults to 'exp0'. + parameters: + parameters that need to be assigned in the experimental content. Defaults to None. + values: + The values corresponding to the parameters that need to be assigned in the experimental content. Defaults to None. + num_shots: + number of repetitions per experiment. Defaults to 12000. + lab_id: + the result returned by the create_experiment interface, experimental set id. Defaults to None. + exp_id: + the result returned by the save_experiment interface, experimental id. Defaults to None. + version: + version description. Defaults to 'version01'. + is_verify: + Is the circuit verified.True verify, False do not verify. Defaults to True. + + Returns: + Union[int, str]: 0 failed, not 0 successful, success returns the query id. + """ + if isinstance(circuit, str): + circuit = [circuit] + if len(circuit) > 1: + version = None + if ( + circuit + and parameters + and values + and len(parameters) == len(circuit) == len(values) + ): + new_circuit = self.assign_parameters(circuit, parameters, values) + if not new_circuit: + print("无法为线路赋值,请检查线路,参数和参数值之后重试") + return 0 + else: + new_circuit = circuit + if self.computer_selection_mark: + url = f"{self.base_url}/sdk/api/multiple/experiment/temporary/save" + else: + url = f"{self.base_url}/sdk/api/experiment/temporary/save" + data = { + "circuit": new_circuit, + "exp_id": exp_id, + "lab_id": lab_id, + "name": exp_name, + "shots": num_shots, + "version": version, + "machineName": self.machine_name, + "source": "SDK", + "is_verify": is_verify, + } + headers = {"sdk_token": self.token} + res = requests.post(url, json=data, headers=headers) + status_code = res.status_code + if status_code != 200: + raise Exception(f"运行实验失败, 请求接口失败, status_code:{status_code}") + result = json.loads(res.text) + code = result.get("code", -1) + msg = result.get("msg", "运行实验失败") + if code == -10: + raise TokenNotSetException() + if code != 0: + print(f"运行实验失败:{msg}") + return 0 + run_result = result.get("data").get("query_id") + return run_result + + def assign_parameters( + self, circuits: List[str], parameters: List[List], values: List[List] + ): + """Check if the number of parameters, values match the circuit definition + + Args: + circuits: + string, QCIS circuit definition with or without parameter place holder + parameters: + list or ndarray of strings, parameters to be filled + values: + list or ndarray of floats, values to be assigned + + Returns: + circuit: circuit with parameters replaced by values or empty string + empty string occurs when errors prevents parameters to be assigned + """ + new_circuit = [] + for circuit, parameter, value in zip(circuits, parameters, values): + circuit = circuit.upper() + p = re.compile(r"\{(\w+)\}") + circuit_parameters = p.findall(circuit) + if circuit_parameters: + # # 如果values为整数或浮点数,改为列表格式########################################################## + # if isinstance(values, (float, int)): + # values = [values] + # # 如果parameters为字符格式,改为列表格式######################################################### + # if isinstance(parameters, str): + # parameters = [parameters] + + # 将所有parameter变为大写, 否则set(parameters) != set(circuit_parameters) 不通过 ############### + after_parameter = [p.upper() for p in parameter] + + if not value: + error_message = ( + f"线路含有参数{circuit_parameters}, 请提供相应的参数值" + ) + print(error_message) + return "" + + else: + if len(circuit_parameters) != len(value): + error_message = f"线路含有{len(circuit_parameters)}个参数, 您提供了{len(value)}个参数值" + print(error_message) + return "" + + elif after_parameter and len(circuit_parameters) != len( + after_parameter + ): + error_message = f"线路含有{len(circuit_parameters)}个参数, 您提供了{len(after_parameter)}个参数" + print(error_message) + return "" + + elif set(after_parameter) != set(circuit_parameters): + error_message = "线路中的参数与您输入的参数名称不符" + print(error_message) + else: + param_dic = {} + ############################# 这个转化可以删了 ######################################### + # parameters_upper = [p.upper() for p in parameters] + for p, v in zip(after_parameter, value): + param_dic[p] = v + expData = circuit.format(**param_dic) + new_circuit.append(expData) + elif parameter or value: + error_message = "线路定义中不含有参数,无法接受您输入的参数或参数值" + print(error_message) + return "" + else: + expData = circuit + new_circuit.append(expData) + return new_circuit + + def get_experiment_data(self, circuit: str): + """Parse circuit description and generate + experiment script and extract number of measured qubits. + + Args: + circuit: + string, QCIS circuit + + Returns: + expData: + string, transformed circuit + """ + # get gates from circuit + if self.login_key: + gates_list = circuit.split("\n") + gates_list_strip = [g.strip() for g in gates_list if g] + gates_list_strip = [g for g in gates_list_strip if g] + + # transform circuit from QCIS to expData + expData = "\n".join(gates_list_strip) + return expData + else: + gates_list = circuit.split("\n") + gates_list_strip = [g.strip() for g in gates_list if g] + gates_list_strip = [g for g in gates_list_strip if g] + + # transform circuit from QCIS to expData + expData = ";".join(gates_list_strip) + return expData + + @reconnect_on_failuer + def set_machine(self, machine_name: str): + """set the machine name. + + Args: + machine_name: name of quantum computer. + + Raises: + Exception: Failed to set machine name, request interface failed. + Exception: Failed to set machine name. + """ + url = f"{self.base_url}/sdk/api/quantum/computer/verify" + data = { + "experimentClipId": "", + "machineName": machine_name, + "name": "", + "source": "SDK", + } + headers = {"sdk_token": self.token} + res = requests.post(url, json=data, headers=headers) + status_code = res.status_code + if status_code != 200: + raise Exception( + f"下载实验参数失败, 请求接口失败, status_code:{status_code}" + ) + result = json.loads(res.text) + code = result.get("code", -1) + msg = result.get("msg", "设置机器名失败") + if code == -10: + raise TokenNotSetException() + if code != 0: + print(f"设置机器名失败:{msg}") + return 0 + self.machine_name = machine_name + data = result.get("data") + if data: + self.computer_selection_mark = 0 + else: + self.computer_selection_mark = 1 + + @reconnect_on_failuer + def download_config(self, read_time=None, down_file: Optional[bool] = True): + """except oneD12 quantum computer, download experimental parameters. + + Args: + read_time: + select configuration data according to the reading time, and the parameter format is yyyy-MM-dd HH:mm:ss, Defaults to None. + down_file: + the parameter is True to write to the file, and False to directly return the experimental parameters. Defaults to True. + + Returns: + Union[int, str]: 0 failed, not 0 successful, success returns the experimental parameters. + """ + if not self.computer_selection_mark: + raise Exception( + f"current quantum computer does not support download_config" + ) + url = f"{self.base_url}/sdk/api/multiple/experiment/config/download" + data = {"name": self.machine_name, "readTime": read_time} + headers = {"sdk_token": self.token} + res = requests.post(url, json=data, headers=headers) + status_code = res.status_code + if status_code != 200: + raise Exception( + f"下载实验参数失败, 请求接口失败, status_code:{status_code}" + ) + result = json.loads(res.text) + if "code" in result: + msg = result.get("msg", "下载实验参数失败") + print(f"下载实验参数失败:{msg}") + return 0 + cur_time = self.current_time() + if down_file: + with open(f"./{self.machine_name}_config_param_{cur_time}.json", "w") as f: + f.write(json.dumps(result)) + return result + + # def convert_qasm_to_qcis( + # self, + # qasm: str, + # qubit_map: Optional[Dict]=None + # ): + # """convert qasm to qcis. + + # Args: + # qasm: + # qasm. + # qubit_map: + # Number mapping in qasm, where the value is None, + # directly maps bits based on the format of number plus 1. Defaults to None. + # Raises: + # Exception: language conversion failed. + + # Returns: + # str: simplified qcis. + # """ + # qcis_raw = self.qasmtoqcis.convert_qasm_to_qcis( + # qasm, qubit_map=qubit_map) + # simplity_qcis = self.simplify_qcis(qcis_raw) + # return simplity_qcis + + # def convert_qasm_to_qcis_from_file( + # self, + # qasm_file: str, + # qubit_map: Optional[Dict]=None): + # """Read qasm from file and convert it to qcis + + # Args: + # qasm_file: + # qasm file. + # qubit_map: + # Number mapping in qasm, where the value is None, + # directly maps bits based on the format of number plus 1. Defaults to None. + + # Raises: + # Exception: language conversion failed. + + # Returns: + # str: simplified qcis. + # """ + # qcis_raw = self.qasmtoqcis.convert_qasm_to_qcis_from_file( + # qasm_file, qubit_map=qubit_map) + # simplity_qcis = self.simplify_qcis(qcis_raw) + # return simplity_qcis + + # def convert_qcis_to_qasm( + # self, + # qcis: str + # ): + # """convert qcis to qasm. + + # Args: + # qcis: qcis + + # Returns: + # str: converted qasm. + # """ + # qasm_circuit = self.qcistoqasm.convert_qcis_to_qasm(qcis) + # return qasm_circuit + + def qcis_check_regular(self, qcis_raw: str): + """qcis regular check,normal returns 1, abnormal returns 0 + + Args: + qcis_raw: qcis + + Returns: + Union[int, str]: 0 failed, not 0 successful, successfully returned the input qics. + """ + url = f"{self.base_url}/server/api/multiple/experiment/verify" + data = {"quantumComputerName": self.machine_name, "qcis": qcis_raw} + headers = {"sdk_token": self.token} + res = requests.post(url, json=data, headers=headers) + status_code = res.status_code + if status_code != 200: + raise Exception( + f"下载实验参数失败, 请求接口失败, status_code:{status_code}" + ) + result = json.loads(res.text) + code = result.get("code", -1) + msg = result.get("msg", "qcis检验失败") + if code == -10: + raise TokenNotSetException() + if code != 0: + print(f"qcis检验失败: {msg}") + return 0 + return qcis_raw + + # def simplify_qcis( + # self, + # qcis_raw: str): + # """simplification of qcis lines. + # If simplification fails, prompt an error message and return the original qcis circuit. + + # Args: + # qcis_raw: qcis + + # Returns: + # str: simplified qcis. + # """ + # simplity_qcis = self.qcis_simplify.simplify(qcis_raw) + # return simplity_qcis + + # def simplify_qasm(self, qasm_raw: str): + # """simplification of qasm lines. + # If simplification fails, prompt an error message and return the original qasm circuit. + + # Args: + # qasm_raw: qasm + + # Returns: + # str: simplified qasm. + # """ + # simplify_qasm = self.qasm_simplify.simplify(qasm_raw) + # return simplify_qasm + + def current_time(self): + """get the current time + + Returns: + str: time string + """ + timestamp = datetime.datetime.fromtimestamp(time()) + str_time = timestamp.strftime("%Y%m%d%H%M%S") + return str_time + + def readout_data_to_state_probabilities(self, result): + state01 = result.get("results") + basis_list = [] + basis_content = "".join( + ["".join([str(s) for s in state]) for state in state01[1:]] + ) + qubits_num = len(state01[0]) # 测量比特个数 + for idx in range(qubits_num): + basis_result = basis_content[idx : len(basis_content) : qubits_num] + basis_list.append([True if res == "1" else False for res in basis_result]) + return basis_list + + # 读取数据转换成量子态概率全部返回 + def readout_data_to_state_probabilities_whole(self, result: Dict): + """read data and convert it into a quantum state probability, all returns. + + Args: + result: the results returned after query_experiment. + + Returns: + Dict: probability + """ + if not self.computer_selection_mark: + raise Exception( + f"{self.machine_name}:current quantum computer does not support computational state probabilities" + ) + basis_list = self.readout_data_to_state_probabilities(result) + probabilities = self.original_onversion_whole(basis_list) + return probabilities + + # 读取数据转换成量子态概率部分,概率为0不返回 + def readout_data_to_state_probabilities_part(self, result: Dict): + """read data and convert it into a quantum state probability, do not return with a probability of 0. + + Args: + result: the results returned after query_experiment. + + Returns: + Dict: probability + """ + if not self.computer_selection_mark: + raise Exception( + f"{self.machine_name}:current quantum computer does not support computational state probabilities" + ) + basis_list = self.readout_data_to_state_probabilities(result) + probabilities = self.original_onversion_part(basis_list) + return probabilities + + def original_onversion_whole(self, state01): + # 当state01为一维时转换成二维数据 + if isinstance(state01[0], bool): + state01 = [state01] + n = len(state01) # 读取比特数 + # 测量比特概率限制 + # if n > MAX_QUBIT_NUM: + # print(f'Number of qubits > {MAX_QUBIT_NUM}, cannot calculate probabilities.') + counts = [0] * (2**n) + state01_T = np.transpose(state01) # 转置 + numShots = len(state01_T) # 测量重复次数 + # 统计所有numShots 列 + for num in range(numShots): + k = 0 + for i in range(n): + k += state01_T[num][i] * (2**i) + counts[k] += 1 + # 计算概率 + # P=[counts[k]/numShots for k in range(2**n)] + P = {bin(k)[2:].zfill(n): counts[k] / numShots for k in range(2**n)} + return P + + def original_onversion_part(self, state01): + # 当state01为一维时转换成二维数据 + if isinstance(state01[0], bool): + state01 = [state01] + n = len(state01) # 读取比特数 + # 测量比特概率限制 + # if n > MAX_QUBIT_NUM: + # raise Exception(f'Number of qubits > {MAX_QUBIT_NUM}, cannot calculate probabilities.') + counts = {} + state01_T = np.transpose(state01) # 转置 + numShots = len(state01_T) # 测量重复次数 + # 统计所有numShots 列 + for num in range(numShots): + k = 0 + for i in range(n): + k += state01_T[num][i] * (2**i) + prob_state = bin(k)[2:].zfill(n) + if prob_state not in counts: + counts[prob_state] = 1 + else: + counts[prob_state] += 1 + # 计算概率 + # P=[counts[k]/numShots for k in range(2**n)] + P = {k: v / numShots for k, v in counts.items()} + return P + + # 量子态概率矫正 + def probability_calibration(self, result: Dict, config_json: Optional[Dict] = None): + """correction of the measured probability of 01 quantum state. + + Args: + result: + the results returned after query_experiment. + config_json: + experimental parameters of quantum computer. + config_json value is None, read the latest experimental parameters for calculation. + Defaults to None. + + Raises: + Exception: cannot calibrate probability with fidelity. + + Returns: + Dict: corrected probability. + """ + if not self.computer_selection_mark: + raise Exception( + f"{self.machine_name}:current quantum computer does not support computational probability correction" + ) + CM_CACHE = {} + if config_json is None: + config_json = self.download_config(down_file=False) + qubit_num = [f"Q{i}" for i in result.get("results")[0]] + n = len(qubit_num) # 测量比特个数 + qubits = config_json["readout"]["readoutArray"]["|0> readout fidelity"][ + "qubit_used" + ] + readout_fidelity0 = config_json["readout"]["readoutArray"][ + "|0> readout fidelity" + ]["param_list"] + readout_fidelity1 = config_json["readout"]["readoutArray"][ + "|1> readout fidelity" + ]["param_list"] + iq2probFidelity = [ + [readout_fidelity0[qubits.index(q)], readout_fidelity1[qubits.index(q)]] + for q in qubit_num + ] + P = self.readout_data_to_state_probabilities_whole(result) + Pm = list(P.values()) + if not isinstance(iq2probFidelity[0], list): + iq2probFidelity = [iq2probFidelity] + f = tuple([float(fi) for fi in sum(iq2probFidelity, [])]) + if f not in CM_CACHE: + inv_CM = 1 + for k in iq2probFidelity[::-1]: + F00 = k[0] + F11 = k[1] + if F00 + F11 == 1: + raise Exception( + f"Cannot calibrate probability with fidelity: [{F00}, {F11}]" + ) + inv_cm = np.array([[F11, F11 - 1], [F00 - 1, F00]]) / (F00 + F11 - 1) + inv_CM = np.kron(inv_CM, inv_cm) + CM_CACHE[f] = inv_CM + else: + inv_CM = CM_CACHE[f] + Pi = np.dot(inv_CM, (np.array(Pm, ndmin=2).T)) + Pi = {bin(idx)[2:].zfill(n): k[0] for idx, k in enumerate(Pi)} + return Pi + + # 对矫正后的概率进行修正 + def probability_correction(self, probabilities): + """correction of the measured probability of 01 quantum state. + If there is a probability greater than 1, change this item to 1; + If there is anything less than 0, change the item to 0. + + Args: + probabilities: + corrected probability. + + Returns: + Dict: corrected probability. + """ + abnormal_fidelity_list = list( + filter(lambda x: x < 0 or x > 1, probabilities.values()) + ) + if not abnormal_fidelity_list: + return probabilities + for k, v in probabilities.items(): + if v > 1: + probabilities[k] = 1 + elif v < 0: + probabilities[k] = 0 + fidelity_sum = sum(probabilities.values()) + for k, v in probabilities.items(): + probabilities[k] = v / fidelity_sum + return probabilities + + def get_coupling_map(self, config_json): + qubits = config_json["overview"]["qubits"] + qubits_used = config_json["qubit"]["singleQubit"]["gate error"]["qubit_used"] + disable_qubits = [q for q in qubits if q not in qubits_used] + coupler_map = config_json["overview"]["coupler_map"] + adjacency_list = [] + for Q1, Q2 in coupler_map.values(): + q1 = int(Q1[1:]) + q2 = int(Q2[1:]) + if Q1 in disable_qubits or Q2 in disable_qubits: + continue + adjacency_list.append([q1, q2]) + return adjacency_list + + # def qcis_mapping_isq( + # self, + # qcis_circuit: str, + # initial_layout: Optional[Dict]=None, + # objective: Optional[str]='size', + # seed: Optional[int]=None, + # use_post_opt: Optional[bool]=False): + # """The script transpiles qcis string by searching for a mapping from virtual to physical qubit + # and a swap strategy such that the circuit described by qcis can be fitted into a hardware + # described by the coupling_map, in the meanwhile reduces circuit depth. + + # Args: + # qcis_circuit: qcis circuit + # initial_layout: + # Initial position of virtual qubits on physical qubits. + # If given, this is the initial state in search of virtual to physical qubit mapping + # e.g.: + # {0:4, 1:1, 2:5, 3:2, 4:0, 5:3}. Defaults to None. + # objective: + # size: min. # of added swaps + # depth: min. depth + # no_swap: try best to find an initial mapping requiring no swaps; raise + # an error if fail. Defaults to 'size'. + # seed: + # Set random seed for the stochastic part of the tranpiler. Defaults to None. + # use_post_opt: + # we provide a genetic alg. which utilizes exchange rules for + # swaps to futher min. depth. Defaults to False. + + # Raises: + # TranspileError: if graph specified by coupling map is disconnected. + + # Returns: + # str: qcis string after transpilation + # """ + # if not self.computer_selection_mark: + # raise Exception(f'current quantum computer does not support qcis_mapping_isq') + # config_json = self.download_config(down_file=False) + # coupling_map = self.get_coupling_map(config_json) + # try: + # qasm_circuit = self.convert_qcis_to_qasm(qcis_circuit) + # cur_time = self.current_time() + # qpu_file = f'./{self.machine_name}_config_param_{cur_time}.json' + # with open(qpu_file, 'w') as f: + # f.write(json.dumps(config_json)) + # qasm_transpiled, _, _, _ = transpile(qasm_circuit, + # coupling_map, + # initial_layout=initial_layout, + # objective=objective, + # seed=seed, + # use_post_opt=use_post_opt) + # simplity_qcis = self.convert_qasm_to_qcis(qasm_transpiled) + # os.remove(qpu_file) + # return simplity_qcis + # except Exception as e: + # print(e) + # print(traceback.format_exc()) + # print('circuit mapping error, will submit using the original route') + # os.remove(qpu_file) + # return qcis_circuit + + # def qcis_mapping_sabre( + # self, + # qcis_circuit: str): + # """The script transpiles qcis string by searching for a mapping from virtual to physical qubit + # and a swap strategy such that the circuit described by qcis can be fitted into a hardware + # described by the coupling_map, in the meanwhile reduces circuit depth. + + # Args: + # qcis_circuit: qcis circuit + + # Returns: + # str : qcis after mapping + # """ + # if not self.computer_selection_mark: + # raise Exception(f'current quantum computer does not support qcis_mapping_quingo') + # config_json = self.download_config(down_file=False) + # try: + # # qcis转换成qasm并写入qasm_file中 + # qasm_circuit = self.qcistoqasm.convert_qcis_to_qasm(qcis_circuit) + # folder_path = 'temp' + # if not os.path.exists(folder_path): + # os.makedirs(folder_path) + # qasm_file = f'{folder_path}/qasm.qasm' + # with open(qasm_file, 'w') as f: + # f.write(qasm_circuit) + # sabre = SabreMapper() + # # 组装chip_info_fn映射信息 + # chip_info_fn = {} + # couplings = [] + # coupler_maps = config_json.get('overview').get('coupler_map') + # coupler_used = config_json.get('twoQubitGate').get( + # 'czGate').get('gate error').get('qubit_used') + # cz_gate_error = config_json.get('twoQubitGate').get( + # 'czGate').get('gate error').get('param_list') + # for coupler, error in zip(coupler_used, cz_gate_error): + # fidelity = 1 - error + # coupler_qubit_map = coupler_maps.get(coupler) + # couplings.append( + # {"fidelity": fidelity, "qubit pair": coupler_qubit_map}) + # chip_info_fn['couplings'] = couplings + # qubit_used = config_json.get('qubit').get( + # 'singleQubit').get('gate error').get('qubit_used') + # qubit_gate_error = config_json.get('qubit').get( + # 'singleQubit').get('gate error').get('param_list') + # qubit_fidelity = {} + # for qubit, error in zip(qubit_used, qubit_gate_error): + # qubit_fidelity[qubit] = 1 - error + # chip_info_fn['fidelity'] = qubit_fidelity + # chip_info_fn['has multiple chips'] = False + # chip_info_fn['qubits'] = qubit_used + + # chip_info_fn_file = f'{folder_path}/chip_info_fn.json' + # mapped_qasm_fn_file = f'{folder_path}/mapped_qasm_fn.qasm' + # qubit_mapping_fn_file = f'{folder_path}/qubit_mapping_fn.json' + # with open(chip_info_fn_file, 'w') as f: + # json.dump(chip_info_fn, f) + # # 调用quMapper做mapping操作 + # success = sabre.map_schedule( + # qasm_file, chip_info_fn_file, mapped_qasm_fn_file, qubit_mapping_fn_file) + # if success: + # with open(qubit_mapping_fn_file, 'r') as f: + # qubit_mapping_fn = json.load(f) + # qubit_map = qubit_mapping_fn.get('physical qubits idx') + # # mapping成功,将转换后的qasm转为qcis,其中编号根据physical qubits idx做映射 + # simplity_qcis = self.convert_qasm_to_qcis_from_file( + # mapped_qasm_fn_file, qubit_map) + # shutil.rmtree(folder_path) + # return simplity_qcis + # except Exception as e: + # print(e) + # print(traceback.format_exc()) + # print('circuit mapping error, will submit using the original route') + # shutil.rmtree(folder_path) + # return qcis_circuit + + @reconnect_on_failuer + def get_experiment_circuit(self, query_id: Union[str, List[str]]): + """according to the exp_id obtained experimental circuit + + Args: + query_id: the result returned by the run_experiment interface, experimental set id. + + The maximum number of experimental line queries supported by the server is 50. + If it exceeds 50, an error message will be displayed. + + Returns: + Union[int, List[Dict]]: 0 failed, not 0 successful, success returns the experimental circuit, + The parameters of the returned experimental circuit include qcis、mapQcis and computerQcis, + qcis is the line submitted by the user, mapQcis is the compiled circuit, + computerQcis is a circuit submitted to a quantum computer. + """ + if isinstance(query_id, str): + query_id = [query_id] + if self.computer_selection_mark: + url = f"{self.base_url}/sdk/api/multiple/experiment/detail" + else: + url = f"{self.base_url}/sdk/api/experiment/detail/find" + data = {"query_id": query_id} + headers = {"sdk_token": self.token} + res = requests.post(url, json=data, headers=headers) + status_code = res.status_code + if status_code != 200: + raise Exception( + f"查询实验线路失败, 请求接口失败, status_code:{status_code}" + ) + result = json.loads(res.text) + code = result.get("code", -1) + msg = result.get("msg", "查询实验线路失败") + if code != 0: + print(f"查询实验线路: {msg}") + return 0 + circuit = result.get("data") + return circuit + + +class StandradError(Exception): + def __init__(self, msg="", code=0): + self.msg = msg + self.code = code + + def __str__(self): + return f"[{self.code}] [{self.msg}]" + + +class TokenNotSetException(StandradError): + code = 10100101 + + def __init__(self): + self.msg = f"user token has expired or not set." diff --git a/src/quingo/lib/qiskit_tools.py b/src/quingo/lib/qiskit_tools.py new file mode 100644 index 0000000000000000000000000000000000000000..117648335f02a04fd1f41833655633f754ff95bd --- /dev/null +++ b/src/quingo/lib/qiskit_tools.py @@ -0,0 +1,33 @@ +"""Visualization functions for quantum circuits. + +Note, this module requires qiskit to be installed. +""" + +from qiskit import QuantumCircuit +from pathlib import Path +from pyqcisim.qcis_to_openqasm import qcis_file_2_qasm_file + + +def qcis_2_qiskit_qc(qcis_fn: Path): + """Convert a QCIS file to a qiskit QuantumCircuit object.""" + qasm_fn = qcis_fn.with_suffix(".qasm") + qasm_str = qcis_file_2_qasm_file(qcis_fn, qasm_fn) + return QuantumCircuit.from_qasm_file(str(qasm_fn)) + + +def draw_circ(qcis_fn: Path, output="mpl"): + """Draw the quantum circuit using qiskit. + Use the output parameter to choose the drawing format: + + **text**: ASCII art TextDrawing that can be printed in the console. + + **mpl**: images with color rendered purely in Python using matplotlib. + + **latex**: high-quality images compiled via latex. + + **latex_source**: raw uncompiled latex output. + """ + qasm_fn = qcis_fn.with_suffix(".qasm") + qasm_str = qcis_file_2_qasm_file(qcis_fn, qasm_fn) + qc = QuantumCircuit.from_qasm_file(str(qasm_fn)) + return qc.draw(output=output) diff --git a/src/quingo/lib/rem.py b/src/quingo/lib/rem.py new file mode 100644 index 0000000000000000000000000000000000000000..6f02729cc3c61a0604a23763a906c53b350da531 --- /dev/null +++ b/src/quingo/lib/rem.py @@ -0,0 +1,157 @@ +""" +Readout Error Mitigation +""" + +# from qiskit import QuantumCircuit +import numpy as np +from scipy.optimize import minimize +from typing import Dict + + +def get_corr_exp_value_for_full_matrix_model( + n_qubits: int, + noisy_circ_prob: Dict[int, float], + calibration_matrix, +) -> float: + """ + Correct the unbiased estimator of expectation value + + Args: + n_qubits: number of qubits + noisy_circ_prob: probability distribution measured in pauli basis (eigenvector of observable) of target noisy circuit + calibration_matrix: that is estimated value of full noise matrix + + Returns: + corr_exp_value: corrected expectation value + """ + + if len(noisy_circ_prob) != 2**n_qubits: + raise RuntimeError("incorrect length of arg noisy_circ_prob") + noisy_prob_vector = np.zeros(2**n_qubits) + for j in range(len(noisy_prob_vector)): + noisy_prob_vector[j] = noisy_circ_prob.get(j, 0) + # get prob corrected + try: + corr_prob_vector = np.linalg.inv(calibration_matrix) @ noisy_prob_vector + except: + corr_prob_vector = np.linalg.pinv(calibration_matrix) @ noisy_prob_vector + negative_prob_occur = False + for prob_value in corr_prob_vector: + if prob_value < 0: + negative_prob_occur = True + break + if not negative_prob_occur: + corr_circ_prob = {i: corr_prob_vector[i] for i in range(len(corr_prob_vector))} + else: # may cost much time for a large number of qubit + func = lambda x: np.linalg.norm( + calibration_matrix @ x - noisy_prob_vector, ord=2 + ) + cons = [{"type": "eq", "fun": lambda x: sum(x) - 1}] + for i in range(2**n_qubits): + cons.append({"type": "ineq", "fun": lambda x, i=i: x[i]}) + cons.append({"type": "ineq", "fun": lambda x, i=i: 1 - x[i]}) + + x0 = corr_prob_vector + + res = minimize(func, x0, method="SLSQP", constraints=cons) + if res.success: + corr_prob_vector = res.x + corr_circ_prob = {i: corr_prob_vector[i] for i in range(len(corr_prob_vector))} + + return corr_circ_prob + # corr_exp_value = 0 + # for state, prob in corr_circ_prob.items(): + # if fmt_str.format(state).count("1") % 2 == 0: + # corr_exp_value += 1 * prob + # else: + # corr_exp_value += -1 * prob + # return corr_exp_value + + +# Tensor Product Model + + +# def build_calibration_circuits_for_tensor_product_model( +# n_qubits: int, observable: str +# ) -> Tuple[List[QuantumCircuit], List[str]]: +# """ """ + +# state_labels = ["0" * n_qubits, "1" * n_qubits] +# calibration_circuits = [] + +# for label in state_labels: +# circ = QuantumCircuit(n_qubits) +# for i in range(n_qubits): +# if observable[::-1][i] == "Z": +# if label[::-1][i] == "1": +# circ.x(i) +# else: +# pass +# elif observable[::-1][i] == "X": +# if label[::-1][i] == "1": +# circ.x(i) +# circ.h(i) +# else: +# circ.h(i) +# elif observable[::-1][i] == "Y": +# if label[::-1][i] == "1": +# circ.x(i) +# circ.h(i) +# circ.s(i) +# else: +# circ.h(i) +# circ.s(i) +# else: +# raise RuntimeError("unsupported observable") +# calibration_circuits.append(circ) + +# return calibration_circuits, state_labels + + +# def cal_noise_matrix_for_tensor_product_model( +# n_qubits: int, cali_circs_prob: List[Dict[int, float]] +# ): +# """ """ + +# calibration_matrices = [] +# fmt_str = "{:0" + str(n_qubits) + "b}" +# for i in range(n_qubits): +# noise_matrix_i = np.zeros((2, 2)) +# prob_0_1 = prob_1_0 = 0 +# for state, prob in cali_circs_prob[0].items(): +# if fmt_str.format(state)[::-1][i] == "1": +# prob_0_1 += prob +# for state, prob in cali_circs_prob[1].items(): +# if fmt_str.format(state)[::-1][i] == "0": +# prob_1_0 += prob +# noise_matrix_i[0][1] = prob_1_0 +# noise_matrix_i[1][1] = 1 - prob_1_0 +# noise_matrix_i[1][0] = prob_0_1 +# noise_matrix_i[0][0] = 1 - prob_0_1 +# calibration_matrices.append(noise_matrix_i) +# return calibration_matrices + + +# def get_corr_exp_value_for_tensor_product_model( +# n_qubits: int, noisy_circ_prob, calibration_matrices +# ): + +# fmt_str = "{:0" + str(n_qubits) + "b}" +# corr_exp_value = 0 +# e_vector = np.array([1, 1]) +# pauli_Z = np.array([[1, 0], [0, -1]]) +# for state, prob in noisy_circ_prob.items(): +# tmp = 1 +# bin_str = fmt_str.format(state) +# for i in range(n_qubits): +# qubit_i_vector = np.zeros(2) +# qubit_i_vector[int(bin_str[::-1][i])] = 1 +# tmp *= ( +# e_vector +# @ pauli_Z +# @ np.linalg.inv(calibration_matrices[i]) +# @ qubit_i_vector +# ) +# corr_exp_value += tmp * prob + +# return corr_exp_value diff --git a/src/quingo/lib/utils.py b/src/quingo/lib/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..7e327153e70a00dbae4ae4e9db643a2ae132a3c1 --- /dev/null +++ b/src/quingo/lib/utils.py @@ -0,0 +1,80 @@ +from quingo import * +import numpy as np +from pathlib import Path +from typing import List, Dict + + +def cal_noise_matrix_for_full_matrix_model( + n_qubits: int, cali_circs_prob: List[Dict[int, float]] +): + """ + Calculate the noise matrix through measuring the probability distribution of calibration circuits + + Args: + n_qubits: number of qubits + cali_circs_probs: probability distributions measured in pauli basis (eigenvector of observable) of calibration circuits + + Returns: + calibration_matrix: that is estimated value of full noise matrix + """ + calibration_matrix = np.zeros((2**n_qubits, 2**n_qubits)) + for i in range(len(cali_circs_prob)): + for label, value in cali_circs_prob[i].items(): + calibration_matrix[label][i] = value + + return calibration_matrix + + +def get_prob_noisy(task: Quingo_task, noise_config, n_qubits, params=(), shots=32000): + cfg = ExeConfig(ExeMode.SimProbability, num_shots=shots, noise_config=noise_config) + qasm_fn = compile(task, params=params) + sim_result = execute(qasm_fn, BackendType.TEQUILA, cfg) + dic = {} + for i in range(len(sim_result[1])): + dic[i] = sim_result[1][i].real + return dic + + +def b_get_prob_noisy(task: Quingo_task, noise_config, n_qubits, params=(), shots=32000): + cfg = ExeConfig(ExeMode.SimShots, num_shots=shots, noise_config=noise_config) + qasm_fn = compile(task, params=params) + sim_result = execute(qasm_fn, BackendType.TEQUILA, cfg) + + def count_elements(lst): + return np.unique(np.array(lst), return_counts=True, axis=0) + + res = count_elements(sim_result[1]) + + def list_to_int(bit_list): + return sum(1 << i for i, bit in enumerate(bit_list) if bit) + + dic = {} + for i in range(2**n_qubits): + dic[i] = 0 + for i in range(len(res[0])): + dic[list_to_int(res[0][i])] = res[1][i] / shots + return dic + + +def int_to_list(number, bit_length=None): + if bit_length is None: + bit_length = number.bit_length() if number >= 0 else 1 + binary_list = [(number >> i) & 1 for i in range(bit_length - 1, -1, -1)] + return binary_list + + +qu_calibration = Path("calibration_circuits.qu") +calibration_circ = Quingo_task(qu_calibration, "calibration_circuits") + + +def calibration_matrix(calibration_circ, nqubits, observable, noise_config): + cali_circs_fm = [] + for ii in range(2**nqubits): + paramsi = (nqubits, observable, int_to_list(ii, nqubits)) + cali_circs_fm.append(paramsi) + cali_circs_prob_fm = [ + b_get_prob_noisy(calibration_circ, noise_config, nqubits, params, shots=100) + for params in cali_circs_fm + ] + cali_matrix_fm = cal_noise_matrix_for_full_matrix_model(nqubits, cali_circs_prob_fm) + return cali_matrix_fm diff --git a/src/quingo/utils.py b/src/quingo/utils.py index 67cfa2c7c400f1a68e9af6126afdbd829a567c68..a38fff9c8f19e1d08d98177d08498eafaf687f26 100644 --- a/src/quingo/utils.py +++ b/src/quingo/utils.py @@ -1,6 +1,7 @@ import sympy as sp from pathlib import Path import numpy as np +import os def ensure_path(fn) -> Path: @@ -10,6 +11,31 @@ def ensure_path(fn) -> Path: return fn +def validate_path(fn) -> Path: + """ + Validates the given file path. + + Args: + fn (str or Path): The file path to validate. + + Returns: + Path: The validated file path as a `Path` object, or `None` if the path is invalid. + """ + if not isinstance(fn, (str, Path)): + return None + + if isinstance(fn, str): + if os.path.isfile(fn): + return Path(fn).resolve() + else: + return None + + if not fn.exists(): + return None + else: + return Path(fn).resolve() + + def is_number(s): try: float(s) @@ -55,3 +81,81 @@ def state_fidelity(state_a: np.ndarray, state_b: np.ndarray): float: The state fidelity between the two quantum states. """ return np.vdot(state_a, state_b) + + +def verify_qubit_map(old_qubit_order, new_qubit_order): + if len(set(old_qubit_order)) != len(set(new_qubit_order)): + raise ValueError( + "The old and new qubit orders do not have the same qubits: \n{}\n{}".format( + old_qubit_order, new_qubit_order + ) + ) + + +def shuffle_qubits_in_state( + old_qubit_order: list, new_qubit_order, state: np.ndarray +) -> np.ndarray: + """This function shuffles the state vector (`state`) with a given qubit order + (`old_qubit_order`), returns a new state vector representing the same state + but the qubit order is the new one (`new_qubit_order`). + + Note: Little-Endian is used in the qubit order, i.e., the least significant + qubit is at index 0, and the most significant bit is at index n - 1. + + Parameters: + old_qubit_order (list): The old qubit order, e.g., [0, 1, 2]. + new_qubit_order (list): The new qubit order, e.g., [1, 2, 0]. + state (np.ndarray): The state vector to shuffle. + + Returns: + np.ndarray: The new state vector with the new qubit order. + + q0, q1, q2 -> q2, q0, q1 + 0 0 0 -> 0 0 0 + 0 0 1 -> 1 0 0 + 0 1 0 -> 0 0 1 + 0 1 1 -> 1 0 1 + 1 0 0 -> 0 1 0 + 1 0 1 -> 1 1 0 + 1 1 0 -> 0 1 1 + 1 1 1 -> 1 1 1 + + how to get the new idx for the old idx (idx_map)? + 1. get the qubit name for each index in the old qubit order (q1) + 2. get the new qubit index according to the qubit map (2) + 0 -> q0 -> 1 + 1 -> q1 -> 2 + 2 -> q2 -> 0 + idx_map = {0: 1, 1: 2, 2: 0} + + After having the new qubit index for each old qubit index, we can calculate the new index + for the value in the new state vector. For example, we have a state vector with 3 qubits, + and the qubit map is {0: 2, 1: 0, 2: 1}. We can calculate the new index for each value + in the state vector by the following steps: + 1. get the binary representation of the old index. + 2. move the bit to the new position according to the idx_map. + 3. get the new index by converting the binary representation to an integer. + + so, new_state[new_idx] = state[old_idx], where new_idx = idx_map[old_idx] + """ + verify_qubit_map(old_qubit_order, new_qubit_order) + num_qubits = len(old_qubit_order) + + old_qubit_idx = {old_qubit_order[i]: i for i in range(num_qubits)} + new_qubit_idx = {new_qubit_order[i]: i for i in range(num_qubits)} + + idx_map = {} # old qubit index -> new qubit index + for this_old_qubit_idx in range(num_qubits): + qubit_name = old_qubit_order[this_old_qubit_idx] + this_new_qubit_idx = new_qubit_idx[qubit_name] + idx_map[this_old_qubit_idx] = this_new_qubit_idx + + new_state = np.zeros_like(state) + for i in range(len(state)): + new_idx = 0 + for j in range(num_qubits): + # select the j-th bit in i, and move it to the new position + new_idx |= (((1 << j) & i) >> j) << idx_map[j] + + new_state[new_idx] = state[i] + return new_state diff --git a/tests/qiskit_tools_test.ipynb b/tests/qiskit_tools_test.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..1911415f297fa858264264f3a53460bc61e8846f --- /dev/null +++ b/tests/qiskit_tools_test.ipynb @@ -0,0 +1,93 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from quingo.lib.qiskit_tools import draw_circ, qcis_2_qiskit_qc\n", + "from pathlib import Path" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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7udwoco9baYmwaQMiPDNu4+K1FKIi18Y0Nl+HveqHXCkCTeMjAyFewZAegroeSBiAoW4+I24KJg2BL4stD8P87ATN5VrHQGZ3ex/osM9V6OIuynExa8EBNiHQlKT4NtzsD/f/jSeghIotkLN2aMRofYx76R4ew++R3udQihmUoiL32oXDUN61p122WYP+kANlYifUIhLi6LetvgbfaRFRAymEBcRMZhCXETEYApxERGD6cCmiPikyMhIrFarW9pauGQVZeUVxERFMuvxBxqc1hSOnk3QHBTiIuKTAgIC3Pbg4dCwcEIvXSY0LLy2TUfTTKThFBERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphD3AQsXLiQjI4O4uDhiY2MZPHgwGzZs8HZZIte0fv16+vbtS1hYGCkpKbzyyiveLqlZbdmyhXHjxtGpUycCAgJ48cUXvVKHQtwHbN68mR/96Ed88sknfP7552RmZjJmzBi2bdvm7dJEHMrJyWHcuHGMHDmS3NxcXnjhBebMmcPrr7/u7dKajdVqpWfPnrz88sskJCR4rY5gr21Zan300Ud1Xr/88sts2LCBNWvWcNttt3mpKpGGvfLKKwwcOJD58+cD0KNHD7788kteeuklpk2b5uXqmseoUaMYNWoUALNnz/ZaHQpxH2Sz2SgrKyMqKsrbpYhhLly8xDeFp+pNr7p8ufbn0a8L6r2+0o1t42gdfe3P3rZt23jkkUfqTBsxYgSLFi2ioKCApKSkpryNJvlX4SkqL16qM83R+23o7yAiLJTk9jc0U7VNpxD3QfPmzePMmTM89thj3i5FDBMSEszWz/dxLK/Q4fyK85X87k/rG3wdG9OKmT+697rbKSoqqjeEUPO6qKjIqyFecvYcq/662eG8q9+vo2k/HD+MZI9W6F4aE/cxixcvZt68ebz33nte/Y8gZgoMCODeUXcSER7m0voTR91JeFiom6tqXn17dqXPLV1cWrd/andSb+7s5oo8SyHuQxYtWsSsWbP48MMPGTp0qLfLEUO1jo5i/DDnj6UMHtibmzq1b9SyiYmJWCyWOtNOnTpVO8/bxt81mJhWkU6tExvTiruHZnqoIs9RiPuI559/nrlz57J+/XoFuDRZWs+upPW4qdHL39g2juG3D2z08rfddhsbN26sM23Dhg106tTJJ/YgIyPCuXfUnY1ePgC4b7SZeyEKcR8wc+ZMFi5cyDvvvMPNN9+MxWLBYrFw9uxZb5cmBht312BiWl3/4HhQYCD3jckiJLjxh8h++tOf8vnnn/Pcc89x+PBhfv/73/Ob3/yGZ555piklu1X3zklk9O/VqGUHD+xDl46N2wupYbVayc3NJTc3l4sXL2KxWMjNzeX48eOulOuygOrq6upm3aLUExAQ4HD6lClTeOutt5q3GPErx74uYPlVB/KuNvz2gWRl9HO67XXr1jFnzhwOHz5MQkICM2bM4Gc/+5mrpXrExUtV/Oat9/m2pOEO0Y1t43hyygSnvsQAPv30U7KysupNv+OOO/j000+dLdVlCnHDfJ1fRFJCO0JCdGKRNM6HH2/jn7u/dDivU4cbefwHYwkM9N+d8vyi/+W37/wFm4OoCwoMZPqUCbS/oY0XKnMP//2X80PnrBUs/9N6Xl66irNlVm+XI4YYccd/0S4+tt700JBg7hud5dcBDpCceANDMvs7nDfsOwOMDnBQiBvls537qKq6TFxMNDHXuRhDpEZoSDD3j8kiMLDusN2Y72bSJi7GS1U1r6yMfiQntqszLSUpgdsH9fFSRe6jEL/C5cuXeeedd7jrrrto164dYWFhdOzYkREjRrBs2TIu//sKL284Z61gR+5BAIYOTm9wHF3EkaTEdnw3M732dY+uHRnY52YvVtS8goJqDt4GARAaGsLE0Xf6xV6I+e/ATcrKyhg2bBiTJ0/mb3/7G6GhoaSlpWGz2di0aROPPvoo586d81p9Nb3wju1vpFtKB6/VIea6M6MvyYk3EBURzj0jbm9xHYF28bGMyroVgLFDMmgT6x97ITqw+W8TJ06svUry7bffrnPU+dSpUyxfvpwZM2a4dD+T3/x+Dees512uzWazYa2wrx8ZHkawk0fRRWrYqm3YbDaCg1ruZ+jipUuEhoR4u4x6oltF8JMp9zi9nkIc2L17NwMGDCA4OJi9e/eSmprq1vbn/c8fKbOWu7VNEfEvMa2imDN9ktPrtdyv4yt88MEHAIwePdrtAQ72b1hXqRcu0jK4mhNKBODgQfsBw4yMDI+078ouUo21f9/OP3L207H9jTzxw7tb3DimiFybQhz7QU2A1q1be6R9V8fEr+yFF5ecYf7iFe4uTUR8hKtj4gpxICbGfpTaU/cqOWc93+Qx8YrKC8AF9xQkIn5DIQ706tWLNWvWsH37do+078pYl8bCRVoWV8fEdXYKsHfvXvr3709ISAi5ubn07NnT2yVpLFxEGkUX+wD9+vXjvvvu49KlS4wcOZLPPvuszvxTp04xf/58ysub5zRBXZ0pIo2lnvi/lZWVMW7cuNpbSHbo0IH27dtTVFREYWEh1dXVlJaWEhsb6/Fa1AsXkcZST/zfYmJi+Pjjj1m+fDl33nknFRUV7Nu3j8DAQIYPH87y5cuJjo5ullpaRUUQHhaqXriIXJd64j6q8sJFwkJDFOIick0KcRERg2k4RUTEYApxERGDKcRFRAymEBcRMZhCXETEYApxERGDKcRFRAymEBcRMZhCXETEYApxERGDKcRFRAymEBcRMZhCXETEYApxERGDKcRFRAymEBcRMZhCXETEYApxERGDKcRFRAymEBcRMZhCXETEYApxERGDKcRFRAymEBcRMZhCXETEYApxERGDKcRFRAymEBcRMZhCXETEYApxERGDKcRFRAymEBcRMZhCXETEYApxERGDKcRFRAymEBcRMZhCXETEYApxERGDKcRFRAz2/wHoKkgHdFVPHgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "qcis_fn = Path('/tmp/quingo-9mp_zcgv/main_kernel_bell_state.qcis')\n", + "draw_circ(qcis_fn, output='mpl')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "qc = qcis_2_qiskit_qc(qcis_fn)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "qc.depth()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "quingo", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.17" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/unittest/backends/test_backends.py b/unittest/backends/test_backends.py index d9f181b6207c08e1e447110ea1cacf118a858694..2fe6ce1d7f13dbe0a79777c72cc406cf20151fc0 100644 --- a/unittest/backends/test_backends.py +++ b/unittest/backends/test_backends.py @@ -1,14 +1,15 @@ -from quingo.backend.pyqcisim_tequila import PyQCISim_tequila +import threading +from pathlib import Path + +from quingo.backend.backend_hub import Backend_hub, BackendType from quingo.backend.pyqcisim_quantumsim import PyQCISim_quantumsim -from quingo.backend.qualesim import QuaLeSim -from quingo.backend.symqc import IfSymQC -from quingo.backend.backend_hub import BackendType, Backend_hub +from quingo.backend.pyqcisim_tequila import PyQCISim_tequila from quingo.backend.qisa import Qisa +from quingo.backend.qualesim import QuaLeSim_quantumsim, QuaLeSim_tequila +from quingo.backend.symqc import IfSymQC +from quingo.backend.tianyan import ZDXLZ_Tianyan from quingo.core.exe_config import * -import threading from quingo.utils import number_distance -from pathlib import Path - unittest_dir = Path(__file__).parent / ".." qcis_fn = unittest_dir / "test_qcis" / "bell.qcis" @@ -28,11 +29,12 @@ class Test_backends: assert sim.is_simulator() == is_sim # QuaLeSim_tequila and QuaLeSim_quantumsim default Qisa type is QCIS - # single(QuaLeSim_tequila, BackendType.QUALESIM_TEQUILA, Qisa.QCIS, True) - single(QuaLeSim, BackendType.QUALESIM_QUANTUMSIM, Qisa.QCIS, True) + single(QuaLeSim_tequila, BackendType.QUALESIM_TEQUILA, Qisa.QCIS, True) + single(QuaLeSim_quantumsim, BackendType.QUALESIM_QUANTUMSIM, Qisa.QCIS, True) single(PyQCISim_tequila, BackendType.TEQUILA, Qisa.QCIS, True) single(PyQCISim_quantumsim, BackendType.QUANTUM_SIM, Qisa.QCIS, True) single(IfSymQC, BackendType.SYMQC, Qisa.QCIS, True) + single(ZDXLZ_Tianyan, BackendType.TIANYAN, Qisa.QCIS, False) def test_upload_program(self): def single(BackendClass, qasm_fn): @@ -42,13 +44,28 @@ class Test_backends: except Exception as e: assert False, "upload_program failed: {}".format(e) - # single(QuaLeSim_tequila, qcis_fn) - # single(QuaLeSim_tequila, quiet_fn) - single(QuaLeSim, qcis_fn) - single(QuaLeSim, quiet_fn) + single(QuaLeSim_tequila, qcis_fn) + single(QuaLeSim_tequila, quiet_fn) + single(QuaLeSim_quantumsim, qcis_fn) + single(QuaLeSim_quantumsim, quiet_fn) single(PyQCISim_tequila, qcis_fn) single(PyQCISim_quantumsim, qcis_fn) single(IfSymQC, qcis_fn) + single(ZDXLZ_Tianyan, qcis_fn) + + def test_upload_program_str(self): + def single(BackendClass, qasm): + sim = BackendClass() + try: + sim.upload_program_str(qasm) + except Exception as e: + assert False, "upload_program failed: {}".format(e) + + qasm_str = "H Q0\nCNOT Q0 Q1\nMEASURE Q0\nMEASURE Q1" + single(PyQCISim_tequila, qasm_str) + single(PyQCISim_quantumsim, qasm_str) + single(IfSymQC, qasm_str) + single(ZDXLZ_Tianyan, qcis_fn) def test_get_from_hub(self): def single(backend_type, simulator_class): @@ -58,8 +75,10 @@ class Test_backends: single(BackendType.QUANTUM_SIM, PyQCISim_quantumsim) single(BackendType.TEQUILA, PyQCISim_tequila) - single(BackendType.QUALESIM_QUANTUMSIM, QuaLeSim) + single(BackendType.QUALESIM_QUANTUMSIM, QuaLeSim_quantumsim) + single(BackendType.QUALESIM_TEQUILA, QuaLeSim_tequila) single(BackendType.SYMQC, IfSymQC) + single(BackendType.TIANYAN, ZDXLZ_Tianyan) if __name__ == "__main__": diff --git a/unittest/backends/test_qualesim.py b/unittest/backends/test_qualesim.py index aeafbcbe3e27a94e92042ba370c06da9c1e8d5ca..9165b8f35486c505138c99890a42e41f16d7250d 100644 --- a/unittest/backends/test_qualesim.py +++ b/unittest/backends/test_qualesim.py @@ -1,6 +1,6 @@ import pytest from quingo.backend.backend_hub import BackendType -from quingo.backend.qualesim import QuaLeSim +from quingo.backend.qualesim import QuaLeSim_quantumsim, QuaLeSim_tequila from quingo.core.exe_config import ExeConfig, ExeMode from quingo import execute from quingo.utils import number_distance @@ -21,27 +21,14 @@ bell_qasm_fns = [ ] -@pytest.fixture( - scope="module", - params=[BackendType.QUALESIM_QUANTUMSIM, BackendType.QUALESIM_TEQUILA], -) -def get_backend_type(request): - return request.param - - -@pytest.fixture(scope="module") -def get_qualesim(get_backend_type): - backend_type = get_backend_type - return QuaLeSim(backend_type) - - @pytest.fixture(params=bell_qasm_fns) def get_bell_qasm_fn(request): return request.param -def test_call_qualesim(get_qualesim, get_bell_qasm_fn): - simulator = get_qualesim +@pytest.fixture(params=[QuaLeSim_quantumsim, QuaLeSim_tequila]) +def test_call_qualesim(params, get_bell_qasm_fn): + simulator = params simulator.upload_program(get_bell_qasm_fn) num_shots = 10 exe_config = ExeConfig(ExeMode.SimShots, num_shots) diff --git a/unittest/backends/test_result_format.py b/unittest/backends/test_result_format.py index 5805597ac9c80738f02e21359ce73f0886bea4a3..9498581b3df8534259223cbce0b73bc91dbc1fd3 100644 --- a/unittest/backends/test_result_format.py +++ b/unittest/backends/test_result_format.py @@ -64,8 +64,15 @@ def test_final_result_with_msmt(get_simulator, get_num_shots): num_shots = get_num_shots exe_config = ExeConfig(ExeMode.SimFinalResult, num_shots=num_shots) - result = execute(bell_qcis_fn, simulator, exe_config) - print("result: ", result) + result_dict = execute(bell_qcis_fn, simulator, exe_config) + print("result for {}: ".format(simulator), result_dict) + assert "classical" in result_dict and "quantum" in result_dict + assert isinstance(result_dict["classical"], dict) + assert len(result_dict["classical"]) == 2 + # assert len(result_dict["quantum"]) == 2 + # names, state_vec = result_dict["quantum"] + # assert names == [] + # assert state_vec == 1 # both qubits are measured def test_final_result_without_msmt(get_simulator, get_num_shots): @@ -73,8 +80,14 @@ def test_final_result_without_msmt(get_simulator, get_num_shots): num_shots = get_num_shots exe_config = ExeConfig(ExeMode.SimFinalResult, num_shots=num_shots) - result = execute(bell_no_msmt_qcis_fn, simulator, exe_config) - print("result: ", result) + result_dict = execute(bell_no_msmt_qcis_fn, simulator, exe_config) + assert "classical" in result_dict and "quantum" in result_dict + assert isinstance(result_dict["classical"], dict) + assert len(result_dict["classical"]) == 0 + assert len(result_dict["quantum"]) == 2 + names, state_vec = result_dict["quantum"] + assert names == ["Q1", "Q2"] + assert state_vec.shape == (2 ** len(names),) def test_state_vector_without_msmt(get_simulator): @@ -95,6 +108,8 @@ def test_state_vector_with_msmt(get_simulator): exe_config = ExeConfig(ExeMode.SimStateVector) qubit_names, state_vec = execute(bell_qcis_fn, simulator, exe_config) + print("sim: ", simulator) + print("state vec: ", state_vec) assert qubit_names == ["Q1", "Q2"] assert isinstance(state_vec, (list, np.ndarray)) assert state_vec.shape == (4,) diff --git a/unittest/backends/test_sim_modes.py b/unittest/backends/test_sim_modes.py new file mode 100644 index 0000000000000000000000000000000000000000..be91e9275b87ddc18a75ec52b34d176e2e585206 --- /dev/null +++ b/unittest/backends/test_sim_modes.py @@ -0,0 +1,35 @@ +import pytest +from pathlib import Path +from quingo.backend.pyqcisim_tequila import PyQCISim_tequila +from quingo.core.exe_config import ExeConfig, ExeMode + + +# @pytest.fixture( +# scope="module", +# params=[BackendType.TEQUILA], +# ) +# def get_backend_type(request): +# return request.param + + +@pytest.fixture(scope="module") +def get_tequila(): + return PyQCISim_tequila() + + +unittest_dir = Path(__file__).parent / ".." + + +@pytest.fixture(scope="module", params=[unittest_dir / "test_qcis" / "bell.qcis"]) +def get_bell_qasm_fn(request): + return request.param + + +def test_call_tequila(get_tequila, get_bell_qasm_fn): + simulator = get_tequila + simulator.upload_program(get_bell_qasm_fn) + exe_config = ExeConfig(ExeMode.SimProbability) + names, p0s = simulator.execute(exe_config) + assert names == ["Q1", "Q2"] + assert len(p0s) == len(names) + assert all(p0 == pytest.approx(0.5) for p0 in p0s) diff --git a/unittest/backends/test_tianyan.py b/unittest/backends/test_tianyan.py new file mode 100644 index 0000000000000000000000000000000000000000..7edf4a14199f4bd804f170590bcc289b2d97d01b --- /dev/null +++ b/unittest/backends/test_tianyan.py @@ -0,0 +1,36 @@ +from pathlib import Path + +from quingo.backend.backend_hub import BackendType +from quingo.backend.qisa import Qisa +from quingo.backend.tianyan import ZDXLZ_Tianyan +from quingo.core.exe_config import * +from quingo.global_config import qgrtsys_repo_dir + +cur_dir = Path(__file__).parent +qcis_fn = qgrtsys_repo_dir / "unittest" / "test_qcis" / "bell_cz.qcis" +token_fn = qgrtsys_repo_dir / "config" / "tianyan.tokens" + + +def test_execute(): + platform = ZDXLZ_Tianyan() + platform.upload_program(qcis_fn) + exe_config = ExeConfig( + mode=ExeMode.RealMachine, + num_shots=10, + qcloud_platform_login_key=token_fn.read_text().strip(), + qcloud_machine_name="tianyan_sw", + ) + results = platform.execute(exe_config) + # assert isinstance(results, list) + # res = results[0] + # assert "resultStatus" in list(res.keys()) + # assert "probability" in list(res.keys()) + # assert ( + # len(res["probability"]) == 2 + # and "00" in res["probability"] + # and "11" in res["probability"] + # ) + + +if __name__ == "__main__": + test_execute() diff --git a/unittest/cim/test_data/coupling_graph.txt b/unittest/cim/test_data/coupling_graph.txt new file mode 100644 index 0000000000000000000000000000000000000000..d885bcc1e407eb9c7a724a7afa78052ef7681e39 --- /dev/null +++ b/unittest/cim/test_data/coupling_graph.txt @@ -0,0 +1,5 @@ +5 4 +1 2 1.0 +2 3 1.0 +3 4 1.0 +4 5 1.0 \ No newline at end of file diff --git a/unittest/cim/test_data/invalid_matrix.txt b/unittest/cim/test_data/invalid_matrix.txt new file mode 100644 index 0000000000000000000000000000000000000000..15f6a32ec6e5f5785cdf29fc2018eba5d600772d --- /dev/null +++ b/unittest/cim/test_data/invalid_matrix.txt @@ -0,0 +1,3 @@ +1 2 3 +invalid data +4 5 \ No newline at end of file diff --git a/unittest/cim/test_data/plain_matrix.txt b/unittest/cim/test_data/plain_matrix.txt new file mode 100644 index 0000000000000000000000000000000000000000..42d37c6515dd22f30cec740113f5148d99c6c623 --- /dev/null +++ b/unittest/cim/test_data/plain_matrix.txt @@ -0,0 +1,5 @@ +0 1 0 0 0 +1 0 1 0 0 +0 1 0 1 0 +0 0 1 0 1 +0 0 0 1 0 \ No newline at end of file diff --git a/unittest/cim/test_data/small_matrix.txt b/unittest/cim/test_data/small_matrix.txt new file mode 100644 index 0000000000000000000000000000000000000000..5e72faea55378a714cbf71fa4fc15ea5c8d902db --- /dev/null +++ b/unittest/cim/test_data/small_matrix.txt @@ -0,0 +1,2 @@ +1.0 2.0 +3.0 4.0 \ No newline at end of file diff --git a/unittest/cim/test_parser.py b/unittest/cim/test_parser.py new file mode 100644 index 0000000000000000000000000000000000000000..69cc5b582e7223a268454301e64cbca0d9a41a92 --- /dev/null +++ b/unittest/cim/test_parser.py @@ -0,0 +1,291 @@ +import pytest +import numpy as np +from pathlib import Path +import tempfile +import os +import sys +import importlib.util +from quingo.cim.parser import read_coupling_graph, read_plain_matrix, read_J + +# 直接从文件路径导入parser模块 +# parser_path = Path(__file__).parent.parent / "src" / "quingo" / "cim" / "parser.py" +# spec = importlib.util.spec_from_file_location("parser", parser_path) +# parser_module = importlib.util.module_from_spec(spec) +# spec.loader.exec_module(parser_module) + +# 从模块中获取函数 +# read_coupling_graph = parser_module.read_coupling_graph +# read_plain_matrix = parser_module.read_plain_matrix +# read_J = parser_module.read_J + + +class TestParser: + """parser.py 模块的单元测试""" + + @classmethod + def setup_class(cls): + """设置测试类,定义测试数据文件路径""" + cls.test_data_dir = Path(__file__).parent / "test_data" + cls.plain_matrix_file = cls.test_data_dir / "plain_matrix.txt" + cls.small_matrix_file = cls.test_data_dir / "small_matrix.txt" + cls.coupling_graph_file = cls.test_data_dir / "coupling_graph.txt" + cls.invalid_matrix_file = cls.test_data_dir / "invalid_matrix.txt" + cls.nonexistent_file = cls.test_data_dir / "nonexistent.txt" + + def test_read_plain_matrix_success(self): + """测试 read_plain_matrix 函数正常读取""" + # 测试读取5x5矩阵 + matrix = read_plain_matrix(self.plain_matrix_file) + expected_matrix = np.array([ + [0, 1, 0, 0, 0], + [1, 0, 1, 0, 0], + [0, 1, 0, 1, 0], + [0, 0, 1, 0, 1], + [0, 0, 0, 1, 0] + ], dtype=float) + + assert matrix.shape == (5, 5) + np.testing.assert_array_equal(matrix, expected_matrix) + + def test_read_plain_matrix_small(self): + """测试 read_plain_matrix 函数读取小矩阵""" + matrix = read_plain_matrix(self.small_matrix_file) + expected_matrix = np.array([ + [1.0, 2.0], + [3.0, 4.0] + ], dtype=float) + + assert matrix.shape == (2, 2) + np.testing.assert_array_equal(matrix, expected_matrix) + + def test_read_plain_matrix_string_path(self): + """测试 read_plain_matrix 函数接受字符串路径参数""" + matrix = read_plain_matrix(str(self.small_matrix_file)) + expected_matrix = np.array([ + [1.0, 2.0], + [3.0, 4.0] + ], dtype=float) + + assert matrix.shape == (2, 2) + np.testing.assert_array_equal(matrix, expected_matrix) + + def test_read_plain_matrix_file_not_found(self): + """测试 read_plain_matrix 函数文件不存在异常""" + with pytest.raises(FileNotFoundError) as exc_info: + read_plain_matrix(self.nonexistent_file) + + assert "文件未找到" in str(exc_info.value) + assert str(self.nonexistent_file) in str(exc_info.value) + + def test_read_plain_matrix_invalid_format(self): + """测试 read_plain_matrix 函数文件格式错误异常""" + with pytest.raises(ValueError): + read_plain_matrix(self.invalid_matrix_file) + + def test_read_coupling_graph_success(self): + """测试 read_coupling_graph 函数正常读取""" + expected_matrix = np.array([ + [0, 1, 0, 0, 0], + [1, 0, 1, 0, 0], + [0, 1, 0, 1, 0], + [0, 0, 1, 0, 1], + [0, 0, 0, 1, 0] + ], dtype=float) + + matrix = read_coupling_graph(self.coupling_graph_file) + + assert matrix.shape == (5, 5) + np.testing.assert_array_equal(matrix, expected_matrix) + + def test_read_coupling_graph_string_path(self): + """测试 read_coupling_graph 函数接受字符串路径参数""" + expected_matrix = np.array([ + [0, 1, 0, 0, 0], + [1, 0, 1, 0, 0], + [0, 1, 0, 1, 0], + [0, 0, 1, 0, 1], + [0, 0, 0, 1, 0] + ], dtype=float) + + matrix = read_coupling_graph(str(self.coupling_graph_file)) + + assert matrix.shape == (5, 5) + np.testing.assert_array_equal(matrix, expected_matrix) + + def test_read_coupling_graph_file_not_found(self): + """测试 read_coupling_graph 函数文件不存在异常""" + with pytest.raises(FileNotFoundError) as exc_info: + read_coupling_graph(self.nonexistent_file) + + assert "文件未找到" in str(exc_info.value) + assert str(self.nonexistent_file) in str(exc_info.value) + + def test_read_coupling_graph_value_error(self): + """测试 read_coupling_graph 函数文件格式错误异常""" + # 创建一个格式错误的耦合图文件 + with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as temp_file: + temp_file.write("invalid format\ndata") + temp_file_path = temp_file.name + + try: + with pytest.raises(ValueError) as exc_info: + read_coupling_graph(temp_file_path) + + assert "文件内容格式错误" in str(exc_info.value) + finally: + os.unlink(temp_file_path) + + def test_read_plain_matrix_with_empty_file(self): + """测试 read_plain_matrix 函数读取空文件""" + with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as temp_file: + temp_file.write("") + temp_file_path = temp_file.name + + try: + matrix = read_plain_matrix(temp_file_path) + # 空文件应该返回空数组 + assert matrix.size == 0 + except ValueError: + # 空文件可能抛出 ValueError,这也是可接受的行为 + pass + finally: + os.unlink(temp_file_path) + + def test_read_plain_matrix_with_whitespace_only(self): + """测试 read_plain_matrix 函数读取只有空白字符的文件""" + with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as temp_file: + temp_file.write(" \n\n \t\n") + temp_file_path = temp_file.name + + try: + matrix = read_plain_matrix(temp_file_path) + # 只有空白字符的文件应该返回空数组或抛出异常 + assert matrix.size == 0 + except ValueError: + # 这是可接受的行为 + pass + finally: + os.unlink(temp_file_path) + + def test_read_plain_matrix_mixed_data_types(self): + """测试 read_plain_matrix 函数读取包含不同数据类型的文件""" + with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as temp_file: + temp_file.write("1.0 2\n3.5 4.0") + temp_file_path = temp_file.name + + try: + matrix = read_plain_matrix(temp_file_path) + expected_matrix = np.array([ + [1.0, 2.0], + [3.5, 4.0] + ], dtype=float) + + assert matrix.shape == (2, 2) + np.testing.assert_array_equal(matrix, expected_matrix) + finally: + os.unlink(temp_file_path) + + # read_J 函数的直接测试 + def test_read_J_success(self): + """测试 read_J 函数正常读取""" + expected_matrix = np.array([ + [0, 1, 0, 0, 0], + [1, 0, 1, 0, 0], + [0, 1, 0, 1, 0], + [0, 0, 1, 0, 1], + [0, 0, 0, 1, 0] + ], dtype=float) + + matrix = read_J(self.coupling_graph_file) + + assert matrix.shape == (5, 5) + np.testing.assert_array_equal(matrix, expected_matrix) + + def test_read_J_string_path(self): + """测试 read_J 函数接受字符串路径参数""" + expected_matrix = np.array([ + [0, 1, 0, 0, 0], + [1, 0, 1, 0, 0], + [0, 1, 0, 1, 0], + [0, 0, 1, 0, 1], + [0, 0, 0, 1, 0] + ], dtype=float) + + matrix = read_J(str(self.coupling_graph_file)) + + assert matrix.shape == (5, 5) + np.testing.assert_array_equal(matrix, expected_matrix) + + def test_read_J_empty_file(self): + """测试 read_J 函数读取空文件""" + with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as temp_file: + temp_file.write("") + temp_file_path = temp_file.name + + try: + with pytest.raises(ValueError) as exc_info: + read_J(temp_file_path) + + assert "文件内容为空" in str(exc_info.value) + finally: + os.unlink(temp_file_path) + + def test_read_J_single_line_file(self): + """测试 read_J 函数读取只有一行的文件""" + with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as temp_file: + temp_file.write("5 4") + temp_file_path = temp_file.name + + try: + with pytest.raises(ValueError) as exc_info: + read_J(temp_file_path) + + assert "文件内容格式错误" in str(exc_info.value) + finally: + os.unlink(temp_file_path) + + def test_read_J_invalid_header(self): + """测试 read_J 函数读取头部格式错误的文件""" + with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as temp_file: + temp_file.write("invalid header\n1 2 1.0") + temp_file_path = temp_file.name + + try: + with pytest.raises(ValueError) as exc_info: + read_J(temp_file_path) + + assert "invalid literal for int() with base 10:" in str(exc_info.value) + finally: + os.unlink(temp_file_path) + + def test_read_J_edge_count_mismatch(self): + """测试 read_J 函数边数不匹配""" + with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as temp_file: + temp_file.write("3 2\n1 2 1.0") # 声明2条边但只有1条 + temp_file_path = temp_file.name + + try: + with pytest.raises(ValueError) as exc_info: + read_J(temp_file_path) + + assert "文件内容格式错误" in str(exc_info.value) + finally: + os.unlink(temp_file_path) + + def test_read_J_invalid_edge_format(self): + """测试 read_J 函数边格式错误""" + with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as temp_file: + temp_file.write("3 2\n1 2\n2 3 1.0") # 第一条边缺少权重 + temp_file_path = temp_file.name + + try: + with pytest.raises(ValueError) as exc_info: + read_J(temp_file_path) + + assert "文件内容格式错误" in str(exc_info.value) + finally: + os.unlink(temp_file_path) + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) \ No newline at end of file diff --git a/unittest/test_build_dir.py b/unittest/test_build_dir.py new file mode 100644 index 0000000000000000000000000000000000000000..1b6aa0831d6bb70596219126f20748d5ec32d820 --- /dev/null +++ b/unittest/test_build_dir.py @@ -0,0 +1,73 @@ +from quingo import Quingo_task +import pytest +from pathlib import Path +import shutil +import os + + +@pytest.fixture(scope="module") +def qcis_fn(): + return "bell.qcis" + + +cur_dir = Path(__file__).parent +bell_qu_fn = cur_dir / "test_qu" / "bell.qu" + + +def test_default_build_dir(): + task = Quingo_task(bell_qu_fn, "bell") + build_dir = task.build_dir + + print(build_dir) + assert task.parent_work_dir == None + assert str(build_dir).startswith("/tmp/") + assert build_dir.exists() + assert build_dir.is_dir() + assert build_dir.name.startswith("qg") + + +def test_default_build_dir_debug(): + task = Quingo_task(bell_qu_fn, "bell", debug_mode=True) + build_dir = task.build_dir + + print(build_dir) + assert str(task.parent_work_dir) == os.path.join(os.getcwd(), "build") + assert build_dir.exists() + assert build_dir.is_dir() + assert build_dir.name.startswith("qg") + + shutil.rmtree(str(build_dir)) + + +def test_custom_build_dir_no_delete(): + build_under = cur_dir / "build-test" + task = Quingo_task( + bell_qu_fn, "bell", build_under=build_under, delete_build_dir=False + ) + build_dir = task.build_dir + + print(build_dir) + assert task.parent_work_dir == build_under + assert build_dir.exists() + assert build_dir.is_dir() + assert build_dir.name.startswith("qg") + + shutil.rmtree(str(build_dir)) + + +def test_custom_build_dir_with_delete(): + build_under = cur_dir / "build" + task = Quingo_task( + bell_qu_fn, "bell", build_under=build_under, delete_build_dir=True + ) + build_dir = task.build_dir + + print(build_dir) + assert task.parent_work_dir == build_under + assert build_dir.exists() + assert build_dir.is_dir() + assert build_dir.name.startswith("qg") + + del task + + assert not build_dir.exists() diff --git a/unittest/test_compile_cmd.py b/unittest/test_compile_cmd.py index e950c9b9a6deb77a1749f04c9f233e9232947d8f..398ac889b491ec7179460f926071d5d555d79e04 100644 --- a/unittest/test_compile_cmd.py +++ b/unittest/test_compile_cmd.py @@ -25,13 +25,14 @@ class TestCompileCmd: mlir_path = Path(get_mlir_path()) cmd = compose_cl_cmd(task, qasm_fn, mlir_path) cmd_eles = cmd.split() - assert len(cmd_eles) == 10 + assert len(cmd_eles) == 13 assert mlir_path.resolve().samefile(cmd_eles[0].strip('"')) - assert cmd_eles[1] == '"{}"'.format(task.cl_entry_fn.resolve()) - assert cmd_eles[2] == "-I" - assert cmd_eles[4] == "-I" - assert cmd_eles[7] == "--isa=qcis" - assert cmd_eles[8] == "-o" + assert cmd_eles[1] == '-u' + assert cmd_eles[2] == '"{}"'.format(task.cl_entry_fn.resolve()) + assert cmd_eles[3] == "-I" + assert cmd_eles[5] == "-I" + assert cmd_eles[10] == "--isa=qcis" + assert cmd_eles[11] == "-o" # assert Path(qasm_fn).samefile(cmd_eles[8].strip('"')) def test_compile(self): @@ -40,8 +41,8 @@ class TestCompileCmd: qasm_fn = compile(task, ()) with qasm_fn.open("r") as f: lines = f.readlines() - assert lines[0].strip() == "H Q0" - assert lines[2].strip() == "CZ Q0 Q1" + assert lines[0].strip().split() == ["H", "Q0"] + assert lines[1].strip().split() == ["CNOT", "Q0", "Q1"] def test_compile2(self): bell_fn = qu_dir / "bell.qu" @@ -50,8 +51,8 @@ class TestCompileCmd: assert qasm_fn.samefile(unittest_dir / "out_bell.qcis") with qasm_fn.open("r") as f: lines = f.readlines() - assert lines[0].strip() == "H Q0" - assert lines[2].strip() == "CZ Q0 Q1" + assert lines[0].strip().split() == ["H", "Q0"] + assert lines[1].strip().split() == ["CNOT", "Q0", "Q1"] if __name__ == "__main__": diff --git a/unittest/test_execution.py b/unittest/test_execution.py index c24dff4c031f1220e0b4e1201e386cef9d0d6cda..7c346c7587b1672f7282626a1332384cc070b06d 100644 --- a/unittest/test_execution.py +++ b/unittest/test_execution.py @@ -1,9 +1,37 @@ +import pytest from pathlib import Path from quingo import BackendType, Quingo_task, ExeConfig, ExeMode from quingo import call, compile, execute unittest_dir = Path(__file__).parent qu_file = unittest_dir / "test_qu" / "bell.qu" +qcis_file = unittest_dir / "test_qcis" / "bell.qcis" + + +@pytest.fixture( + scope="module", + params=[qcis_file, "H Q0\nCNOT Q0 Q1\nMEASURE Q0\nMEASURE Q1"], +) +def qasm_fn_or_str(request): + return request.param + + +@pytest.fixture( + scope="module", + params=[BackendType.QUANTUM_SIM, BackendType.TEQUILA, BackendType.SYMQC], +) +def backends_for_qcis(request): + return request.param + + +def test_execute(qasm_fn_or_str, backends_for_qcis): + num_shot = 10 + cfg = ExeConfig(ExeMode.SimShots, num_shot) + res = execute(qasm_fn_or_str, backends_for_qcis, cfg) + print(res) + + assert len(res[0]) == 2 + assert len(res[1]) == 10 def test_compile_execute(): diff --git a/unittest/test_qcis/bell_cz.qcis b/unittest/test_qcis/bell_cz.qcis new file mode 100644 index 0000000000000000000000000000000000000000..6bb88f7b37664e8bc0fd92c082e29a0c2c56c2bf --- /dev/null +++ b/unittest/test_qcis/bell_cz.qcis @@ -0,0 +1,6 @@ + H Q14 + H Q15 + CZ Q14 Q15 + H Q15 + M Q14 + M Q15 \ No newline at end of file diff --git a/unittest/test_utils.py b/unittest/test_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..1b69e7064c009dc56278911ab96a1e6f34ee0382 --- /dev/null +++ b/unittest/test_utils.py @@ -0,0 +1,76 @@ +import pytest +import numpy as np +from quingo.utils import shuffle_qubits_in_state + + +@pytest.mark.parametrize( + "old_qubit_order, new_qubit_order, state, expected_state", + [ + ( + ["q0", "q1"], + ["q1", "q0"], + np.array([0, 1, 2, 3]), + np.array([0, 2, 1, 3]), + ), + ( + ["q0", "q1"], + ["q0", "q1"], + np.array([0, 1, 2, 3]), + np.array([0, 1, 2, 3]), + ), + ( + ["q0", "q1", "q2"], + ["q2", "q0", "q1"], + np.array([0, 1, 2, 3, 4, 5, 6, 7]), + np.array([0, 4, 1, 5, 2, 6, 3, 7]), + ), + ( + ["q2", "q0", "q1"], + ["q0", "q1", "q2"], + np.array([0, 4, 1, 5, 2, 6, 3, 7]), + np.array([0, 1, 2, 3, 4, 5, 6, 7]), + ), + ], +) +def test_shuffle_qubits_in_state( + old_qubit_order, new_qubit_order, state, expected_state +): + shuffled_state = shuffle_qubits_in_state(old_qubit_order, new_qubit_order, state) + np.testing.assert_array_equal(shuffled_state, expected_state) + + +@pytest.mark.parametrize( + "old_qubit_order, new_qubit_order", + [ + ([0, 1, 2, 3], [2, 3, 1, 0]), + ([0, 1, 2, 3], [3, 2, 1, 0]), + ([0, 1, 2, 3, 4], [2, 4, 3, 1, 0]), + ([2, 4, 3, 1, 0], [0, 1, 2, 3, 4]), + ([0, 1, 2, 3, 4, 5], [2, 4, 3, 5, 1, 0]), + ([0, 1, 2, 3, 4, 5, 6], [2, 6, 4, 3, 5, 1, 0]), + ([0, 1, 2, 3, 4, 5, 6, 7], [2, 7, 1, 3, 5, 4, 6, 0]), + ], +) +def test_single_value_state(old_qubit_order, new_qubit_order): + + num_qubits = len(old_qubit_order) + + old_name_2_idx_map = {old_qubit_order[i]: i for i in range(num_qubits)} + new_name_2_idx_map = {new_qubit_order[i]: i for i in range(num_qubits)} + + for idx in range(num_qubits): + qubit_name = old_qubit_order[idx] + + state = np.zeros(2**num_qubits) + state[1 << old_name_2_idx_map[qubit_name]] = 1 # pure state, e.g., |0010> + print("state: ", state) + + exp_state = np.zeros(2**num_qubits) + # pure state after shuffling, e.g., |0100> + exp_state[1 << new_name_2_idx_map[qubit_name]] = 1 + print("exp_state: ", exp_state) + + shuffled_state = shuffle_qubits_in_state( + old_qubit_order, new_qubit_order, state + ) + print("shuffled_state: ", shuffled_state)