Skip to content

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

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open

Conversation

Shivansh20128
Copy link
Contributor

Description

This PR is a work I progress for adding the function kraus_to_channel.

#Resolves #14

Changes

  • Added code for kraus_to_channel function.
  • Added tests for the function.

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.

  • Use ruff for errors related to code style and formatting.
  • Verify all previous and newly added unit tests pass in pytest.
  • Check the documentation build does not lead to any failures. Sphinx build can be checked locally for any failures related to your PR
  • Use linkcheck to check for broken links in the documentation
  • Use doctest to verify the examples in the function docstrings work as expected.

Copy link

codecov bot commented Mar 24, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 98.0%. Comparing base (1379480) to head (5eef4c6).

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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

import numpy as np
import pytest

import toqito.state_ops
Copy link
Collaborator

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

Copy link
Contributor Author

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)

Copy link
Owner

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?

Copy link
Contributor Author

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.

Copy link
Owner

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 !

Copy link
Contributor Author

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.

@Shivansh20128 Shivansh20128 marked this pull request as ready for review March 28, 2025 05:32
Comment on lines +27 to +44
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⟩
]
Copy link
Collaborator

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.

Copy link
Contributor Author

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

Copy link
Collaborator

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

Copy link
Contributor Author

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

Copy link
Owner

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!

Shivansh20128 and others added 2 commits March 30, 2025 12:07
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`
Copy link
Collaborator

@purva-thakre purva-thakre Mar 31, 2025

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

image

Each function should be :func:.name_function where .name_function is sandwiched between `

My apologies!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature: Convert kraus representation to channel (superoperator)
3 participants