Coverage for qpdk / cells / derived / transmon_with_resonator_and_probeline.py: 99%

68 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-02 17:50 +0000

1"""Transmons with resonators coupled.""" 

2 

3from __future__ import annotations 

4 

5import uuid 

6from functools import partial 

7 

8import gdsfactory as gf 

9from gdsfactory.component import Component 

10from gdsfactory.typings import ComponentSpec, CrossSectionSpec 

11from klayout.db import DCplxTrans 

12 

13from qpdk.cells.capacitor import plate_capacitor_single 

14from qpdk.cells.resonator import ( 

15 resonator_quarter_wave_bend_start, 

16) 

17from qpdk.cells.waveguides import coupler_straight 

18from qpdk.helper import show_components 

19from qpdk.tech import route_bundle_cpw 

20 

21 

22def _transmon_with_resonator_base( 

23 qubit: ComponentSpec = "double_pad_transmon_with_bbox", 

24 resonator: ComponentSpec = partial( 

25 resonator_quarter_wave_bend_start, length=4000, meanders=6 

26 ), 

27 resonator_meander_start: tuple[float, float] = (-700, -1300), 

28 resonator_length: float = 5000.0, 

29 resonator_meanders: int = 5, 

30 resonator_bend_spec: ComponentSpec = "bend_circular", 

31 resonator_cross_section: CrossSectionSpec = "cpw", 

32 resonator_open_start: bool = False, 

33 resonator_open_end: bool = True, 

34 coupler: ComponentSpec = partial(plate_capacitor_single, width=20, length=394), 

35 qubit_rotation: float = 90, 

36 coupler_port: str = "left_pad", 

37 coupler_offset: tuple[float, float] = (-45, 0), 

38 with_probeline: bool = True, 

39 probeline_coupler: ComponentSpec = coupler_straight, 

40 probeline_coupling_gap: float = 16.0, 

41 probeline_coupling_length: float | None = None, 

42) -> Component: 

43 """Base function for creating a transmon coupled to a resonator, optionally with a probeline. 

44 

45 Args: 

46 qubit: Qubit component. 

47 resonator: Resonator component. 

48 resonator_meander_start: (x, y) position of the start of the resonator meander. 

49 resonator_length: Length of the resonator in µm. 

50 resonator_meanders: Number of meander sections for the resonator. 

51 resonator_bend_spec: Specification for the bend component used in meanders. 

52 resonator_cross_section: Cross-section specification for the resonator. 

53 resonator_open_start: If True, adds an etch section at the start of the resonator. 

54 resonator_open_end: If True, adds an etch section at the end of the resonator. 

55 coupler: Coupler spec. 

56 qubit_rotation: Rotation angle for the qubit in degrees. 

57 coupler_port: Name of the qubit port to position the coupler relative to. 

58 coupler_offset: (x, y) offset for the coupler position. 

59 with_probeline: Whether to include a probeline coupler. 

60 probeline_coupler: Component spec for the probeline coupling section. 

61 probeline_coupling_gap: Gap between the resonator and probeline 

62 waveguides in the coupling section in µm. 

63 probeline_coupling_length: Length of the coupling section in µm. 

64 If None, it will be calculated based on the distance from the 

65 resonator meander start to the coupler position. 

66 

67 Returns: 

68 Component: A gdsfactory component with the transmon, resonator, and optionally a probeline. 

69 

70 Raises: 

71 ValueError: If probeline_coupling_length is not provided and cannot be calculated. 

72 """ 

73 c = Component() 

74 

75 qubit_ref = c << gf.get_component(qubit) 

76 qubit_ref.rotate(qubit_rotation) 

77 coupler_ref = c << gf.get_component(coupler) 

78 

79 # Position coupler close to qubit 

80 coupler_ref.transform( 

81 qubit_ref.ports[coupler_port].dcplx_trans 

82 * DCplxTrans.R180 

83 * DCplxTrans(*coupler_offset) 

84 ) 

85 

86 coupling_o1_port = None 

87 coupling_o2_port = None 

