Skip to content

Commit c66ff37

Browse files
committed
add pulse support
1 parent 9383dee commit c66ff37

File tree

8 files changed

+340
-6
lines changed

8 files changed

+340
-6
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,4 @@ cython_debug/
193193
.cursorignore
194194
.cursorindexingignore
195195
uv.lock
196+
local_docs/

examples/pulse_demo.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import sys
2+
import os
3+
import getpass
4+
import matplotlib.pyplot as plt
5+
# Add the directory containing your module to Python's search path
6+
module_path = ".."
7+
sys.path.insert(0, module_path)
8+
9+
from tyxonq import Circuit, Param, gates, waveforms
10+
from tyxonq.cloud import apis
11+
import re
12+
13+
shots_const = 1000
14+
15+
token = getpass.getpass("Enter your token: ")
16+
apis.set_token(token)
17+
apis.set_provider("tencent")
18+
19+
ds = apis.list_devices()
20+
print(ds)
21+
22+
23+
24+
# TQASM 0.2;
25+
# QREG a[1];
26+
# defcal rabi_test a {
27+
# frame drive_frame = newframe(a);
28+
# play(drive_frame, cosine_drag($formatted_t, 0.2, 0.0, 0.0)); }
29+
# rabi_test a[0];
30+
# MEASZ a[0];
31+
32+
def gen_parametric_waveform_circuit(t):
33+
qc = Circuit(1)
34+
35+
param0 = Param("a")
36+
37+
builder = qc.calibrate("rabi_test", [param0])
38+
builder.new_frame("drive_frame", param0)
39+
builder.play("drive_frame", waveforms.CosineDrag(t, 0.2, 0.0, 0.0))
40+
print("defcal rabi_test , instructions: ")
41+
for instruction in builder.instructions:
42+
print(instruction)
43+
44+
# 这段的代码设计有问题,!
45+
builder.build() # 注册 calibration
46+
qc.add_calibration('rabi_test', ['q[0]']) # 添加调用
47+
48+
tqasm_code = qc.to_tqasm()
49+
50+
print(tqasm_code)
51+
return qc
52+
53+
def run_circuit(qc):
54+
device_name = "tianji_m2"
55+
t = apis.submit_task(
56+
circuit=qc,
57+
shots=shots_const,
58+
device=device_name,
59+
enable_qos_gate_decomposition=False,
60+
enable_qos_qubit_mapping=False,
61+
)
62+
print(t)
63+
rf = t.results()
64+
return rf
65+
66+
def exp_rabi():
67+
result_lst = []
68+
for t in range(1, 4, 2):
69+
qc = gen_parametric_waveform_circuit(t)
70+
result = run_circuit(qc)
71+
result['duration'] = t
72+
result_lst.append(result)
73+
return result_lst
74+
75+
76+
77+
def draw_rabi(result_lst):
78+
data = {
79+
'duration': [],
80+
'0': [],
81+
'1': []
82+
}
83+
84+
for result in result_lst:
85+
data['0'].append(int(result['0']) / shots_const)
86+
data['1'].append(int(result['1']) / shots_const)
87+
data['duration'].append(result['duration'])
88+
89+
90+
plt.figure(figsize=(10,6))
91+
plt.plot(data['duration'], data['0'], 'b-o', label='State |0>')
92+
plt.plot(data['duration'], data['1'], 'r--s', label='State |1>')
93+
94+
95+
plt.title('Rabi Oscillation Experiment')
96+
plt.xlabel('Duration (dt)')
97+
plt.ylabel('Probability')
98+
plt.grid(alpha=0.3)
99+
plt.legend()
100+
plt.tight_layout()
101+
102+
103+
plt.savefig('rabi.png', dpi=300)
104+
plt.show()
105+
106+
107+
def run_rabi():
108+
qc =gen_parametric_waveform_circuit(1)
109+
print(qc)
110+
print("-------------------------------- QC TQASM --------------------------------")
111+
print(qc.to_tqasm())
112+
print("-------------------------------- QC TQASM END --------------------------------")
113+
114+
result = run_circuit(qc)
115+
# data: , duration: 1, 3
116+
# 1: {'0': 970, '1': 30, 'duration': 1}
117+
# 3: {'0': 979, '1': 21, 'duration': 3}
118+
119+
120+
{'0': 970, '1': 30, 'duration': 1}
121+
{'0': 979, '1': 21, 'duration': 3}

