Source code for blind_transpiler.translators.homomorphicTranslator

from math import pi
from ..essentials.bop import BOP
from .base import BaseTranslator
from ..global_value_store import GlobalVariablesAndFunctions

# class StandardBlindTranslation(BaseTranslator):
[docs] class HomomorphicTranslation(BaseTranslator): def __init__(self): ''' Implementation is based on paper "Joshi, Mohit, Manoj Kumar Mishra, and S. Karthikeyan. "Quantum computing on encrypted data with arbitrary rotation gates." arXiv preprint arXiv:2508.18811 (2025)." Rightnow, this function have no use, made to remain consistent with other classes and for unseen future needs. The return type of each function is tuple of class BOP and the length is variable. ''' GVF = GlobalVariablesAndFunctions() self.constant_compute_space = GVF.qhe_compute_space # Note: This space can be reduce to 9 as rz can perform s,t gate also. self.ri = {'rz':(0,)} # resource_index: index of resource in server. Note, all other gates do not require additional ancilla space self.M = GVF.M None
[docs] def h(self, key, qargs, gate_seq): ''' Encryption and decryption logic of 'h' gate for qhe. Needed size of encryption key = 2 Args: key (list[int]): contains randomly generated binary keys, each element can be 0 or 1. qargs (list[int]): contains the argument on which the key has to be applied. gate_seq (int): used to store the information of which gate from original circuit, this translation is coming from. Returns: Tuple[BOP]: tuple of BOP class object sequence needed for encryption, compute and decryption of the h gate. Raise: None Library Dependency: qiksit.circuit.library: XGate, ZGate, HGate ''' from qiskit.circuit.library import XGate, ZGate, HGate q = self._create_qubit_map(qargs) A,B = key enc_1 = BOP('enc', (q[0],), [] , XGate(), A, gate_seq=gate_seq) enc_2 = BOP('enc', (q[0],), [], ZGate(),B, gate_seq=gate_seq) compute = self._server_resource( q, 'h', gate_seq, theta=None) dec_1 = BOP('dec', (q[0],), [], XGate(),B, gate_seq=gate_seq) dec_2 = BOP('dec',(q[0],), [], ZGate(), A, gate_seq=gate_seq) return (enc_1, enc_2, *compute, dec_1, dec_2)
[docs] def s(self, key, qargs, gate_seq): ''' Encryption and decryption logic of 's' gate for qhe. Needed size of encryption key = 2 Args: key (list[int]) contains randomly generated binary keys, each element can be 0 or 1. qargs (list[int]) contains the argument on which the key has to be applied. gate_seq (int) used to store the information of which gate from original circuit, this translation is coming from. Return: Tuple[BOP]: tuple of BOP class object sequence needed for encryption, compute and decryption of the s gate. Raise: None Library Dependency: qiksit.circuit.library: XGate, ZGate, RZGate math: pi ''' from qiskit.circuit.library import XGate, ZGate, RZGate from math import pi q = self._create_qubit_map(qargs) A,B = key enc_1 = BOP('enc', (q[0],), [], XGate(), A, gate_seq=gate_seq) enc_2 = BOP('enc', (q[0],), [], ZGate(), B, gate_seq=gate_seq) compute = self._server_resource( q, 's', gate_seq, theta=None) dec_1 = BOP('dec', (q[0],), [], ZGate(), A^B, gate_seq=gate_seq) dec_2 = BOP('dec', (q[0],), [], XGate(), A, gate_seq=gate_seq) return (enc_1, enc_2, *compute, dec_1, dec_2)
[docs] def sdg(self, key, qargs, gate_seq): ''' Encryption and decryption logic of 'sdg' gate for qhe. Needed size of encryption key = 2 Args: key (list[int]) contains randomly generated binary keys, each element can be 0 or 1. qargs (list[int]) contains the argument on which the key has to be applied. gate_seq (int) used to store the information of which gate from original circuit, this translation is coming from. Return: Tuple[BOP]: tuple of BOP class object sequence needed for encryption, compute and decryption of the sdg gate. Raise: None Library Dependency: qiksit.circuit.library: XGate, ZGate, RZGate math: pi ''' from qiskit.circuit.library import XGate, ZGate, RZGate from math import pi q = self._create_qubit_map(qargs) A,B = key enc_1 = BOP('enc', (q[0],), [], XGate(), A, gate_seq=gate_seq) enc_2 = BOP('enc', (q[0],), [], ZGate(), B, gate_seq=gate_seq) compute = self._server_resource( q, 'sdg', gate_seq, theta=None) dec_1 = BOP('dec', (q[0],), [], ZGate(), A^B, gate_seq=gate_seq) dec_2 = BOP('dec', (q[0],), [], XGate(), A, gate_seq=gate_seq) return (enc_1, enc_2, *compute, dec_1, dec_2)
[docs] def t(self, key, qargs, gate_seq): ''' Encryption and decryption logic of 't' gate for qhe. Needed size of encryption key = 4 Args: key (list[int]): contains randomly generated binary keys, each element can be 0 or 1. qargs (list[int]): contains the argument on which the key has to be applied. gate_seq (int): used to store the information of which gate from original circuit, this translation is coming from. Return: Tuple[BOP]: tuple of BOP class object sequence needed for encryption, compute and decryption of the 't' gate. Raise: None Library Dependency: qiksit.circuit.library: XGate, ZGate, RZGate math: pi ''' from qiskit.circuit.library import XGate, ZGate, IGate from math import pi q = self._create_qubit_map(qargs) A,B, A1,B1 = key enc_1 = BOP('enc', (q[0],), [], XGate(), A, gate_seq=gate_seq) enc_2 = BOP('enc', (q[0],), [], ZGate(), B, gate_seq=gate_seq) # compute = BOP('compute', (q[0],), [], RZGate(pi/4), gate_seq=gate_seq) compute = self._server_resource( q, 't', gate_seq, theta=None) dec_1 = BOP('dec', (q[0],), [], ZGate(), B, gate_seq=gate_seq) dec_2 = BOP('dec', (q[0],), [], XGate(), A, gate_seq=gate_seq) if A == 1: s_compute = self.s( key=[A1, B1], qargs=qargs, gate_seq=gate_seq) else: s_compute = [BOP('client', (q[0],), [], IGate(), A, gate_seq=gate_seq) for _ in range(5)] # done to make the length of callback same when used in fdqc library return (enc_1, enc_2, *compute, dec_1, dec_2, *s_compute)
[docs] def tdg(self, key, qargs, gate_seq): ''' Encryption and decryption logic of 't' gate for qhe. Needed size of encryption key = 4 Args: key (list[int]): contains randomly generated binary keys, each element can be 0 or 1. qargs (list[int]): contains the argument on which the key has to be applied. gate_seq (int): used to store the information of which gate from original circuit, this translation is coming from. Return: Tuple[BOP]: tuple of BOP class object sequence needed for encryption, compute and decryption of the 't' gate. Raise: None Library Dependency: qiksit.circuit.library: XGate, ZGate, RZGate math: pi ''' from qiskit.circuit.library import XGate, ZGate, IGate from math import pi q = self._create_qubit_map(qargs) A,B, A1,B1 = key enc_1 = BOP('enc', (q[0],), [], XGate(), A, gate_seq=gate_seq) enc_2 = BOP('enc', (q[0],), [], ZGate(), B, gate_seq=gate_seq) # compute = BOP('compute', (q[0],), [], RZGate(pi/4), gate_seq=gate_seq) compute = self._server_resource( q, 'tdg', gate_seq, theta=None) dec_1 = BOP('dec', (q[0],), [], ZGate(), B, gate_seq=gate_seq) dec_2 = BOP('dec', (q[0],), [], XGate(), A, gate_seq=gate_seq) if A == 1: sdg_compute = self.sdg( key=[A1, B1], qargs=qargs, gate_seq=gate_seq) else: sdg_compute = [BOP('client', (q[0],), [], IGate(), A, gate_seq=gate_seq) for _ in range(5)] # done to make the length of callback same when used in fdqc library return (enc_1, enc_2, *compute, dec_1, dec_2, *sdg_compute)
[docs] def rz(self, theta, key, qargs, gate_seq): ''' Encryption and Decryption logic for 'rz' gate of qhe. (Can't handle different keys for all the gates rightnow.) Decomposes the theta in integral power of series = a*pi + b*pi/2 + c*pi/4 + d*pi/8 + e*pi/16 + ..... (default= 20 precision points) approximatedly equal to theta, if theta is not integer power of pi/4, using function '_rz_integral' Logic of the function: * Case 1: exact expansion for n*pi/4 if n is integer. * Case 2: approximate expansion if n is not integer. Args: theta: float contain the theta parameter (in radian) to apply theta rotation on the circuit. key: list[int] contains randomly generated binary keys, each element can be 0 or 1. qargs: list[int] contains the argument on which the key has to be applied. gate_seq: int used to store the information of which gate from original circuit, this translation is coming from. Return: Tuple[BOP] tuple of BOP class object sequence needed for encryption, compute and decryption of the s gate. Here as the circuit was of variable length, we have used recursive_roll to revert the output. Raise: None Library Dependency: qiksit.circuit.library XGate, ZGate, RZGate math pi ''' ## NOTE: function based on my paper 'quantum computing on encrypted data with arbitrary rotation gates' ## check the correctness here from qiskit.circuit.library import SwapGate, XGate, ZGate from math import ceil, pi,log # remove this line after testing ri = self.ri qargs = self._create_qubit_map(qargs) bin, int_part =self._decompose_theta(theta) # precision can also be asked from user x_key = [key[i] for i in range(len(key)) if i % 2 == 0] z_key = [key[i] for i in range(len(key)) if i % 2 != 0] full_variable_unroll = [] M = len(bin) # handle integral part if int_part % 2 == 1: full_variable_unroll.append(BOP('client', (qargs[0],), [], ZGate(), 1, gate_seq=gate_seq)) # pad the bin to make it run for bin[M] bin.insert(0,0) for m in range(M, 0,-1): if bin[m] == 0: pass # no need to do anything if angle is not present # this logic is written just for clarity if bin[m] == 1 or bin[m]==-1: run_of_one = False cond = 1 if bin[m] == 1 or bin[m] == -1 else 0 full_variable_unroll.append(BOP('enc', (qargs[0],), [], XGate(), x_key[m], gate_seq=gate_seq)) full_variable_unroll.append(BOP('enc', (qargs[0],), [], ZGate(), z_key[m], gate_seq=gate_seq)) full_variable_unroll.append(BOP('swap', (qargs[0],ri['rz'][0]), [], SwapGate(), cond, gate_seq=gate_seq )) full_variable_unroll.append(*self._server_resource( qargs, 'rz', gate_seq, theta=bin[m]*pi/2**m)) full_variable_unroll.append(BOP('swap', (qargs[0],ri['rz'][0]), [], SwapGate(), cond ,gate_seq=gate_seq )) full_variable_unroll.append(BOP('dec', (qargs[0],), [], ZGate(), z_key[m], gate_seq=gate_seq)) full_variable_unroll.append(BOP('dec', (qargs[0],), [], XGate(), x_key[m], gate_seq=gate_seq)) if (x_key[m]) == 1: run_of_one = True # for recursive correction of 2\theta for k in range(m-1, 0, -1 ): if run_of_one == True: cond = x_key[k+1] full_variable_unroll.append(BOP('enc', (qargs[0],), [], XGate(), x_key[k], gate_seq=gate_seq)) full_variable_unroll.append(BOP('enc', (qargs[0],), [], ZGate(), z_key[k], gate_seq=gate_seq)) full_variable_unroll.append(BOP('swap', (qargs[0],ri['rz'][0]), [], SwapGate(), cond ,gate_seq=gate_seq )) full_variable_unroll.append(*self._server_resource( qargs, 'rz', gate_seq, theta=bin[m]*pi/2**k)) full_variable_unroll.append(BOP('swap', (qargs[0],ri['rz'][0]), [], SwapGate(), cond,gate_seq=gate_seq )) full_variable_unroll.append(BOP('dec', (qargs[0],), [], ZGate(), z_key[k], gate_seq=gate_seq)) full_variable_unroll.append(BOP('dec', (qargs[0],), [], XGate(), x_key[k], gate_seq=gate_seq)) if (x_key[k]) == 0: run_of_one = False if run_of_one == False: full_variable_unroll.append(BOP('enc', (qargs[0],), [], XGate(), x_key[k], gate_seq=gate_seq)) full_variable_unroll.append(BOP('enc', (qargs[0],), [], ZGate(), z_key[k], gate_seq=gate_seq)) full_variable_unroll.append(*self._server_resource( qargs, 'rz', gate_seq, theta=bin[m]*pi/2**k)) full_variable_unroll.append(BOP('dec', (qargs[0],), [], ZGate(), z_key[k], gate_seq=gate_seq)) full_variable_unroll.append(BOP('dec', (qargs[0],), [], XGate(), x_key[k], gate_seq=gate_seq)) # last Z correction if run_of_one == True: cond = 1 if bin[m] == 1 or bin[m] == -1 else 0 full_variable_unroll.append(BOP('client', (qargs[0],), [], ZGate(), cond, gate_seq=gate_seq)) return tuple(full_variable_unroll)
[docs] def cx(self, key, qargs, gate_seq): ''' Encryption and decryption logic of 'cx' gate for qhe. Needed size of encryption key = 4 Args: key: list[int] contains randomly generated binary keys, each element can be 0 or 1. qargs: list[int] contains the argument on which the key has to be applied. gate_seq: int used to store the information of which gate from original circuit, this translation is coming from. Return: Tuple[BOP] tuple of BOP class object sequence needed for encryption, compute and decryption of the 'cx' gate. Raise: None Library Dependency: qiksit.circuit.library XGate, ZGate math - pi ''' from qiskit.circuit.library import XGate, ZGate q = self._create_qubit_map(qargs) A,B,C,D = key enc_1 = BOP('enc', (q[0],), [], XGate(), A, gate_seq=gate_seq) enc_2 = BOP('enc', (q[0],), [], ZGate(), B, gate_seq=gate_seq) enc_3 = BOP('enc', (q[1],), [], XGate(), C, gate_seq=gate_seq) enc_4 = BOP('enc', (q[1],), [], ZGate(), D, gate_seq=gate_seq) compute = self._server_resource( q, 'cx', gate_seq, theta=None) dec_1 = BOP('dec', (q[0],), [], ZGate(), B^D, gate_seq=gate_seq) dec_2 = BOP('dec', (q[0],), [], XGate(), A, gate_seq=gate_seq ) dec_3 =BOP('dec', (q[1],), [], ZGate(), D, gate_seq=gate_seq) dec_4 = BOP('dec', (q[1],), [], XGate(), A^C, gate_seq=gate_seq) return (enc_1, enc_2, enc_3, enc_4, *compute, dec_1, dec_2, dec_3, dec_4)
[docs] def cz(self, key, qargs, gate_seq): ''' Encryption and decryption logic of 'cz' gate for qhe. Needed size of encryption key = 4 Args: key: list[int] contains randomly generated binary keys, each element can be 0 or 1. qargs: list[int] contains the argument on which the key has to be applied. gate_seq: int used to store the information of which gate from original circuit, this translation is coming from. Return: Tuple[BOP] tuple of BOP class object sequence needed for encryption, compute and decryption of the 'cz' gate. Raise: None Library Dependency: qiksit.circuit.library - XGate, ZGate math - pi ''' from qiskit.circuit.library import XGate, ZGate q = self._create_qubit_map(qargs) A,B,C,D = key # a,b,c,d = key enc_1 = BOP('enc', (q[0],), [], XGate(), A, gate_seq=gate_seq) enc_2 = BOP('enc', (q[0],), [], ZGate(), B, gate_seq=gate_seq) enc_3 = BOP('enc', (q[1],), [], XGate(), C, gate_seq=gate_seq) enc_4 = BOP('enc', (q[1],), [], ZGate(), D, gate_seq=gate_seq) compute = self._server_resource( q, 'cz', gate_seq, theta=None) dec_1 = BOP('dec', (q[0],), [], ZGate(), B^C, gate_seq=gate_seq) dec_2 = BOP('dec', (q[0],), [], XGate(), A, gate_seq=gate_seq) dec_3 = BOP('dec', (q[1],), [], ZGate(), A^D, gate_seq=gate_seq) dec_4 = BOP('dec', (q[1],), [], XGate(), C, gate_seq=gate_seq) return (enc_1, enc_2, enc_3, enc_4, *compute, dec_1, dec_2, dec_3, dec_4)
[docs] def ccx(self, key, qargs, gate_seq): ''' Encryption and decryption logic of 't' gate for qhe. Needed size of encryption key = 18 (Need hidden conditionals for decryption: 1 CZ and 2 CX = 6 + 1*4 + 2*4 = 18) Args: key: list[int] contains randomly generated binary keys, each element can be 0 or 1. qargs: list[int] contains the argument on which the key has to be applied. gate_seq: int used to store the information of which gate from original circuit, this translation is coming from. Return: Tuple[BOP] tuple of BOP class object sequence needed for encryption, compute and decryption of the 't' gate. Raise: None Library Dependency: qiksit.circuit.library - XGate, ZGate math - pi ''' from qiskit.circuit.library import XGate, ZGate q = self._create_qubit_map(qargs) A,B,C,D,E,F, G,H,I,J, K,L,M,N, O,P,Q,R = key enc_1 = BOP('enc', (q[0],), [], XGate(), A, gate_seq=gate_seq) enc_2 = BOP('enc', (q[0],), [], ZGate(), B, gate_seq=gate_seq) enc_3 = BOP('enc', (q[1],), [], XGate(), C, gate_seq=gate_seq) enc_4 = BOP('enc', (q[1],), [], ZGate(), D, gate_seq=gate_seq) enc_5 = BOP('enc', (q[2],), [], XGate(), E, gate_seq=gate_seq) enc_6 = BOP('enc', (q[2],), [], ZGate(), F, gate_seq=gate_seq) compute = self._server_resource( q, 'ccx', gate_seq, theta=None) dec_1 = BOP('dec', (q[2],), [], ZGate(), F, gate_seq=gate_seq) dec_2 = BOP('dec', (q[2],), [], XGate(), E, gate_seq=gate_seq) #apply conditional cz cz_enc_1, cz_enc_2, cz_enc_3, cz_enc_4, cz_compute, cz_dec_1, cz_dec_2, cz_dec_3, cz_dec_4 = self._apply_conditional(F, self.cz((G,H,I,J), (q[0],q[1]), gate_seq=gate_seq)) dec_4 = BOP('dec', (q[1],), [], ZGate(), D, gate_seq=gate_seq) dec_5 = BOP('dec', (q[1],), [], XGate(), C, gate_seq=gate_seq) #apply first conditional cx cx1_enc_1, cx1_enc_2, cx1_enc_3, cx1_enc_4, cx1_compute, cx1_dec_1, cx1_dec_2, cx1_dec_3, cx1_dec_4 = self._apply_conditional(C, self.cx((K,L,M,N), (q[0],q[2]), gate_seq=gate_seq)) dec_6 = BOP('dec',(q[0],), [], ZGate(), B, gate_seq=gate_seq) dec_7 = BOP('dec', (q[0],), [], XGate(), A, gate_seq=gate_seq) #apply second conditional cx cx2_enc_1, cx2_enc_2, cx2_enc_3, cx2_enc_4, cx2_compute, cx2_dec_1, cx2_dec_2, cx2_dec_3, cx2_dec_4 = self._apply_conditional(A, self.cx((O,P,Q,R),(q[1],q[2]), gate_seq=gate_seq)) return (enc_1, enc_2, enc_3, enc_4, enc_5, enc_6, *compute, dec_1, dec_2, cz_enc_1, cz_enc_2, cz_enc_3, cz_enc_4, cz_compute, cz_dec_1, cz_dec_2, cz_dec_3, cz_dec_4, dec_4, dec_5, cx1_enc_1, cx1_enc_2, cx1_enc_3, cx1_enc_4, cx1_compute, cx1_dec_1, cx1_dec_2, cx1_dec_3, cx1_dec_4, dec_6, dec_7, cx2_enc_1, cx2_enc_2, cx2_enc_3, cx2_enc_4, cx2_compute, cx2_dec_1, cx2_dec_2, cx2_dec_3, cx2_dec_4 )
# utility functions def _apply_conditional(self, x, op_tuple): ''' Modifies the conditional of the list of object of BOP class, with Binary ANDing it with incoming x, using the 'conditional_and' function of BOP class. Args: x ({0,1}): conditional to be used for conditional applying gate like cx and cz in ccx decryption op_tuple (tuple[BOP]): list of object of BOP class to change the conditional Return: tuple[BOP] Tuple of object of BOP class with modified conditional Raise: None Library Dependency: None ''' lst = [] for op in op_tuple: new_op = op.conditional_and(x) lst.append(new_op) return tuple(lst) def _create_qubit_map(self, new_qargs): ''' Creates a map between old and new qargs to be used for update the position of operation. This is used if place like cx(1,2) has to applies but library is made such that it implements cx(0,1), this function ensures hassle-free proper translation. Args: new_qargs (List[int]): store new qubit position, according to above example store [1,2] Returns: Dict[int->int]: dictionary map of old_qubit->new_qubit Raise: None Library Dependency: None ''' qarg_map = {} for i, qargs in enumerate(new_qargs): qarg_map[i] = qargs return qarg_map def _server_resource(self, q, gate, gate_seq, theta=None): ''' Defines the server resource whict take in gate and apply it on demand Args: q: list of qubit locations gate: the gate that is needed to be implemented gate_seq: the sequence for which the gate is being implemented theta: additional argument need to implementation of Rz gate only Return: unroll: tuple that contains the gates that have been implemented by server Raise: ValueError('Internal Error: no theta found.') Raises error if theta is not gate but gate=Rz which requires theta ''' from qiskit.circuit.library import HGate, RZGate, CXGate, CZGate, CCXGate unroll = [] if gate == 'h': unroll.append(BOP('compute', (q[0],), [], HGate(), gate_seq=gate_seq)) elif gate == 's': unroll.append(BOP('compute', (q[0],), [], RZGate(pi/2), gate_seq=gate_seq)) elif gate == 'sdg': unroll.append(BOP('compute', (q[0],), [], RZGate(-pi/2), gate_seq=gate_seq)) elif gate == 't': unroll.append(BOP('compute', (q[0],), [], RZGate(pi/4), gate_seq=gate_seq)) elif gate == 'tdg': unroll.append(BOP('compute', (q[0],), [], RZGate(-pi/4), gate_seq=gate_seq)) elif gate == 'rz': if theta == None: raise ValueError('Internal Error: no theta found.') unroll.append(BOP('compute', self.ri['rz'], [], RZGate(theta), gate_seq=gate_seq)) elif gate == 'cx': unroll.append(BOP('compute', (q[0],q[1]), [], CXGate(), gate_seq=gate_seq)) elif gate == 'cz': unroll.append(BOP('compute', (q[0],q[1]), [], CZGate(), gate_seq=gate_seq)) elif gate == 'ccx': unroll.append(BOP('compute', (q[0],q[1],q[2]), [], CCXGate(), gate_seq=gate_seq) ) return tuple(unroll)