def get_rho(self): # Runs the circuit specified by self.qc and determines the expectation values for 'ZI', 'IZ', 'ZZ', 'XI', 'IX', 'XX', 'ZX' and 'XZ' (and the ones with Ys too if needed). if self.y_boxes: corr = ['ZZ', 'ZX', 'XZ', 'XX', 'YY', 'YX', 'YZ', 'XY', 'ZY'] ps = ['X', 'Y', 'Z'] else: corr = ['ZZ', 'ZX', 'XZ', 'XX'] ps = ['X', 'Z'] self.rho = {} results = {} for basis in corr: temp_qc = copy.deepcopy(self.qc) for j in range(2): if basis[j] == 'X': temp_qc.h(self.qr[j]) elif basis[j] == 'Y': temp_qc.sdg(self.qr[j]) temp_qc.h(self.qr[j]) if self.backend == None: ket = Statevector([1, 0, 0, 0]) ket = ket.from_instruction(temp_qc) results[basis] = ket.probabilities_dict() else: temp_qc.barrier(self.qr) temp_qc.measure(self.qr, self.cr) job = execute(temp_qc, backend=self.backend, shots=self.shots) results[basis] = job.result().get_counts() for string in results[basis]: results[basis][ string] = results[basis][string] / self.shots prob = {} # prob of expectation value -1 for single qubit observables for j in range(2): for p in ps: pauli = {} for pp in ['I'] + ps: pauli[pp] = (j == 1) * pp + p + (j == 0) * pp prob[pauli['I']] = 0 for ppp in ps: basis = pauli[ppp] for string in results[basis]: if string[(j + 1) % 2] == '1': prob[pauli['I']] += results[basis][string] / ( 2 + self.y_boxes) # prob of expectation value -1 for two qubit observables for basis in corr: prob[basis] = 0 for string in results[basis]: if string[0] != string[1]: prob[basis] += results[basis][string] for pauli in prob: self.rho[pauli] = 1 - 2 * prob[pauli]
def solve_qiskit_qaoa(self, p, **kwargs): print("USING QISKIT CODE") point = kwargs.get("point", None) tqa = kwargs.get('tqa', False) points = kwargs.get("points", None) construct_circ = kwargs.get("construct_circ", False) fourier_parametrise = kwargs.get("fourier_parametrise", False) if tqa: deltas = np.arange(0.25, 0.91, 0.05) point = np.append([(i + 1) / p for i in range(p)], [1 - (i + 1) / p for i in range(p)]) points = [delta * point for delta in deltas] if fourier_parametrise: points = [ convert_to_fourier_point(point, len(point)) for point in points ] qaoa_results, circ = QiskitQAOA( self.operator, self.quantum_instance, self.optimizer, reps=p, initial_state=self.initial_state, mixer=self.mixer, list_points=points, construct_circ=construct_circ, fourier_parametrise=fourier_parametrise) elif points is not None: if point is not None: points.append(point) if fourier_parametrise: points = [ convert_to_fourier_point(point, len(point)) for point in points ] qaoa_results, circ = QiskitQAOA( self.operator, self.quantum_instance, self.optimizer, reps=p, initial_state=self.initial_state, mixer=self.mixer, list_points=points, construct_circ=construct_circ, fourier_parametrise=fourier_parametrise) elif point is not None: if fourier_parametrise: point = convert_to_fourier_point(point, 2 * p) qaoa_results, circ = QiskitQAOA( self.operator, self.quantum_instance, self.optimizer, reps=p, initial_state=self.initial_state, initial_point=point, mixer=self.mixer, construct_circ=construct_circ, fourier_parametrise=fourier_parametrise) else: points = [[0] * (2 * p)] + [[ 1.98 * np.pi * (np.random.rand() - 0.5) for _ in range(2 * p) ] for _ in range(10)] qaoa_results, circ = QiskitQAOA( self.operator, self.quantum_instance, self.optimizer, reps=p, initial_state=self.initial_state, list_points=points, mixer=self.mixer, construct_circ=construct_circ, fourier_parametrise=fourier_parametrise) if circ: print(circ.draw(fold=200)) optimal_point = qaoa_results.optimal_point self.optimal_point = optimal_point self.qaoa_result = qaoa_results eigenstate = qaoa_results.eigenstate if self.quantum_instance.is_statevector: from qiskit.quantum_info import Statevector eigenstate = Statevector(eigenstate) eigenstate = eigenstate.probabilities_dict() else: eigenstate = dict([(u, v**2) for u, v in eigenstate.items() ]) #Change to probabilities num_qubits = len(list(eigenstate.items())[0][0]) solutions = [] eigenvalue = 0 for bitstr, sampling_probability in eigenstate.items(): bitstr = bitstr[::-1] value = self.qubo.objective.evaluate([int(bit) for bit in bitstr]) eigenvalue += value * sampling_probability solutions += [(bitstr, value, sampling_probability)] qaoa_results.eigenstate = solutions qaoa_results.eigenvalue = eigenvalue #Sort states by decreasing probability sorted_eigenstate_by_prob = sorted(qaoa_results.eigenstate, key=lambda x: x[2], reverse=True) #print sorted state in a table self.print_state(sorted_eigenstate_by_prob) #Other print stuff print("Eigenvalue: {}".format(eigenvalue)) print("Optimal point: {}".format(optimal_point)) print("Optimizer Evals: {}".format(qaoa_results.optimizer_evals)) scale = self.random_energy - self.opt_value approx_quality_2 = np.round( (self.random_energy - sorted_eigenstate_by_prob[0][1]) / scale, 3) energy_prob = {} for x in qaoa_results.eigenstate: energy_prob[np.round( x[1], 6)] = energy_prob.get(np.round(x[1], 6), 0) + x[2] prob_s = np.round(energy_prob.get(np.round(self.opt_value, 6), 0), 6) self.prob_s.append(prob_s) self.eval_s.append(eigenvalue) self.approx_s.append(approx_quality_2) print("\nQAOA most probable solution: {}".format( sorted_eigenstate_by_prob[0])) print("Approx_quality: {}".format(approx_quality_2))
def solve_qaoa(self, p, **kwargs): if self.optimal_point is not None and 'point' not in kwargs: point = self.optimal_point else: point = kwargs.get("point", None) fourier_parametrise = kwargs.get("fourier_parametrise", False) self.optimizer.set_options(maxeval = 1000) tqa = kwargs.get('tqa', False) points = kwargs.get("points", None) symmetrised = self.symmetrise #Can sometimes end up with zero operator when substituting variables when we only have ZZ terms (symmetrised qubo), #e.g. if H = ZIZ (=Z1Z3 for 3 qubit system) and we know <Z1 Z3> = 1, so after substition H = II for the 2 qubit system. #H = II is then treated as an offset and not a Pauli operator, so the QUBO.to_ising() method returns a zero (pauli) operator. #In such cases it means the QUBO is fully solved and any solution will do, so chose "0" string as the solution. #This also makes sure that ancilla bit is in 0 state. (we could equivalently choose any other string with ancilla in 0 state) def valid_operator(qubo): num_vars = qubo.get_num_vars() operator, _ = qubo.to_ising() valid = False operator = [operator] if isinstance(operator, PauliOp) else operator #Make a list if only one single PauliOp for op_1 in operator: coeff, op_1 = op_1.to_pauli_op().coeff, op_1.to_pauli_op().primitive if coeff >= 1e-6 and op_1 != "I"*num_vars: #if at least one non-zero then return valid ( valid = True ) valid = True return valid valid_op = valid_operator(self.qubo) num_vars = self.qubo.get_num_vars() if num_vars >= 1 and symmetrised and not valid_op: qaoa_results = self.qaoa_result qaoa_results.eigenstate = np.array( [ 1 ] + [ 0 ]*(2**num_vars - 1) ) qaoa_results.optimizer_evals = 0 qaoa_results.eigenvalue = self.qubo.objective.evaluate([0]*num_vars) qc = QuantumCircuit(num_vars) elif tqa: deltas = np.arange(0.45, 0.91, 0.05) point = np.append( [ (i+1)/p for i in range(p) ] , [ 1-(i+1)/p for i in range(p) ] ) points = [delta*point for delta in deltas] if fourier_parametrise: points = [ QAOAEx.convert_to_fourier_point(point, len(point)) for point in points ] qaoa_results, _ = QiskitQAOA( self.operator, self.quantum_instance, self.optimizer, reps = p, initial_state = self.initial_state, mixer = self.mixer, fourier_parametrise = fourier_parametrise, list_points = points, qubo = self.qubo ) elif points is not None: if fourier_parametrise: points = [ QAOAEx.convert_to_fourier_point(point, len(point)) for point in points ] qaoa_results, _ = QiskitQAOA( self.operator, self.quantum_instance, self.optimizer, reps = p, initial_state = self.initial_state, mixer = self.mixer, fourier_parametrise = fourier_parametrise, list_points = points, qubo = self.qubo ) elif point is None: points = [ [0]*(2*p) ] + [ [ 2 * np.pi* ( np.random.rand() - 0.5 ) for _ in range(2*p)] for _ in range(10) ] qaoa_results, _ = QiskitQAOA( self.operator, self.quantum_instance, self.optimizer, reps = p, initial_state = self.initial_state, list_points = points, mixer = self.mixer, fourier_parametrise = fourier_parametrise, qubo = self.qubo ) else: if fourier_parametrise: point = QAOAEx.convert_to_fourier_point(point, len(point)) qaoa_results, _ = QiskitQAOA( self.operator, self.quantum_instance, self.optimizer, reps = p, initial_state = self.initial_state, initial_point = point, mixer = self.mixer, fourier_parametrise = fourier_parametrise, qubo = self.qubo ) point = qaoa_results.optimal_point eigenstate = qaoa_results.eigenstate if self.quantum_instance.is_statevector: from qiskit.quantum_info import Statevector eigenstate = Statevector(eigenstate) eigenstate = eigenstate.probabilities_dict() else: eigenstate = dict([(u, v**2) for u, v in eigenstate.items()]) #Change to probabilities num_qubits = len(list(eigenstate.items())[0][0]) solutions = [] eigenvalue = 0 for bitstr, sampling_probability in eigenstate.items(): bitstr = bitstr[::-1] value = self.qubo.objective.evaluate([int(bit) for bit in bitstr]) eigenvalue += value * sampling_probability solutions += [(bitstr, value, sampling_probability)] qaoa_results.eigenstate = solutions qaoa_results.eigenvalue = eigenvalue self.optimal_point = point self.qaoa_result = qaoa_results #Sort states by increasing energy and decreasing probability sorted_eigenstate_by_energy = sorted(qaoa_results.eigenstate, key = lambda x: x[1]) sorted_eigenstate_by_prob = sorted(qaoa_results.eigenstate, key = lambda x: x[2], reverse = True) #print energy-sorted state in a table self.print_state(sorted_eigenstate_by_energy) #Other print stuff print("Eigenvalue: {}".format(qaoa_results.eigenvalue)) print("Optimal point: {}".format(qaoa_results.optimal_point)) print("Optimizer Evals: {}".format(qaoa_results.optimizer_evals)) scale = self.random_energy - self.opt_value approx_quality = np.round( (self.random_energy - sorted_eigenstate_by_energy[0][1])/ scale, 3 ) approx_quality_2 = np.round( ( self.random_energy - sorted_eigenstate_by_prob[0][1] ) / scale, 3 ) energy_prob = {} for x in qaoa_results.eigenstate: energy_prob[ np.round(x[1], 6) ] = energy_prob.get(np.round(x[1], 6), 0) + x[2] prob_s = np.round( energy_prob.get(np.round(self.opt_value, 6), 0), 6 ) self.prob_s.append( prob_s ) self.approx_s.append( [approx_quality, approx_quality_2] ) print( "\nQAOA lowest energy solution: {}".format(sorted_eigenstate_by_energy[0]) ) print( "Approx_quality: {}".format(approx_quality) ) print( "\nQAOA most probable solution: {}".format(sorted_eigenstate_by_prob[0]) ) print( "Approx_quality: {}".format(approx_quality_2) ) return qaoa_results
def optimize_circuit_sgd(self, qc, quantum_circuit_parameter, angle_degrees, cost_function='mse', learning_rate = 2): ''' This function is used for optimizing the values of angle_degrees, using Gradient Descent method. Parameters: ----------- qc : Quantum Circuit object quantum_circuit_parameter : parameter object angle_degrees : Angle(in degrees) by which the parameterised gates will rotate cost_function : The type of cost function to be used[default: mse] learning_rate : The learning rate for optimization[default: 2] ''' i = 0 max_i = 500 previous_step_size = 1 precision = -1 loss_function = [] vn_entropy = [] epochs = [] epoch_var = 0 while i<max_i and previous_step_size>precision: #iterating over until the error converges epoch_var+=1 epochs.append(epoch_var) theta_radians = radians(angle_degrees) #converting the degrees to radians previous_angle = angle_degrees bell_state = execute(qc, backend = Aer.get_backend('statevector_simulator'), shots = self.shots, parameter_binds=[{quantum_circuit_parameter: theta_radians}]).result().get_statevector() #counts = job.result().get_counts() psi = Statevector(bell_state) counts = psi.probabilities_dict() print(counts) D = DensityMatrix(bell_state) vn_entropy_val = entropy(D, base=2) vn_entropy.append(vn_entropy_val) #print(counts) try: prob_avg_01 = counts['00'] except: prob_avg_01 = 0 try: prob_avg_10 = counts['11'] except: prob_avg_10 = 0 if cost_function == 'mse': loss_function.append(self.mse_cost_function(prob_avg_01, prob_avg_10)) angle_degrees = angle_degrees - learning_rate*self.mse_cost_function(prob_avg_01, prob_avg_10) if cost_function == 'unsymmetrical': angle_degrees = angle_degrees - learning_rate*self.unsymmetrical_cost_function(prob_avg_01, prob_avg_10) previous_step_size = abs(angle_degrees - previous_angle) i+=1 print(angle_degrees) return angle_degrees, counts, epochs, vn_entropy, loss_function