Skip to content
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

Bugfix reparameterize #480

Open
wants to merge 25 commits into
base: develop
Choose a base branch
from
Open

Bugfix reparameterize #480

wants to merge 25 commits into from

Conversation

juangmendoza19
Copy link

Addressing issue #425.

convert() function has been changed for POVM elements and state preparation. They now support proper conversion from full TP to GLND parameterization. The optimization is now done over non-gauge directions, which are found through Jacobians.

This function still needs to be changed to appropriately handle other reparameterizations.

Lastly, this is not guaranteed to work for POVMs, just because the number of degrees of freedom of a POVM can be greater than the number of error generators that span TP maps for a given number of qubits. By pigeon hole principle, we may not be able to find a description for a POVM consisting of Ideal_POVM + error generators. There is an assertion to try to catch this.

@juangmendoza19 juangmendoza19 requested review from rileyjmurray and a team as code owners August 23, 2024 18:12
@juangmendoza19 juangmendoza19 requested a review from sserita August 23, 2024 18:12
Copy link
Contributor

@sserita sserita left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just bookkeeping: Not ready for review, rerequest from me when ready.

TODO: 2 qubit labels returning None when label does exist in self.labels.
'(support, left_support) not in self._offsets[eetype]' returning True incorrectly


Copy link
Author

@juangmendoza19 juangmendoza19 Feb 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fancy code below (now commented out) to find the index of a label was not working for 2 qubits. Temporary easy solution implemented by just calling index(). This was creating problems during the creation of 2 qubit FOGI models

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure that I implemented a similar fix for this on PR #538. Making note of this in case there are merge conflicts that need to eventually get resolved here.

if ideal_effect is not None:
base_items.append((lbl, convert_effect(ideal_effect, 'static', basis, ideal_effect, flatten_structure)))
else:
base_items.append((lbl, convert_effect(vec, 'static', basis, idl.get(lbl, None), flatten_structure)))
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is code to properly collect the ideal POVM effects. Before, the ideal_effect passed in was being ignored

raise ValueError("Failed to find an errorgen such that <ideal|exp(errorgen) = <effect|")
errgen_vec = _np.linalg.pinv(phys_directions) @ soln.x
errorgen.from_vector(errgen_vec)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code added to find errgen parameterization for POVMs based on optimization, just like state prep was being done before, but for some reason was never implemented on POVMs. Note that the optimization variations are done over a POVM space that lacks the dumb-gauge/metagauge. To find that space we wrote the function calc_physical_subspace which does a numerical calculation of the Jacobian with respect to POVM parameters.

phys_directions = calc_physical_subspace(dense_state)

#We use optimization to find the best error generator representation
#we only vary physical directions, not independent error generators
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just as explained in the POVM init file, we added a function to identify the dumb-gauge/metagauge and move orthogonally to it. This allows for better optimization.

return _np.linalg.norm(proc_matrix @ dense_st - dense_state) + cptp_penalty * sum_of_negative_choi_eigenvalues_gate(proc_matrix, dense_basis)

soln = _spo.minimize(_objfn, _np.zeros(len(phys_directions), 'd'), method="Nelder-Mead", options={},
tol=1e-13) # , callback=callback)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the objective function to be optimized over was modified to work with linear combinations of error generators which lack these dumb-gauge degrees of freedom

@@ -340,7 +340,7 @@ def __getitem__(self, label):
raise KeyError("Key %s has an invalid prefix" % label)

def convert_members_inplace(self, to_type, categories_to_convert='all', labels_to_convert='all',
ideal_model=None, flatten_structure=False, set_default_gauge_group=False, cptp_truncation_tol= 1e-6):
ideal_model=None, flatten_structure=False, set_default_gauge_group=False, cptp_truncation_tol= 1e-7, spam_cptp_penalty = 1e-6):
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added piping for the cptp penalty factor to be used in the optimization within the SPAM convert functions discussed above

evals = _np.linalg.eigvals(J) # could use eigvalsh, but wary of this since eigh can be wrong...
for ev in evals:
if ev.real < 0: sumOfNeg -= ev.real
return sumOfNeg
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created a function to sum the negative choi eigenvalues of a single gate to be used in our optimization functions to measure CPTPness

Catching up this branch with develop. Specifically I am trying to see if this fixes some unit tests that are currently failing.
@juangmendoza19 juangmendoza19 requested review from kmrudin and a team as code owners February 21, 2025 19:15
@juangmendoza19
Copy link
Author

All the code that was changed which does not have a comment attached was a result of merging the develop branch.