88 

89 if with_probeline: 

90 if probeline_coupling_length is None: 

91 xs = gf.get_cross_section(resonator_cross_section) 

92 radius = getattr(xs, "radius", 100.0) 

93 probeline_coupling_length = ( 

94 abs(resonator_meander_start[0] - coupler_ref.ports["o1"].x) - radius 

95 ) 

96 

97 if probeline_coupling_length <= 0: 

98 raise ValueError( 

99 f"Auto-computed probeline_coupling_length={probeline_coupling_length:.1f} µm is non-positive. " 

100 "Increase the distance between resonator_meander_start and the coupler, or pass an explicit probeline_coupling_length." 

101 ) 

102 

103 # Create probeline coupling. 

104 cs_ref = c << gf.get_component( 

105 probeline_coupler, 

106 gap=probeline_coupling_gap, 

107 length=probeline_coupling_length, 

108 cross_section=resonator_cross_section, 

109 ) 

110 

111 # Position the coupler_straight so that o2 (bottom left) is at 

112 # the resonator_meander_start position. 

113 cs_ref.move(( 

114 resonator_meander_start[0] - cs_ref.ports["o2"].x, 

115 resonator_meander_start[1] - cs_ref.ports["o2"].y, 

116 )) 

117 route_start_port = cs_ref.ports["o3"] 

118 resonator_connect_port = cs_ref.ports["o2"] 

119 coupling_o1_port = cs_ref.ports["o1"] 

120 coupling_o2_port = cs_ref.ports["o4"] 

121 added_length = probeline_coupling_length 

122 else: 

123 # We need a port to start the route from, so we create a dummy port 

124 dummy = gf.Component(name=f"dummy_route_start_{uuid.uuid4().hex[:8]}") 

125 dummy.add_port( 

126 name="o1", 

127 center=(0, 0), 

128 width=10, 

129 orientation=180, 

130 cross_section=resonator_cross_section, 

131 ) 

132 dummy_port = dummy.ports["o1"].copy() 

133 dummy_port.center = resonator_meander_start 

134 dummy_port.orientation = 0 

135 route_start_port = dummy_port 

136 resonator_connect_port = None 

137 added_length = 0.0 

138 

139 # Route from meander start to the plate capacitor 

140 routes = route_bundle_cpw( 

141 component=c, 

142 ports1=[route_start_port], 

143 ports2=[coupler_ref.ports["o1"]], 

144 auto_taper=False, 

145 ) 

146 route = routes[0] 

147 

148 # Create resonator, accounting for both the route and coupling lengths 

149 resonator_ref = c << gf.get_component( 

150 resonator, 

151 length=resonator_length - route.length * c.kcl.dbu - added_length, 

152 meanders=resonator_meanders, 

153 bend_spec=resonator_bend_spec, 

154 cross_section=resonator_cross_section, 

155 open_start=resonator_open_start, 

156 open_end=resonator_open_end, 

157 ) 

158 

159 if with_probeline: 

160 # Connect the resonator's shorted end to the coupler_straight's top-left port. 

161 resonator_ref.connect("o1", resonator_connect_port) 

162 else: 

163 # Connect to the start of the route 

164 resonator_ref.connect("o1", route.instances[0].ports["o1"]) 

165 

166 c.info["qubit_type"] = qubit_ref.cell.info.get("qubit_type") 

167 c.info["resonator_type"] = resonator_ref.cell.info.get("resonator_type") 

168 c.info["coupler_type"] = coupler_ref.cell.info.get("coupler_type") 

169 c.info["length"] = ( 

170 resonator_ref.cell.info.get("length") + route.length * c.kcl.dbu + added_length 

171 ) 

172 

173 c.add_ports(qubit_ref.ports.filter(regex=r"junction")) 

174 if with_probeline and coupling_o1_port: 

175 c.add_port("coupling_o1", port=coupling_o1_port) 

176 c.add_port("coupling_o2", port=coupling_o2_port) 

177 c.add_port( 

178 port=resonator_ref.ports["o1"], 

179 port_type="placement", 

180 ) 

