-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[RFC] Improve handling of nested control structures #602
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,4 +16,4 @@ | |
Version number (major.minor.patch[-label]) | ||
""" | ||
|
||
__version__ = "0.35.0-dev7" | ||
__version__ = "0.35.0-dev8" |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -117,27 +117,6 @@ | |
"CRX", | ||
"CRY", | ||
"CRZ", | ||
"C(PauliX)", | ||
"C(PauliY)", | ||
"C(PauliZ)", | ||
"C(Hadamard)", | ||
"C(S)", | ||
"C(T)", | ||
"C(PhaseShift)", | ||
"C(RX)", | ||
"C(RY)", | ||
"C(RZ)", | ||
"C(SWAP)", | ||
"C(IsingXX)", | ||
"C(IsingXY)", | ||
"C(IsingYY)", | ||
"C(IsingZZ)", | ||
"C(SingleExcitation)", | ||
"C(SingleExcitationMinus)", | ||
"C(SingleExcitationPlus)", | ||
"C(DoubleExcitation)", | ||
"C(DoubleExcitationMinus)", | ||
"C(DoubleExcitationPlus)", | ||
"CRot", | ||
"IsingXX", | ||
"IsingYY", | ||
|
@@ -367,20 +346,36 @@ | |
Returns: | ||
array[complex]: the output state tensor | ||
""" | ||
basename = "PauliX" if operation.name == "MultiControlledX" else operation.base.name | ||
if basename == "Identity": | ||
return | ||
method = getattr(sim, f"{basename}", None) | ||
control_wires = self.wires.indices(operation.control_wires) | ||
control_values = ( | ||
[bool(int(i)) for i in operation.hyperparameters["control_values"]] | ||
if operation.name == "MultiControlledX" | ||
else operation.control_values | ||
) | ||
|
||
if operation.name == "MultiControlledX": | ||
basename = "PauliX" | ||
control_values = [bool(int(i)) for i in operation.hyperparameters["control_values"]] | ||
target_wires = list(set(self.wires.indices(operation.wires)) - set(control_wires)) | ||
Comment on lines
351
to
354
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This special case is not necessary anymore. The |
||
else: | ||
basename = operation.base.name | ||
control_values = operation.control_values | ||
target_wires = self.wires.indices(operation.target_wires) | ||
|
||
if basename == "Identity": | ||
return | ||
# This special handling is required because PennyLane doesn't canonicalize | ||
# C(CNOT/Toffoli) in qml.simplify or qml.ctrl/Controlled, and we now allow `C(...)` | ||
# instances through for any supported base gate. However, the C++ simulator `method` | ||
# for CNOT/Toffoli does not accept additional control wires. | ||
elif basename == "CNOT": | ||
basename = "PauliX" | ||
control_values += [True] | ||
control_wires += operation.base.wires[0] | ||
target_wires = [operation.base.wires[1]] | ||
elif basename == "Toffoli": | ||
basename = "PauliX" | ||
control_values += [True, True] | ||
control_wires += operation.base.wires[0:2] | ||
target_wires = [operation.base.wires[2]] | ||
Comment on lines
+366
to
+375
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe after my PR, in the current master of pennylane, if you call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
method = getattr(sim, f"{basename}", None) | ||
|
||
if method is not None: # apply n-controlled specialized gate | ||
inv = False | ||
param = operation.parameters | ||
|
@@ -402,6 +397,15 @@ | |
operation.base.matrix, control_wires, control_values, target_wires, False | ||
) | ||
|
||
def supports_operation(self, operation): | ||
"""Overwrite base class implementation to allow arbitrarily nested Controlled instances | ||
to be applied by Lightning.""" | ||
|
||
while operation[0:2] == "C(": | ||
operation = operation[2:-1] | ||
|
||
return super().supports_operation(operation) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dime10 could you please clarify how e.g. UPD: I understand that PL would decompose gates like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems PennyLane will spit out There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A slight caveat here: a |
||
|
||
def apply_lightning(self, state, operations): | ||
"""Apply a list of operations to the state tensor. | ||
|
||
|
@@ -420,8 +424,10 @@ | |
# Skip over identity operations instead of performing | ||
# matrix multiplication with it. | ||
for operation in operations: | ||
if operation.name == "Identity": | ||
continue | ||
elif operation.name[0:2] == "C(": | ||
operation = qml.simplify(operation) | ||
method = getattr(sim, operation.name, None) | ||
wires = self.wires.indices(operation.wires) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(This is more a question, I am not asking for immediate changes in this PR)
The set of supported gates (in Lighthing at least) is defined here and in C++ (and I would say that the complete spec would also include threading model and the number of qubits). Is the Python list below - a duplicate of these C++ enums?
Could we use the device API to read the supported gates somehow? Maybe, we can autogenerate Python sources from the C++ ones if we want to have this information in compile time without running C funcitons?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is we do not have a tight and robust mechanism to communicate natively supported gates. The
enum
s are just that,enum
s. So they are not lists of the supported gates, just gates that might be supported on some device. What dictates which gates are decomposed are theallowed_operations
dictionaries, but these are manually maintained and might not include gates that are natively supported (usually goes uncaught), and vice versa (usually fails some test). Some gates likeOrbitalRotation
are listed inallowed_operations
but are not natively supported and do not crash any test. This is because gates that do not have a specialized implementation are applied via matrices. In the case ofOrbitalRotation
, this is fine because the number of wire is fixed at 4, a reasonable number. For other gates however, likeQFT
, this can cause issues since the number of wire is unlimited, possibly requiring the generation of a large dense matrix at the Python layer (filling out the memory).