<jupyter_start><jupyter_text>Installations<jupyter_code>!pip3 install gurobipy
!pip3 install qiskit[visualization]
!pip3 install olsq
!pip3 install anytree

!pip3 install pytket
!pip3 install pytket-qiskit
!git clone https://github.com/arnavdas88/qiskit_helper_functions
!cp -r qiskit_helper_functions/* ./ && python3 setup.py install
!pip3 uninstall cutqc
!rm -r cutqc
!rm -r CutQC
!git clone https://github.com/arnavdas88/CutQC
!cp -r CutQC/* ./ && python3 setup.py install

# !git clone https://github.com/weiT1993/CutQC
# !cp -r CutQC/* ./ && python3 setup.py install<jupyter_output>[33mWARNING: Skipping cutqc as it is not installed.[0m
Cloning into 'CutQC'...
remote: Enumerating objects: 389, done.[K
remote: Counting objects: 100% (389/389), done.[K
remote: Compressing objects: 100% (192/192), done.[K
remote: Total 389 (delta 140), reused 376 (delta 127), pack-reused 0[K
Receiving objects: 100% (389/389), 232.16 KiB | 10.55 MiB/s, done.
Resolving deltas: 100% (140/140), done.
running install
running bdist_egg
running egg_info
writing qiskit_helper_functions.egg-info/PKG-INFO
writing dependency_links to qiskit_helper_functions.egg-info/dependency_links.txt
writing top-level names to qiskit_helper_functions.egg-info/top_level.txt
reading manifest file 'qiskit_helper_functions.egg-info/SOURCES.txt'
adding license file 'LICENSE'
writing manifest file 'qiskit_helper_functions.egg-info/SOURCES.txt'
installing library code to build/bdist.linux-x86_64/egg
running install_lib
running build_py
copying cutqc/evaluator.py -> build/lib/cutqc
copying cutqc/cutter.py -> b[...]<jupyter_text>Code<jupyter_code>from qiskit.quantum_info import random_unitary
# from qiskit.circuit.random import random_circuit
from qiskit_helper_functions.non_ibmq_functions import generate_circ
from qiskit_helper_functions.non_ibmq_functions import apply_measurement
from qiskit_helper_functions.conversions import dict_to_array
 
from cutqc.cutter import find_cuts
from cutqc.post_process import generate_summation_terms # build
from qiskit_helper_functions.non_ibmq_functions import find_process_jobs
from cutqc.evaluator import run_subcircuit_instances
from cutqc.verify import verify
 
from anytree import Node, RenderTree
from anytree.exporter import DotExporter
from anytree.search import findall_by_attr
 
from pprint import pprint

import multiprocessing as mp
 
from cutqc.cutter import subcircuits_parser, get_pairs, get_counter, cost_estimate
from cutqc.evaluator import modify_subcircuit_instance, mutate_measurement_basis, measure_prob

from qiskit.circuit import QuantumCircuit
from qiskit.quantum_info import random_unitary

from qiskit_helper_functions.non_ibmq_functions import generate_circ

from cutqc.main import CutQC
task_3 = {
        'name':'supremacy',
        'circuit':generate_circ(num_qubits=12,depth=20,circuit_type='bv'),
        'kwargs':dict(
            max_subcircuit_width=12,
            max_subcircuit_cuts=10,
            max_subcircuit_size=None,
            quantum_cost_weight=1.0,
            max_cuts=10,
            num_subcircuits=[2,3,4],
            min_subcircuit_width=3
        )
    }
cutqc = CutQC(tasks=[task_3],verbose=True)
cutqc.cut()
def constant_shots_fn(circuit):
    return 1024
cutqc.evaluate(eval_mode='sv',num_shots_fn=constant_shots_fn,mem_limit=24,num_threads=4)
cutqc.verify()
import numpy as np
from qiskit import QuantumCircuit, QuantumRegister
from inspect import signature

def random_circuit(width=3, depth=3, max_operands=3):
        """Generate random circuit of arbitrary size.
        Note: the depth is the layers of independent operation. true depth
        in the image may be more for visualization purposes, if gates overlap.
        Args:
            width (int): number of quantum wires
            depth (int): layers of operations
            max_operands (int): maximum operands of each gate
        Returns:
            QuantumCircuit: constructed circuit
        """
        qr = QuantumRegister(width, "q")
        qc = QuantumCircuit(qr)

        one_q_ops = "x,y,z,h"
        two_q_ops = "cx,cy,ch"
        three_q_ops = "ccx"

        seed = np.random.randint(0, np.iinfo(np.int32).max)
        rng = np.random.RandomState(seed)
        # apply arbitrary random operations at every depth
        for _ in range(depth):
            # choose either 1, 2, or 3 qubits for the operation
            remaining_qubits = list(range(width))
            while remaining_qubits:
                max_possible_operands = min(len(remaining_qubits), max_operands)
                num_operands = rng.choice(range(max_possible_operands)) + 1
                rng.shuffle(remaining_qubits)
                operands = remaining_qubits[:num_operands]
                remaining_qubits = [q for q in remaining_qubits if q not in operands]
                if num_operands == 1:
                    operation = rng.choice(one_q_ops.split(','))
                elif num_operands == 2:
                    operation = rng.choice(two_q_ops.split(','))
                elif num_operands == 3:
                    operation = rng.choice(three_q_ops.split(','))
                # every gate is defined as a method of the QuantumCircuit class
                # the code below is so we can call a gate by its name
                gate = getattr(QuantumCircuit, operation)
                register_operands = [qr[i] for i in operands]
                gate(qc, *register_operands)

        return qc

for _ in range(1000):
    random_circuit()
types = [
         # qiskit_helper_functions
         "bv",
         "supremacy",
         "supremacy_linear",
         "hwea",
         "qft",
         "aqft",
         "sycamore",
         "adder",
         "grover",
         "random",

         # qiskit.circuit.random
         "qiskit-random"
    ]
ENABLE_GPU = False

def generate_circuit(register_size = 12, depth=7, circuit_type = "bv"):

    if not circuit_type in types:
        raise Exception("Unknown circuit type.")

    if circuit_type == "qiskit-random":
        return random_circuit(register_size, depth, 2)

    return generate_circ(num_qubits=register_size, depth=depth, circuit_type=circuit_type)

def run_batch(definition, batch = 5, context = {}):
    return timeit.repeat(stmt="definition()", setup="", repeat=batch, number = batch, globals={ **globals(), **context, "definition": definition })


def avg(arr):
    return sum(arr) / len(arr)
def cut(circuit, min_subcircuit_width=None, max_subcircuit_width=None, max_subcircuit_size=None, verbose = True):
    circuit_const = int( circuit.size() / circuit.num_qubits )
    
    if not max_subcircuit_width:
        max_subcircuit_width = circuit.num_qubits - circuit_const
    if not min_subcircuit_width:
        min_subcircuit_width = 2
    if not max_subcircuit_size:
        max_subcircuit_size = circuit.size() - circuit_const

    max_subcircuit_cuts = circuit.num_qubits
    quantum_cost_weight = 1.0
    max_cuts = circuit.num_qubits - 1

    result = {}
    last_try = False
    circuit_const = circuit_const + 1
    while result == {} or last_try:
        circuit_const = circuit_const - 1
        if circuit_const < circuit.num_qubits - 1 and circuit_const > 1:
           num_subcircuits = [ circuit_const for s in range(circuit_const, circuit.num_qubits - 1) ]
        else:
            last_try = True
            num_subcircuits = [ s for s in range(2, circuit.num_qubits - 1) ]

        # print("circuit_const: ", circuit_const)
        kwargs = {
            "max_subcircuit_width": max_subcircuit_width,
            "min_subcircuit_width": min_subcircuit_width,
            "max_subcircuit_cuts": max_subcircuit_cuts,
            "max_subcircuit_size": max_subcircuit_size,
            "quantum_cost_weight": quantum_cost_weight,
            "max_cuts": max_cuts,
            "num_subcircuits": num_subcircuits
        }

        result = find_cuts(circuit=circuit, **kwargs, verbose=verbose)
    # pprint({"circuit_const":circuit_const, **kwargs})
    return result

from pytket.routing import Architecture, route, place_with_map
from pytket.transform import Transform
from pytket.routing import Placement, LinePlacement, GraphPlacement, NoiseAwarePlacement
from pytket.extensions.qiskit import qiskit_to_tk, tk_to_qiskit

def Tket(circuit, backend = None, transform = False):
    pytket_circ = qiskit_to_tk(circuit)

    backend = get_backend_name(backend)
    backend = available_backend[backend]
    backend = backend()
    check_chip_compatiblity(backend, circuit) # Validate Chip Size to Circuit Size

    if transform:
        Transform.RemoveRedundancies().apply(physical_c)
        Transform.OptimisePostRouting().apply(physical_c)
        Transform.RebaseToQiskit().apply(physical_c)


    arc = Architecture(backend._configuration.coupling_map)

    # placement = Placement(arc)
    # placement_map = placement.get_placement_map(pytket_circ)

    physical_c = route(pytket_circ, arc)

    # placement.place(physical_c)
    # place_with_map(pytket_circ, placement_map)

    final_circ = tk_to_qiskit(physical_c)

    return final_circ

from qiskit.circuit import QuantumRegister, Qubit
from pprint import pprint
def tket_path(pathmap):
    map = pathmap.copy()
    q_node = Qubit(QuantumRegister(6, 'q'), 0)
    q_node.register.size, q_node.index
    
    for key, values in map.items():
        for index, conn in enumerate(map[key]): 
            # {'subcircuit_idx': 0, 'subcircuit_qubit': Qubit(QuantumRegister(4, 'q'), 0)}
            qubit = conn['subcircuit_qubit']
            qubit = Qubit(QuantumRegister(qubit.register.size, 'node'), qubit.index)
            qubit._hash = pathmap[key][index]['subcircuit_qubit']._hash
            map[key][index]['subcircuit_qubit'] = qubit
    return map

from qiskit import QuantumCircuit, QuantumRegister
def tket_rename_nodes(circuit):
    qreg = QuantumRegister(circuit.num_qubits , 'q')
    if circuit.cregs:
        creg = QuantumRegister(len(circuit.cregs) , 'c')
        circ = QuantumCircuit(qreg, creg)
    else:
        circ = QuantumCircuit(qreg)

    for instr, qargs, cargs in circuit._data:
        qreg = [ reg.index for reg in qargs ]
        circ.append(instr, qreg)

    return circ
from time import time
import itertools, copy

import numpy as np

from qiskit import IBMQ, transpile, Aer, execute

from qiskit.providers.aer import noise
from qiskit import QuantumCircuit
from qiskit.providers.aer import AerSimulator
from qiskit.tools.visualization import plot_histogram
from qiskit.test.mock import FakeBackend, FakeTokyo, FakeVigo, FakeMelbourne, FakePoughkeepsie, FakeQasmSimulator, FakeRueschlikon, FakeTenerife

available_backend = {
    "FakeTokyo": FakeTokyo, 
    "FakeVigo": FakeVigo, 
    "FakeMelbourne": FakeMelbourne, 
    "FakePoughkeepsie": FakePoughkeepsie, 
    "FakeRueschlikon": FakeRueschlikon, 
    "FakeTenerife": FakeTenerife
}

def get_alloted_backend(backend_stack, circ):
    for device, circuits in backend_stack.items():
        if circ in circuits:
            return device
    from pprint import pprint
    pprint(backend_stack)
    print("\n\n")
    print(type(circ))
    print([ circ ])
    raise Exception(f"The circuit {[circ]} has not been alocated to any device")

def get_backend_name(backend):
    for name, device in available_backend.items():
        if device is backend:
            return name
        if backend == name:
            return name
    raise Exception(f"Backend Error: No specified backend {str(backend)} found.")

def evaluate_circ(circuit, backend, options=None, TKET = False):
    
    if "tket_" in str(backend):
        backend_name = get_backend_name(backend[5:])
        TKET = True
    else:
        backend_name = get_backend_name(backend)
    # print("BACKEND : ", backend_name)
    if backend_name in available_backend:
        backend = available_backend[backend_name]()
        # if ENABLE_GPU:
        #     backend.set_options(device='GPU')
        # backend.set_options(max_parallel_threads=300, max_parallel_experiments=20, max_parallel_shots=128)
        noise_model = noise.NoiseModel.from_backend(backend)
        if isinstance(options,dict) and 'num_shots' in options:
            num_shots = options['num_shots']
        else:
            num_shots = max(1024,2**circuit.num_qubits)
        if isinstance(options,dict) and 'memory' in options:
            memory = options['memory']
        else:
            memory = False
        if circuit.num_clbits == 0:
            circuit = apply_measurement(circuit=circuit,qubits=circuit.qubits)
            if TKET:
                circuit = Tket(circuit, backend_name)
            check_chip_compatiblity(backend, circuit)
            print('Executing Circuit')
            job = execute(circuit, backend=backend, noise_model=noise_model, shots=num_shots, memory=memory).result()
        if memory:
            qasm_memory = np.array(job.get_memory(0))
            assert len(qasm_memory)==num_shots
            return qasm_memory
        else:
            counts = job.get_counts(0)
            assert sum(counts.values())==num_shots
            counts = dict_to_array(distribution_dict=counts,force_prob=True)
            return counts
    if type(backend) is str:
        if backend=='statevector_simulator':
            backend = Aer.get_backend('statevector_simulator')
            job = execute(circuit, backend=backend, optimization_level=0)
            result = job.result()
            output_sv = result.get_statevector(circuit)
            output_p = []
            for x in output_sv:
                amplitude = np.absolute(x)**2
                if amplitude>1e-16:
                    output_p.append(amplitude)
                else:
                    output_p.append(0)
            output_p = np.array(output_p)
            return output_p
        elif backend == 'noiseless_qasm_simulator':
            if isinstance(options,dict) and 'num_shots' in options:
                num_shots = options['num_shots']
            else:
                num_shots = max(1024,2**circuit.num_qubits)
            backend = Aer.get_backend('qasm_simulator')

            if isinstance(options,dict) and 'memory' in options:
                memory = options['memory']
            else:
                memory = False
            if circuit.num_clbits == 0:
                circuit = apply_measurement(circuit=circuit,qubits=circuit.qubits)
            noiseless_qasm_result = execute(circuit, backend, shots=num_shots, memory=memory).result()

            if memory:
                qasm_memory = np.array(noiseless_qasm_result.get_memory(0))
                assert len(qasm_memory)==num_shots
                return qasm_memory
            else:
                noiseless_counts = noiseless_qasm_result.get_counts(0)
                assert sum(noiseless_counts.values())==num_shots
                noiseless_counts = dict_to_array(distribution_dict=noiseless_counts,force_prob=True)
                return noiseless_counts
        elif backend=='noisy_qasm_simulator':
            noisy_qasm_result = execute(circuit, Aer.get_backend('qasm_simulator'),
            coupling_map=options['coupling_map'],
            basis_gates=options['basis_gates'],
            noise_model=options['noise_model'],
            shots=options['num_shots']).result()

            noisy_counts = noisy_qasm_result.get_counts(0)
            assert sum(noisy_counts.values())==options['num_shots']
            noisy_counts = dict_to_array(distribution_dict=noisy_counts,force_prob=True)
            return noisy_counts
        else:
            raise NotImplementedError

def simulate_subcircuit(subcircuit,eval_mode,num_shots, tket=False):
    '''
    Simulate a subcircuit
    '''
    if eval_mode=='sv':
        subcircuit_inst_prob = evaluate_circ(circuit=subcircuit,backend='statevector_simulator')
    elif eval_mode=='qasm':
        subcircuit_inst_prob = evaluate_circ(circuit=subcircuit,backend='noiseless_qasm_simulator',options={'num_shots':num_shots})
    else:
        subcircuit_inst_prob = evaluate_circ(circuit=subcircuit,backend=eval_mode,options={'num_shots':num_shots}, TKET=tket)
    return subcircuit_inst_prob


def compute(subcircuit_order, summation_terms, subcircuit_entry_probs):
	reconstructed_prob = None
	overhead = {'additions':0,'multiplications':0}
	for summation_term in summation_terms:
		summation_term_prob = None
		for subcircuit_idx in subcircuit_order:
			subcircuit_entry_idx = summation_term[subcircuit_idx]
			subcircuit_entry_prob = subcircuit_entry_probs[subcircuit_idx][subcircuit_entry_idx]
			if summation_term_prob is None:
				summation_term_prob = subcircuit_entry_prob
			else:
				summation_term_prob = np.kron(summation_term_prob,subcircuit_entry_prob)
				overhead['multiplications'] += len(summation_term_prob)
		if reconstructed_prob is None:
			reconstructed_prob = summation_term_prob
		else:
			reconstructed_prob += summation_term_prob
			overhead['additions'] += len(reconstructed_prob)
	return reconstructed_prob, overhead

def build(summation_terms, subcircuit_entry_probs, num_cuts, num_threads):
	smart_order = sorted(list(subcircuit_entry_probs.keys()),key=lambda subcircuit_idx:len(subcircuit_entry_probs[subcircuit_idx][0]))
	args = []
	for i in range(num_threads*5):
		segment_summation_terms = find_process_jobs(jobs=summation_terms,rank=i,num_workers=num_threads*5)
		if len(segment_summation_terms)==0:
			break
		arg = (smart_order,segment_summation_terms,subcircuit_entry_probs)
		args.append(arg)
	pool = mp.Pool(num_threads)
	results = pool.starmap(compute,args)
	pool.close()
	overhead = {'additions':0,'multiplications':0}
	reconstructed_prob = None
	for result in results:
		thread_reconstructed_prob, thread_overhead = result
		if reconstructed_prob is None:
			reconstructed_prob = thread_reconstructed_prob
		else:
			reconstructed_prob += thread_reconstructed_prob
		overhead['additions'] += thread_overhead['additions']
		overhead['multiplications'] += thread_overhead['multiplications']
	reconstructed_prob /= 2**num_cuts
	return reconstructed_prob, smart_order, overhead

def run_subcircuit_instances(subcircuits,subcircuit_instances,eval_mode,num_shots_fn, tket=False):
    '''
    subcircuit_instance_probs[subcircuit_idx][subcircuit_instance_idx] = measured probability
    '''
    print("run_subcircuit_instances()")
    print(subcircuit_instances)
    print("\n\n\n")
    # input()

    total_loop = 0
    for subcircuit_idx in subcircuit_instances:
        total_loop += len(subcircuit_instances[subcircuit_idx])
    print("Total Loop : ", total_loop)
    subcircuit_instance_probs = {}
    for subcircuit_idx in subcircuit_instances:
        subcircuit_instance_probs[subcircuit_idx] = {}
        num_shots = num_shots_fn(subcircuits[subcircuit_idx])
        for init_meas in subcircuit_instances[subcircuit_idx]:
            subcircuit_instance_idx = subcircuit_instances[subcircuit_idx][init_meas]
            if subcircuit_instance_idx not in subcircuit_instance_probs[subcircuit_idx]:
                print('Subcircuit %d instance %d'%(subcircuit_idx,subcircuit_instance_idx))
                subcircuit_instance = modify_subcircuit_instance(
                    subcircuit=subcircuits[subcircuit_idx],
                    init=init_meas[0],meas=init_meas[1])
                if  type(eval_mode) is dict:
                    subcircuit_inst_prob = simulate_subcircuit(subcircuit=subcircuit_instance,eval_mode=get_alloted_backend(eval_mode, subcircuits[subcircuit_idx]),num_shots=num_shots)
                else:
                    subcircuit_inst_prob = simulate_subcircuit(subcircuit=subcircuit_instance,eval_mode=eval_mode,num_shots=num_shots)
                mutated_meas = mutate_measurement_basis(meas=init_meas[1])
                for meas in mutated_meas:
                    measured_prob = measure_prob(unmeasured_prob=subcircuit_inst_prob,meas=meas)
                    mutated_subcircuit_instance_idx = subcircuit_instances[subcircuit_idx][(init_meas[0],meas)]
                    subcircuit_instance_probs[subcircuit_idx][mutated_subcircuit_instance_idx] = measured_prob
                    # print('Measured instance %d'%mutated_subcircuit_instance_idx)
    return subcircuit_instance_probs



    # subcircuit_instance_probs = {}
    # for subcircuit_idx in subcircuit_instances:
    #     subcircuit_instance_probs[subcircuit_idx] = {}
    #     num_shots = num_shots_fn(subcircuits[subcircuit_idx])
    #     # print("subcircuit_instances[subcircuit_idx] : ")
    #     # pprint(subcircuit_instances[subcircuit_idx])
    #     print("\n")
    #     for init_meas in subcircuit_instances[subcircuit_idx]:
    #         subcircuit_instance_idx = subcircuit_instances[subcircuit_idx][init_meas]
    #         if subcircuit_instance_idx not in subcircuit_instance_probs[subcircuit_idx]:
    #             # print('Subcircuit %d instance %d'%(subcircuit_idx,subcircuit_instance_idx))
    #             subcircuit_instance = modify_subcircuit_instance(
    #                 subcircuit=subcircuits[subcircuit_idx],
    #                 init=init_meas[0],meas=init_meas[1])
    #             print("simulate_subcircuit()")
    #             if  type(eval_mode) is dict:
    #                 # print("FOUND DICT", get_alloted_backend(eval_mode, subcircuits[subcircuit_idx]))
    #                 subcircuit_inst_prob = simulate_subcircuit(subcircuit=subcircuit_instance,eval_mode=get_alloted_backend(eval_mode, subcircuits[subcircuit_idx]),num_shots=num_shots, tket=tket)
    #             else:
    #                 # print("FOUND STR", get_alloted_backend(eval_mode, subcircuit_instance))
    #                 subcircuit_inst_prob = simulate_subcircuit(subcircuit=subcircuit_instance,eval_mode=eval_mode,num_shots=num_shots)
    #             mutated_meas = mutate_measurement_basis(meas=init_meas[1])
    #             for meas in mutated_meas:
    #                 measured_prob = measure_prob(unmeasured_prob=subcircuit_inst_prob,meas=meas)
    #                 mutated_subcircuit_instance_idx = subcircuit_instances[subcircuit_idx][(init_meas[0],meas)]
    #                 subcircuit_instance_probs[subcircuit_idx][mutated_subcircuit_instance_idx] = measured_prob
    #                 # print('Measured instance %d'%mutated_subcircuit_instance_idx)
    # return subcircuit_instance_probs

def constant_shots_fn(circuit):
    return 1024

def _attribute_shots(summation_terms, subcircuit_entries, subcircuit_instances, subcircuits, backend=None, tket=False):
    '''
    eval_mode = qasm: simulate shots
    eval_mode = sv: statevector simulation
    num_shots_fn: a function that gives the number of shots to take for a given circuit
    '''
    eval_mode = 'sv'
    if backend:
        eval_mode = backend

    num_shots_fn = constant_shots_fn
    # print("subcircuits : ", [ circ.num_qubits for circ in subcircuits ])
    # pprint("subcircuit_instances : ", subcircuit_instances)
    # print("_attribute_shots()")
    # subcircuit_instance_probs = run_subcircuit_instances(subcircuits=subcircuits,subcircuit_instances=subcircuit_instances, eval_mode=eval_mode,num_shots_fn=num_shots_fn, tket=tket)
    # print("subcircuit_instance_probs : ", [ f"{key}: {len(proba)}    " for key, proba in subcircuit_instance_probs.items() ])

    # subcircuit_entry_probs = {}
    # for subcircuit_idx in subcircuit_entries:
    #     subcircuit_entry_probs[subcircuit_idx] = {}
    #     for label in subcircuit_entries[subcircuit_idx]:
    #         subcircuit_entry_idx, kronecker_term = subcircuit_entries[subcircuit_idx][label]
    #         # print('Subcircuit {:d} entry {:d} kronecker_term {}'.format(
    #         #     subcircuit_idx, subcircuit_entry_idx, kronecker_term
    #         # ))
    #         subcircuit_entry_prob = None
    #         for term in kronecker_term:
    #             coefficient, subcircuit_instance_idx = term
    #             if subcircuit_entry_prob is None:
    #                 subcircuit_entry_prob = coefficient * subcircuit_instance_probs[subcircuit_idx][subcircuit_instance_idx]
    #             else:
    #                 subcircuit_entry_prob += coefficient * subcircuit_instance_probs[subcircuit_idx][subcircuit_instance_idx]
    #         subcircuit_entry_probs[subcircuit_idx][subcircuit_entry_idx] = subcircuit_entry_prob
    # return subcircuit_entry_probs
    
    subcircuit_instance_probs = run_subcircuit_instances(subcircuits=subcircuits,subcircuit_instances=subcircuit_instances, eval_mode=eval_mode,num_shots_fn=num_shots_fn, tket=tket)
    attribute_begin = time()
    subcircuit_entry_probs = {}
    for subcircuit_idx in subcircuit_entries:
        subcircuit_entry_probs[subcircuit_idx] = {}
        for label in subcircuit_entries[subcircuit_idx]:
            subcircuit_entry_idx, kronecker_term = subcircuit_entries[subcircuit_idx][label]
            # print('Subcircuit {:d} entry {:d} kronecker_term {}'.format(
            #     subcircuit_idx, subcircuit_entry_idx, kronecker_term
            # ))
            subcircuit_entry_prob = None
            for term in kronecker_term:
                coefficient, subcircuit_instance_idx = term
                if subcircuit_entry_prob is None:
                    subcircuit_entry_prob = coefficient * subcircuit_instance_probs[subcircuit_idx][subcircuit_instance_idx]
                else:
                    subcircuit_entry_prob += coefficient * subcircuit_instance_probs[subcircuit_idx][subcircuit_instance_idx]
            subcircuit_entry_probs[subcircuit_idx][subcircuit_entry_idx] = subcircuit_entry_prob
    attribute_time = time()-attribute_begin
    if self.verbose:
        print('attribute took %.3e seconds'%(attribute_time),flush=True)
    return subcircuit_entry_probs

from cutqc.post_process import get_label, attribute_label, fill_label, get_init_meas, convert_to_physical_init, get_cut_qubit_pairs
def generate_summation_terms(subcircuits, complete_path_map, num_cuts):
	'''
	Final CutQC reconstruction result = Sum(summation_terms)

	summation_terms (list) : [summation_term_0, summation_term_1, ...] --> 4^#cuts elements

	summation_term[subcircuit_idx] = subcircuit_entry_idx
	E.g. summation_term = {0:0,1:13,2:7} = Kron(subcircuit_0_entry_0, subcircuit_1_entry_13, subcircuit_2_entry_7)

	subcircuit_entries[subcircuit_idx][init_label,meas_label] = subcircuit_entry_idx, kronecker_term
	kronecker_term (list): (coefficient, subcircuit_instance_idx)
	Add coefficient*subcircuit_instance to subcircuit_entry

	subcircuit_instances[subcircuit_idx][init,meas] = subcircuit_instance_idx
	'''
	summation_terms = []
	subcircuit_entries = {subcircuit_idx:{} for subcircuit_idx in range(len(subcircuits))}
	subcircuit_instances = {subcircuit_idx:{} for subcircuit_idx in range(len(subcircuits))}
	O_rho_pairs = get_cut_qubit_pairs(complete_path_map=complete_path_map)
	for summation_term_idx in range(4**num_cuts):
		label = get_label(label_idx=summation_term_idx, num_cuts=num_cuts)
		print('%d/%d summation term:'%(summation_term_idx+1,4**num_cuts),label)
		summation_term = {}
		subcircuit_labels = attribute_label(label=label,O_rho_pairs=O_rho_pairs,num_subcircuits=len(subcircuits))
		for subcircuit_idx in range(len(subcircuits)):
			print('subcircuit %d label :'%subcircuit_idx,subcircuit_labels[subcircuit_idx])
			subcircuit_entry_key = (subcircuit_labels[subcircuit_idx]['init'],subcircuit_labels[subcircuit_idx]['meas'])
			if subcircuit_entry_key in subcircuit_entries[subcircuit_idx]:
				subcircuit_entry_idx, kronecker_term = subcircuit_entries[subcircuit_idx][subcircuit_entry_key]
				print('Already in, subcircuit_entry {:d} : {}'.format(subcircuit_entry_idx,kronecker_term))
			else:
				subcircuit_full_label = fill_label(subcircuit_idx=subcircuit_idx,
				subcircuit=subcircuits[subcircuit_idx],
				subcircuit_label=subcircuit_labels[subcircuit_idx],
				O_rho_pairs=O_rho_pairs)
				print('Full label :',subcircuit_full_label)
				subcircuit_init_meas = get_init_meas(init_label=subcircuit_full_label[0],meas_label=subcircuit_full_label[1])
				kronecker_term = []
				for init_meas in subcircuit_init_meas:
					init, meas = init_meas
					coefficient, init = convert_to_physical_init(init=list(init_meas[0]))
					if (init,meas) in subcircuit_instances[subcircuit_idx]:
						subcircuit_instance_idx = subcircuit_instances[subcircuit_idx][(init,meas)]
					else:
						subcircuit_instance_idx = len(subcircuit_instances[subcircuit_idx])
						subcircuit_instances[subcircuit_idx][(init,meas)] = subcircuit_instance_idx
					kronecker_term.append((coefficient,subcircuit_instance_idx))
				subcircuit_entry_idx = len(subcircuit_entries[subcircuit_idx])
				subcircuit_entries[subcircuit_idx][subcircuit_entry_key] = subcircuit_entry_idx, kronecker_term
				print('New subcircuit_entry, {:d} : {}'.format(subcircuit_entry_idx,kronecker_term))
			summation_term[subcircuit_idx] = subcircuit_entry_idx
		summation_terms.append(summation_term)
		print('summation_term =',summation_term,'\n')
	return summation_terms, subcircuit_entries, subcircuit_instances


def reconstruct(subcircuits, complete_path_map, full_circuit, num_cuts = None, backend = None, tket=False):

    if not num_cuts:
        num_cuts = graph_output_circuit_nums(complete_path_map) - 1
    print("num_cuts : ", num_cuts)

    summation_terms, subcircuit_entries, subcircuit_instances = generate_summation_terms(subcircuits, complete_path_map, num_cuts)

    print("subcircuit_instances", subcircuit_instances.keys())

    subcircuit_entry_probs = _attribute_shots(summation_terms, subcircuit_entries, subcircuit_instances, subcircuits, backend=backend, tket=tket)

    reconstructed_prob, smart_order, overhead = build(summation_terms, subcircuit_entry_probs, num_cuts, 200)

    unordered_prob = reconstructed_prob
    reconstructed_output, metrics = verify(full_circuit=full_circuit,
                unordered=unordered_prob,
                complete_path_map=complete_path_map,
                subcircuits=subcircuits,
                smart_order=smart_order,
                tket=tket)
    return reconstructed_output, metrics

import pickle
import argparse
import glob
import numpy as np
from qiskit.quantum_info import Statevector

# from qiskit_helper_functions.non_ibmq_functions import evaluate_circ
from qiskit_helper_functions.conversions import quasi_to_real
from qiskit_helper_functions.metrics import chi2_distance, MSE, MAPE, cross_entropy, HOP

def verify(full_circuit,unordered,complete_path_map,subcircuits,smart_order, tket):
    ground_truth = simulate_subcircuit(full_circuit, 'FakeTokyo', constant_shots_fn(full_circuit), tket)

    '''
    Reorder the probability distribution
    '''
    subcircuit_out_qubits = {subcircuit_idx:[] for subcircuit_idx in smart_order}
    for input_qubit in complete_path_map:
        path = complete_path_map[input_qubit]
        output_qubit = path[-1]
        subcircuit_out_qubits[output_qubit['subcircuit_idx']].append((output_qubit['subcircuit_qubit'],full_circuit.qubits.index(input_qubit)))
    for subcircuit_idx in subcircuit_out_qubits:
        subcircuit_out_qubits[subcircuit_idx] = sorted(subcircuit_out_qubits[subcircuit_idx],
        key=lambda x:subcircuits[subcircuit_idx].qubits.index(x[0]),reverse=True)
        subcircuit_out_qubits[subcircuit_idx] = [x[1] for x in subcircuit_out_qubits[subcircuit_idx]]
    # print('subcircuit_out_qubits:',subcircuit_out_qubits)
    unordered_qubit = []
    for subcircuit_idx in smart_order:
        unordered_qubit += subcircuit_out_qubits[subcircuit_idx]
    # print('CutQC out qubits:',unordered_qubit)
    reconstructed_output = np.zeros(len(unordered))
    for unordered_state, unordered_p in enumerate(unordered):
        bin_unordered_state = bin(unordered_state)[2:].zfill(full_circuit.num_qubits)
        _, ordered_bin_state = zip(*sorted(zip(unordered_qubit, bin_unordered_state),reverse=True))
        ordered_bin_state = ''.join([str(x) for x in ordered_bin_state])
        ordered_state = int(ordered_bin_state,2)
        ground_p = ground_truth[ordered_state]
        reconstructed_output[ordered_state] = unordered_p
    reconstructed_output = np.array(reconstructed_output)

    metrics = {}
    for quasi_conversion_mode in ['nearest','naive']:
        real_probability = quasi_to_real(quasiprobability=reconstructed_output,mode=quasi_conversion_mode)
        
        chi2 = chi2_distance(target=ground_truth,obs=real_probability)
        mse = MSE(target=ground_truth,obs=real_probability)
        mape = MAPE(target=ground_truth,obs=real_probability)
        ce = cross_entropy(target=ground_truth,obs=real_probability)
        hop = HOP(target=ground_truth,obs=real_probability)
        metrics[quasi_conversion_mode] = {
            'chi2':chi2,
            'Mean Squared Error':mse,
            'Mean Absolute Percentage Error':mape,
            'Cross Entropy':ce,
            'HOP':hop
            }
    return reconstructed_output, metrics
from qiskit import QuantumCircuit, execute

class CircuitLargerThanChip(Exception):
    pass

def check_chip_compatiblity(backend, circuit, raise_exception=True):
    if backend._configuration.n_qubits < circuit.num_qubits:
        if raise_exception:
            raise CircuitLargerThanChip(f"Circuit is larger than chip size.\nChip:{backend}\tCircuit:{circuit.num_qubits}")
        else:
            return False
    # print(f"Chip:{backend} {backend._configuration.n_qubits}\tCircuit:{circuit.num_qubits}")
    return True<jupyter_output><empty_output><jupyter_text>```json{    'max_cuts': 9,    'max_subcircuit_cuts': 10,    'max_subcircuit_size': 20,    'max_subcircuit_width': 13,    'min_subcircuit_width': 2,    'num_subcircuits': [2, 3, 4, 5, 6, 7, 8, 9],    'quantum_cost_weight': 1.0} ```<jupyter_code># circuit = generate_circuit( 6, 10, "bv") # "qiskit-random" )
# # print(circuit.draw())

# try:
#     cuts = cut(circuit, 2, 20, None, verbose=False)
#     if cuts == {}: raise
# except:
#     cuts = cut(circuit.decompose(), 2, 20, None, verbose=False)

# cuts
circuit = generate_circuit( 8, 12, "bv" )
print(circuit.draw())
try:
    cuts = cut(circuit, 2, 20, None, verbose=False)
    if cuts == {}: raise
except:
    cuts = cut(circuit.decompose(), 2, 20, None, verbose=False)
pprint(cuts)
num_cuts =  cuts['num_cuts']
subcircuits = cuts['subcircuits']
pprint(subcircuits)
complete_path_map = cuts['complete_path_map']
solution = get_solution(circuit, subcircuits, complete_path_map)

# pprint(solution)

reconstructed_output, metrics = reconstruct(subcircuits, complete_path_map, circuit, num_cuts=num_cuts, backend="FakeTokyo")
pprint(metrics)

reconstructed_output, metrics = reconstruct(subcircuits, complete_path_map, circuit, num_cuts=num_cuts, backend="tket_FakeTokyo")
pprint(metrics)
# print(Tket(circuit, FakeTokyo).draw())
# tket_rename_nodes(Tket(circuit, FakeTokyo)).draw()
{ name:device()._configuration.n_qubits for name, device in available_backend.items() }
# !git clone https://github.com/uuudown/QASMBench
import os, qiskit

from qiskit.dagcircuit.dagcircuit import DAGCircuit
from qiskit.converters import circuit_to_dag, dag_to_circuit

def circuit_measure_stripping(circuit):
    # Remove all single qubit gates and barriers in the circuit
    # print(circuit.draw())
    dag = circuit_to_dag(circuit)
    stripped_dag = DAGCircuit()
    [stripped_dag.add_qreg(x) for x in circuit.qregs]
    for vertex in dag.topological_op_nodes():
        # if len(vertex.qargs) == 2 and vertex.op.name!='barrier':
        # print(vertex.op.name, "   ", vertex.op.num_clbits)
        # while True:
        #     print(eval(input(">>>")))
        # print(vertex.op.name)
        if vertex.op.name not in ["measure","swap"]:
            stripped_dag.apply_operation_back(op=vertex.op, qargs=vertex.qargs, cargs=vertex.cargs)
        else:
            print(vertex.op.name)
    # print( list(new_dag.cregs.values()) )
    return dag_to_circuit(stripped_dag)

def load_qasm(root, qasm_files):
    result = {}
    for qasm_file in qasm_files:
        try:
            result [ qasm_file.replace(".qasm", "") ] = circuit_measure_stripping ( QuantumCircuit.from_qasm_file(f"{root}/{qasm_file}") )
        except qiskit.qasm.QasmError as ex:
            print(f"Cannot load qasm file {qasm_file} because of QASM Error...")
        except Exception as ex:
            print(f"Cannot load qasm file {qasm_file} because of Unknown Error...")
            # raise ex
    return result

def qasm_benchmark(base) -> dict:
    result = {}
    for (root,dirs,files) in os.walk(base):
        qasm_files = [ file for file in files if ".qasm" == file[-5:] ]
        if not qasm_files:
            circs = [ qasm_benchmark(directory).items() for directory in dirs ]
            result.update( dict( [circ for circ in circs if len(circ) == 2] ))
        else:
            result.update( load_qasm(root, qasm_files) )
            # print(qasm_files)
    return result

# benchmark_circuits = qasm_benchmark('QASMBench/small')
class Schedular:
    def __init__(self, backend_list):
        self.backend_list = backend_list
        self.backend_sizes = { backend()._configuration.n_qubits: backend for backend in backend_list }
        self.backend_run_stack = { backend: [] for backend in backend_list }
        self.pnode = None
        self.smallest_chip_size = min([device()._configuration.n_qubits for device in backend_list])
        self.largest_chip_size = max([device()._configuration.n_qubits for device in backend_list])
        self.circuit_stack = []
        
    def __call__(self, circuit, tket=False, save_results=True):
        # circuit = circuit_measure_stripping(circuit)
        if save_results:
            save_name = "tket " + name if tket else name
            if os.path.exists(f'{save_name}.pickle'):
                print(f"circuit {save_name} already exist...")
                return (None , None)

        if tket:
            ub = int(self.largest_chip_size - (self.smallest_chip_size / 2))
        else:
            ub = self.largest_chip_size - 1

        if self.smallest_chip_size < (circuit.num_qubits * 1.5) or tket:
            lb = 2
        else:
            lb = self.smallest_chip_size - 1

        try:
            cuts = cut(circuit, lb, ub, None, verbose=False)
            if cuts == {}: raise
        except:
            cuts = cut(circuit.decompose(), lb, ub, None, verbose=False)
        # except AssertionError as ex:
        #     return (None, None)

        if cuts == {}:
            print(f"cuts = cut(circuit, {lb}, {ub}, None, verbose=False)")
            raise Exception("No Subcircuit Found")
        

        # print("Counter: ", cuts['counter'])
        # pprint(cuts)

        self.circuit_stack = cuts['subcircuits']
        complete_path_map = cuts['complete_path_map']
        num_cuts = cuts['num_cuts']

        [ self.alot(circuit, tket) for circuit in self.circuit_stack ]


        # Distribute the circuit if some devices are free

        run_stack_size = [ len(run_stack) for backend, run_stack in self.backend_run_stack.items() ]
        
        empty_devices = [backend for backend, run_stack in self.backend_run_stack.items() if len(run_stack) == 0]
        most_busy_devices = [backend for backend, run_stack in self.backend_run_stack.items() if len(run_stack) == max(run_stack_size)]
        least_busy_devices = [backend for backend, run_stack in self.backend_run_stack.items() if len(run_stack) == min(run_stack_size)]
        
        # Device needed to be freed
        most_busy = [ (backend, run_stack) for backend, run_stack in self.backend_run_stack.items() if len(run_stack) == max(run_stack_size) ]

        for (backend, run_stack) in most_busy:
            size = len(run_stack)
            new_run_stack_1 = run_stack[:int(size//2)]
            new_run_stack_2 = run_stack[int(size//2):]

            self.backend_run_stack[backend] = new_run_stack_1

            allocated = False
            for devices in empty_devices:
                if check_chip_compatiblity(devices(), circuit, raise_exception=False):
                    self.backend_run_stack[devices] += new_run_stack_2
                    allocated = True
            if not allocated :
                self.backend_run_stack[backend] += new_run_stack_2


        subcircuits = self.circuit_stack
        if len(subcircuits)  < 2:
            print(f"Total {len(subcircuits)} subcircuits found...")
            return (None , None)

        solution = get_solution(circuit, subcircuits, complete_path_map)
        reconstructed_output, metrics = reconstruct(subcircuits, complete_path_map, circuit, backend=self.backend_run_stack, num_cuts = num_cuts, tket=tket)

        if save_results:
            if list(reconstructed_output) != None and metrics: 
                save_name = "tket " + name if tket else name
                save({"reconstructed_output": reconstructed_output, "metrics": metrics}, save_name)

        return (reconstructed_output, metrics) # self.backend_run_stack

    def alot(self, circuit, tket=False):
        # Fill `backend_run_stack` in order from low to high
        size_list = list(self.backend_sizes.keys())
        size_list = list(sorted(list(size_list)))
        for size in size_list:
            backend = self.backend_sizes[size]
            if check_chip_compatiblity(backend(), circuit, raise_exception=False):
                self.backend_run_stack[backend] += [circuit]
                return backend
        raise Exception("Circuit too big for available devices.")

    def kernal(self, circuit):
        pass
# { backend()._configuration.n_qubits: backend for backend in backend_list }
# !rm -r ./circ/*
# !rm -r "./tket circ/"
# !cp "circuit *.pickle" circ/
# !unrar x ./circ.rar
# !unrar x "./tket circ.rar"
import pickle

def save(obj, filename):
    with open(f'{filename}.pickle', 'wb') as handle:
        pickle.dump(obj, handle, protocol=pickle.HIGHEST_PROTOCOL)

metrics_list = []

backend_list = list(available_backend.values())

print("Creating Circuit...")
!mkdir "circ"
!mkdir "tket circ"
circuit_list = {}
for circtype in [
         # qiskit_helper_functions
         "bv",
        #  "supremacy",
        #  "supremacy_linear", # `/

        #  "hwea", # `/
        #  "qft",
        #  "aqft",
        #  "sycamore",

        #  "adder", # `/
        #  "grover", # `/
        #  "random",
         "qiskit-random" # `/
    ]:
    for qubit in range(6, 12):
        for depth in range(10, 20):
            try:
                circuit_list.update( { f"circ/cirucit [{circtype.replace('-','_')}-{depth}-{qubit}]": generate_circuit( qubit, depth, circtype )  } )
            except:
                print(f"cirucit [{circtype}-{depth}-{qubit}] not possible")

print(circuit_list)
print("Circuits Loaded...")

# from pprint import pprint

# def fill_label(subcircuit_idx, subcircuit, subcircuit_label, O_rho_pairs):
# 	'''
# 	Given a subcircuit, its label and O-rho cut qubit pairs
# 	Fill the full init,meas strings
# 	'''
# 	subcircuit_init_label_counter = 0
# 	subcircuit_meas_label_counter = 0
# 	init = ['zero' for q in range(subcircuit.num_qubits)]
# 	meas = ['comp' for q in range(subcircuit.num_qubits)]
# 	print("O Rho Pairs", O_rho_pairs)
# 	for pair in O_rho_pairs:
# 		O_qubit, rho_qubit = pair
# 		print(O_qubit, rho_qubit)
# 		print("subcircuit index : ", subcircuit_idx)
# 		if O_qubit['subcircuit_idx']==subcircuit_idx:
# 			print("O qubit", subcircuit_label, subcircuit_meas_label_counter)
# 			qubit_idx = subcircuit.qubits.index(O_qubit['subcircuit_qubit'])
# 			meas[qubit_idx] = subcircuit_label['meas'][subcircuit_meas_label_counter]
# 			subcircuit_meas_label_counter += 1
# 		if rho_qubit['subcircuit_idx']==subcircuit_idx:
# 			print("rho qubit", subcircuit_label, subcircuit_init_label_counter)
# 			qubit_idx = subcircuit.qubits.index(rho_qubit['subcircuit_qubit'])
# 			init[qubit_idx] = subcircuit_label['init'][subcircuit_init_label_counter]
# 			subcircuit_init_label_counter += 1
# 	return init, meas

# import cutqc

# cutqc.post_process.fill_label = fill_label
# from cutqc.post_process import generate_summation_terms
for name, circ in circuit_list.items():
    print(" === ", name, " === ")
    print(f"{name} circuit is being run")

    try:
        circ1 = circ.copy('c1')
        # print(circ1.draw())
        schedule = Schedular(backend_list)
        reconstructed_output, metrics = schedule(circ1, False, save_results=True)
        if metrics:
            metrics_list += [ metrics ]

    except Exception as ex:
        raise ex
        print(f"Exception found for circuit {ex}...")
        pass
        
    try:
        circ2 = circ.copy('c2')
        # print(circ2.draw())
        schedule = Schedular(backend_list)
        reconstructed_output, metrics = schedule(circ2, True, save_results=True)
        if metrics:
            metrics_list += [ metrics ]
    except Exception as ex:
        raise ex
        print(f"Exception found for circuit {ex}...")
        pass
    print("="*20)
    print("\n"*3)
    # circ/cirucit [bv-10-11]
!pip3 install pickle5
import pickle5 as pickle
import os # , pickle
import matplotlib as plt

from pprint import pprint

def get_circ_metadata(file):
    file = file.split('.')[0]
    file = file.split(' ')[-1]
    t, d, w = file[1:-1].split('-')
    return t, int(d), int(w)

def load_data(file):
    return pickle.load(open(file, 'rb'))

def get_inbetween(low, high, ticks):
    interval = ( high - low ) / (ticks+1)
    return [ low + (tick*interval) for tick in range(1, ticks + 1) ]

depth_start = 10
width_start = 6

depth_end = 20
width_end = 12

def load_attr(dir, attr):
    types = ["bv"]
    circ_data_nearest = {}
    circ_data_naive = {}

    for t in types:
        circ_data_nearest[t] = { (y, x):None for y in range(depth_start, depth_end) for x in range(width_start, width_end)} 
        circ_data_naive[t] = { (y, x):None for y in range(depth_start, depth_end) for x in range(width_start, width_end)}

    for (root,dirs,files) in os.walk(dir):
        for file in files:
            circ_type, depth, width = get_circ_metadata(file)
            data = load_data(f"{root}/{file}")
            # print( circ_type, depth, width )
            circ_data_nearest[circ_type][(depth, width)] = data['metrics']['nearest'][attr]
            circ_data_naive[circ_type][(depth, width)] = data['metrics']['naive'][attr]

    return circ_data_nearest, circ_data_naive

circ_data_nearest, circ_data_naive = load_attr('./circ', 'chi2')
pprint(circ_data_nearest)
# !zip -r circ.zip circ/
# !zip -r tketcirc.zip "tket circ/"
metrics_list[0]
# importing the required libraries
import matplotlib.pyplot as plt
import numpy as np

def draw(line1, line2, title, start_from = 0):
    plt.plot([i for i in range(start_from, start_from+len(line1))], line1) # Plot the chart
    plt.plot([i for i in range(start_from, start_from+len(line2))], line2, '-.') # Plot the chart
    # plt.xlabel("X-axis data")
    # plt.ylabel("Y-axis data")
    plt.title(title)
    plt.show() # display

def plot_metric(metrics_list, column):
    hop_naive = [metric['naive'][column]  for metric in metrics_list]
    hop_nearest = [metric['nearest'][column]  for metric in metrics_list]

    draw(hop_naive, hop_nearest, column, 6)

plot_metric(metrics_list, 'Cross Entropy')
plot_metric(metrics_list, 'Mean Squared Error')
# importing the required libraries
import matplotlib.pyplot as plt
import numpy as np

def draw(line1, line2, title, start_from = 0):
    plt.plot([i for i in range(start_from, start_from+len(line1))], line1) # Plot the chart
    plt.plot([i for i in range(start_from, start_from+len(line2))], line2, '-.') # Plot the chart
    # plt.xlabel("X-axis data")
    # plt.ylabel("Y-axis data")
    plt.title(title)
    plt.show() # display

def plot_metric(metrics_list, column):
    hop_naive = [metric['naive'][column]  for metric in metrics_list]
    hop_nearest = [metric['nearest'][column]  for metric in metrics_list]

    draw(hop_naive, hop_nearest, column, 6)

plot_metric(metrics_list, 'Cross Entropy')
plot_metric(metrics_list, 'Mean Squared Error')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09822096599883615, 'fraction_numerical': 0.007398786266522571, 'mean_word_length': 3.5125199287254993, 'pattern_counts': {'":': 19, '<': 16, '<?xml version=': 0, '>': 21, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '264603', 'license': 'MIT', 'n_tokens_mistral': 16525, 'n_tokens_neox': 15130, 'n_words': 2988, 'path': 'notebooks/Copy_of_CutQC_Workpad_2.ipynb', 'repo_name': 'LegacYFTw/qurzon', 'size': '48116'}