Esempio n. 1
0
def test_circ2graph(filename='inst_2x2_7_0.txt'):
    """
    This function tests direct reading of circuits to graphs.
    It should be noted that graphs can not to be used in place
    of buckets yet, since the information about transpositions
    of tensors (denoted by edges) is not kept during node
    relabelling
    """
    import networkx as nx

    nq, circuit = ops.read_circuit_file(filename)
    graph = gm.circ2graph(nq, circuit)

    n_qubits, circuit = ops.read_circuit_file(filename)
    buckets_original, _, bra_vars, ket_vars = opt.circ2buckets(
        n_qubits, circuit)
    graph_original = gm.buckets2graph(
        buckets_original, ignore_variables=bra_vars+ket_vars)

    from networkx.algorithms import isomorphism
    GM = isomorphism.GraphMatcher(graph, graph_original)

    print('Isomorphic? : {}'.format(GM.is_isomorphic()))
    graph = nx.relabel_nodes(graph, GM.mapping, copy=True)

    if not GM.is_isomorphic():
        gm.draw_graph(graph, 'new_graph.png')
        gm.draw_graph(graph_original, 'orig_graph.png')

    return GM.is_isomorphic()
Esempio n. 2
0
def eval_with_np(filename, initial_state=0):
    """
    Loads circuit from file and evaluates all amplitudes
    using the bucket elimination algorithm (with Numpy tensors).
    Same amplitudes are evaluated with Cirq for comparison.
    """
    # Prepare graphical model
    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)

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

    # 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)

    # Take the subtensor corresponding to the initial state
    slice_dict = utils.slice_from_bits(initial_state, ket_vars)

    amplitudes = []
    for target_state in range(2**n_qubits):
        # Take appropriate subtensors for different target bitstrings
        slice_dict.update(
            utils.slice_from_bits(target_state, bra_vars)
        )
        sliced_buckets = npfr.get_sliced_np_buckets(
            perm_buckets, data_dict, slice_dict)
        result = opt.bucket_elimination(
            sliced_buckets, npfr.process_bucket_np)
        amplitudes.append(result.data)

    # Cirq returns the amplitudes in big endian (largest bit first)

    amplitudes_reference = get_amplitudes_from_cirq(
        filename, initial_state)
    print('Result:')
    print(np.round(np.array(amplitudes), 3))
    print('Reference:')
    print(np.round(np.array(amplitudes_reference), 3))
    print('Max difference:')
    print(np.max(np.abs(
        np.array(amplitudes) - np.array(amplitudes_reference))))
Esempio n. 3
0
def test_bucket_operation_speed():
    """
    This tests the speed of forming, permuting and transforming
    buckets.
    """
    import time
    tim1 = time.time()

    filename = 'test_circuits/inst/cz_v2/10x10/inst_10x10_60_1.txt'

    # Prepare graphical model
    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)

    # Get peo
    peo = [opt.Var(node, name=data['name'], size=data['size']) for
           node, data in graph.nodes(data=True)]
    peo = list(np.random.permutation(peo))

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

    # 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)

    # Take the subtensor corresponding to the initial state
    initial_state = 0
    slice_dict = utils.slice_from_bits(initial_state, ket_vars)

    # Take appropriate subtensors for target bitstring
    target_state = 0
    slice_dict.update(
        utils.slice_from_bits(target_state, bra_vars)
    )
    # Form final buckets
    sliced_buckets = npfr.get_sliced_np_buckets(
        perm_buckets, data_dict, slice_dict)

    tim2 = time.time()
    print(tim2 - tim1)
Esempio n. 4
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)
    ))
Esempio n. 5
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
Esempio n. 6
0
def prepare_parallel_evaluation_np(filename, n_var_parallel):
    """
    Prepares for parallel evaluation of the quantum circuit.
    Some of the variables in the circuit are parallelized over.
    Unsliced Numpy buckets in the optimal order of elimination
    are returned
    """
    # import pdb
    # pdb.set_trace()
    # Prepare graphical model
    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)

    # find a reduced graph
    vars_parallel, graph_reduced = gm.split_graph_by_metric_greedy(
        graph, n_var_parallel,
        metric_fn=gm.splitters.get_node_by_mem_reduction)

    # run quickbb once again to get peo and treewidth
    peo, treewidth = gm.get_peo(graph_reduced)

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

    # 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)
    vars_parallel = sorted([perm_dict[idx] for idx in vars_parallel],
                           key=str)

    environment = dict(
        bra_vars=bra_vars,
        ket_vars=ket_vars,
        vars_parallel=vars_parallel,
        buckets=perm_buckets,
        data_dict=data_dict,
    )

    return environment
