-
Notifications
You must be signed in to change notification settings - Fork 110
Adding Kraus to channel #1071
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
base: master
Are you sure you want to change the base?
Adding Kraus to channel #1071
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #1071 +/- ##
======================================
Coverage 98.0% 98.0%
======================================
Files 181 182 +1
Lines 3520 3525 +5
Branches 773 773
======================================
+ Hits 3452 3457 +5
Misses 44 44
Partials 24 24 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
import numpy as np | ||
import pytest | ||
|
||
import toqito.state_ops |
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.
Can you add a unit test where the superoperator acts on a state?
See Section 2.2 of https://arxiv.org/pdf/1509.02921 for more info.
It would also be pretty easy to construct a superoperator from the channels in Section 2.1.1
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.
Before going into that, can you tell me if the function has been implemented correctly. Because I was not very sure about it. If you read the previous discussion on the issue, you will see that the two results, one from using the function on a vector, and other from directly applying a series of kraus operators were coming out to be transpose of each other.
To tackle that, I modified the implementation of the function a little by changing the order of A and B ( from (A, B.conj())
to (B, A.conj())
) But I am not sure about it.
super_op = sum(tensor(B, A.conj()) for A, B in kraus_list)
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.
Hi @Shivansh20128 ,
Thanks for your response. From what I can tell (and what I recall) I believe your approach here is sensible.
Perhaps one way to sanity check your approach would be to convert a set of Kraus operators to a quantum channel. Then, if you can use some of the existing functionality in toqito
to check that you can either go from the channel back to the operators, or apply the channel in a way that gives the same result as a quantum channel that was arrived at through another means, perhaps that would be serve as some good sanity checks?
I understand that some of the above was a bit vague, but in general, do you think that's a sensible approach?
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.
apply the channel in a way that gives the same result as a quantum channel that was arrived at through another means, perhaps that would be serve as some good sanity checks?
I tried this approach here:
import numpy as np
from toqito.matrix_ops import tensor
def kraus_to_channel(
kraus_list: list[tuple[np.ndarray, np.ndarray]]
) -> np.ndarray:
"""Convert a collection of Kraus operators into the corresponding quantum channel (superoperator).
:param kraus_list: List of tuples (A, B) where A and B are Kraus operators as numpy arrays
:return: The superoperator as a numpy array
"""
super_op = sum(tensor(B, A.conj()) for A, B in kraus_list)
return super_op
from toqito.channel_ops import apply_channel
kraus_operators = [
(np.array([[1, 0], [0, 0]]), np.array([[1, 0], [0, 0]])), # Projection onto |0⟩
(np.array([[0, 1], [0, 0]]), np.array([[0, 1], [0, 0]])), # Projection onto |1⟩
]
# Generate the quantum channel using your function
quantum_channel = kraus_to_channel(kraus_operators)
# Define a test quantum state (density matrix)
rho = np.array([[0.5, 0.5], [0.5, 0.5]])
# Apply the quantum channel using Toqito's apply_channel function
rho_after_channel = apply_channel(rho, kraus_operators)
# Apply the superoperator to the vectorized form of rho
rho_vec = rho.flatten("F") # Column-major order
rho_after_super_op = quantum_channel @ rho_vec
rho_after_super_op = rho_after_super_op.reshape(2, 2, order="F") # Reshape back
# Compare both methods
print("Using apply_channel:\n", rho_after_channel)
print("Using superoperator:\n", rho_after_super_op)
print("Difference:\n", rho_after_channel - rho_after_super_op)
# The difference should be close to zero
assert np.allclose(rho_after_channel, rho_after_super_op), "Mismatch in quantum channel application!"
And I am getting a match on the results:
Using apply_channel:
[[1. 0.]
[0. 0.]]
Using superoperator:
[[1. 0.]
[0. 0.]]
Difference:
[[0. 0.]
[0. 0.]]
If you also think this is good enough, we can go ahead.
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.
You might want to try other values of rho
, but I think this is definitely the correct sanity check approach for what you are working on, and it seems right to me! Nice approach, @Shivansh20128 !
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.
Okay. I will add more values of rho
in the test cases.
p_1 = 0.1 # Probability of bit flip (Bit-Flip Channel) | ||
kraus_operators_1 = [ | ||
(np.sqrt(1 - p_1) * np.array([[1, 0], [0, 1]]), np.sqrt(1 - p_1) * np.array([[1, 0], [0, 1]])), # Identity | ||
(np.sqrt(p_1) * np.array([[0, 1], [1, 0]]), np.sqrt(p_1) * np.array([[0, 1], [1, 0]])), # Bit-flip (X) | ||
] | ||
|
||
p_2 = 0.2 # Depolarizing Channel | ||
kraus_operators_2 = [ | ||
(np.sqrt(1 - 3 * p_2 / 4) * np.eye(2), np.sqrt(1 - 3 * p_2 / 4) * np.eye(2)), # Identity | ||
(np.sqrt(p_2 / 4) * np.array([[0, 1], [1, 0]]), np.sqrt(p_2 / 4) * np.array([[0, 1], [1, 0]])), # X | ||
(np.sqrt(p_2 / 4) * np.array([[0, -1j], [1j, 0]]), np.sqrt(p_2 / 4) * np.array([[0, -1j], [1j, 0]])), # Y | ||
(np.sqrt(p_2 / 4) * np.array([[1, 0], [0, -1]]), np.sqrt(p_2 / 4) * np.array([[1, 0], [0, -1]])), # Z | ||
] | ||
|
||
kraus_operators_3 = [ | ||
(np.array([[1, 0], [0, 0]]), np.array([[1, 0], [0, 0]])), # Projection onto |0⟩ | ||
(np.array([[0, 1], [0, 0]]), np.array([[0, 1], [0, 0]])), # Projection onto |1⟩ | ||
] |
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.
We have commonly predefined channels in toqito/channels
.
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.
I will work on this later.
Thanks
A = np.random.rand(2, 2) + 1j * np.random.rand(2, 2) | ||
rho_5 = A @ A.conj().T | ||
rho_5 /= np.trace(rho_5) # Normalize trace to 1 | ||
|
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.
We have quite a few functions to generate random things. See if those are useful, instead of manually defining your own randomly generated input.
https://toqito--1071.org.readthedocs.build/en/1071/autoapi/rand/index.html
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.
I will work on this later.
Thanks
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.
Yes, I think specifically in this case the random_density_matrix
function would be useful!
Co-authored-by: Purva Thakre <66048318+purva-thakre@users.noreply.github.com>
Co-authored-by: Purva Thakre <66048318+purva-thakre@users.noreply.github.com>
|
||
See Also | ||
======== | ||
func:`.choi_to_kraus`, func:`.kraus_to_choi` |
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.
@Shivansh20128 something has gone wrong with the formatting for this. I was trying to make these clickable similar to what I did in #1091
Each function should be :func:.name_function
where .name_function
is sandwiched between `
My apologies!
Description
This PR is a work I progress for adding the function kraus_to_channel.
#Resolves #14
Changes
Checklist
Before marking your PR ready for review, make sure you checked the following locally. If this is your first PR, you might be notified of some workflow failures after a maintainer has approved the workflow jobs to be run on your PR.
Additional information is available in the documentation.
ruff
for errors related to code style and formatting.pytest
.Sphinx
build can be checked locally for any failures related to your PRlinkcheck
to check for broken links in the documentationdoctest
to verify the examples in the function docstrings work as expected.