Skip to content

Commit dd83163

Browse files
authored
Merge pull request #175 from GenericP3rson/nlocal
NLocal Circuits
2 parents b39ac31 + e15d756 commit dd83163

File tree

3 files changed

+366
-0
lines changed

3 files changed

+366
-0
lines changed

torchquantum/layer/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,5 @@
2323
"""
2424

2525
from .layers import *
26+
from .nlocal import *
2627
from .n_local import *

torchquantum/layer/layers.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"RandomLayer",
4343
"RandomLayerAllTypes",
4444
"Op1QAllLayer",
45+
"RandomOp1All",
4546
"Op2QAllLayer",
4647
"Op2QButterflyLayer",
4748
"Op2QDenseLayer",
@@ -227,6 +228,46 @@ def forward(self, q_device: tq.QuantumDevice):
227228
self.op(q_device, wires=[self.n_gate - 1, 0])
228229

229230

231+
class RandomOp1All(tq.QuantumModule):
232+
def __init__(
233+
self, n_wires: int, op_types=(tq.RX, tq.RY, tq.RZ), op_ratios=None, seed=None
234+
):
235+
"""Layer adding a random gate to all wires
236+
237+
Params:
238+
n_wires (int): number of wires/gates in integer format
239+
op_types (Iterable): single-wire gates to select from in iterable format
240+
op_ratios (Iterable): probabilities to select each gate option in iterable format
241+
seed (int): random seed in integer format
242+
"""
243+
super().__init__()
244+
self.n_wires = n_wires
245+
self.op_types = op_types
246+
self.op_ratios = op_ratios
247+
self.seed = seed
248+
self.gate_all = nn.ModuleList()
249+
if seed is not None:
250+
np.random.seed(seed)
251+
self.build_random_layer()
252+
253+
def build_random_layer(self):
254+
for k in range(self.n_wires):
255+
op = np.random.choice(self.op_types, p=self.op_ratios)
256+
self.gate_all.append(op())
257+
258+
@tq.static_support
259+
def forward(self, q_device: tq.QuantumDevice, x):
260+
# op on all wires, assert the number of gate is the same as the number
261+
# of wires in the device.
262+
assert self.n_gate == q_device.n_wires, (
263+
f"Number of gates ({self.n_wires}) is different from number "
264+
f"of wires ({q_device.n_wires})!"
265+
)
266+
267+
for k in range(self.n_wires):
268+
self.gate_all[k](q_device, wires=k, params=x[:, k])
269+
270+
230271
class RandomLayer(tq.QuantumModule):
231272
"""
232273
Quantum module that represents a random layer of quantum operations applied to specified wires.