Esempio n. 7
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
Esempio n. 8
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)))
Esempio n. 9
0
def prepare_parallel_evaluation_tf(filename, n_var_parallel):
    """
    Prepares for parallel evaluation of the quantum circuit.
    Some of the variables in the circuit are parallelized over.
    Symbolic bucket elimination is performed with tensorflow and
    the resulting computation graph (as GraphDef) and other
    supporting information is returned
    """
    # Prepare graphical model
    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)

    # find a reduced graph
    vars_parallel, graph_reduced = gm.split_graph_by_metric(
        graph, n_var_parallel,
        metric_fn=gm.splitters.get_node_by_mem_reduction)

    # run quickbb once again to get peo and treewidth
    peo, treewidth = gm.get_peo(graph_reduced)

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

    # 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)
    vars_parallel = sorted([perm_dict[idx] for idx in vars_parallel],
                           key=str)

    # Populate slice dict. Only shapes of slices are needed at this stage
    slice_dict = utils.slice_from_bits(
        0, bra_vars + ket_vars + vars_parallel)

    # create placeholders with proper shapes
    tf.reset_default_graph()
    tf_buckets, placeholders_dict = tffr.get_sliced_tf_buckets(
        perm_buckets, slice_dict)
    # save only placeholder's names as they are not picklable
    picklable_placeholders = {key.name: val for key, val in
                              placeholders_dict.items()}

    # Do symbolic computation of the result
    result = opt.bucket_elimination(
        tf_buckets, tffr.process_bucket_tf)
    comput_graph = tf.identity(result.data, name='result')

    environment = dict(
        bra_vars=bra_vars,
        ket_vars=ket_vars,
        vars_parallel=vars_parallel,
        tf_graph_def=tf.get_default_graph().as_graph_def(),
        data_dict=data_dict,
        picklable_placeholders=picklable_placeholders
    )

    return environment
Esempio n. 10
0
def eval_with_tf(filename, initial_state=0):
    """
    Loads circuit from file and evaluates all amplitudes
    using the bucket elimination algorithm (with tensorflow tensors).
    Same amplitudes are evaluated with Cirq for comparison.
    """
    # Prepare graphical model
    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)

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

    # 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)

    # Populate slice dict. Only shapes of slices are needed at this stage
    slice_dict = utils.slice_from_bits(initial_state, ket_vars)
    slice_dict.update(utils.slice_from_bits(
        initial_state, bra_vars))

    # create placeholders with proper shapes
    tf_buckets, placeholders_dict = tffr.get_sliced_tf_buckets(
        perm_buckets, slice_dict)

    # build the Tensorflow operation graph
    result = opt.bucket_elimination(
        tf_buckets, tffr.process_bucket_tf)
    comput_graph = result.data

    # prepare static part of the feed_dict
    feed_dict = tffr.assign_tensor_placeholders(
        placeholders_dict, data_dict)

    amplitudes = []
    for target_state in range(2**n_qubits):
        # Now the bounds of slices are needed
        slice_dict.update(
            utils.slice_from_bits(target_state, bra_vars))
        # populate feed dict with slice variables
        feed_dict.update(tffr.assign_variable_placeholders(
            placeholders_dict, slice_dict))

        amplitude = tffr.run_tf_session(comput_graph, feed_dict)
        amplitudes.append(amplitude)

    amplitudes_reference = get_amplitudes_from_cirq(filename,
                                                    initial_state)
    print('Result:')
    print(np.round(np.array(amplitudes), 3))
    print('Reference:')
    print(np.round(amplitudes_reference, 3))
    print('Max difference:')
    print(np.max(np.abs(amplitudes - np.array(amplitudes_reference))))