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()
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))))
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)
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) ))
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
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
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
def test_eval_circuit(filename='inst_2x2_7_0.txt'): import numpy as np initial_state = 0 final_state = 0 measured_initial = [1, 2, 3] measured_final = [0, 1, 2, 3] # prepare reference # calculate proper slices n_qubits, circuit = ops.read_circuit_file(filename) buckets, data_dict, bra_vars, ket_vars = opt.circ2buckets( n_qubits, circuit) free_bra_vars = [bra_vars[idx] for idx in range(n_qubits) if idx not in measured_final] fixed_bra_vars = [bra_vars[idx] for idx in range(n_qubits) if idx in measured_final] free_ket_vars = [ket_vars[idx] for idx in range(n_qubits) if idx not in measured_initial] fixed_ket_vars = [ket_vars[idx] for idx in range(n_qubits) if idx in measured_initial] slice_dict = utils.slice_from_bits(final_state, fixed_bra_vars) slice_dict.update({var: slice(None) for var in free_bra_vars}) slice_dict.update( utils.slice_from_bits(initial_state, fixed_ket_vars)) slice_dict.update({var: slice(None) for var in free_ket_vars}) # sort slice in the big endian order for Cirq bra_subtensor = tuple([slice_dict[var] for var in bra_vars]) ket_subtensor = tuple([slice_dict[var] for var in ket_vars]) reference = np.empty([2]*n_qubits+[2]*n_qubits, dtype=np.complex64)[ bra_subtensor + ket_subtensor] temp_slice_dict = utils.slice_from_bits(initial_state, fixed_ket_vars) for ket_state in range(2**len(free_ket_vars)): amplitudes = get_amplitudes_from_cirq(filename, ket_state) slice_of_amplitudes = amplitudes.reshape( [2]*n_qubits)[bra_subtensor] temp_slice_dict.update( utils.slice_from_bits(ket_state, free_ket_vars)) partial_ket_subtensor = tuple( [temp_slice_dict[var] for var in ket_vars]) final_shape = slice_of_amplitudes.shape + (1,) * n_qubits reference[ bra_subtensor+partial_ket_subtensor ] = slice_of_amplitudes.reshape(final_shape) # get the result result = eval_circuit(n_qubits, circuit, final_state=final_state, initial_state=initial_state, measured_final=measured_final, measured_initial=measured_initial) reference = reference.flatten() result = result.flatten() # difference print('Result:') print(np.round(result, 3)) print('Reference:') print(np.round(reference, 3)) print('Max difference:') print(np.max(np.abs(result - reference)))
name = 'MyGate' _changes_qubits = (0, ) def gen_tensor(self): tensor = 1 / np.sqrt(2) * np.array([[1, 1], [1, -1]]) return tensor myGate = MyGate(0) myGate # - # + from qtree import optimizer tensor_expr, data_dict, bra, ket = optimizer.circ2buckets(1, [[myGate]]) 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
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)))
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
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))))