Source code for blind_transpiler.controllers.orchestrator

from qiskit import QuantumCircuit
from ..essentials.bop import  BOP
from ..essentials.bqc_instruction import BQCInstruction

from .qhe import QHE
from .fdqc import FDQC
from .ssdqc import SSDQC
from .ubqc import UBQC

from .base import BaseController

from ..global_value_store import GlobalVariablesAndFunctions

[docs] class BQC(BaseController): def __init__(self): BaseController.__init__(self) self.keys = None self.key_size = None
[docs] def generate_bqc(self, circ, format='qhe', basis_gates= None, keys=None ): ''' Controller function to translate a circuit to one to the four available formats: * qhe (Quantum Homomorphic Encryption) * fdqc (UBQC using ordered set of universal gates) * ssdqc (UBQC using fixed rotation gates) * ubqc (UBQC using recursive rotation gates) Args: circ (QuantumCircuit): object of quantum circuit format (str): accepted formats are the four available format: 'qhe', 'fdqc', 'ssdqc', and 'ubqc' basis_gates (list[str]): righnow not supported, might be helpfull later keys (str): string of binary random integers, need to generated using .generate_random() function of this class or need to generated size of random keys using .estimate_keysize() function Returns: bqc_instructions (BQCInstruction): list of all the instruction of BOP class Library Dependency: random ''' if basis_gates is None: basis_gates = self.default_basis_gates if keys is None: keys = self.generate_random_key(format=format, circ=circ) # define range by size of qc (done) key_size = self.estimate_keysize( format=format, circ=circ) # checking if the given key size is smaller than expected if len(keys) < key_size: import warnings warnings.warn(f'Overriding random key. Key size {len(keys)} given was insufficient (Needed={key_size})', category=UserWarning) keys = self.generate_random_key(format=format, circ=circ) # define range by size of qc (done) if format == 'ssdqc': # not implemented bqc_instructions = SSDQC().ssdqc(self.transpiled_circ, keys) elif format == 'fdqc': bqc_instructions = FDQC().fdqc(self.transpiled_circ, keys) elif format == 'qhe': bqc_instructions = QHE().qhe(self.transpiled_circ, keys) elif format == 'ubqc': bqc_instructions = UBQC().ubqc(self.transpiled_circ, keys) # store the key value in the class object self.keys = keys self.key_size = len(keys) # return the values return bqc_instructions
[docs] def transpile_circuit(self, circ: QuantumCircuit, format: str ) -> QuantumCircuit: ''' Transpiles the circuit into given basis set to be translated into blind form as written in translator library. Args: circ (QuantumCircuit): object of quantum circuit basis_gates (list[str]): rightnow, based on the default basis gates defined in init function of this class. Returns: transpiled_circ (QuantumCircuit): output of transpiled circuit. Library Dependency: qiskit.transpiler.preset_passmanager: generate_preset_pass_manager ''' from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager from qiskit.transpiler import PassManager from qiskit.transpiler.passes import SolovayKitaev GVF = GlobalVariablesAndFunctions() self._generate_basis(format) # will store the correst values for client and server basis according to format given # need to run this before using the default_basis_gates variable of base class, which depends on format pm = generate_preset_pass_manager( optimization_level=GVF.optimization_level, # Note with optimization_level > 1, transpiler is sometimes not working. basis_gates=self.default_basis_gates + ['rz'] # as rz gate is not in fdqc and ssdqc default basis, hence giving error # solution is to first transpile circuit with availability of rz gate and then use solovay pass if the basis set do not have rz gate ) transpiled_circ = pm.run(circ) if 'rz' not in self.default_basis_gates: # if rz is not present then transpilation should be using solovay kitaev pm = PassManager(SolovayKitaev(recursion_degree=GVF.recursion_degree, basis_gates=['x','z','h','s','sdg','t','tdg'] )) transpiled_circ = pm.run(transpiled_circ) transpiled_circ = pm.run(transpiled_circ) self.transpiled_circ = transpiled_circ # print(transpiled_circ) return transpiled_circ
########### utility functions ####################################################################################################
[docs] def estimate_keysize(self, format, circ): ''' Estimate the size of random key needed to perform classically assisted blind quantum computation. The size of key will be dependent on the format in which the circuit is needed to be blinded, and the circuit itself. Args: format (str): is a str having one of the four available format: 'qhe', 'fdqc', 'ssdqc', and 'ubqc' circ (QuantumCircuit): the circuit that need to be blinded. Returns: n_keys (int): size of the random key needed. Raises: ValueError(f'Instr: {instr.name} not in basis_set') ''' n_keys = 0 if self.transpiled_circ ==None: self.transpiled_circ = self.transpile_circuit(circ, format) # print(transpiled_circ.draw()) # print(f'ubqc server basis {server_basis}') for data in self.transpiled_circ.data: instr = data.operation # print(f'given inst {instr.name}') if instr.name in self.server_basis: n_keys += self.key_estimate[format][instr.name] if instr.name not in self.default_basis_gates and instr.name != 'barrier': raise ValueError(f'Instr: {instr.name} not in basis_set') return n_keys
[docs] def generate_random_key(self, key_size=None, format=None, circ=None, key_style='rand'): ''' Generate random key needed to perform classically assisted blind quantum computation. The size of key will be dependent on the format in which the circuit is needed to be blinded, and the circuit itself. Need to give either key_size pregenerated with 'estimate_key' function or circ and format to generate the keysize using estiamte_key and then generate key. Args: key_size (int): size of random key needed to blind the given circuit in given format. format (str): is a str having one of the four available format: 'qhe', 'fdqc', 'ssdqc', and 'ubqc' circ (QuantumCircuit): the circuit that need to be blinded. key_style attain four values 'rand', 'all_0', 'all_1', 'rand_fix' Returns: n_keys (int): size of the random key needed. Raises: ValueError('Give one of the two (key_size or (circ and format)) to generate the random key') Library Dependency: random Future Work: take random seed from user, as optional choice ''' import random if (key_size is None) & (circ is None or format is None): # Xor condition raise ValueError('Give one of the two (key_size or (circ and format)) to generate the random key') if key_size is None: # if keysize is not given then use circ to estimate the key size first key_size = self.estimate_keysize(format=format, circ=circ) key = None if key_style == 'rand': key = random.choices([0, 1], k=key_size) elif key_style == 'all_0': key = random.choices([0], k=key_size) elif key_style == 'all_1': key = random.choices([1], k=key_size) elif key_style == 'rand_fix': random.seed(42) key = random.choices([0,1], k=key_size) return key
bqc = BQC()