Skip to content

Commit df229f4

Browse files
committed
revise to cloud 3rd phase.
1 parent e160cc1 commit df229f4

File tree

8 files changed

+222
-62
lines changed

8 files changed

+222
-62
lines changed

tensorcircuit/circuit.py

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -128,23 +128,23 @@ def def_calibration(
128128
})
129129

130130
def add_calibration(
131-
self, name: str, parameters: List[str]
131+
self, builder: DefcalBuilder, parameters: List[str]
132132
) -> None:
133133
self.calibration_invokes.append({
134-
"name": name,
135-
"parameters": parameters
134+
"name": builder.name,
135+
"parameters": parameters,
136+
"pos": len(self._qir)
136137
})
137138

138139

139-
def to_tqasm(self) -> str:
140+
def to_tqasm(self, pragma: str) -> str:
140141
qasm_lines = []
142+
if pragma:
143+
qasm_lines.append(pragma)
144+
141145
qasm_lines.append("TQASM 0.2;")
142-
qasm_lines.append(f"QREG q[{self._nqubits}];")
143146

144-
for gate in self._qir:
145-
gname = gate["name"]
146-
targets = ", ".join(f"q[{i}]" for i in gate["target"])
147-
qasm_lines.append(f"{gname} {targets};")
147+
qasm_lines.append(f"QREG q[{self._nqubits}];")
148148

149149
for cal in getattr(self, "calibrations", []):
150150
pname = ", ".join(cal["parameters"])
@@ -158,12 +158,42 @@ def to_tqasm(self) -> str:
158158
qasm_lines.append(f" play({inst['frame']}, {wf_type}({args_str}));")
159159
qasm_lines.append("}")
160160

161+
162+
# 先把 calibration_invokes 按 pos 分组,并保留同 pos 内的插入顺序
163+
from collections import defaultdict
164+
cals_by_pos = defaultdict(list)
161165
for cal in getattr(self, "calibration_invokes", []):
162-
pname = ", ".join(cal["parameters"])
163-
qasm_lines.append(f"\n {cal['name']} {pname};")
166+
# pos 记录的是加入时的 len(self._qir)
167+
pos = cal.get("pos", len(self._qir))
168+
cals_by_pos[pos].append(cal)
169+
170+
# 交错输出:在第 i 个门之前输出所有 pos == i 的校准
171+
for i, gate in enumerate(self._qir):
172+
for cal in cals_by_pos.get(i, []):
173+
# print(cal)
174+
pname = ", ".join(cal.get("parameters", []))
175+
qasm_lines.append(f"{cal['name']} {pname};")
176+
177+
# print(gate)
178+
gname = gate["name"]
179+
gname = gname.upper()
180+
if gname == "CNOT":
181+
gname = "CX"
182+
targets = ", ".join(f"q[{idx}]" for idx in gate["index"])
183+
if (gname == "RX") or (gname == "RY") or (gname == "RZ"):
184+
theta = gate["parameters"]["theta"]
185+
qasm_lines.append(f"{gname} ({theta}) {targets};")
186+
else:
187+
qasm_lines.append(f"{gname} {targets};")
188+
189+
# 收尾:把 pos == len(self._qir) 的校准放在最后
190+
for cal in cals_by_pos.get(len(self._qir), []):
191+
# print(cal)
192+
pname = ", ".join(cal.get("parameters", []))
193+
qasm_lines.append(f"{cal['name']} {pname};")
194+
195+
return ("\n".join(qasm_lines))
164196

165-
return "\n".join(qasm_lines)
166-
167197
def calibrate(self, name: str, parameters: List["Param"]) -> "DefcalBuilder":
168198
return DefcalBuilder(self, name, parameters)
169199

