-
Notifications
You must be signed in to change notification settings - Fork 575
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
Tensor product in Pennylane #715
Comments
HI @AlaricCheng! This is a known issue with the Tensor class; during implementation, we made the assumptions that
As a result, any deviation from this (multiple operators on the same wire, or decreasing non-strictly increasing wire order) will lead to undefined behaviour. One option would simply be to include stricter input validation, so that only operator inputs satisfying the above conditions are allowed. However, we are planning to instead extend the behaviour of the tensor class to cover the cases described in your post. |
Just a comment: The first point, that operators act on different wires, should be taken care after we merge a refactor of the way PL handles wires soon. From then on, the Tensor class will complain if your observables act on the same wires. I'm not sure if that also resolves the second point though. |
Maybe my reply in Issue #591 will also helps to illuminate the issue, @AlaricCheng? Basically, we cannot in future infer the wire ordering just from the wire labels, because users are soon allowed to define non-ascending or non-numeric wire labels... But you are right, we need to see if we can "catch out" the most obvious cases where a user goes wrong. |
Update on this (@josh146, @mariaschuld)
@property
def matrix(self):
# group the observables based on what wires they act on
U_list = []
key = lambda x: x.wires.labels # new line added
_obs = sorted(self.obs, key=key) # new line added
for _, g in itertools.groupby(self.obs, key): # changed line, recycled the key here
# extract the matrices of each diagonalizing gate
mats = [i.matrix for i in g]
if len(mats) > 1:
# multiply all unitaries together before appending
mats = [multi_dot(mats)]
# append diagonalizing unitary for specific wire to U_list
U_list.append(mats[0])
# Return the Hermitian matrix representing the observable
# over the defined wires.
return functools.reduce(np.kron, U_list) Note that itertool's
The advantage of these two changes is that they only happen in the |
@dwierichs I wonder if these planned changes will offer a more thorough way to solve the issue once and for all:
This logic would hopefully cover the intuition:
I hope this will be a set of clear and consistent rules? The deeper issue here is that how we use the tensor symbol in maths always implies that the order of chaining determines the subspaces. However, in our operator abstraction, the subspaces are determined by the wires. The |
I am quite happy to add an error for the overlapping wires case in the meantime though! But I am not sure what the expected behaviour actually is for |
Thanks for the pointer, @mariaschuld ! |
I believe this issue can also be closed with the replacement of |
Hello,
I'm confused by the output of
qml.operation.Tensor
. It doesn't seem like a usual tensor product that I would expect. Surely, something likeTensor(qml.PauliX(0), qml.PauliX(1))
works perfectly well. But if the wires is not so ordered, then the output is wired. Below are several example I tries out.The output of$X*X = I$ .
Tensor(qml.PauliX(0), qml.PauliX(0))
is actuallyThe output of$X_0 \otimes Y_1$ instead of $Y_0 \otimes X_1$ .
Tensor(qml.PauliX(1), qml.PauliY(0))
isThe output of$X_0 \otimes Y_1 \otimes X_2$ ,.
Tensor(qml.PauliX(0), qml.PauliY(1), qml.PauliX(0))
isBut that of$(X_0 * Y_0) \otimes X_1$ .
Tensor(qml.PauliX(1), qml.PauliY(1), qml.PauliX(0))
isFrom these examples, it seems that
Tensor
will reindex the wires of each observables, and if two consecutive observables have the same wire, then they will be first multiplied. I don't know whether the rules I summurized are complete.It would be more clear if you could give a detailed description in the documentation, with explicit mathematical expressions.
But this 'wiring strategy' is not so intuitive. I think a better strategy would be to reorder the wire in the numerical order, and multiply the observable with the same wires. For example, (1) the output of$X_0 \otimes Y_1$ , as the wires in observables have already indicated the order; (2) the output of $Y_0 \otimes X_1 Z_1 \otimes X_2$ , where the $X_1$ and $Z_1$ are multiplied. I hope I have clarified the idea.
Tensor(qml.PauliX(0), qml.PauliY(1))
andTensor(qml.PauliY(1), qml.PauliX(0))
would be the sameTensor(qml.PauliX(1), qml.PauliY(0), qml.PauliX(2), qml.PauliZ(1))
would beThank you for your time and your good work :)
The text was updated successfully, but these errors were encountered: