Germ Selection

The purpose of this tutorial is to illustrate "germ selection". Germ selection is the process by which a complete set of germ gate sequences is constructed. The defining property which makes a set of gate sequences a "complete germ set" is the amplification of all possible gate errors. More precisely, the repetition of a complete set of germs, sandwiched between preparation and measurement fiducial sequences, will yield a sensitivity to all gate errors that scales with the number of times each germ is repeated. This completeness is relative to the set of gates under consideration, typically a set of desired or "target" gates.

In this tutorial, we find a complete germ set for the standard $X(\pi/2)$, $Y(\pi/2)$, $I$ gate set.

In [1]:
import pygsti
from import std1Q_XYI as std

The perfect target gates may contain symmetries that result in certain parameters being amplified by the same germ sequence coincidentally. We want to find a set of germs that amplifies slight deviations from the ideal gates as well, and so we find germs for a "noisy" version of the target gates, in which each ideal gate is composed with a random small unitary map.

In [2]:
gs_target_noisy = std.gs_target.randomize_with_unitary(0.001, seed=1234)

The germ selection algorithm works by throwing unnecessary germs away from an initial (large) list that is assumed to be complete. In this example, our initial list includes all gatestrings up to length three that are distinct up to powers and cycles. Since germ sequences are repeated and sandwiched between fiducial sequences, a germ that is a power of another germ (i.e. the other germ repeated some number of times) or that is a cyclic permutation of another germ is effectively the same as the other germ. The list_all_gatestrings_without_powers_and_cycles function provides a convenient way to list sequences gate sequences that are distinct up to such powers and cycles.

In [3]:
germsToTest =
                std.gs_target.gates.keys(), 3)

The optimize_integer_germs_slack function thins out an initial list of germs with the goal of finding a smallest complete set. The parameter fixedSlack or slackFrac specifies how much of an increase in the quantity 1.0/smallest-nongauge-jacobian-eigenvalue will be tolerated when excluding a germ sequence from the final set (see the function's documentation for further details). A good value to begin with is fixedSlack=0.1.

In [4]:
finalGerms = pygsti.alg.optimize_integer_germs_slack(
                gs_target_noisy, germsToTest, initialWeights=None, 
                fixedSlack=0.1, slackFrac=False, returnAll=False, tol=1e-6, verbosity=4)
Starting germ set optimization. Lower score is better.
Gateset has 16 gauge params.
Iteration 0: score=12.0189, nGerms=14
No better neighbor. Relaxing score w/slack: 12.0189 => 12.1189
Found better neighbor: nGerms = 13 score = 12.084
Moving to better neighbor
Iteration 1: score=12.084, nGerms=13
No better neighbor. Relaxing score w/slack: 12.084 => 12.184
Found better neighbor: nGerms = 12 score = 12.0849
Moving to better neighbor
Iteration 2: score=12.0849, nGerms=12
No better neighbor. Relaxing score w/slack: 12.0849 => 12.1849
Found better neighbor: nGerms = 11 score = 12.0984
Moving to better neighbor
Iteration 3: score=12.0984, nGerms=11
No better neighbor. Relaxing score w/slack: 12.0984 => 12.1984
Found better neighbor: nGerms = 10 score = 12.1251
Moving to better neighbor
Iteration 4: score=12.1251, nGerms=10
No better neighbor. Relaxing score w/slack: 12.1251 => 12.2251
Stationary point found!
score =  12.2251346425
weights =  [1 1 1 1 0 0 1 1 0 1 1 0 1 1]
L1(weights) =  10

And that's it! We now have a small complete germ set that can be used in later GST calculations. Typically one would save this list using, but for this tutorial we'll just print them.

In [5]:
print "Germs:"
print "\n".join(map(str,finalGerms))

# To save to a file:
#"mygerms.lst",finalGerms,"List of germs from tutorial")
In [6]: