Пример #1
0
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
Пример #2
0
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