Exemple #1
0
def eval_contraction_cost(filename):
    """
    Loads circuit from file, evaluates contraction cost
    with and without optimization
    """
    # Prepare graphical model
    n_qubits, circuit = ops.read_circuit_file(filename)
    buckets, data_dict, bra_vars, ket_vars = opt.circ2buckets(
        n_qubits, circuit)

    graph_raw = gm.buckets2graph(
        buckets,
        ignore_variables=bra_vars+ket_vars)

    # estimate cost
    mem_raw, flop_raw = gm.get_contraction_costs(graph_raw)
    mem_raw_tot = sum(mem_raw)

    # optimize node order
    peo, treewidth = gm.get_peo(graph_raw)

    # get cost for reordered graph
    graph, label_dict = gm.relabel_graph_nodes(
        graph_raw, dict(zip(peo, sorted(graph_raw.nodes(), key=int)))
    )
    mem_opt, flop_opt = gm.get_contraction_costs(graph)
    mem_opt_tot = sum(mem_opt)

    # split graph and relabel in optimized way
    n_var_parallel = 3
    _, reduced_graph = gm.split_graph_by_metric(
        graph_raw, n_var_parallel)
    # peo, treewidth = gm.get_peo(reduced_graph)
    peo, treewidth = gm.get_peo(reduced_graph)

    graph_parallel, label_dict = gm.relabel_graph_nodes(
        reduced_graph, dict(zip(
            peo, sorted(reduced_graph.nodes(), key=int)))
    )

    mem_par, flop_par = gm.get_contraction_costs(graph_parallel)
    mem_par_tot = sum(mem_par)

    print('Memory (in doubles):\n raw: {} optimized: {}'.format(
        mem_raw_tot, mem_opt_tot))
    print(' parallel:\n  node: {} total: {} n_tasks: {}'.format(
        mem_par_tot, mem_par_tot*2**(n_var_parallel),
        2**(n_var_parallel)
    ))
Exemple #2
0
def get_optimal_graphical_model(
        filename):
    """
    Builds a graphical model to contract a circuit in ``filename``
    and finds its tree decomposition
    """
    n_qubits, circuit = ops.read_circuit_file(filename)
    buckets, data_dict, bra_vars, ket_vars = opt.circ2buckets(
        n_qubits, circuit)
    graph = gm.buckets2graph(buckets, ignore_variables=bra_vars+ket_vars)
    peo, tw = gm.get_peo(graph)
    graph_optimal, label_dict = gm.relabel_graph_nodes(
        graph, dict(zip(peo, range(1, len(peo) + 1)))
    )
    return graph_optimal
Exemple #3
0
def einsum_sequential_np(subscripts, *operands):
    """
    This function implements a sequential einsum using graphical models.

    Parameters
    ----------
    subsrcipts : str, set of indices in Einstein notation, i.e. 'ij,jk,klm,lm->i'

    operands: list of array_like, tensor

    Returns
    -------
    result: array_like
        Result of the contraction
    """
    graph_initial, free_variables, data_dict = einsum2graph(
        subscripts, *operands)

    # Calculate elimination order
    graph = gm.make_clique_on(graph_initial, free_variables)
    peo_initial, treewidth = gm.get_peo(graph)

    # transform peo so free_variables are at the end
    peo = gm.get_equivalent_peo(graph, peo_initial, free_variables)

    # reorder graph and get buckets
    graph_optimal, _ = gm.relabel_graph_nodes(
        graph_initial, dict(zip(peo, range(len(peo)))))
    buckets = opt.graph2buckets(graph_optimal)

    # populate buckets with actual arrays and perform elimination
    np_buckets = npfr.get_np_buckets(buckets, data_dict)

    result = opt.bucket_elimination(
        np_buckets, npfr.process_bucket_np,
        n_var_nosum=len(free_variables))

    return result.data