181 return c 

182 

183 

184@gf.cell(tags=("qubits", "transmons", "resonators", "couplers")) 

185def transmon_with_resonator_and_probeline( 

186 qubit: ComponentSpec = "double_pad_transmon_with_bbox", 

187 resonator: ComponentSpec = partial( 

188 resonator_quarter_wave_bend_start, length=4000, meanders=6 

189 ), 

190 resonator_meander_start: tuple[float, float] = (-900, -1200), 

191 resonator_length: float = 5000.0, 

192 resonator_meanders: int = 5, 

193 resonator_bend_spec: ComponentSpec = "bend_circular", 

194 resonator_cross_section: CrossSectionSpec = "cpw", 

195 resonator_open_start: bool = False, 

196 resonator_open_end: bool = True, 

197 coupler: ComponentSpec = partial(plate_capacitor_single, width=20, length=394), 

198 qubit_rotation: float = 90, 

199 coupler_port: str = "left_pad", 

200 coupler_offset: tuple[float, float] = (-45, 0), 

201 probeline_coupler: ComponentSpec = coupler_straight, 

202 probeline_coupling_gap: float = 16.0, 

203 probeline_coupling_length: float | None = None, 

204) -> Component: 

205 """Returns a transmon qubit coupled to a quarter wave resonator and a probeline. 

206 

207 Uses a :func:`~qpdk.cells.waveguides.coupler_straight` to couple the 

208 resonator to a probeline. The coupling section is inserted at the start 

209 of the resonator meander, with one waveguide carrying the resonator signal 

210 and the other providing ports for probeline routing. 

211 

212 Args: 

213 qubit: Qubit component. 

214 resonator: Resonator component. 

215 resonator_meander_start: (x, y) position of the start of the resonator meander. 

216 resonator_length: Length of the resonator in µm. 

217 resonator_meanders: Number of meander sections for the resonator. 

218 resonator_bend_spec: Specification for the bend component used in meanders. 

219 resonator_cross_section: Cross-section specification for the resonator. 

220 resonator_open_start: If True, adds an etch section at the start of the resonator. 

221 resonator_open_end: If True, adds an etch section at the end of the resonator. 

222 coupler: Coupler spec. 

223 qubit_rotation: Rotation angle for the qubit in degrees. 

224 coupler_port: Name of the qubit port to position the coupler relative to. 

225 coupler_offset: (x, y) offset for the coupler position. 

226 probeline_coupler: Component spec for the probeline coupling section. 

227 probeline_coupling_gap: Gap between the resonator and probeline 

228 waveguides in the coupling section in µm. 

229 probeline_coupling_length: Length of the coupling section in µm. 

230 """ 

231 return _transmon_with_resonator_base( 

232 qubit=qubit, 

233 resonator=resonator, 

234 resonator_meander_start=resonator_meander_start, 

235 resonator_length=resonator_length, 

236 resonator_meanders=resonator_meanders, 

237 resonator_bend_spec=resonator_bend_spec, 

238 resonator_cross_section=resonator_cross_section, 

239 resonator_open_start=resonator_open_start, 

240 resonator_open_end=resonator_open_end, 

241 coupler=coupler, 

242 qubit_rotation=qubit_rotation, 

243 coupler_port=coupler_port, 

244 coupler_offset=coupler_offset, 

245 with_probeline=True, 

246 probeline_coupler=probeline_coupler, 

247 probeline_coupling_gap=probeline_coupling_gap, 

248 probeline_coupling_length=probeline_coupling_length, 

249 ) 

250 

251 

252@gf.cell(tags=("qubits", "transmons", "resonators")) 