pyproject.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
[project]
22
name = "tyxonq"
3-
version = "0.1.1"
3+
version = "0.3.0"
44
description = "Quantum computing framework with multi-backend support"
55
readme = "README.md"
6-
requires-python = ">=3.11"
6+
requires-python = ">=3.10,<3.13"
77
authors = [
88
{ name = "Albert Lee", email = "code@quregenai.com" },
99
]
@@ -36,6 +36,7 @@ dependencies = [
3636
"tensorflow>=2.19.0",
3737
"tensornetwork>=0.4.6",
3838
"torch>=2.7.1",
39+
"matplotlib",
3940
]
4041

4142
[project.optional-dependencies]

src/tyxonq/__init__.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
__version__ = "0.1.1"
2-
__author__ = "TensorCircuit Authors"
3-
__creator__ = "refraction-ray"
1+
__version__ = "0.3.0"
2+
__author__ = "TyxonQ Authors"
3+
__creator__ = "TyxonQ Authors"
44

55
from .utils import gpu_memory_share
66

@@ -24,8 +24,9 @@
2424
) # prerun of set hooks
2525
from . import gates
2626
from . import basecircuit
27+
from . import waveforms
2728
from .gates import Gate
28-
from .circuit import Circuit, expectation
29+
from .circuit import Circuit, expectation, Param
2930
from .mpscircuit import MPSCircuit
3031
from .densitymatrix import DMCircuit as DMCircuit_reference
3132
from .densitymatrix import DMCircuit2

src/tyxonq/abstractcircuit.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,15 @@ class AbstractCircuit:
7979
mpogates = mpogates
8080
gate_aliases = gate_aliases
8181

82+
def __init__(self, nqubits: int, *args, **kwargs):
83+
self._nqubits = nqubits
84+
self._qir = []
85+
self._extra_qir = []
86+
self.inputs = None
87+
self.circuit_param = {}
88+
self.is_mps = False
89+
90+
8291
def apply_general_gate(
8392
self,
8493
gate: Union[Gate, QuOperator],
@@ -114,6 +123,7 @@ def apply(self: "AbstractCircuit", *index: int, **vars: Any) -> None:
114123
gate_dict = {
115124
"gatef": gatef,
116125
"index": index,
126+
"target": list(index),
117127
"name": localname,
118128
"split": split,
119129
"mpo": mpo,
@@ -785,6 +795,7 @@ def to_openqasm(self, **kws: Any) -> str:
785795
"""
786796
#return self.to_qiskit(enable_instruction=True).qasm(**kws) # type: ignore
787797
"for new version of qiskit, use qiskit.qasm2.dump"
798+
788799
from qiskit.qasm2 import dump, dumps
789800
q = self.to_qiskit(enable_instruction=True)
790801
return dumps(q)

src/tyxonq/basecircuit.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ class BaseCircuit(AbstractCircuit):
3939
split: Optional[Dict[str, Any]]
4040

4141
is_mps = False
42+
43+
def __init__(self, nqubits: int, *args, **kwargs):
44+
super().__init__(nqubits, *args, **kwargs)
4245

4346
@staticmethod
4447
def all_zero_nodes(n: int, d: int = 2, prefix: str = "qb-") -> List[tn.Node]:

src/tyxonq/circuit.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ def __init__(
6060
``max_singular_values`` and ``max_truncation_err``.
6161
:type split: Optional[Dict[str, Any]]
6262
"""
63+
64+
super().__init__(
65+
nqubits=nqubits, inputs=inputs, mps_inputs=mps_inputs, split=split
66+
)
67+
self.calibrations = []
68+
self.calibration_invokes = []
69+
6370
self.inputs = inputs
6471
self.mps_inputs = mps_inputs
6572
self.split = split
@@ -112,6 +119,55 @@ def __init__(
112119
self._qir: List[Dict[str, Any]] = []
113120
self._extra_qir: List[Dict[str, Any]] = []
114121

122+
123+
def def_calibration(
124+
self, name: str, parameters: List[str], instructions: List[Dict]
125+
) -> None:
126+
self.calibrations.append({
127+
"name": name,
128+
"parameters": parameters,
129+
"instructions": instructions
130+
})
131+
132+
def add_calibration(
133+
self, name: str, parameters: List[str]
134+
) -> None:
135+
self.calibration_invokes.append({
136+
"name": name,
137+
"parameters": parameters
138+
})
139+
140+
def to_tqasm(self) -> str:
141+
qasm_lines = []
142+
qasm_lines.append("TQASM 0.2;")
143+
qasm_lines.append(f"QREG q[{self._nqubits}];")
144+
145+
for gate in self._qir:
146+
gname = gate["name"]
147+
targets = ", ".join(f"q[{i}]" for i in gate["target"])
148+
qasm_lines.append(f"{gname} {targets};")
149+
150+
for cal in getattr(self, "calibrations", []):
151+
pname = ", ".join(cal["parameters"])
152+
qasm_lines.append(f"\ndefcal {cal['name']} {pname} {{")
153+
for inst in cal["instructions"]:
154+
if inst["type"] == "frame":
155+
qasm_lines.append(f" frame {inst['frame']} = newframe({inst['qubit']});")
156+
elif inst["type"] == "play":
157+
args_str = ", ".join(str(x) for x in inst["args"])
158+
wf_type = inst["waveform_type"]
159+
qasm_lines.append(f" play({inst['frame']}, {wf_type}({args_str}));")
160+
qasm_lines.append("}")
161+
162+
for cal in getattr(self, "calibration_invokes", []):
163+
pname = ", ".join(cal["parameters"])
164+
qasm_lines.append(f"\n {cal['name']} {pname};")
165+
166+
return "\n".join(qasm_lines)
167+
168+
def calibrate(self, name: str, parameters: List["Param"]) -> "DefcalBuilder":
169+
return DefcalBuilder(self, name, parameters)
170+
115171
def replace_mps_inputs(self, mps_inputs: QuOperator) -> None:
116172
"""
117173
Replace the input state in MPS representation while keep the circuit structure unchanged.
@@ -998,3 +1054,46 @@ def expectation(
9981054
else:
9991055
den = 1.0
10001056
return num / den
1057+
1058+
class Param:
1059+
def __init__(self, name: str):
1060+
self.name = name
1061+
1062+
class DefcalBuilder:
1063+
def __init__(self, circuit, name: str, parameters: List["Param"]):
1064+
self.circuit = circuit
1065+
self.name = name
1066+
self.parameters = parameters
1067+
self.instructions = []
1068+
1069+
def new_frame(self, frame_name: str, param: "Param"):
1070+
self.instructions.append({
1071+
"type": "frame",
1072+
"frame": frame_name,
1073+
"qubit": param.name,
1074+
})
1075+
return self
1076+
1077+
def play(self, frame_name: str, waveform: Any, start_time: int = None):
1078+
if not hasattr(waveform, "__dataclass_fields__"):
1079+
raise TypeError("Unsupported waveform type")
1080+
1081+
waveform_type = waveform.qasm_name()
1082+
args = waveform.to_args()
1083+
if start_time is not None:
1084+
args = [start_time] + args
1085+
1086+
self.instructions.append({
1087+
"type": "play",
1088+
"frame": frame_name,
1089+
"waveform_type": waveform_type,
1090+
"args": args,
1091+
})
1092+
return self
1093+
1094+
def build(self):
1095+
self.circuit.def_calibration(
1096+
name=self.name,
1097+
parameters=[p.name for p in self.parameters],
1098+
instructions=self.instructions,
1099+
)

0 commit comments

Comments
 (0)