Closed rileyjmurray closed 3 weeks ago
I am down with all of this. Excellent work sorting through all of this, @rileyjmurray.
Edit: I think this is fine to approve, btw, but will hold off temporarily to allow other to chime in with opinions on this.
Ping @enielse
Quick follow up following today's meeting. It sounds like we agreed to add a note in the place of the original code for these functions pointing to the latest SHA and or pyGSTi version for which these functions were present. Once you've added those notes I think we're good to merge this.
We can just remove the wildcard optimization code using cvxopt. These cvxopt-specific routines were just used as a part of research when the QPL was exploring various options for wildcard optimization. It's possible we'd want to reference the optimization code at some later time, so we could either comment it out or replace the routines with a comment referencing the last commit where the code exists to leave this door open.
This is a follow-up on https://github.com/ionq/pyGSTi/issues/1. There are currently three functions that call cvxopt's nonlinear optimization interface directly,
As far as user exposure goes, these functions could be called if a GSTBadFitOptions constructor was called with certain values for wildcard_methods ("cvxopt", "cvxopt_smoothed", and "cvxopt_small"). I looked throughout pygsti's codebase -- including tests and notebooks -- and there was not a single place were we hit this codepath.
My original plan for this PR was to rewrite these functions to avoid relying on cvxopt explicitly, and instead rely on cvxpy. But once I started messing with the code it was clear (see details below) that this would take an inordinate amount of work. Given that these functions seem to have low user exposure I suggest we just remove them entirely in pyGSTi 0.9.13.
Details
I spent time trying to figure out how
optimize_wildcard_budget_cvxopt
could be modified to use cvxpy instead. Right now it's relying on cvxopt's general nonlinear (convex) optimization interface. This got me nervous because it's possible to define extremely complicated problems using that interface.Luckily,
optimize_wildcard_budget_cvxopt
only uses cvxopt's advanced interface for a single nonlinear constraint! The objective function and all other constraints are linear. So the question is, can I model the nonlinear constraint using cvxpy's primitives? Here's the desired constraint at a mathematical level, whereprobs
is a function of the optimization variable and everything else in the expression is constant:This was promising at first. CVXPY is perfectly happy to work with these constraints as long as
probs
is an affine function of the optimization variable.Unfortunately, the code for evaluating
probs
as a function of the decision variable is very complicated. Here's a taste of the call sequence, in terms of the optimization variablex
:budget.from_vector(_np.array(x))
, wherebudget
is an instance ofPrimitiveOpsWildcardBudgetBase
. This basically just setsbudget.wildcard_vector = _np.array(x)
andbudget.per_op_wildcard_vector = _np.array(x)
.probs
that we care about gets written to the second argument ofbudget.update_probs(...)
. So the question question is howbudget.update_probs(...)
writes to its second argument as a function ofself.wildcard_vector
orself.per_op_wildcard_vector
.budget.update_probs(...)
only accessesself.
twice.circuit_budgets = self.circuit_budgets(...)
. There are two codepaths here depending on whether or not something called "precomp" has been created. The codepath whenprecomp
is provided is very simple (a matrix-vector product withself.wildcard_vector
). The codepath withprecomp
is None is less simple, but at the end of the day it's an affine function ofself.wildcard_error_per_op
(which is a property that basically exposesself.per_op_wildcard_vector
as a dictionary keyed by Label objects).self.precompute_for_same_probs_freqs
that seems to not depend onself.wildcard_vector
orself.per_op_wildcard_vector
.enumerate(zip(circuits, circuit_budgets, probs_freqs_precomp))
that's about 130 lines long. We care about how the variableW
is used in that for-loop. It's mentioned 19 times between the code and comments. The for loop makes calls to several other functions defined in the file -- both instance methods and free functions (that is, functions that aren't attached to a class in any way).I decided to give up once I saw the complexity in step 4 of that call sequence.