torchquantum/layer/nlocal.py

Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
"""
2+
MIT License
3+
4+
Copyright (c) 2020-present TorchQuantum Authors
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in all
14+
copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
SOFTWARE.
23+
"""
24+
25+
26+
import torch
27+
import torchquantum as tq
28+
from torchquantum.layer.layers import (
29+
LayerTemplate0,
30+
Op1QAllLayer,
31+
Op2QAllLayer,
32+
RandomOp1All,
33+
)
34+
35+
__all__ = [
36+
"NLocal",
37+
"TwoLocal",
38+
"ExcitationPreserving",
39+
"EfficientSU2",
40+
"RealAmplitudes",
41+
"PauliTwoDesign",
42+
]
43+
44+
45+
class NLocal(LayerTemplate0):
46+
"""Layer Template for a NLocal Class
47+
48+
Args:
49+
rotation_ops (list): gates for the rotation layer as a list of torchquantum operations
50+
entanglement_ops (list): gates for the entanglement layer as a list of torchquantum operations
51+
arch (dict): circuit architecture in a dictionary format
52+
rotation_layer (torchquantum.QuantumModule): type of rotation layer in a torchquantum.QuantumModule format
53+
entanglement_layer (torchquantum.QuantumModule): type of entanglement layer in a torchquantum.QuantumModule format
54+
reps (int): number of reptitions of the rotation and entanglement layers in a integer format
55+
rotation_layer_params (dict): additional parameters for the rotation layer in a dictionary format
56+
entanglement_layer_params (dict): additional parameters for the entanglement layer in a dictionary format
57+
initial_circuit (torchquantum.QuantumModule): initial gates or layer in a QuantumModule format
58+
skip_final_rotation_layer (bool): whether or not to add the final rotation layer as a boolean
59+
"""
60+
61+
def __init__(
62+
self,
63+
rotation_ops: list = None,
64+
entanglement_ops: list = None,
65+
arch: dict = None,
66+
rotation_layer: tq.QuantumModule = Op1QAllLayer,
67+
entanglement_layer: tq.QuantumModule = Op2QAllLayer,
68+
reps: int = 1,
69+
rotation_layer_params: dict = {},
70+
entanglement_layer_params: dict = {},
71+
initial_circuit: tq.QuantumModule = None,
72+
skip_final_rotation_layer: bool = False,
73+
):
74+
# rotation block options
75+
self.rotation_ops = rotation_ops
76+
self.rotation_layer = rotation_layer
77+
self.rotation_layer_params = rotation_layer_params
78+
79+
# entanglement block options
80+
self.entanglement_ops = entanglement_ops
81+
self.entanglement_layer = entanglement_layer
82+
self.entanglement_layer_params = entanglement_layer_params
83+
84+
# extra parameters
85+
self.initial_circuit = initial_circuit
86+
self.skip_final_rotation_layer = skip_final_rotation_layer
87+
self.reps = reps
88+
89+
# initialize the LayerTemplate0
90+
super().__init__(arch)
91+
92+
def build_initial_layer(self):
93+
"""Build the initial layer"""
94+
return self.initial_circuit
95+
96+
def build_rotation_block(self):
97+
"""Build rotation block"""
98+
rotation_layers = []
99+
for rot in self.rotation_ops:
100+
rotation_layers.append(
101+
self.rotation_layer(
102+
op=rot, n_wires=self.n_wires, **self.rotation_layer_params
103+
)
104+
)
105+
return rotation_layers
106+
107+
def build_entanglement_block(self):
108+
"""Build entanglement block"""
109+
entanglement_layers = []
110+
for entanglement in self.entanglement_ops:
111+
entanglement_layers.append(
112+
self.entanglement_layer(
113+
op=entanglement,
114+
n_wires=self.n_wires,
115+
**self.entanglement_layer_params,
116+
)
117+
)
118+
return entanglement_layers
119+
120+
def build_layers(self):
121+
"""Build nlocal circuit"""
122+
layers_all = tq.QuantumModuleList()
123+
124+
# add the initial circuit
125+
initial_circuit = self.build_initial_layer()
126+
if initial_circuit is not None:
127+
layers_all.append(initial_circuit)
128+
129+
# repeat for each rep
130+
for _ in range(self.reps):
131+
# add rotation blocks to the qubits
132+
layers_all.extend(self.build_rotation_block())
133+
134+
# add entanglement blocks to the qubits
135+
layers_all.extend(self.build_entanglement_block())
136+
137+
# add final rotation layer
138+
if not self.skip_final_rotation_layer:
139+
layers_all.extend(self.build_rotation_block())
140+
141+
# return QuantumModuleList
142+
return layers_all
143+
144+
145+
class TwoLocal(NLocal):
146+
"""Layer Template for a TwoLocal Class
147+
148+
Args:
149+
rotation_ops (list): gates for the rotation layer as a list of torchquantum operations
150+
entanglement_ops (list): gates for the entanglement layer as a list of torchquantum operations
151+
arch (dict): circuit architecture in a dictionary format
152+
rotation_layer (torchquantum.QuantumModule): type of rotation layer in a torchquantum.QuantumModule format
153+
entanglement_layer (str): type of entanglement layer in a string ("linear", "reverse_linear", "circular", "full") or tq.QuantumModule format
154+
reps (int): number of reptitions of the rotation and entanglement layers in a integer format
155+
entanglement_layer_params (dict): additional parameters for the entanglement layer in a dictionary forma
156+
initial_circuit (torchquantum.QuantumModule): initial gates or layer in a QuantumModule formatt
157+
skip_final_rotation_layer (bool): whether or not to add the final rotation layer as a boolean
158+
"""
159+
160+
def __init__(
161+
self,
162+
rotation_ops: list = None,
163+
entanglement_ops: list = None,
164+
arch: dict = None,
165+
rotation_layer: tq.QuantumModule = Op1QAllLayer,
166+
entanglement_layer: str = "linear",
167+
reps: int = 1,
168+
entanglement_layer_params: dict = {},
169+
initial_circuit: tq.QuantumModule = None,
170+
skip_final_rotation_layer: bool = False,
171+
):
172+
# if passed as string, determine entanglement type
173+
if entanglement_layer == "linear":
174+
entanglement_layer = Op2QAllLayer
175+
elif entanglement_layer == "reverse_linear":
176+
entanglement_layer = Op2QAllLayer
177+
entanglement_layer_params = {"wire_reverse": True}
178+
elif entanglement_layer == "circular":
179+
entanglement_layer = Op2QAllLayer
180+
entanglement_layer_params = {"circular": True}
181+
elif entanglement_layer == "full":
182+
entanglement_layer = Op2QDenseLayer
183+
184+
# initialize
185+
super().__init__(
186+
arch=arch,
187+
rotation_ops=rotation_ops,
188+
rotation_layer=rotation_layer,
189+
rotation_layer_params={"has_params": True, "trainable": True},
190+
entanglement_ops=entanglement_ops,
191+
entanglement_layer=entanglement_layer,
192+
entanglement_layer_params=entanglement_layer_params,
193+
initial_circuit=initial_circuit,
194+
reps=reps,
195+
skip_final_rotation_layer=skip_final_rotation_layer,
196+
)
197+
198+
199+
class ExcitationPreserving(TwoLocal):
200+
"""Layer Template for a ExcitationPreserving circuit
201+
202+
Args:
203+
arch (dict): circuit architecture in a dictionary format
204+
entanglement_layer (str): type of entanglement layer in a string ("linear", "reverse_linear", "circular", "full") or tq.QuantumModule format
205+
reps (int): number of reptitions of the rotation and entanglement layers in a integer format
206+
skip_final_rotation_layer (bool): whether or not to add the final rotation layer as a boolean
207+
"""
208+
209+
def __init__(
210+
self,
211+
arch: dict = None,
212+
entanglement_layer: str = "full",
213+
reps: int = 3,
214+
skip_final_rotation_layer: bool = False,
215+
):
216+
# construct circuit with rotation layers of RZ and entanglement with RXX and RYY
217+
super().__init__(
218+
arch=arch,
219+
rotation_ops=[tq.RZ],
220+
entanglement_ops=[tq.RXX, tq.RYY],
221+
entanglement_layer=entanglement_layer,
222+
entanglement_layer_params={"has_params": True, "trainable": True},
223+
reps=reps,
224+
skip_final_rotation_layer=skip_final_rotation_layer,
225+
)
226+
227+
228+
class EfficientSU2(TwoLocal):
229+
"""Layer Template for a EfficientSU2 circuit
230+
231+
Args:
232+
arch (dict): circuit architecture in a dictionary format
233+
entanglement_layer (str): type of entanglement layer in a string ("linear", "reverse_linear", "circular", "full") or tq.QuantumModule format
234+
reps (int): number of reptitions of the rotation and entanglement layers in a integer format
235+
skip_final_rotation_layer (bool): whether or not to add the final rotation layer as a boolean
236+
"""
237+
238+
def __init__(
239+
self,
240+
arch: dict = None,
241+
entanglement_layer: str = "reverse_linear",
242+
reps: int = 3,
243+
skip_final_rotation_layer: bool = False,
244+
):
245+
# construct circuit with rotation layers of RY and RZ and entanglement with CX
246+
super().__init__(
247+
arch=arch,
248+
rotation_ops=[tq.RY, tq.RZ],
249+
entanglement_ops=[tq.CNOT],
250+
entanglement_layer=entanglement_layer,
251+
reps=reps,
252+
skip_final_rotation_layer=skip_final_rotation_layer,
253+
)
254+
255+
256+
class RealAmplitudes(TwoLocal):
257+
"""Layer Template for a RealAmplitudes circuit
258+
259+
Args:
260+
arch (dict): circuit architecture in a dictionary format
261+
entanglement_layer (str): type of entanglement layer in a string ("linear", "reverse_linear", "circular", "full") or tq.QuantumModule format
262+
reps (int): number of reptitions of the rotation and entanglement layers in a integer format
263+
skip_final_rotation_layer (bool): whether or not to add the final rotation layer as a boolean
264+
"""
265+
266+
def __init__(
267+
self,
268+
arch: dict = None,
269+
entanglement_layer: str = "reverse_linear",
270+
reps: int = 3,
271+
skip_final_rotation_layer: bool = False,
272+
):
273+
# construct circuit with rotation layers of RY and entanglement with CX
274+
super().__init__(
275+
arch=arch,
276+
rotation_ops=[tq.RY],
277+
entanglement_ops=[tq.CNOT],
278+
entanglement_layer=entanglement_layer,
279+
reps=reps,
280+
skip_final_rotation_layer=skip_final_rotation_layer,
281+
)
282+
283+
284+
class PauliTwoDesign(TwoLocal):
285+
"""Layer Template for a PauliTwoDesign circuit
286+
287+
Args:
288+
arch (dict): circuit architecture in a dictionary format
289+
entanglement_layer (str): type of entanglement layer in a string ("linear", "reverse_linear", "circular", "full") or tq.QuantumModule format
290+
reps (int): number of reptitions of the rotation and entanglement layers in a integer format
291+
skip_final_rotation_layer (bool): whether or not to add the final rotation layer as a boolean
292+
"""
293+
294+
def __init__(
295+
self,
296+
arch: dict = None,
297+
entanglement_layer: str = "reverse_linear",
298+
reps: int = 3,
299+
skip_final_rotation_layer: bool = False,
300+
seed: int = 0,
301+
):
302+
# set seed
303+
self.seed = seed
304+
# construct circuit with entanglement with CX
305+
super().__init__(
306+
arch=arch,
307+
entanglement_ops=[tq.CNOT],
308+
entanglement_layer=entanglement_layer,
309+
reps=reps,
310+
skip_final_rotation_layer=skip_final_rotation_layer,
311+
)
312+
313+
def build_initial_layer(self):
314+
# add an initial layer of ry with rotation pi/4
315+
return tq.QuantumModule.from_op_history(
316+
[
317+
{"name": "ry", "wires": wire, "params": torch.pi / 4}
318+
for wire in range(self.arch["n_wires"])
319+
]
320+
)
321+
322+
def build_rotation_block(self):
323+
# make a random layer of rotations
324+
return [RandomOp1All(n_wires=self.n_wires, seed=self.seed)]

0 commit comments

Comments
 (0)