def qng_metric_tensor_blocks(expectation, initial_values=None, samples=None, backend=None, backend_options=None, noise=None): U = expectation.U moments = U.canonical_moments sub = [ QCircuit.from_moments(moments[:i]) for i in range(1, len(moments), 2) ] parametric_moms = [moments[i] for i in range(1, len(moments) + 1, 2)] generators = [] for pm in parametric_moms: set = [] if len(pm.gates) != 0: for gate in pm.gates: gen = get_generator(gate) set.append(gen) if len(set) != 0: generators.append(set) else: generators.append(None) blocks = [] for i, set in enumerate(generators): if set is None: pass else: block = [[0 for _ in range(len(set))] for _ in range(len(set))] for k, gen1 in enumerate(set): for q, gen2 in enumerate(set): if k == q: arg = (ExpectationValue(U=sub[i], H=gen1 * gen1) - ExpectationValue(U=sub[i], H=gen1)**2) / 4 else: arg = (ExpectationValue(U=sub[i], H=gen1 * gen2) - ExpectationValue(U=sub[i], H=gen1) * ExpectationValue(U=sub[i], H=gen2)) / 4 block[k][q] = compile_objective( arg, variables=initial_values, samples=samples, backend=backend, noise=noise, backend_options=backend_options) blocks.append(block) return blocks
def stokes_block( expectation, initial_values=None, samples=None, device=None, backend=None, noise=None ) -> typing.List[typing.List[typing.List[typing.Union[float, Objective]]]]: """ returns the blocks of the layerwise block-diagonal approximation to the qgt. The default for all qng-based optimizations, as a method for obtaining the qgt. See: Stokes et. al, https://arxiv.org/abs/1909.02108 Parameters ---------- expectation: ExpectationValueImpl: the expectation value whose qgt is to be built. initial_values: dict, optional: a dictionary of initial values with which Objectives in the qgt should be compiled samples: int, optional: the number of samples with which Objectives in the qgt should be compiled device: optional: the device (real, or simulated, and specific to the backend) with which Objectives in the qgt should be compiled. Generally, a string, but backend specific types also accepted. backend: str, optional: the backend with which Objectives in the qgt should be compiled noise: str or NoiseModel, optional: the noise model with which Objectives in the qgt should be compiled Returns ------- list of list of lists: list of list of lists representing the blocks of the block diagonal layerwise approx to the qgt. """ U = expectation.U ### orders the circuit into alternating layer ansatz, where a moment is all simultaneous gates moments = U.canonical_moments ### rebuild the sub circuits used in the expectation values that populate the QGT sub = [ QCircuit.from_moments(moments[:i]) for i in range(1, len(moments), 2) ] ### this is the list of just the moments which are parametrized. parametric_moms = [moments[i] for i in range(1, len(moments) + 1, 2)] generators = [] ### generators is a list of lists, ultimately, where each sublist is all the generators in order ### for a given parametric layer (if said layer is ### occupied, which it might not be! a layer can be nothing, I.E, the identity.) for pm in parametric_moms: set = [] if len(pm.gates) != 0: for gate in pm.gates: ### get_generator takes a gaussian gate, and returns the pauli that is its generator. ### See that function for detail. gen = get_generator(gate) set.append(gen) if len(set) != 0: generators.append(set) else: ### blank sets get passed over generators.append(None) blocks = [] for i, set in enumerate(generators): if set is None: pass else: ### a block is a list of lists, and indexing it should correspond to indexing a matrix in A[row][column] fashion. ### alternate functions could have the whole QGT be a single block, but you need to have as a return a List of (List of Lists)!!!! block = [[0 for _ in range(len(set))] for _ in range(len(set))] for k, gen1 in enumerate(set): for q, gen2 in enumerate(set): ### make sure you compile the objectives! otherwise this bad boy will not run if k == q: arg = (ExpectationValue(U=sub[i], H=gen1 * gen1) - ExpectationValue(U=sub[i], H=gen1)**2) / 4 else: arg = (ExpectationValue(U=sub[i], H=gen1 * gen2) - ExpectationValue(U=sub[i], H=gen1) * ExpectationValue(U=sub[i], H=gen2)) / 4 block[k][q] = compile_objective(arg, variables=initial_values, samples=samples, backend=backend, device=device, noise=noise) blocks.append(block) return blocks