253def transmon_with_resonator( 

254 qubit: ComponentSpec = "double_pad_transmon_with_bbox", 

255 resonator: ComponentSpec = partial( 

256 resonator_quarter_wave_bend_start, length=4000, meanders=6 

257 ), 

258 resonator_meander_start: tuple[float, float] = (-900, -1200), 

259 resonator_length: float = 5000.0, 

260 resonator_meanders: int = 5, 

261 resonator_bend_spec: ComponentSpec = "bend_circular", 

262 resonator_cross_section: CrossSectionSpec = "cpw", 

263 resonator_open_start: bool = False, 

264 resonator_open_end: bool = True, 

265 coupler: ComponentSpec = partial(plate_capacitor_single, width=20, length=394), 

266 qubit_rotation: float = 90, 

267 coupler_port: str = "left_pad", 

268 coupler_offset: tuple[float, float] = (-45, 0), 

269) -> Component: 

270 """Returns a transmon qubit coupled to a quarter wave resonator. 

271 

272 Args: 

273 qubit: Qubit component. 

274 resonator: Resonator component. 

275 resonator_meander_start: (x, y) position of the start of the resonator meander. 

276 resonator_length: Length of the resonator in µm. 

277 resonator_meanders: Number of meander sections for the resonator. 

278 resonator_bend_spec: Specification for the bend component used in meanders. 

279 resonator_cross_section: Cross-section specification for the resonator. 

280 resonator_open_start: If True, adds an etch section at the start of the resonator. 

281 resonator_open_end: If True, adds an etch section at the end of the resonator. 

282 coupler: Coupler spec. 

283 qubit_rotation: Rotation angle for the qubit in degrees. 

284 coupler_port: Name of the qubit port to position the coupler relative to. 

285 coupler_offset: (x, y) offset for the coupler position. 

286 """ 

287 return _transmon_with_resonator_base( 

288 qubit=qubit, 

289 resonator=resonator, 

290 resonator_meander_start=resonator_meander_start, 

291 resonator_length=resonator_length, 

292 resonator_meanders=resonator_meanders, 

293 resonator_bend_spec=resonator_bend_spec, 

294 resonator_cross_section=resonator_cross_section, 

295 resonator_open_start=resonator_open_start, 

296 resonator_open_end=resonator_open_end, 

297 coupler=coupler, 

298 qubit_rotation=qubit_rotation, 

299 coupler_port=coupler_port, 

300 coupler_offset=coupler_offset, 

301 with_probeline=False, 

302 ) 

303 

304 

305# Create specific functions as partials of the general function 

306double_pad_transmon_with_resonator_and_probeline = partial( 

307 transmon_with_resonator_and_probeline, 

308 qubit="double_pad_transmon_with_bbox", 

309 coupler=partial(plate_capacitor_single, width=20, length=394), 

310 qubit_rotation=90, 

311 coupler_port="left_pad", 

312 coupler_offset=(-45, 0), 

313) 

314 

315flipmon_with_resonator_and_probeline = partial( 

316 transmon_with_resonator_and_probeline, 

317 qubit="flipmon_with_bbox", 

318 coupler=partial(plate_capacitor_single, width=10, length=58), 

319 qubit_rotation=-90, 

320 coupler_port="outer_ring_outside", 

321 coupler_offset=(-10, 0), 

322) 

323 

324double_pad_transmon_with_resonator = partial( 

325 transmon_with_resonator, 

326 qubit="double_pad_transmon_with_bbox", 

327 coupler=partial(plate_capacitor_single, width=20, length=394), 

328 qubit_rotation=90, 

329 coupler_port="left_pad", 

330 coupler_offset=(-45, 0), 

331) 

332 

333flipmon_with_resonator = partial( 

334 transmon_with_resonator, 

335 qubit="flipmon_with_bbox", 

336 coupler=partial(plate_capacitor_single, width=10, length=58), 

337 qubit_rotation=-90, 

338 coupler_port="outer_ring_outside", 

339 coupler_offset=(-10, 0), 

340) 

341 

342 

343if __name__ == "__main__": 

344 show_components( 

345 transmon_with_resonator_and_probeline, 

346 double_pad_transmon_with_resonator_and_probeline, 

347 flipmon_with_resonator_and_probeline, 

348 transmon_with_resonator, 

349 double_pad_transmon_with_resonator, 

350 flipmon_with_resonator, 

351 )