def convert_measurements(self, backend_result, target_qubits=None) -> QubitWaveFunction: """ Transform backend evaluation results into QubitWaveFunction Parameters ---------- backend_result: the return value of backend simulation. Returns ------- QubitWaveFunction results transformed to tequila native QubitWaveFunction """ result = QubitWaveFunction() # todo there are faster ways for k, v in backend_result.frequencies(binary=True).items(): converted_key = BitString.from_bitstring( other=BitString.from_binary(binary=k)) result._state[converted_key] = v if target_qubits is not None: mapped_target = [self.qubit_map[q].number for q in target_qubits] mapped_full = [ self.qubit_map[q].number for q in self.abstract_qubits ] keymap = KeyMapRegisterToSubregister(subregister=mapped_target, register=mapped_full) result = result.apply_keymap(keymap=keymap) return result
def test_bitstring_lsb(): for i in range(15): bit = BitString.from_int(integer=i) bit_lsb = BitStringLSB.from_int(integer=i) assert (bit.integer == i) assert (bit.binary == format(i, 'b')) assert (bit.binary == bin(i)[2:]) assert (bit_lsb.integer == bit.integer) assert (bit != bit_lsb) arrays = [[0, 0, 1], [1, 0, 0], [1, 0, 1], [1, 0, 1, 1, 1, 0], [1, 0, 0, 1, 1, 1, 0, 0, 1, 0]] integers = [1, 4, 5, 32 + 8 + 4 + 2, 2 + 16 + 32 + 64 + 512] integers_lsb = [4, 1, 5, 1 + 4 + 8 + 16, 1 + 8 + 16 + 32 + 256] for i, arr in enumerate(arrays): nbits = len(arr) bita = BitString.from_array(array=arr, nbits=nbits) bitb = BitString.from_int(integer=integers[i], nbits=nbits) bitc = BitStringLSB.from_array(array=arr, nbits=nbits) assert (bita == bitb) assert (bita.integer == integers[i]) assert (bita.array == arr) assert (bitb.integer == integers[i]) assert (bitb.array == arr) assert (bitc.integer == integers_lsb[i]) assert (bitc.array == arr) assert (bitc.binary == bita.binary)
def decompose_transfer_operator( ket: BitString, bra: BitString, qubits: typing.List[int] = None) -> QubitHamiltonian: """ Notes ---------- Create the operator Note that this is operator is not necessarily hermitian So be careful when using it as a generator for gates e.g. decompose_transfer_operator(ket="01", bra="10", qubits=[2,3]) gives the operator .. math:: \\lvert 01 \\rangle \\langle 10 \\rvert_{2,3} acting on qubits 2 and 3 Parameters ---------- ket: pass an integer, string, or tequila BitString bra: pass an integer, string, or tequila BitString qubits: pass the qubits onto which the operator acts Returns ------- """ opmap = {(0, 0): Qp, (0, 1): Sp, (1, 0): Sm, (1, 1): Qm} nbits = None if qubits is not None: nbits = len(qubits) if isinstance(bra, int): bra = BitString.from_int(integer=bra, nbits=nbits) if isinstance(ket, int): ket = BitString.from_int(integer=ket, nbits=nbits) b_arr = bra.array k_arr = ket.array assert (len(b_arr) == len(k_arr)) n_qubits = len(k_arr) if qubits is None: qubits = range(n_qubits) assert (n_qubits <= len(qubits)) result = QubitHamiltonian.unit() for q, b in enumerate(b_arr): k = k_arr[q] result *= opmap[(k, b)](qubit=qubits[q]) return result
def __call__(self, input_state: BitStringLSB, initial_state: int = None) -> BitString: if isinstance(input_state, numbers.Integral): return BitString.from_int(integer=input_state) else: return BitString.from_int(integer=input_state.integer, nbits=input_state.nbits)
def get_qubit_key(self, state): qubit_string = BitString.from_int(integer=0, nbits=self.n_qubits) for p, v in state.items(): for m, occ in v.items(): mode = self.get_mode(p, m) occ = BitString.from_int(integer=occ, nbits=mode.n_qubits) for i, q in enumerate(mode.qubits): qubit_string[q] = occ[i] return qubit_string
def __call__(self, input_state: BitString, initial_state: BitString = None) -> BitString: """ Map from register to subregister :param input_state: :return: input_state only on subregister """ input_state = BitString.from_int(integer=input_state.integer, nbits=len(self._register)) output_state = BitString.from_int(integer=0, nbits=len(self._subregister)) for k, v in enumerate(self._subregister): output_state[k] = input_state[v] return output_state
def inverted(self, input_state: int) -> BitString: """ Map from register to subregister :param input_state: :return: input_state only on subregister """ input_state = BitString.from_int(integer=input_state, nbits=len(self._register)) output_state = BitString.from_int(integer=0, nbits=len(self._subregister)) for k, v in enumerate(self._subregister): output_state[k] = input_state[v] return output_state
def __call__(self, input_state: BitString, initial_state: BitString = None) -> BitString: if initial_state is None: initial_state = BitString.from_int(integer=0) input_state = BitString.from_int(integer=input_state, nbits=len(self._subregister)) initial_state = BitString.from_int(integer=initial_state, nbits=len(self._subregister)) output_state = BitString.from_int(integer=initial_state.integer, nbits=len(self._register)) for k, v in enumerate((self._subregister)): output_state[v] = input_state[k] return output_state
def add_basis_state(self, state: Dict[str, Dict[int, int]], coeff=1): if isinstance(state, str): state = self.string_to_basis_state(string=state) qubit_string = BitString.from_int(integer=0, nbits=self.n_qubits) for p, v in state.items(): for m, occ in v.items(): mode = self.get_mode(p, m) occ = BitString.from_int(integer=occ, nbits=mode.n_qubits) for i, q in enumerate(mode.qubits): qubit_string[q] = occ[i] self._state += QubitWaveFunction.from_int(i=qubit_string, coeff=coeff)
def test_transfer_operators(): assert (paulis.decompose_transfer_operator(ket=0, bra=0) == paulis.Qp(0)) assert (paulis.decompose_transfer_operator(ket=0, bra=1) == paulis.Sp(0)) assert (paulis.decompose_transfer_operator(ket=1, bra=0) == paulis.Sm(0)) assert (paulis.decompose_transfer_operator(ket=1, bra=1) == paulis.Qm(0)) assert (paulis.decompose_transfer_operator( ket=BitString.from_binary(binary="00"), bra=BitString.from_binary("00")) == paulis.Qp(0) * paulis.Qp(1)) assert (paulis.decompose_transfer_operator( ket=BitString.from_binary(binary="01"), bra=BitString.from_binary("01")) == paulis.Qp(0) * paulis.Qm(1)) assert (paulis.decompose_transfer_operator( ket=BitString.from_binary(binary="01"), bra=BitString.from_binary("10")) == paulis.Sp(0) * paulis.Sm(1)) assert (paulis.decompose_transfer_operator( ket=BitString.from_binary(binary="00"), bra=BitString.from_binary("11")) == paulis.Sp(0) * paulis.Sp(1)) assert (paulis.decompose_transfer_operator(ket=0, bra=0, qubits=[1]) == paulis.Qp(1)) assert (paulis.decompose_transfer_operator(ket=1, bra=0, qubits=[1]) == paulis.Sm(1)) assert (paulis.decompose_transfer_operator(ket=1, bra=1, qubits=[1]) == paulis.Qm(1))
def convert_measurements(self, backend_result) -> QubitWaveFunction: """0. :param backend_result: array from pyquil as list of lists of integers. :return: backend_result in Tequila format. """ def string_to_array(s): listing = [] for letter in s: if letter not in [',', ' ', '[', ']', '.']: listing.append(int(letter)) return listing result = QubitWaveFunction() bit_dict = {} for b in backend_result: try: bit_dict[str(b)] += 1 except: bit_dict[str(b)] = 1 for k, v in bit_dict.items(): arr = string_to_array(k) result._state[BitString.from_array(arr)] = v return result
def test_conversion(): for i in range(15): bita = BitString.from_int(integer=i) bita_lsb = BitStringLSB.from_int(integer=i) bita_converted = BitString.from_bitstring(other=bita_lsb) assert (bita == bita_converted) arrays = [[0, 0, 1], [1, 0, 0], [1, 0, 1], [1, 0, 1, 1, 1, 0], [1, 0, 0, 1, 1, 1, 0, 0, 1, 0]] for i, arr in enumerate(arrays): nbits = len(arr) bita = BitString.from_array(array=arr, nbits=nbits) bita_lsb = BitStringLSB.from_bitstring(other=bita) assert (bita_lsb.array == [x for x in reversed(arr)]) assert (bita.binary == bita_lsb.binary[::-1]) assert (bita.integer == bita_lsb.integer)
def convert_measurements(self, backend_result, target_qubits=None) -> QubitWaveFunction: """ map backend results to QubitWaveFunction Parameters ---------- backend_result: the result returned directly qiskit simulation. Returns ------- QubitWaveFunction: measurements converted into wave function form. """ qiskit_counts = backend_result.result().get_counts() result = QubitWaveFunction() # todo there are faster ways for k, v in qiskit_counts.items(): converted_key = BitString.from_bitstring( other=BitStringLSB.from_binary(binary=k)) result._state[converted_key] = v if target_qubits is not None: mapped_target = [self.qubit_map[q].number for q in target_qubits] mapped_full = [ self.qubit_map[q].number for q in self.abstract_qubits ] keymap = KeyMapRegisterToSubregister(subregister=mapped_target, register=mapped_full) result = result.apply_keymap(keymap=keymap) return result
def convert_measurements(self, backend_result) -> QubitWaveFunction: """ convert measurements from backend. Parameters ---------- backend_result: list of ints: the result of measurement in pyquil. Returns ------- QubitWaveFunction: measurement results translated into a QubitWaveFunction. """ def string_to_array(s): listing = [] for letter in s: if letter not in [',', ' ', '[', ']', '.']: listing.append(int(letter)) return listing result = QubitWaveFunction() bit_dict = {} for b in backend_result: try: bit_dict[str(b)] += 1 except: bit_dict[str(b)] = 1 for k, v in bit_dict.items(): arr = string_to_array(k) result._state[BitString.from_array(arr)] = v return result
def test_endianness_simulators(): tests = [ "000111", "111000", "101010", "010101", "10010010001", "111100101000010" ] for string in tests: number = int(string, 2) binary = BitString.from_binary(binary=string) c = QCircuit() for i, v in enumerate(binary): if v == 1: c += gates.X(target=i) c += gates.Measurement(target=[x for x in range(len(string))]) wfn_cirq = simulate(c, initial_state=0, backend="cirq") counts_cirq = simulate(c, samples=1, backend="cirq") counts_qiskit = simulate(c, samples=1, backend="qiskit") print("counts_cirq =", type(counts_cirq)) print("counts_qiskit=", type(counts_qiskit)) print("counts_cirq =", counts_cirq) print("counts_qiskit=", counts_qiskit) assert (counts_cirq == counts_qiskit) assert (wfn_cirq.state == counts_cirq.state)
def test_endianness_simulators(): tests = ["000111", "111000", "101010", "010101", "10010010001", "111100101000010"] for string in tests: binary = BitString.from_binary(binary=string) c = QCircuit() for i, v in enumerate(binary): if v == 1: c += gates.X(target=i) if v == 0: c += gates.Z(target=i) wfn_cirq = simulate(c, initial_state=0, backend="cirq") counts_cirq = simulate(c, samples=1, backend="cirq") counts_qiskit = simulate(c, samples=1, backend="qiskit") print("counts_cirq =", type(counts_cirq)) print("counts_qiskit=", type(counts_qiskit)) print("counts_cirq =", counts_cirq) print("counts_qiskit=", counts_qiskit) assert (counts_cirq.isclose(counts_qiskit)) assert (wfn_cirq.state == counts_cirq.state)
def convert_measurements( self, backend_result: cirq.TrialResult) -> QubitWaveFunction: """ Take the results of a cirq measurement and translate them to teuqila QubitWaveFunction. Parameters ---------- backend_result: cirq.TrialResult: the result of sampled measurements. Returns ------- QubitWaveFunction: the result of sampling, as a tequila QubitWavefunction. """ assert (len(backend_result.measurements) == 1) for key, value in backend_result.measurements.items(): counter = QubitWaveFunction() for sample in value: binary = BitString.from_array(array=sample.astype(int)) if binary in counter._state: counter._state[binary] += 1 else: counter._state[binary] = 1 return counter
def do_simulate(self, variables, initial_state, *args, **kwargs): """ Internal helper function for performing wavefunction simulation. Parameters ---------- variables: the variables of parameters in the circuit to use during simulation initial_state: indicates, in some fashion, the initial state to which the self.circuit is applied. args kwargs Returns ------- QubitWaveFunction: The wave function resulting from simulation. """ simulator = pyquil.api.WavefunctionSimulator() n_qubits = self.n_qubits msb = BitString.from_int(initial_state, nbits=n_qubits) iprep = pyquil.Program() for i, val in enumerate(msb.array): if val > 0: iprep += pyquil.gates.X(i) backend_result = simulator.wavefunction(iprep + self.circuit, memory_map=self.resolver) return QubitWaveFunction.from_array(arr=backend_result.amplitudes, numbering=self.numbering)
def apply_on_standard_basis(cls, gate: QGate, basisfunction: BitString, qubits:dict, variables) -> QubitWaveFunction: basis_array = basisfunction.array if gate.is_controlled(): do_apply = True check = [basis_array[qubits[c]] == 1 for c in gate.control] for c in check: if not c: do_apply = False if not do_apply: return QubitWaveFunction.from_int(basisfunction) if len(gate.target) > 1: raise Exception("Multi-targets not supported for symbolic simulators") result = QubitWaveFunction() for tt in gate.target: t = qubits[tt] qt = basis_array[t] a_array = copy.deepcopy(basis_array) a_array[t] = (a_array[t] + 1) % 2 current_state = QubitWaveFunction.from_int(basisfunction) altered_state = QubitWaveFunction.from_int(BitString.from_array(a_array)) fac1 = None fac2 = None if gate.name == "H": fac1 = (sympy.Integer(-1) ** qt * sympy.sqrt(sympy.Rational(1 / 2))) fac2 = (sympy.sqrt(sympy.Rational(1 / 2))) elif gate.name.upper() == "CNOT" or gate.name.upper() == "X": fac2 = sympy.Integer(1) elif gate.name.upper() == "Y": fac2 = sympy.I * sympy.Integer(-1) ** (qt) elif gate.name.upper() == "Z": fac1 = sympy.Integer(-1) ** (qt) elif gate.name.upper() == "RX": angle = sympy.Rational(1 / 2) * gate.parameter(variables) fac1 = sympy.cos(angle) fac2 = -sympy.sin(angle) * sympy.I elif gate.name.upper() == "RY": angle = -sympy.Rational(1 / 2) * gate.parameter(variables) fac1 = sympy.cos(angle) fac2 = +sympy.sin(angle) * sympy.Integer(-1) ** (qt + 1) elif gate.name.upper() == "RZ": angle = sympy.Rational(1 / 2) * gate.parameter(variables) fac1 = sympy.exp(-angle * sympy.I * sympy.Integer(-1) ** (qt)) else: raise Exception("Gate is not known to simulators, " + str(gate)) if fac1 is None or fac1 == 0: result += fac2 * altered_state elif fac2 is None or fac2 == 0: result += fac1 * current_state elif fac1 is None and fac2 is None: raise Exception("???") else: result += fac1 * current_state + fac2 * altered_state return result
def get_random_target_space(n_qubits): result = [] while (len(result) < n_qubits): i = numpy.random.randint(0, 2**n_qubits) if i not in result: result.append(i) return [BitString.from_int(i, nbits=n_qubits).binary for i in result]
def test_notation(silent=True): qpm = 2 S = 2 # State |-201>_(abc) represented with 2 qubits per mode # in Qubits # |01 00 00 00 00 | 00 00 01 00 00 | 00 00 00 01 00 > test1 = BitString.from_binary(binary='010000000000000100000000000100') paths = PhotonicPaths(path_names=['a', 'b', 'c'], S=S, qpm=qpm) wfn1 = QubitWaveFunction.from_int(i=test1) test1x = PhotonicStateVector(paths=paths, state=wfn1) test1y = PhotonicStateVector.from_string( paths=paths, string='1.0|10000>_a|00100>_b|00010>_c') if not silent: print("got = ", test1x) print("expected = ", test1y) assert (test1x == test1y) # Do a 332 State: # Photonic notation: (one photon in each mode and I added -2 and +2 modes) # |1-11>_a+|000>_b+|-111>_c # Qudit Notation: With modes -2 ... 2 # | 0 0 0 1 0 >_a | 0 1 0 0 0 >_b | 0 0 0 1 0 >_c # + | 0 0 1 0 0 >_a | 0 0 1 0 0 >_b | 0 0 1 0 0 >_c # + | 0 1 0 0 0 >_a | 0 0 0 1 0 >_b | 0 0 0 1 0 >_c # Qubit notation: (using 2 qubits for each mode) # | 00 00 00 01 00 >_a | 00 01 00 00 00 >_b | 00 00 00 01 00 >_c # + | 00 00 01 00 00 >_a | 00 00 01 00 00 >_b | 00 00 01 00 00 >_c # + | 00 01 00 00 00 >_a | 00 00 00 01 00 >_b | 00 00 00 01 00 >_c string = '1.0|00010>_a|01000>b|00010>_c+1.0|00100>_a|00100>_b|00100>_c+1.0|01000>_a|00010>_b|00010>_c' test_332x = PhotonicStateVector.from_string(paths=paths, string=string) q1 = BitString.from_binary('000000010000010000000000000100') q2 = BitString.from_binary('000001000000000100000000010000') q3 = BitString.from_binary('000100000000000001000000000100') wfn = QubitWaveFunction() for q in [q1, q2, q3]: wfn += QubitWaveFunction.from_int(i=q) test_332y = PhotonicStateVector(paths=paths, state=wfn) if not silent: print("got = ", test_332y) print("expected = ", test_332x) assert (test_332x == test_332y)
def do_simulate(self, variables, initial_state, *args, **kwargs): simulator = pyquil.api.WavefunctionSimulator() n_qubits = self.n_qubits msb = BitString.from_int(initial_state, nbits=n_qubits) iprep = pyquil.Program() for i, val in enumerate(msb.array): if val > 0: iprep += pyquil.gates.X(i) backend_result = simulator.wavefunction(iprep + self.circuit, memory_map=self.resolver) return QubitWaveFunction.from_array(arr=backend_result.amplitudes, numbering=self.numbering)
def test_bitstrings(): for i in range(15): bit = BitString.from_int(integer=i) assert (bit.integer == i) assert (bit.binary == format(i, 'b')) assert (bit.binary == bin(i)[2:]) arrays = [[0, 0, 1], [1, 0, 0], [1, 0, 1], [1, 0, 1, 1, 1, 0], [1, 0, 0, 1, 1, 1, 0, 0, 1, 0]] integers = [1, 4, 5, 32 + 8 + 4 + 2, 2 + 16 + 32 + 64 + 512] for i, arr in enumerate(arrays): nbits = len(arr) bita = BitString.from_array(array=arr, nbits=nbits) bitb = BitString.from_int(integer=integers[i], nbits=nbits) assert (bita == bitb) assert (bita.integer == integers[i]) assert (int(bita) == bita.integer) assert (bita.array == arr) assert (bitb.integer == integers[i]) assert (bitb.array == arr)
def convert_measurements( self, backend_result: cirq.TrialResult) -> QubitWaveFunction: assert (len(backend_result.measurements) == 1) for key, value in backend_result.measurements.items(): counter = QubitWaveFunction() for sample in value: binary = BitString.from_array(array=sample.astype(int)) if binary in counter._state: counter._state[binary] += 1 else: counter._state[binary] = 1 return counter
def test_endianness(): tests = [ "000111", "111000", "101010", "010101", "10010010001", "111100101000010" ] for string in tests: bits = len(string) i1 = BitString.from_int(int(string, 2)) i2 = BitString.from_binary(binary=string) assert (i1 == i2) i11 = BitStringLSB.from_int(int(string, 2)) i22 = BitStringLSB.from_binary(binary=string[::-1]) assert (i11 == i22) assert (i11.integer == i1.integer) assert (i22.integer == i2.integer) assert (i11.integer == i2.integer) assert (i1 == BitString.from_bitstring(i11)) assert (i1 == BitString.from_bitstring(i22)) assert (i2 == BitString.from_bitstring(i11))
def convert_measurements(self, backend_result) -> QubitWaveFunction: """0. :param qiskit_counts: qiskit counts as dictionary, states are binary in little endian (LSB) :return: Counts in OpenVQE format, states are big endian (MSB) """ qiskit_counts = backend_result.result().get_counts() result = QubitWaveFunction() # todo there are faster ways for k, v in qiskit_counts.items(): converted_key = BitString.from_bitstring( other=BitStringLSB.from_binary(binary=k)) result._state[converted_key] = v return result
def do_simulate(self, variables, initial_state=0, *args, **kwargs) -> QubitWaveFunction: """ Helper function for performing simulation. Parameters ---------- variables: variables to pass to the circuit for simulation. initial_state: indicate initial state on which the unitary self.circuit should act. args kwargs Returns ------- QubitWaveFunction: the result of simulation. """ if self.noise_model is None: qiskit_backend = self.retrieve_device('statevector_simulator') else: raise TequilaQiskitException( "wave function simulation with noise cannot be performed presently" ) optimization_level = None if "optimization_level" in kwargs: optimization_level = kwargs['optimization_level'] opts = None if initial_state != 0: array = numpy.zeros(shape=[2**self.n_qubits]) i = BitStringLSB.from_binary( BitString.from_int(integer=initial_state, nbits=self.n_qubits).binary) print(initial_state, " -> ", i) array[i.integer] = 1.0 opts = {"initial_statevector": array} print(opts) backend_result = qiskit.execute(experiments=self.circuit, optimization_level=optimization_level, backend=qiskit_backend, parameter_binds=[self.resolver], backend_options=opts).result() return QubitWaveFunction.from_array(arr=backend_result.get_statevector( self.circuit), numbering=self.numbering)
def extract_occupation_numbers( self, i: BitString) -> Dict[str, Dict[int, BitString]]: """ Same as interpret bitstring, but returns a dictionary of dictionaries (indexed by paths and modes) holding occupation numbers e.g. result['a'][-1] gives back the occupation number of mode S=-1 in path a """ result = dict() for pname, p in self.items(): modes = dict() for mname, m in p.items(): nocc = BitString.from_array(array=[i[k] for k in m.qubits], nbits=self.n_qubits) modes[mname] = nocc result[pname] = modes return result
def interpret_bitstring(self, i: BitString) -> str: """ Print the bitstring which represents a QubitWaveFunction basis element in the photonic notation e.g. |010001> --> |1>_a|0>_b|2>_c (in this example we have just one mode in each path) :param i: the bitstring :return: photonic notation as string """ result = "" for pname, p in self.items(): result += "|" for mname, m in p.items(): nocc = BitString.from_array(array=[i[k] for k in m.qubits], nbits=self.n_qubits) result += str(nocc.integer) result += ">_" + str(pname) return result
def test_constructor(): for i in range(15): bita = BitString.from_int(integer=i) bita_lsb = BitStringLSB.from_int(integer=i) bitb = BitString.from_int(integer=bita) bitc = BitString.from_int(integer=bita_lsb) bitd = BitString.from_array(array=bita) bite = BitString.from_array(array=bita_lsb) bitf = BitString.from_binary(binary=bita) bitg = BitString.from_binary(binary=bita_lsb) assert (bita == bitb) assert (bita == bitc) assert (bita == bitd) assert (bita == bite) assert (bita == bitf) assert (bita == bitg)