@@ -1057,7 +1087,11 @@ def expectation(
10571087
class Param:
10581088
def __init__(self, name: str):
10591089
self.name = name
1060-
1090+
1091+
class Frame:
1092+
def __init__(self, name: str):
1093+
self.name = name
1094+
10611095
class DefcalBuilder:
10621096
def __init__(self, circuit, name: str, parameters: List["Param"]):
10631097
self.circuit = circuit
@@ -1066,14 +1100,15 @@ def __init__(self, circuit, name: str, parameters: List["Param"]):
10661100
self.instructions = []
10671101

10681102
def new_frame(self, frame_name: str, param: "Param"):
1103+
frame = Frame(frame_name)
10691104
self.instructions.append({
10701105
"type": "frame",
1071-
"frame": frame_name,
1106+
"frame": frame,
10721107
"qubit": param.name,
10731108
})
1074-
return self
1109+
return frame
10751110

1076-
def play(self, frame_name: str, waveform: Any, start_time: int = None):
1111+
def play(self, frame: Frame, waveform: Any, start_time: int = None):
10771112
if not hasattr(waveform, "__dataclass_fields__"):
10781113
raise TypeError("Unsupported waveform type")
10791114

@@ -1084,7 +1119,7 @@ def play(self, frame_name: str, waveform: Any, start_time: int = None):
10841119

10851120
self.instructions.append({
10861121
"type": "play",
1087-
"frame": frame_name,
1122+
"frame": frame.name,
10881123
"waveform_type": waveform_type,
10891124
"args": args,
10901125
})

tensorcircuit/cloud/abstraction.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,57 @@ def resubmit(self) -> "Task":
380380
from .apis import resubmit_task
381381

382382
return resubmit_task(self)
383+
384+
@partial(arg_alias, alias_dict={"format": ["format_"], "blocked": ["wait"]})
385+
def m_results(
386+
self,
387+
format: Optional[str] = None,
388+
blocked: bool = True,
389+
mitigated: bool = False,
390+
calibriation_options: Optional[Dict[str, Any]] = None,
391+
readout_mit: Optional[rem.ReadoutMit] = None,
392+
mitigation_options: Optional[Dict[str, Any]] = None,
393+
) -> counts.ct:
394+
"""
395+
get task results of the qjob
396+
397+
:param format: unsupported now, defaults to None, which is "count_dict_bin"
398+
:type format: Optional[str], optional
399+
:param blocked: whether blocked to wait until the result is returned, defaults to False,
400+
which raise error when the task is unfinished
401+
:type blocked: bool, optional
402+
:param mitigated: whether enable readout error mitigation, defaults to False
403+
:type mitigated: bool, optional
404+
:param calibriation_options: option dict for ``ReadoutMit.cals_from_system``,
405+
defaults to None
406+
:type calibriation_options: Optional[Dict[str, Any]], optional
407+
:param readout_mit: if given, directly use the calibriation info on ``readout_mit``,
408+
defaults to None
409+
:type readout_mit: Optional[rem.ReadoutMit], optional
410+
:param mitigation_options: option dict for ``ReadoutMit.apply_correction``, defaults to None
411+
:type mitigation_options: Optional[Dict[str, Any]], optional
412+
:return: count dict results
413+
:rtype: Any
414+
"""
415+
if not blocked:
416+
s = self.state()
417+
if s != "completed":
418+
raise TaskUnfinished(self.id_, s)
419+
r = self.details()["multi_results"]
420+
r = counts.sort_count(r) # type: ignore
421+
else:
422+
s = self.state()
423+
tries = 0
424+
while s != "completed":
425+
if s in ["failed"]:
426+
err = self.details().get("err", "")
427+
raise TaskFailed(self.id_, s, err)
428+
time.sleep(0.5 + tries / 10)
429+
tries += 1
430+
s = self.state()
431+
r = self.m_results(format=format, blocked=False, mitigated=False)
432+
433+
return r # type: ignore
383434

384435
@partial(arg_alias, alias_dict={"format": ["format_"], "blocked": ["wait"]})
385436
def results(

tensorcircuit/cloud/tencent.py

Lines changed: 92 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,84 @@
1919
logger = logging.getLogger(__name__)
2020

2121

22+
class Topology:
23+
def __init__(self, device: Device) -> None:
24+
topology = device.topology()
25+
26+
self._qubits = list(set(num for row in topology for num in row))
27+
self._qubit_pairs = topology
28+
29+
self._used_chip_qubits = []
30+
self._used_user_qubits = []
31+
self._used_user_pairs = []
32+
33+
def map_qubits(self, chip_addrs: Optional[List[int]], user_addrs: List[int] = None) -> None:
34+
"""
35+
Map chip address to user address, and remove other unused chip addresses.
36+
"""
37+
if user_addrs is None:
38+
user_addrs = range(len(chip_addrs))
39+
if len(user_addrs) != len(chip_addrs):
40+
raise ValueError("user_addrs and chip_addrs should have the same length")
41+
for addr in chip_addrs:
42+
if addr not in self._qubits:
43+
raise ValueError(f"chip_addr {addr} not in the device")
44+
45+
self._used_chip_qubits = chip_addrs
46+
self._used_user_qubits = user_addrs
47+
self._used_user_pairs = [e for e in self._qubit_pairs if e[0] in chip_addrs and e[1] in user_addrs]
48+
49+
def map_qubit(self, chip_addr: int, user_addr: int) -> None:
50+
if chip_addr not in self._qubits:
51+
raise ValueError(f"chip_addr {chip_addr} not in the device")
52+
try:
53+
subscript = self._used_chip_qubits.index(chip_addr)
54+
if self._used_user_qubits[subscript] != user_addr:
55+
raise ValueError(f"chip_addr {chip_addr} is already mapped to user_addr {self._used_user_qubits[subscript]}")
56+
except ValueError: # not found, so add it.
57+
self._used_chip_qubits.append(chip_addr)
58+
self._used_user_qubits.append(user_addr)
59+
return
60+
61+
def pair_qubit(self, user_addr1: int, user_addr2: int, dual: bool = True, add_remove: bool = True) -> None:
62+
original_pair = (user_addr1, user_addr2)
63+
64+
def update_pairs(user_addr1: int, user_addr2: int, add_remove: bool = True):
65+
if add_remove:
66+
if original_pair in getattr(self, "_used_user_pairs", []):
67+
return
68+
self._qubit_mapping = getattr(self, "_used_user_pairs", []) + [original_pair]
69+
else:
70+
try:
71+
self._qubit_mapping = getattr(self, "_used_user_pairs", []) - [original_pair]
72+
except ValueError:
73+
raise ValueError(f"Qubit pair {user_addr1}-{user_addr2} does not exist to remove")
74+
update_pairs(user_addr1, user_addr2, add_remove)
75+
if dual:
76+
update_pairs(user_addr2, user_addr1, add_remove)
77+
return
78+
79+
def pragmam(self) -> str:
80+
lines = []
81+
if self._used_chip_qubits == [] or self._used_user_qubits == []:
82+
return None
83+
if len(self._used_user_pairs) == 0:
84+
raise ValueError("No qubit pairs are defined, please use pair_qubit to define qubit pairs")
85+
86+
pragma = "#pragma qubits.mapping ["
87+
for c, u in zip(self._used_chip_qubits, self._used_user_qubits):
88+
pragma += f"[{u}, {c}], "
89+
pragma = pragma[:-2] + "]"
90+
lines.append(pragma)
91+
92+
pragma = "#pragma qubits.coupling []"
93+
for u1, u2 in getattr(self, "_used_user_pairs", []):
94+
pragma += f"[{u1}, {u2}], "
95+
pragma = pragma[:-2] + "]"
96+
lines.append(pragma)
97+
98+
return "\n".join(lines)
99+
22100
def tencent_headers(token: Optional[str] = None) -> Dict[str, str]:
23101
if token is None:
24102
token = "ANY;0"
@@ -133,6 +211,7 @@ def submit_task(
133211
enable_qos_gate_decomposition: bool = True,
134212
enable_qos_initial_mapping: bool = False,
135213
qos_dry_run: bool = False,
214+
topology: Optional[Topology] = None,
136215
**kws: Any
137216
) -> List[Task]:
138217
"""
@@ -211,10 +290,14 @@ def c2qasm(c: Any, compiling: bool) -> str:
211290
else:
212291
if isinstance(c, QuantumCircuit):
213292
s = c.qasm()
293+
lang = "OPENQASM"
214294
# nq = c.num_qubits
215295
else:
216-
s = c.to_tqasm()
217-
print(s)
296+
prag = None
297+
if topology is not None:
298+
prag = topology.praggam()
299+
s = c.to_tqasm(prag)
300+
lang = "TQASM"
218301
#s = c.to_openqasm()
219302
# nq = c._nqubits
220303
# s = _free_pi(s) # tQuk translation now supports this
@@ -227,13 +310,12 @@ def c2qasm(c: Any, compiling: bool) -> str:
227310
# slist.append("")
228311
# s = "\n".join(slist)
229312
s = _replace_rz_to_st(s)
230-
return s # type: ignore
313+
return s, lang # type: ignore
231314

232315
if is_sequence(circuit):
233316
source = [c2qasm(c, compiling) for c in circuit] # type: ignore
234317
else:
235-
source = c2qasm(circuit, compiling)
236-
lang = "OPENQASM"
318+
source, lang = c2qasm(circuit, compiling)
237319

238320
if len(device.name.split("?")) > 1:
239321
device_str = device.name
@@ -363,6 +445,10 @@ def get_task_details(
363445
r["task"]["results"] = r["task"]["result"]["counts"]
364446
else:
365447
r["task"]["results"] = r["task"]["result"]
448+
if "multi_results" in r["task"]:
449+
for i, res in enumerate(r["task"]["multi_results"]):
450+
if "counts" in res:
451+
r["task"]["multi_results"][i] = counts.sort_count(res["counts"])
366452
if "optimization" in r["task"]:
367453
if (
368454
"pairs" in r["task"]["optimization"]
@@ -413,4 +499,5 @@ def get_task_details(
413499
'101': 135,
414500
'110': 128,
415501
'111': 131}
502+
'multi_results': [{'000': 123, '001': 126, ...}, {...}, ...]
416503
"""

tests/01_test_gate.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,5 @@ def run_circuit(qc):
4646
print(rf)
4747

4848
qc = gen_gate_circuit(1.0)
49-
result = run_circuit(qc)
49+
result = run_circuit(qc)
50+
print(result)

tests/02_test_param_pulse.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,5 @@ def run_circuit(qc):
5656
print(rf)
5757

5858
qc = gen_parametric_waveform_circuit(1.0)
59-
result = run_circuit(qc)
59+
result = run_circuit(qc)
60+
print(result)

tests/03_test_gate_pulse_mix.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,5 @@ def run_circuit(qc):
5757
print(rf)
5858

5959
qc = gen_gate_pulse_mix_circuit(1.0)
60-
result = run_circuit(qc)
60+
result = run_circuit(qc)
61+
print(result)

0 commit comments

Comments
 (0)