@coreyostrove
Copy link
Contributor

The commits coming from merging in develop shouldn't be showing up, so something got glitched out here. I can confirm that when I just went into the new PR creation interface and looked at the diff that was created for merging in this branch into develop only the new commits related to these bugfixes showed up. This might be the sort of thing that fixes itself if you switch the merge target to something other than develop (e.g. master) and then back again. It doesn't look like I have the permissions to do that on my end, so could either @juangmendoza19 or someone else with the requisite permissions give that a shot?

@juangmendoza19 juangmendoza19 changed the base branch from develop to master February 23, 2025 04:11
@juangmendoza19 juangmendoza19 changed the base branch from master to develop February 23, 2025 04:11
Copy link
Contributor

@coreyostrove coreyostrove left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my inline comments and questions below. Thanks!

TODO: 2 qubit labels returning None when label does exist in self.labels.
'(support, left_support) not in self._offsets[eetype]' returning True incorrectly


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure that I implemented a similar fix for this on PR #538. Making note of this in case there are merge conflicts that need to eventually get resolved here.

@@ -327,13 +331,19 @@ def convert(povm, to_type, basis, ideal_povm=None, flatten_structure=False):
are separately converted, leaving the original POVM's structure
unchanged. When `True`, composed and embedded operations are "flattened"
into a single POVM of the requested `to_type`.

cptp_penalty : float, optional (default 1e-7)
Converting SPAM operations to error generator types includes extra degrees of gauge freedom (dumb gauge).
Copy link
Contributor

@coreyostrove coreyostrove Feb 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not eternalize this naming choice in the documentation. If there are other places this was introduced take this comment as a blanket request to change those. Could you switch this to something like 'trivial gauge', or something else? Here is a candidate for new docstring:

Converting SPAM operations to an error generator representation may introduce trivial gauge degrees of freedom. These gauge degrees of freedom are called trivial because they quite literally do not change the dense representation (i.e. Hilbert-Schmidt vectors) at all. Despite being trivial, error generators along this trivial gauge orbit may be non-CP, so this cptp penalty is used to favor channels within this gauge orbit which are CPTP.

I'll also note that strictly speaking this is only a CP penalty, as TP is enforced by construction when using elementary error generators, but that is mostly a pedantic comment. You can leave the name alone.

base_povm = UnconstrainedPOVM(base_items, povm.evotype, povm.state_space)

proj_basis = 'PP' if povm.state_space.is_entirely_qubits else basis
errorgen = _LindbladErrorgen.from_error_generator(povm.state_space.dim, lndtype, proj_basis,
basis, truncate=True, evotype=povm.evotype)
#if to_type == 'GLND' and isinstance(povm, destination_types.get('full TP', NoneType)):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commented out line looks like it can be safely deleted.

#This has been called "the dumb gauge", and this function is meant to avoid it
def calc_physical_subspace(dense_ideal_povm, epsilon = 1e-9):

errgen = _LindbladErrorgen.from_error_generator(povm.state_space.dim, parameterization=to_type)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless this is just a visual artifact on github it looks like you've double indented this function block. Could you fix that?

soln = _spo.minimize(_objfn, _np.zeros(len(phys_directions), 'd'), method="Nelder-Mead", options={},
tol=1e-13) # , callback=callback)
#print("DEBUG: opt done: ",soln.success, soln.fun, soln.x) # REMOVE
if not soln.success and soln.fun > 1e-6: # not "or" because success is often not set correctly
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the two REMOVE lines.

errorgen.from_vector(L_vec)
proc_matrix = _spl.expm(errorgen.to_dense())
#basis of to_dense() CHECK if always pp
dense_basis = Basis.cast('pp', (2**num_qubits)**2)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't assume a hardcoded pauli-product basis. I don't think the error generator modelmember itself has a basis attribute to grab, but it should have a reference to a parent model which would have this available. Can you update this to grab the appropriate basis? (And if you were doing something similar for the POVMs and I missed it add the change there too.)

@@ -306,9 +306,6 @@ def diamonddist(a, b, mx_basis='pp', return_x=False):

# currently cvxpy is only needed for this function, so don't import until here
import cvxpy as _cvxpy
old_cvxpy = bool(tuple(map(int, _cvxpy.__version__.split('.'))) < (1, 0))
if old_cvxpy:
raise RuntimeError('CVXPY 0.4 is no longer supported. Please upgrade to CVXPY 1.0 or higher.')

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the official @rileyjmurray endorsed fix for this? Do we no longer need this check?

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.

3 participants