Esempio n. 1
0
def simulate_buckets(tensor_expr, data_dict, slice_dict):
    numpy_buckets = npfr.get_sliced_np_buckets(tensor_expr, data_dict,
                                               slice_dict)
    print(numpy_buckets)
    print("Output tensor:", numpy_buckets[0][0].data)
    print("Input tensor:", numpy_buckets[-2][0].data)
    result = optimizer.bucket_elimination(numpy_buckets,
                                          npfr.process_bucket_np)
    return result.data
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 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
Esempio n. 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
Esempio n. 5
0
print(tensor_expr)
print(data_dict)
print(bra)
print(ket)
# -

from qtree import np_framework as npfr

# ## Sum of all amplitudes for all inputs
#
# This is just a full contraction of the tensor network

numpy_buckets = npfr.get_sliced_np_buckets(tensor_expr, data_dict, {})
numpy_buckets

result = optimizer.bucket_elimination(numpy_buckets, npfr.process_bucket_np)
result

result.data

# ## Sum of amplitudes for single input
#
# This is a contraction of a network that was sliced over input indices

initial_state = 0
slice_dict = qtree.utils.slice_from_bits(initial_state, ket)
slice_dict

numpy_buckets = npfr.get_sliced_np_buckets(tensor_expr, data_dict, slice_dict)
print(numpy_buckets)
print("Output tensor:", numpy_buckets[0][0].data)
Esempio n. 6
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. 7
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. 8
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))))
Esempio n. 9
0
def eval_with_np_parallel_mpi(filename, initial_state=0):
    """
    Evaluate quantum circuit using MPI to parallelize
    over some of the variables.
    """
    comm = MPI.COMM_WORLD
    comm_size = comm.size
    rank = comm.rank

    # number of variables to split by parallelization
    # this should be adjusted by the algorithm from memory/cpu
    # requirements
    n_var_parallel = 2
    if rank == 0:
        env = prepare_parallel_evaluation_np(filename, n_var_parallel)
    else:
        env = None

    env = comm.bcast(env, root=0)

    # restore buckets
    buckets = env['buckets']

    # restore other parts of the environment
    bra_vars = env['bra_vars']
    ket_vars = env['ket_vars']
    vars_parallel = env['vars_parallel']

    # restore data dictionary
    data_dict = env['data_dict']

    # Construct slice dictionary for initial state
    slice_dict = utils.slice_from_bits(initial_state, ket_vars)

    # Loop over all amplitudes
    amplitudes = []
    for target_state in range(2**len(bra_vars)):
        # Construct slice dictionary for the target state
        slice_dict.update(
            utils.slice_from_bits(target_state, bra_vars))

        # main computation loop. Populate respective slices
        # and do contraction

        amplitude = 0
        for parallel_slice_dict in utils.slice_values_generator(
                vars_parallel, rank, comm_size):
            slice_dict.update(parallel_slice_dict)
            sliced_buckets = npfr.get_sliced_np_buckets(
                buckets, data_dict, slice_dict)
            result = opt.bucket_elimination(
                sliced_buckets, npfr.process_bucket_np)

            amplitude += result.data

        amplitude = comm.reduce(amplitude, op=MPI.SUM, root=0)
        amplitudes.append(amplitude)

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