Exemple #4
0
def eval_circuit(n_qubits, circuit, final_state,
                 initial_state=0, measured_final=None,
                 measured_initial=None, pdict={}):
    """
    Evaluate a circuit with specified initial and final states.

    Parameters
    ----------
    n_qubits: int
              Number of qubits in the circuit
    circuit: list of lists
             List of lists of gates
    final_state: int
             Values of measured qubits at the end of the circuit (bra).
             Bitwise coded with the length of
             min(n_qubits, len(measured_final)
    initial_state: int
             Values of the measured qubits at the beginning of the
             circuit (ket). Bitwise coded with the length of
             min(n_qubits, len(measured_initial)
    measured_final: list, default None
             Iterable with the positions of qubits which are measured. If
             not all qubits are measured, then a subset of amplitudes will
             be evaluated
    measured_initial: list, default None
             Iterable with the positions of qubits which are measured
             initially. If not all are measured, then the resulting
             amplitudes will be evaluated for a subset of initial states.
    pdict: dict, default {}
    Returns
    -------
    amplitudes: numpy.array
    """
    # Check which qubits are measured
    all_qubits = set(range(n_qubits))
    if measured_final is None:
        measured_final = tuple(range(n_qubits))
    else:
        if not set(measured_final).issubset(all_qubits):
            raise ValueError(f'measured_final qubits outside allowed'
                             f' range: {measured_final}')

    if measured_initial is None:
        measured_initial = tuple(range(n_qubits))
    else:
        if not set(measured_initial).issubset(all_qubits):
            raise ValueError(f'measured_initial qubits outside allowed'
                             f' range: {measured_initial}')

    # Prepare graphical model
    buckets, data_dict, bra_vars, ket_vars = opt.circ2buckets(
        n_qubits, circuit, pdict=pdict)

    # Collect free qubit variables
    free_final = sorted(all_qubits - set(measured_final))
    free_bra_vars = [bra_vars[idx] for idx in free_final]
    bra_vars = [bra_vars[idx] for idx in measured_final]

    free_initial = sorted(all_qubits - set(measured_initial))
    free_ket_vars = [ket_vars[idx] for idx in free_initial]
    ket_vars = [ket_vars[idx] for idx in measured_initial]

    if len(free_bra_vars) > 0:
        log.info('Evaluate amplitudes over all final states of qubits:')
        log.info(f'{free_final}')

    if len(free_ket_vars) > 0:
        log.info('Evaluate amplitudes over all initial states of qubits:')
        log.info(f'{free_initial}')

    graph_initial = gm.buckets2graph(
        buckets,
        ignore_variables=bra_vars+ket_vars)

    graph = gm.make_clique_on(graph_initial, free_bra_vars+free_ket_vars)

    # Get PEO
    peo_initial, treewidth = gm.get_peo(graph)

    # transform peo so free_bra_vars and free_ket_vars are at the end
    # this fixes the layout of the tensor
    peo = gm.get_equivalent_peo(graph, peo_initial,
                                free_bra_vars+free_ket_vars)

    # place bra and ket variables to beginning, so these variables
    # will be contracted first
    perm_buckets, perm_dict = opt.reorder_buckets(
        buckets, bra_vars + ket_vars + peo)
    perm_graph, _ = gm.relabel_graph_nodes(
        graph, perm_dict)

    # extract bra and ket variables from variable list and sort according
    # to qubit order
    ket_vars = sorted([perm_dict[idx] for idx in ket_vars], key=str)
    bra_vars = sorted([perm_dict[idx] for idx in bra_vars], key=str)

    # make proper slice dictionaries. We choose ket = |0>,
    # bra = |0> on fixed entries
    slice_dict = utils.slice_from_bits(initial_state, ket_vars)
    slice_dict.update(utils.slice_from_bits(final_state, bra_vars))
    slice_dict.update({var: slice(None) for var in free_bra_vars})
    slice_dict.update({var: slice(None) for var in free_ket_vars})

    # Finally make numpy buckets and calculate
    sliced_buckets = npfr.get_sliced_np_buckets(
        perm_buckets, data_dict, slice_dict)
    result = opt.bucket_elimination(
        sliced_buckets, npfr.process_bucket_np,
        n_var_nosum=len(free_bra_vars+free_ket_vars))

    return result.data
Exemple #5
0
def eval_with_multiamp_np(filename, initial_state=0):
    """
    Loads circuit from file and evaluates
    multiple amplitudes at once using np framework
    """
    # Values of the fixed bra qubits.
    # this can be changed to your taste
    target_state = 0

    # Prepare graphical model
    n_qubits, circuit = ops.read_circuit_file(filename)
    buckets, data_dict, bra_vars, ket_vars = opt.circ2buckets(
        n_qubits, circuit)

    # Collect free qubit variables
    free_final_qubits = [1, 3]
    free_bra_vars = []
    for ii in free_final_qubits:
        try:
            free_bra_vars.append(bra_vars[ii])
        except IndexError:
            pass
    bra_vars = [var for var in bra_vars if var not in free_bra_vars]

    if len(free_bra_vars) > 0:
        print('Evaluate all amplitudes over final qubits:')
        print(free_final_qubits)
        print('Free variables in the resulting expression:')
        print(free_bra_vars)

    graph_initial = gm.buckets2graph(
        buckets,
        ignore_variables=bra_vars+ket_vars)

    graph = gm.make_clique_on(graph_initial, free_bra_vars)

    # Run quickbb
    peo_initial, treewidth = gm.get_peo(graph)

    # transform peo so free_bra_vars are at the end
    peo = gm.get_equivalent_peo(graph, peo_initial, free_bra_vars)

    # place bra and ket variables to beginning, so these variables
    # will be contracted first
    perm_buckets, perm_dict = opt.reorder_buckets(
        buckets, bra_vars + ket_vars + peo)
    perm_graph, _ = gm.relabel_graph_nodes(
        graph, perm_dict)

    # extract bra and ket variables from variable list and sort according
    # to qubit order
    ket_vars = sorted([perm_dict[idx] for idx in ket_vars], key=str)
    bra_vars = sorted([perm_dict[idx] for idx in bra_vars], key=str)

    # make proper slice dictionaries. We choose ket = |0>,
    # bra = |0> on fixed entries
    slice_dict = utils.slice_from_bits(initial_state, ket_vars)
    slice_dict.update(utils.slice_from_bits(target_state, bra_vars))
    slice_dict.update({var: slice(None) for var in free_bra_vars})

    # Finally make numpy buckets and calculate
    sliced_buckets = npfr.get_sliced_np_buckets(
        perm_buckets, data_dict, slice_dict)
    result = opt.bucket_elimination(
        sliced_buckets, npfr.process_bucket_np,
        n_var_nosum=len(free_bra_vars))
    amplitudes = result.data.flatten()

    # Now calculate the reference
    amplitudes_reference = get_amplitudes_from_cirq(filename)

    # Get a slice as we do not need full amplitude
    bra_slices = {var: slice_dict[var] for var in slice_dict
                  if var.name.startswith('o')}

    # sort slice in the big endian order for Cirq
    computed_subtensor = [slice_dict[var]
                          for var in sorted(bra_slices, key=str)]

    slice_of_amplitudes = amplitudes_reference.reshape(
        [2]*n_qubits)[tuple(computed_subtensor)]
    slice_of_amplitudes = slice_of_amplitudes.flatten()

    print('Result:')
    print(np.round(amplitudes, 3))
    print('Reference:')
    print(np.round(slice_of_amplitudes, 3))
    print('Max difference:')
    print(np.max(np.abs(amplitudes - slice_of_amplitudes)))