Esempio n. 1
0
    def __init__(self, **kwargs):
        self.qubo = kwargs.get("qubo", None)
        if self.qubo is not None:
            self.operator, self.offset = self.qubo.to_ising()
        self.no_cars = kwargs.get("no_cars", 0)
        self.no_routes = kwargs.get("no_routes", 0)
        self.symmetrise = kwargs.get("symmetrise", False)
        self.customise = kwargs.get("customise", False)
        opt_str = kwargs.get('opt_str', "LN_BOBYQA")
        print("Optimizer: {}".format(opt_str))
        self.optimizer = NLOPT_Optimizer(opt_str)
        self.optimizer.set_options(maxeval=200)
        self.original_qubo = deepcopy(self.qubo)

        #Benchmarking, using classical result for ground state energy, and then random energy measurement.
        self.classical_result = kwargs.get("classical_result", None)
        self.solve_classically()
        self.random_instance = QuantumInstance(
            backend=Aer.get_backend("aer_simulator_matrix_product_state"),
            shots=1000)  #1000 randomly measured
        self.get_random_energy()

        #Simulation methods
        simulator = kwargs.get("simulator", None)
        noise_model = kwargs.get("noise_model", None)
        if simulator == None:
            simulator = "aer_simulator_density_matrix"
        print("Using " + simulator)

        self.quantum_instance = QuantumInstance(
            backend=Aer.get_backend(simulator),
            shots=8192,
            noise_model=noise_model,
            basis_gates=["cx", "x", "sx", "rz", "id"])

        #Symmetrise
        if self.symmetrise:
            self.symmetrise_qubo()

        #Customise QAOA
        if self.customise:
            self.construct_initial_state(symmetrise=self.symmetrise)
            self.construct_mixer()
        else:
            self.initial_state = None
            self.mixer = None

        #Results placeholder
        self.prob_s = []
        self.eval_s = []
        self.approx_s = []
Esempio n. 2
0
 def __init__(self, qubo, no_cars, no_routes):
     var_list = qubo.variables
     opt_str = "LN_SBPLX"
     print("Optimizer: {}".format(opt_str))
     self.optimizer = NLOPT_Optimizer(opt_str)
     self.original_qubo = qubo
     self.qubo = qubo
     op, offset = qubo.to_ising()
     self.operator = op
     self.offset = offset
     self.quantum_instance = QuantumInstance(backend = Aer.get_backend("aer_simulator_matrix_product_state"), shots = 1024)
     self.replacements = {var.name:None for var in var_list}
     self.no_cars = no_cars
     self.no_routes = no_routes
     self.car_blocks = np.empty(shape = (no_cars,), dtype=object)
     for car_no in range(no_cars):
         self.car_blocks[car_no] = ["X_{}_{}".format(car_no, route_no) for route_no in range(no_routes)]
     self.qaoa_result = None
     self.benchmark_energy = None
     self.var_values = {}
     self.construct_initial_state()
     self.construct_mixer()
     self.get_random_energy()
     self.get_benchmark_energy()
Esempio n. 3
0
 def __init__(self, qubo, no_cars, no_routes, **kwargs):
     opt_str = kwargs.get('opt_str', "LN_SBPLX")
     self.symmetrise = kwargs.get('symmetrise', False)
     var_list = qubo.variables
     self.optimizer = NLOPT_Optimizer(opt_str)
     self.optimizer.set_options(max_eval=1000)
     self.original_qubo = qubo
     self.qubo = qubo
     self.solve_classically()
     op, offset = qubo.to_ising()
     self.operator = op
     self.offset = offset
     if self.symmetrise:
         self.symmetrise_qubo()
     self.quantum_instance = QuantumInstance(
         backend=Aer.get_backend("aer_simulator_matrix_product_state"),
         shots=4096)
     self.replacements = {var.name: None for var in var_list}
     self.no_cars = no_cars
     self.no_routes = no_routes
     self.car_blocks = np.empty(shape=(no_cars, ), dtype=object)
     for car_no in range(no_cars):
         self.car_blocks[car_no] = [
             "X_{}_{}".format(car_no, route_no)
             for route_no in range(no_routes)
         ]
     self.qaoa_result = None
     self.benchmark_energy = None
     self.var_values = {}
     self.construct_initial_state()
     self.construct_mixer()
     self.get_random_energy()
     self.get_benchmark_energy()
     self.prob_s = []
     self.approx_s = []
     self.optimal_point = None
Esempio n. 4
0
class QAOA_Base:
    def __init__(self, **kwargs):
        self.qubo = kwargs.get("qubo", None)
        if self.qubo is not None:
            self.operator, self.offset = self.qubo.to_ising()
        self.no_cars = kwargs.get("no_cars", 0)
        self.no_routes = kwargs.get("no_routes", 0)
        self.symmetrise = kwargs.get("symmetrise", False)
        self.customise = kwargs.get("customise", False)
        opt_str = kwargs.get('opt_str', "LN_BOBYQA")
        print("Optimizer: {}".format(opt_str))
        self.optimizer = NLOPT_Optimizer(opt_str)
        self.optimizer.set_options(maxeval=200)
        self.original_qubo = deepcopy(self.qubo)

        #Benchmarking, using classical result for ground state energy, and then random energy measurement.
        self.classical_result = kwargs.get("classical_result", None)
        self.solve_classically()
        self.random_instance = QuantumInstance(
            backend=Aer.get_backend("aer_simulator_matrix_product_state"),
            shots=1000)  #1000 randomly measured
        self.get_random_energy()

        #Simulation methods
        simulator = kwargs.get("simulator", None)
        noise_model = kwargs.get("noise_model", None)
        if simulator == None:
            simulator = "aer_simulator_density_matrix"
        print("Using " + simulator)

        self.quantum_instance = QuantumInstance(
            backend=Aer.get_backend(simulator),
            shots=8192,
            noise_model=noise_model,
            basis_gates=["cx", "x", "sx", "rz", "id"])

        #Symmetrise
        if self.symmetrise:
            self.symmetrise_qubo()

        #Customise QAOA
        if self.customise:
            self.construct_initial_state(symmetrise=self.symmetrise)
            self.construct_mixer()
        else:
            self.initial_state = None
            self.mixer = None

        #Results placeholder
        self.prob_s = []
        self.eval_s = []
        self.approx_s = []

    def symmetrise_qubo(self):
        new_operator = []
        operator, _ = self.qubo.to_ising()
        for op_1 in operator:
            coeff, op_1 = op_1.to_pauli_op().coeff, op_1.to_pauli_op(
            ).primitive
            op_1_str = op_1.to_label()
            Z_counts = op_1_str.count('Z')
            if Z_counts == 1:
                op_1_str = "Z" + op_1_str  #Add a Z in the last qubit to single Z terms
            else:
                op_1_str = "I" + op_1_str  #Add an I in the last qubit to ZZ terms (no change in operator)
            pauli = PauliOp(primitive=Pauli(op_1_str), coeff=coeff)
            new_operator.append(pauli)
        symmetrised_qubo = QuadraticProgram()
        symmetrised_operator = sum(new_operator)
        symmetrised_qubo.from_ising(symmetrised_operator,
                                    self.offset,
                                    linear=True)
        self.qubo = symmetrised_qubo
        self.rename_qubo_variables()
        self.operator, self.offset = self.qubo.to_ising()

    def rename_qubo_variables(self):
        original_qubo = self.original_qubo
        qubo = self.qubo
        variable_names = [variable.name for variable in qubo.variables]
        original_variable_names = [(variable.name, 1)
                                   for variable in original_qubo.variables]
        new_variable_names = original_variable_names + [
            ("X_anc", 1)
        ] if self.symmetrise else original_variable_names
        variables_dict = dict(zip(variable_names, new_variable_names))
        for new_variable_name in variables_dict.values():
            qubo.binary_var(name=new_variable_name[0])
        qubo = qubo.substitute_variables(variables=variables_dict)
        if qubo.status == QuadraticProgram.Status.INFEASIBLE:
            raise QiskitOptimizationError(
                'Infeasible due to variable substitution')
        self.qubo = qubo

    def get_random_energy(self):
        #Get random benchmark energy for 0 layer Custom-QAOA (achieved by using layer 1 Cust-QAOA with [0,0] angles i.e. sampling from feasible states with equal prob)
        self.construct_initial_state(symmetrise=False)
        self.construct_mixer()
        random_energy, _ = CustomQAOA(
            operator=self.operator,
            quantum_instance=self.random_instance,
            optimizer=self.optimizer,
            reps=1,
            initial_state=self.initial_state,
            mixer=self.mixer,
            solve=False,
        )
        #Remove custom initial state if using BASE QAOA
        self.initial_state = None
        self.mixer = None
        temp = random_energy
        self.random_energy = temp + self.offset
        if np.round(self.random_energy - self.opt_value, 6) < 1e-7:
            print(
                "0 layer QAOA converged to exact solution. Shifting value up by |exact_ground_energy| instead to avoid dividing by 0 in approx quality."
            )
            self.random_energy += np.abs(self.random_energy)
        self.benchmark_energy = self.random_energy
        print("random energy: {}\n".format(self.random_energy))

    def construct_initial_state(self, **kwargs):
        symmetrise = kwargs.get("symmetrise", False)
        self.initial_state = construct_initial_state(self.no_routes,
                                                     self.no_cars)
        if symmetrise:
            ancilla_reg = QuantumRegister(1, 'ancilla')
            self.initial_state.add_register(ancilla_reg)
            self.initial_state.h(ancilla_reg[0])

    def construct_mixer(self):
        self.mixer = n_qbit_mixer(self.initial_state)

    def solve_classically(self):
        if self.classical_result:
            print(
                "There is an existing classical result. Using this as code proceeds."
            )
            print(self.classical_result)
            self.opt_value = self.classical_result.fval
        else:
            print("No classical result already available.")
            print("Now solving classically")
            _, opt_value, classical_result, _ = find_all_ground_states(
                self.original_qubo)
            self.classical_result = classical_result
            self.opt_value = opt_value

    #Using Gary's Qiskit code
    def solve_qaoa(self, p, **kwargs):
        print("USING CUSTOM CODE")
        point = kwargs.get(
            "point", None
        )  #Make sure point here is already in FOURIER space of length 2(p-1)
        fourier_parametrise = kwargs.get("fourier_parametrise", False)
        tqa = kwargs.get('tqa', False)
        points = kwargs.get("points", None)
        construct_circ = kwargs.get("construct_circ", 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 = CustomQAOA(
                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,
                construct_circ=construct_circ)
        elif points is not None:
            if fourier_parametrise:
                points = [
                    convert_to_fourier_point(point, len(point))
                    for point in points
                ]
                if point is not None:
                    points.append(convert_to_fourier_point(point, len(point)))
            else:
                points.append(point)

            qaoa_results, circ = CustomQAOA(
                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,
                construct_circ=construct_circ)
        elif point is not None:
            if fourier_parametrise:
                initial_point = convert_to_fourier_point(point, len(point))
            qaoa_results, circ = CustomQAOA(
                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,
                construct_circ=construct_circ)
        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 = CustomQAOA(
                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,
                construct_circ=construct_circ)
        if circ:
            print(circ.draw(fold=200))
        optimal_point = qaoa_results.optimal_point
        eigenvalue = sum([x[1] * x[2] for x in qaoa_results.eigenstate])
        qaoa_results.eigenvalue = eigenvalue
        self.optimal_point = optimal_point

        self.qaoa_result = qaoa_results

        #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_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 print_state(self, eigenstate):
        header = '|'
        for var in self.qubo.variables:
            var_name = var.name
            header += '{:<5}|'.format(var_name.replace("_", ""))
        header += '{:<6}|'.format('Cost')
        header += '{:<5}|'.format("Prob")
        print("-" * len(header))
        print(header)
        print("-" * len(header))

        count = 0
        for item in eigenstate:
            string = '|'
            for binary_var in item[0]:
                string += '{:<5} '.format(binary_var)  #Binary string
            string += '{:<6} '.format(np.round(item[1], 2))  #Cost
            string += '{:<5}|'.format(np.round(item[2], 3))  #Prob
            print(string)

            #Print only first 20 states of lowest energy
            count += 1
            if count == 20:
                break
        print("-" * len(header))
Esempio n. 5
0
def main(args=None):
    """[summary]

    Args:
        raw_args ([type], optional): [description]. Defaults to None.
    """
    start = time()
    if args == None:
        args = parse()

    qubo_no = args["no_samples"]
    print_to_file("-" * 50)
    print_to_file("QUBO_{}".format(qubo_no))
    #Load generated qubo_no
    with open(
            'qubos_{}_car_{}_routes/qubo_{}.pkl'.format(
                args["no_cars"], args["no_routes"], qubo_no), 'rb') as f:
        qubo, max_coeff, operator, offset, routes = pkl.load(f)

    qubo = QuadraticProgram()
    qubo.from_ising(operator)

    x_s, opt_value, classical_result = find_all_ground_states(qubo)
    print_to_file(classical_result)

    #Set optimizer method
    method = args["method"]
    optimizer = NLOPT_Optimizer(method=method, result_message=False)
    # optimizer = COBYLA()
    backend = Aer.get_backend("statevector_simulator")
    quantum_instance = QuantumInstance(backend=backend)

    approx_ratios = []
    prob_s_s = []
    p_max = args["p_max"]
    no_routes, no_cars = (args["no_routes"], args["no_cars"])

    custom = True
    if custom:
        initial_state = construct_initial_state(no_routes=no_routes,
                                                no_cars=no_cars)
        mixer = n_qbit_mixer(initial_state)
    else:
        initial_state, mixer = (None, None)

    fourier_parametrise = args["fourier"]
    print_to_file("-" * 50)
    print_to_file(
        "Now solving with TQA_QAOA... Fourier Parametrisation: {}".format(
            fourier_parametrise))
    #     maxeval = 125
    for p in range(1, p_max + 1):
        construct_circ = False
        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]
        print_to_file("-" * 50)
        print_to_file("    " + "p={}".format(p))
        if fourier_parametrise:
            points = [
                convert_to_fourier_point(point, len(point)) for point in points
            ]


#         maxeval *= 2 #Double max_allowed evals for optimizer
#         optimizer.set_options(maxeval = maxeval)
        optimizer.set_options(maxeval=1000 * p)
        qaoa_results, optimal_circ = CustomQAOA(
            operator,
            quantum_instance,
            optimizer,
            reps=p,
            initial_state=initial_state,
            mixer=mixer,
            construct_circ=construct_circ,
            fourier_parametrise=fourier_parametrise,
            list_points=points,
            qubo=qubo)
        exp_val = qaoa_results.eigenvalue * max_coeff
        state_solutions = {
            item[0][::-1]: item[1:]
            for item in qaoa_results.eigenstate
        }
        for item in sorted(state_solutions.items(),
                           key=lambda x: x[1][1],
                           reverse=True)[0:5]:
            print_to_file(item)
        prob_s = 0
        for string in x_s:
            prob_s += state_solutions[string][
                1] if string in state_solutions else 0
        prob_s /= len(x_s)  #normalise
        optimal_point = qaoa_results.optimal_point
        if fourier_parametrise:
            optimal_point = convert_from_fourier_point(optimal_point,
                                                       len(optimal_point))
        approx_ratio = 1 - np.abs((opt_value - exp_val) / opt_value)
        nfev = qaoa_results.cost_function_evals
        print_to_file(
            "    " + "Optimal_point: {}, Nfev: {}".format(optimal_point, nfev))
        print_to_file("    " +
                      "Exp_val: {}, Prob_s: {}, approx_ratio: {}".format(
                          exp_val, prob_s, approx_ratio))
        approx_ratios.append(approx_ratio)
        prob_s_s.append(prob_s)
    print_to_file("-" * 50)
    print_to_file("QAOA terminated")
    print_to_file("-" * 50)
    print_to_file("Approximation ratios per layer: {}".format(approx_ratios))
    print_to_file("Prob_success per layer: {}".format(prob_s_s))
    save_results = np.append(approx_ratios, prob_s_s)
    if fourier_parametrise:
        with open(
                'results_{}cars{}routes/TQA_F_{}.csv'.format(
                    args["no_cars"], args["no_routes"], args["no_samples"]),
                'w') as f:
            np.savetxt(f, save_results, delimiter=',')
        print_to_file(
            "Results saved in results_{}cars{}routes/TQA_F_{}.csv".format(
                args["no_cars"], args["no_routes"], args["no_samples"]))
    else:
        with open(
                'results_{}cars{}routes/TQA_NF_{}.csv'.format(
                    args["no_cars"], args["no_routes"], args["no_samples"]),
                'w') as f:
            np.savetxt(f, save_results, delimiter=',')
        print_to_file(
            "Results saved in results_{}cars{}routes/TQA_NF_{}.csv".format(
                args["no_cars"], args["no_routes"], args["no_samples"]))
    finish = time()
    print_to_file("Time Taken: {}".format(finish - start))
Esempio n. 6
0
class RQAOA:
    def __init__(self, qubo, no_cars, no_routes, **kwargs):
        opt_str = kwargs.get('opt_str', "LN_SBPLX")
        self.symmetrise = kwargs.get('symmetrise', False)
        var_list = qubo.variables
        self.optimizer = NLOPT_Optimizer(opt_str)
        self.optimizer.set_options(max_eval=1000)
        self.original_qubo = qubo
        self.qubo = qubo
        self.solve_classically()
        op, offset = qubo.to_ising()
        self.operator = op
        self.offset = offset
        if self.symmetrise:
            self.symmetrise_qubo()
        self.quantum_instance = QuantumInstance(
            backend=Aer.get_backend("aer_simulator_matrix_product_state"),
            shots=4096)
        self.replacements = {var.name: None for var in var_list}
        self.no_cars = no_cars
        self.no_routes = no_routes
        self.car_blocks = np.empty(shape=(no_cars, ), dtype=object)
        for car_no in range(no_cars):
            self.car_blocks[car_no] = [
                "X_{}_{}".format(car_no, route_no)
                for route_no in range(no_routes)
            ]
        self.qaoa_result = None
        self.benchmark_energy = None
        self.var_values = {}
        self.construct_initial_state()
        self.construct_mixer()
        self.get_random_energy()
        self.get_benchmark_energy()
        self.prob_s = []
        self.approx_s = []
        self.optimal_point = None

    def construct_initial_state(self):
        qc = QuantumCircuit()
        for car_no in range(self.no_cars):
            car_block = self.car_blocks[car_no]
            R = len(car_block)
            if R != 0 and R != 1:
                num_qubits = qc.num_qubits
                q_regs = QuantumRegister(R, 'car_{}'.format((car_no)))
                qc.add_register(q_regs)
                w_one = QuantumCircuit(q_regs)
                for r in range(0, R - 1):
                    if r == 0:
                        w_one.ry(2 * np.arccos(1 / np.sqrt(R - r)), r)
                    elif r != R - 1:
                        w_one.cry(2 * np.arccos(1 / np.sqrt(R - r)), r - 1, r)
                for r in range(1, R):
                    w_one.cx(R - r - 1, R - r)
                w_one.x(0)
                qc.append(w_one, range(num_qubits, num_qubits + R))
            elif R == 1:
                num_qubits = qc.num_qubits
                q_regs = QuantumRegister(R, 'car_{}'.format((car_no)))
                qc.add_register(q_regs)
                w_one = QuantumCircuit(q_regs)
                w_one.h(0)
                qc.append(w_one, range(num_qubits, num_qubits + R))
            else:
                continue
        qc = qc.decompose()
        self.initial_state = qc

        if self.symmetrise:
            ancilla_reg = QuantumRegister(1, 'ancilla')
            self.initial_state.add_register(ancilla_reg)
            self.initial_state.h(ancilla_reg[0])

    def symmetrise_qubo(self):
        new_operator = []
        operator, _ = self.qubo.to_ising()
        for op_1 in operator:
            coeff, op_1 = op_1.to_pauli_op().coeff, op_1.to_pauli_op(
            ).primitive
            op_1_str = op_1.to_label()
            Z_counts = op_1_str.count('Z')
            if Z_counts == 1:
                op_1_str = "Z" + op_1_str  #Add a Z in the last qubit to single Z terms
            else:
                op_1_str = "I" + op_1_str  #Add an I in the last qubit to ZZ terms (no change in operator)
            pauli = PauliOp(primitive=Pauli(op_1_str), coeff=coeff)
            new_operator.append(pauli)
        symmetrised_qubo = QuadraticProgram()
        symmetrised_operator = sum(new_operator)
        symmetrised_qubo.from_ising(symmetrised_operator,
                                    self.offset,
                                    linear=True)
        self.qubo = symmetrised_qubo
        self.rename_qubo_variables()
        operator, _ = self.qubo.to_ising()
        self.operator = operator

    def rename_qubo_variables(self):
        original_qubo = self.original_qubo
        qubo = self.qubo
        variable_names = [variable.name for variable in qubo.variables]
        original_variable_names = [(variable.name, 1)
                                   for variable in original_qubo.variables]
        new_variable_names = original_variable_names + [
            ("X_anc", 1)
        ] if self.symmetrise else original_variable_names
        variables_dict = dict(zip(variable_names, new_variable_names))
        for (variable_name, new_variable_name) in variables_dict.items():
            qubo.binary_var(name=new_variable_name[0])
        qubo = qubo.substitute_variables(variables=variables_dict)
        if qubo.status == QuadraticProgram.Status.INFEASIBLE:
            raise QiskitOptimizationError(
                'Infeasible due to variable substitution')
        self.qubo = qubo

    def construct_mixer(self):
        from qiskit.circuit.parameter import Parameter
        initial_state = self.initial_state
        no_qubits = initial_state.num_qubits
        t = Parameter('t')
        mixer = QuantumCircuit(no_qubits)
        mixer.append(initial_state.inverse(), range(no_qubits))
        mixer.rz(2 * t, range(no_qubits))
        mixer.append(initial_state, range(no_qubits))
        self.mixer = mixer

    def solve_classically(self):
        x_s, opt_value, classical_result, _ = find_all_ground_states(
            self.original_qubo)
        self.result = classical_result
        x_arr = classical_result.x
        self.x_s = [x_str[::-1] for x_str in x_s]
        self.opt_value = opt_value

    solve

    def get_random_energy(self):
        #Get random benchmark energy for 0 layer QAOA (achieved by using layer 1 QAOA with [0,0] angles)
        random_energy, _ = CustomQAOA(
            operator=self.operator,
            quantum_instance=self.quantum_instance,
            optimizer=self.optimizer,
            reps=1,
            initial_state=self.initial_state,
            mixer=self.mixer,
            solve=False,
        )
        temp = random_energy
        self.random_energy = temp + self.offset
        if np.round(self.random_energy - self.opt_value, 6) < 1e-7:
            print(
                "0 layer QAOA converged to exact solution. Shifting value up by |exact_ground_energy| instead to avoid dividing by 0 in approx quality."
            )
            self.random_energy += np.abs(self.random_energy)
        print("random energy: {}".format(self.random_energy))

    def get_benchmark_energy(self):
        #Get benchmark energy with 0-layer QAOA (just as random_energy)
        benchmark_energy, _ = CustomQAOA(
            operator=self.operator,
            quantum_instance=self.quantum_instance,
            optimizer=self.optimizer,
            reps=1,
            initial_state=self.initial_state,
            mixer=self.mixer,
            solve=False,
        )
        temp = benchmark_energy + self.offset
        #Choose minimum of benchmark_energy if there already exists self.benchmark_energy
        self.benchmark_energy = min(self.benchmark_energy,
                                    temp) if self.benchmark_energy else temp
        return self.benchmark_energy

    def solve_qaoa(self, p, **kwargs):
        if self.optimal_point and 'point' not in kwargs:
            point = self.optimal_point
        else:
            point = kwargs.get("point", None)
        fourier_parametrise = True
        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 results to 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 something like "100" instead the "000" for 3 remaining variables)solve
        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 = [
                ('0' * num_vars, self.qubo.objective.evaluate([0] * 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]
            fourier_parametrise = True
            if fourier_parametrise:
                points = [
                    QAOAEx.convert_to_fourier_point(point, len(point))
                    for point in points
                ]
            qaoa_results, _ = CustomQAOA(
                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:
            fourier_parametrise = True
            if fourier_parametrise:
                points = [
                    QAOAEx.convert_to_fourier_point(point, len(point))
                    for point in points
                ]
            qaoa_results, _ = CustomQAOA(
                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:
            list_points = [0] * (2 * p) + [[
                2 * np.pi * (np.random.rand() - 0.5) for _ in range(2 * p)
            ] for _ in range(5)]
            fourier_parametrise = True
            if fourier_parametrise:
                points = [
                    QAOAEx.convert_to_fourier_point(point, len(point))
                    for point in points
                ]
            qaoa_results, _ = CustomQAOA(
                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:
            fourier_parametrise = True
            if fourier_parametrise:
                point = QAOAEx.convert_to_fourier_point(point, len(point))
            qaoa_results, _ = CustomQAOA(
                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
        qaoa_results.eigenvalue = sum(
            [x[1] * x[2] for x in qaoa_results.eigenstate])
        self.optimal_point = QAOAEx.convert_to_fourier_point(
            point, len(point)) if fourier_parametrise else 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.result.fval
        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.result.fval, 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 print_state(self, eigenstate):
        header = '|'
        for var in self.qubo.variables:
            var_name = var.name
            header += '{:<5}|'.format(var_name.replace("_", ""))
        header += '{:<6}|'.format('Cost')
        header += '{:<5}|'.format("Prob")
        print("-" * len(header))
        print(header)
        print("-" * len(header))
        for item in eigenstate:
            string = '|'
            for binary_var in item[0]:
                string += '{:<5} '.format(binary_var)  #Binary string
            string += '{:<6} '.format(np.round(item[1], 2))  #Cost
            string += '{:<5}|'.format(np.round(item[2], 3))  #Prob
            print(string)
        print("-" * len(header))

    def perform_substitution_from_qaoa_results(self,
                                               qaoa_results,
                                               update_benchmark_energy=True,
                                               biased=True):

        correlations = self.get_biased_correlations(
            qaoa_results.eigenstate) if biased else self.get_correlations(
                qaoa_results.eigenstate)
        i, j = self.find_strongest_correlation(correlations)
        correlation = correlations[i, j]
        new_qubo = deepcopy(self.qubo)
        x_i, x_j = new_qubo.variables[i].name, new_qubo.variables[j].name

        if x_i == "X_anc":
            print("X_i was ancilla. Swapped")
            x_i, x_j = x_j, x_i  #So ancilla qubit is never substituted out
            i, j = j, i  #Also swap i and j

        print("\nCorrelation: < {} {} > = {}".format(x_i.replace("_", ""),
                                                     x_j.replace("_", ""),
                                                     correlation))

        car_block = int(x_i[2])

        #         #If same car_block and x_i = x_j, then both must be 0 since only one 1 in a car block
        #         if x_i[2] == x_j[2] and correlation > 0 and len(self.car_blocks[car_block]) > 2:
        #             # set x_i = x_j = 0
        #             new_qubo = new_qubo.substitute_variables({x_i: 0, x_j:0})
        #             if new_qubo.status == QuadraticProgram.Status.INFEASIBLE:
        #                 raise QiskitOptimizationError('Infeasible due to variable substitution {} = {} = 0'.format(x_i, x_j))
        #             self.var_values[x_i] = 0
        #             self.var_values[x_j] = 0
        #             self.car_blocks[car_block].remove(x_i)
        #             self.car_blocks[car_block].remove(x_j)
        #             print("Two variable substitutions were performed due to extra information from constraints.")

        if correlation > 0:
            # set x_i = x_j
            new_qubo = new_qubo.substitute_variables(variables={x_i: (x_j, 1)})
            if new_qubo.status == QuadraticProgram.Status.INFEASIBLE:
                raise QiskitOptimizationError(
                    'Infeasible due to variable substitution {} = {}'.format(
                        x_i, x_j))
            self.replacements[x_i] = (x_j, 1)
            self.car_blocks[car_block].remove(x_i)

        else:
            # set x_i = 1 - x_j, this is done in two steps:
            # 1. set x_i = 1 + x_i
            # 2. set x_i = -x_j

            # 1a. get additional offset from the 1 on the RHS of (xi -> 1+xi)
            constant = new_qubo.objective.constant
            constant += new_qubo.objective.linear[i]
            constant += new_qubo.objective.quadratic[i, i]
            new_qubo.objective.constant = constant

            #1b get additional linear part from quadratic terms becoming linear due to the same 1 in the 1+xi as above
            for k in range(new_qubo.get_num_vars()):
                coeff = new_qubo.objective.linear[k]
                if k == i:
                    coeff += 2 * new_qubo.objective.quadratic[i, k]
                else:
                    coeff += new_qubo.objective.quadratic[i, k]

                # set new coefficient if not too small
                if np.abs(coeff) > 1e-10:
                    new_qubo.objective.linear[k] = coeff
                else:
                    new_qubo.objective.linear[k] = 0

            #2 set xi = -xj
            new_qubo = new_qubo.substitute_variables(
                variables={x_i: (x_j, -1)})
            if new_qubo.status == QuadraticProgram.Status.INFEASIBLE:
                raise QiskitOptimizationError(
                    'Infeasible due to variable substitution {} = -{}'.format(
                        x_i, x_j))
            self.replacements[x_i] = (x_j, -1)
            self.car_blocks[car_block].remove(x_i)

#         #If only one remaining variable and all other variables are 0, then remaining must be 1.
#         check = sum( [self.var_values.get("X_{}_{}".format(car_block, route_no), 0) for route_no in range(self.no_routes)] )
#         if len(self.car_blocks[car_block]) == 1 and check == 0:
#             x_r = self.car_blocks[car_block][0] #remaining variable
#             new_qubo = new_qubo.substitute_variables({x_r: 1})
#             if new_qubo.status == QuadraticProgram.Status.INFEASIBLE:
#                 raise QiskitOptimizationError('Infeasible due to variable substitution {} = 1'.format(x_r))
#             self.car_blocks[car_block].remove(x_r)
#             print("{} = 1 can also be determined from all other variables being 0 for car_{}".format(x_r, car_block))

#Update variable eliminated QUBO
        self.qubo = new_qubo
        op, offset = new_qubo.to_ising()
        self.operator = op
        self.offset = offset
        self.construct_initial_state()
        self.construct_mixer()


#         if update_benchmark_energy:
#             temp = self.get_benchmark_energy()

    def get_correlations(self, states) -> np.ndarray:
        """
        Get <Zi x Zj> correlation matrix from the eigenstate(state: Dict).

        Returns:
            A correlation matrix.
        """
        x, _, prob = states[0]
        n = len(x)
        correlations = np.zeros((n, n))
        for x, cost, prob in states:
            for i in range(n):
                for j in range(i):
                    if x[i] == x[j]:
                        correlations[i, j] += prob
                    else:
                        correlations[i, j] -= prob
        return correlations

    def get_biased_correlations(self, states) -> np.ndarray:
        """
        Get <Zi x Zj> correlation matrix from the eigenstate(states: Dict).

        Returns:
            A correlation matrix.
        """
        x, _, prob = states[0]
        n = len(x)
        correlations = np.zeros((n, n))
        for x, cost, prob in states:
            scaled_approx_quality = 1 / (1 +
                                         2**(-self.benchmark_energy + cost))
            for i in range(n):
                for j in range(i):
                    if x[i] == x[j]:
                        correlations[i, j] += scaled_approx_quality * prob
                    else:
                        correlations[i, j] -= scaled_approx_quality * prob
        return correlations

    def find_strongest_correlation(self, correlations):

        # get absolute values and set diagonal to -1 to make sure maximum is always on off-diagonal
        abs_correlations = np.abs(correlations)
        diagonal = np.diag(np.ones(len(correlations)))
        abs_correlations = abs_correlations - diagonal

        # get index of maximum (by construction on off-diagonal)
        m_max = np.argmax(abs_correlations.flatten())

        # translate back to indices
        i = int(m_max // len(correlations))
        j = int(m_max - i * len(correlations))

        return (i, j)
Esempio n. 7
0
class RQAOA:
    def __init__(self, qubo, no_cars, no_routes):
        var_list = qubo.variables
        opt_str = "LN_SBPLX"
        print("Optimizer: {}".format(opt_str))
        self.optimizer = NLOPT_Optimizer(opt_str)
        self.original_qubo = qubo
        self.qubo = qubo
        op, offset = qubo.to_ising()
        self.operator = op
        self.offset = offset
        self.quantum_instance = QuantumInstance(backend = Aer.get_backend("aer_simulator_matrix_product_state"), shots = 1024)
        self.replacements = {var.name:None for var in var_list}
        self.no_cars = no_cars
        self.no_routes = no_routes
        self.car_blocks = np.empty(shape = (no_cars,), dtype=object)
        for car_no in range(no_cars):
            self.car_blocks[car_no] = ["X_{}_{}".format(car_no, route_no) for route_no in range(no_routes)]
        self.qaoa_result = None
        self.benchmark_energy = None
        self.var_values = {}
        self.construct_initial_state()
        self.construct_mixer()
        self.get_random_energy()
        self.get_benchmark_energy()

    
    def construct_initial_state(self):
        qc = QuantumCircuit()
        for car_no in range(self.no_cars):
            car_block = self.car_blocks[car_no]
            R = len(car_block)
            if R != 0 and R != 1:
                num_qubits = qc.num_qubits
                q_regs = QuantumRegister(R, 'car_{}'.format((car_no)))
                qc.add_register(q_regs)
                w_one = QuantumCircuit(q_regs)
                for r in range(0,R-1):
                    if r == 0:
                        w_one.ry(2*np.arccos(1/np.sqrt(R-r)),r)
                    elif r != R-1:
                        w_one.cry(2*np.arccos(1/np.sqrt(R-r)), r-1, r)
                for r in range(1,R):
                    w_one.cx(R-r-1,R-r)
                w_one.x(0)
                qc.append(w_one, range(num_qubits, num_qubits+R))
            elif R == 1:
                num_qubits = qc.num_qubits
                q_regs = QuantumRegister(R, 'car_{}'.format((car_no)))
                qc.add_register(q_regs)
                w_one = QuantumCircuit(q_regs)
                w_one.h(0)
                qc.append(w_one, range(num_qubits, num_qubits+R))
            else:
                continue
        qc = qc.decompose()
        self.initial_state = qc
    
    def construct_mixer(self):
        from qiskit.circuit.parameter import Parameter
        initial_state = self.initial_state 
        no_qubits = initial_state.num_qubits
        t = Parameter('t')
        mixer = QuantumCircuit(no_qubits)
        mixer.append(initial_state.inverse(), range(no_qubits))
        mixer.rz(2*t, range(no_qubits))
        mixer.append(initial_state, range(no_qubits))
        self.mixer = mixer
    
    def solve_classically(self):
        classical_result, _ = solve_classically(self.qubo)
        self.result = classical_result
    
    def get_random_energy(self):
        #Get random benchmark energy for 0 layer QAOA (achieved by using layer 1 QAOA with [0,0] angles)
        random_energy = CustomQAOA(operator = self.operator,
                    quantum_instance = self.quantum_instance,
                    optimizer = self.optimizer,
                    reps = 1,
                    initial_state = self.initial_state,
                    mixer = self.mixer,
                    solve = False,
                    )
        temp = random_energy
        self.random_energy = temp + self.offset
        print("random energy: {}".format(self.random_energy))
        
        
    
    def get_benchmark_energy(self):
        #Get benchmark energy with 0-layer QAOA (just as random_energy)
        benchmark_energy = CustomQAOA(operator = self.operator,
                    quantum_instance = self.quantum_instance,
                    optimizer = self.optimizer,
                    reps = 1,
                    initial_state = self.initial_state,
                    mixer = self.mixer,
                    solve = False,
                    )
        temp = benchmark_energy + self.offset
        #Choose minimum of benchmark_energy if there already exists self.benchmark_energy
        self.benchmark_energy = min(self.benchmark_energy, temp) if self.benchmark_energy else temp
        return self.benchmark_energy
    
    def solve_tqa_qaoa(self, p):
        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]
        fourier_parametrise = True
        if fourier_parametrise:
            points = [ QAOAEx.convert_to_fourier_point(point, len(point)) for point in points ]
        self.optimizer.set_options(maxeval = 1000)
        qaoa_results, _, _ = CustomQAOA( 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
                                                    )
        point = qaoa_results.optimal_point
        qaoa_results.eigenvalue = sum( [ x[1] * x[2] for x in qaoa_results.eigenstate ] )
        self.optimal_point = QAOAEx.convert_to_fourier_point(point, len(point)) if fourier_parametrise else point
        self.qaoa_result = qaoa_results
        return qaoa_results
    
    def solve_qaoa(self, p, **kwargs):
        point = self.optimal_point if 'point' not in kwargs else kwargs['point']
        fourier_parametrise = True
        self.optimizer.set_options(maxeval = 1000)
        qaoa_results, _, _ = CustomQAOA( 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
        qaoa_results.eigenvalue = sum( [ x[1] * x[2] for x in qaoa_results.eigenstate ] )
        self.optimal_point = QAOAEx.convert_to_fourier_point(point, len(point)) if fourier_parametrise else point
        self.qaoa_result = qaoa_results
        return qaoa_results
    
    def perform_substitution_from_qaoa_results(self, qaoa_results, update_benchmark_energy=True):
        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)
        scale = self.random_energy - self.result.fval
        approx_quality = (self.random_energy - sorted_eigenstate_by_energy[0][1])/ scale 
        eigenstate = dict( [ (x[0], x[2]) for x in qaoa_results.eigenstate ] )
        self.approx_quality = approx_quality
        self.eigenstate = eigenstate
        print( "QAOA lowest energy solution: {}".format(sorted_eigenstate_by_energy[0]) )
        print( "Approx_quality: {}".format(approx_quality)
        print( "QAOA most probable solution: {}".format(sorted_eigenstate_by_prob[0]) )
        print( "Approx_quality: {}".format((self.random_energy - sorted_eigenstate_by_prob[0][1])/ scale) )
        
        correlations = self.get_correlations(qaoa_results.eigenstate)
        i, j = self.find_strongest_correlation(correlations)
        new_qubo = deepcopy(self.qubo)
        x_i, x_j = new_qubo.variables[i].name, new_qubo.variables[j].name
        print( "\nCorrelation: < {} {} > = {}".format(x_i, x_j, correlations[i, j])) 
        
        car_block = int(x_i[2])
        #If same car_block and x_i = x_j, then both must be 0 since only one 1 in a car block
        if x_i[2] == x_j[2] and correlations[i, j] > 0 and len(self.car_blocks[car_block]) > 2: 
            # set x_i = x_j = 0
            new_qubo = new_qubo.substitute_variables({x_i: 0, x_j:0})
            if new_qubo.status == QuadraticProgram.Status.INFEASIBLE:
                raise QiskitOptimizationError('Infeasible due to variable substitution {} = {} = 0'.format(x_i, x_j))
            self.var_values[x_i] = 0
            self.var_values[x_j] = 0
            self.car_blocks[car_block].remove(x_i)
            self.car_blocks[car_block].remove(x_j)
            print("Two variable substitutions were performed due to extra information from constraints.")
            if len(self.car_blocks[car_block]) == 1: #If only one remaining variable
                x_r = self.car_blocks[car_block][0] #remaining variable
                #Check if all other variables are 0 (then their sum should be 0) -> so x_r must be 1
                check = sum( [self.var_values.get("X_{}_{}".format(car_block, route_no), 0) for route_no in range(self.no_routes)] )
                if check == 0:
                    new_qubo = new_qubo.substitute_variables({x_r: 1})
                    if new_qubo.status == QuadraticProgram.Status.INFEASIBLE:
                        raise QiskitOptimizationError('Infeasible due to variable substitution {} = 1'.format(x_r))
                    self.car_blocks[car_block].remove(x_r)
                    print("{} = 1 can also be determined from all other variables being 0 for car_{}".format(x_r, car_block))
        elif x_i[2] != x_j[2] and correlations[i, j] > 0: 
            # set x_i = x_j
            new_qubo = new_qubo.substitute_variables(variables={i: (j, 1)})
            if new_qubo.status == QuadraticProgram.Status.INFEASIBLE:
                raise QiskitOptimizationError('Infeasible due to variable substitution {} = {}'.format(x_i, x_j))            
            self.replacements[x_i] = (x_j, 1)
            self.car_blocks[car_block].remove(x_i)
        else:
            # set x_i = 1 - x_j, this is done in two steps:
            # 1. set x_i = 1 + x_i
            # 2. set x_i = -x_j

            # 1a. get additional offset from the 1 on the RHS of (xi -> 1+xi)
            constant = new_qubo.objective.constant
            constant += new_qubo.objective.linear[i]
            constant += new_qubo.objective.quadratic[i, i]
            new_qubo.objective.constant = constant

            #1b get additional linear part from quadratic terms becoming linear due to the same 1 in the 1+xi as above
            for k in range(new_qubo.get_num_vars()):
                coeff = new_qubo.objective.linear[k]
                if k == i:
                    coeff += 2*new_qubo.objective.quadratic[i, k]
                else:
                    coeff += new_qubo.objective.quadratic[i, k]

                # set new coefficient if not too small
                if np.abs(coeff) > 1e-10:
                    new_qubo.objective.linear[k] = coeff
                else:
                    new_qubo.objective.linear[k] = 0

            #2 set xi = -xj
            new_qubo = new_qubo.substitute_variables(variables={i: (j, -1)})
            if new_qubo.status == QuadraticProgram.Status.INFEASIBLE:
                raise QiskitOptimizationError('Infeasible due to variable substitution {} = -{}'.format(x_i, x_j))      
            self.replacements[x_i] = (x_j, -1)
            self.car_blocks[car_block].remove(x_i)
        self.qubo = new_qubo
        op, offset = new_qubo.to_ising()
        self.operator = op
        self.offset = offset
        self.construct_initial_state()
        self.construct_mixer()
        if update_benchmark_energy:
            temp = self.get_benchmark_energy()
        
        
    def get_correlations(self, state) -> np.ndarray:
        """
        Get <Zi x Zj> correlation matrix from the eigenstate(state: Dict).

        Returns:
            A correlation matrix.
        """
        states = state
        # print(states)
        x, _, prob = states[0]
        n = len(x)
        correlations = np.zeros((n, n))
        for x, cost, prob in states:
            if cost < self.benchmark_energy:
                scaled_approx_quality = (self.benchmark_energy - cost)
                for i in range(n):
                    for j in range(i):
                        if x[i] == x[j]:
                            correlations[i, j] += scaled_approx_quality * prob
                        else:
                            correlations[i, j] -= scaled_approx_quality * prob
        return correlations

    def find_strongest_correlation(self, correlations):

        # get absolute values and set diagonal to -1 to make sure maximum is always on off-diagonal
        abs_correlations = np.abs(correlations)
        diagonal = np.diag( np.ones(len(correlations)) )
        abs_correlations = abs_correlations - diagonal


        # get index of maximum (by construction on off-diagonal)
        m_max = np.argmax(abs_correlations.flatten())

        # translate back to indices
        i = int(m_max // len(correlations))
        j = int(m_max - i*len(correlations))

        return (i, j)
    

def main(args=None):
    start = time()
    if args == None:
        args = parse()
    with open('qubos_{}_car_{}_routes/qubo_{}.pkl'.format(args["no_cars"], args["no_routes"], args["no_samples"]), 'rb') as f:
        load_data = pkl.load(f)
        if len(load_data) == 5:
            qubo, max_coeff, operator, offset, routes = load_data
        else:
            qubo, max_coeff, operator, offset, routes, classical_result = load_data
    rqaoa = RQAOA(qubo, args["no_cars"], args["no_routes"])
    rqaoa.solve_classically()
    print(rqaoa.result)
    print("First round of TQA-QAOA. Results below:")
    qaoa_results = rqaoa.solve_tqa_qaoa(2)
    rqaoa.perform_substitution_from_qaoa_results(qaoa_results)
    print("Performed variable substition(s) and constructed new initial state and mixer.")
    num_vars = rqaoa.qubo.get_num_vars()
    print("Remaining variables: {}".format(num_vars))
    t = 1
    while num_vars > 1:
        print("-"*50)
        t+=1
        qaoa_results = rqaoa.solve_tqa_qaoa(2)
        print( "Round {} of TQA-QAOA. Results below:".format(t) )
        rqaoa.perform_substitution_from_qaoa_results(qaoa_results)
        print("Performed variable substition(s) and constructed new initial state and mixer.")
        num_vars = rqaoa.qubo.get_num_vars()
        print("Remaining variables: {}".format(num_vars))
    print("-"*50)
    p=2
    qaoa_results = rqaoa.solve_qaoa( p, point = [0]* (2*p) )
    print( "Final round of QAOA Done. Eigenstate below:" )
    pprint(qaoa_results.eigenstate)
    print( rqaoa.prob_s )
    print( rqaoa.approx_s )
    max_state = sorted(qaoa_results.eigenstate, key = lambda x: x[2], reverse=True)[0]
    var_last = rqaoa.qubo.variables[0].name
    rqaoa.var_values[var_last] = int(max_state[0])
    var_values = rqaoa.var_values
    replacements = rqaoa.replacements
    while True:
        for var, replacement in replacements.items():
            if replacement == None:
                continue
            elif replacement[0] in var_values and var not in var_values and replacement[1] != None:
                var_values[var] = var_values[ replacement[0] ] if replacement[1] == 1 else 1 - var_values[ replacement[0] ]
        if len(var_values.keys()) == args["no_cars"]*args["no_routes"]:
            break
    print(rqaoa.result)
    print(var_values)
    list_values = list(var_values.values())
    cost = rqaoa.original_qubo.objective.evaluate(var_values)
    print("{}, Cost: {}".format(list_values, cost))

if __name__ == '__main__':
    main()

finish = time()
print("Time taken: {} s".format(finish - start))
Esempio n. 8
0
def main(args=None):
    """[summary]

    Args:
        raw_args ([type], optional): [description]. Defaults to None.
    """
    start = time()
    if args == None:
        args = parse()

    prob_s_s = []
    qubo_no = args["no_samples"]
    print("__" * 50, "\nQUBO NO: {}\n".format(qubo_no), "__" * 50)

    #Load generated qubo_no
    with open(
            'qubos_{}_car_{}_routes/qubo_{}.pkl'.format(
                args["no_cars"], args["no_routes"], qubo_no), 'rb') as f:
        qubo, max_coeff, operator, offset, routes = pkl.load(f)
    print(operator)

    x_s = find_all_ground_states(qubo)

    # Visualise
    if args["visual"]:
        graph = import_map('melbourne.pkl')
        visualise_solution(graph, routes)

    # Solve QAOA from QUBO with valid solution
    no_couplings = count_coupling_terms(operator)
    print("Number of couplings: {}".format(no_couplings))
    print("Solving with QAOA...")
    no_shots = 10000
    backend = Aer.get_backend('statevector_simulator')
    quantum_instance = QuantumInstance(backend, shots=no_shots)
    optimizer_method = "LN_SBPLX"
    optimizer = NLOPT_Optimizer(method=optimizer_method)
    print("_" * 50, "\n" + optimizer.__class__.__name__)
    print("_" * 50)

    quantum_instance = QuantumInstance(backend)
    prob_s_s = []
    initial_state = construct_initial_state(args["no_routes"], args["no_cars"])
    mixer = n_qbit_mixer(initial_state)
    next_fourier_point, next_fourier_point_B = [0, 0], [
        0, 0
    ]  #Not used for p=1 then gets updated for p>1.
    for p in range(1, args["p_max"] + 1):
        print("p = {}".format(p))
        if p == 1:
            points = [[0.75,0]] \
                # + [[ np.pi*(2*np.random.rand() - 1) for _ in range(2) ] for _ in range(args["no_restarts"])]

            draw_circuit = True
        else:
            penalty = 0.6
            points = generate_points(next_fourier_point,
                                     no_perturb=10,
                                     penalty=0.6)
            print(points) \
                # + generate_points(next_fourier_point_B, 10, penalty)

            draw_circuit = False
        #empty lists to save following results to choose best result
        results = []
        exp_vals = []
        for r in range(len(points)):
            point = points[r]
            if np.amax(np.abs(point)) < np.pi / 2:
                qaoa_results, optimal_circ = CustomQAOA(
                    operator,
                    quantum_instance,
                    optimizer,
                    reps=p,
                    initial_fourier_point=points[r],
                    initial_state=initial_state,
                    mixer=mixer,
                    construct_circ=draw_circuit)
                if r == 0:
                    next_fourier_point = np.array(qaoa_results.optimal_point)
                    next_fourier_point = QAOAEx.convert_from_fourier_point(
                        next_fourier_point, 2 * p + 2)
                    next_fourier_point = QAOAEx.convert_to_fourier_point(
                        next_fourier_point, 2 * p + 2)
                exp_val = qaoa_results.eigenvalue * max_coeff + offset
                exp_vals.append(exp_val)
                prob_s = 0
                for string in x_s:
                    prob_s += qaoa_results.eigenstate[
                        string] if string in qaoa_results.eigenstate else 0
                results.append((qaoa_results, optimal_circ, prob_s))
                print("Point_no: {}, Exp_val: {}, Prob_s: {}".format(
                    r, exp_val, prob_s))
            else:
                print(
                    "Point_no: {}, was skipped because it is outside of bounds"
                    .format(r))
        minim_index = np.argmin(exp_vals)
        optimal_qaoa_result, optimal_circ, optimal_prob_s = results[
            minim_index]
        # if draw_circuit:
        #     print(optimal_circ.draw())
        minim_exp_val = exp_vals[minim_index]
        print("Minimum: {}, prob_s: {}".format(minim_exp_val, optimal_prob_s))
        prob_s_s.append(optimal_prob_s)
        next_fourier_point_B = np.array(optimal_qaoa_result.optimal_point)
        print("Optimal_point: {}".format(next_fourier_point_B))
        next_fourier_point_B = QAOAEx.convert_from_fourier_point(
            next_fourier_point_B, 2 * p + 2)
        next_fourier_point_B = QAOAEx.convert_to_fourier_point(
            next_fourier_point_B, 2 * p + 2)

    print(prob_s_s)

    with open(
            'results/{}cars{}routes_qubo{}.csv'.format(args["no_cars"],
                                                       args["no_routes"],
                                                       args["no_samples"]),
            'w') as f:
        np.savetxt(f, prob_s_s, delimiter=',')
    finish = time()
    print("Time Taken: {}".format(finish - start))
Esempio n. 9
0
def main(args = None):
    """[summary]

    Args:
        raw_args ([type], optional): [description]. Defaults to None.
    """
    start = time()
    if args == None:
        args = parse()

    qubo_no = args["no_samples"]
    print_to_file("-"*50)
    print_to_file("QUBO_{}".format(qubo_no))
    #Load generated qubo_no
    with open('qubos_{}_car_{}_routes/qubo_{}.pkl'.format(args["no_cars"], args["no_routes"], qubo_no), 'rb') as f:
        qubo, max_coeff, operator, offset, routes = pkl.load(f)
    qubo = QuadraticProgram()
    qubo.from_ising(operator)
    
    x_s, opt_value, classical_result = find_all_ground_states(qubo)
    print_to_file(classical_result)
    
    #Set optimizer method
    method = args["method"]
    optimizer = NLOPT_Optimizer(method = method, result_message=False)
    backend = Aer.get_backend("statevector_simulator")
    quantum_instance = QuantumInstance(backend = backend)

    approx_ratios = []
    prob_s_s = []
    p_max = args["p_max"]
    no_routes, no_cars = (args["no_routes"], args["no_cars"])

    custom = True
    if custom:
        initial_state = construct_initial_state(no_routes = no_routes, no_cars = no_cars)
        mixer = n_qbit_mixer(initial_state)
    else:
        initial_state, mixer = (None, None)

    fourier_parametrise = args["fourier"]
    print_to_file("-"*50)
    print_to_file("Now solving with QAOA... Fourier Parametrisation: {}".format(fourier_parametrise))
    for p in range(1, p_max+1):
        if p == 1:
            points = [[0,0]] + [ np.random.uniform(low = -np.pi/2+0.01, high = np.pi/2-0.01, size = 2*p) for _ in range(2**p)]
            next_point = []
        else:
            penalty = 0.6
            points = [next_point_l] + generate_points(next_point, no_perturb=min(2**p-1,10), penalty=penalty)
        construct_circ = False
        #empty lists to save following results to choose best result
        results = []
        exp_vals = []
        print_to_file("-"*50)
        print_to_file("    "+"p={}".format(p))
        optimizer.set_options(maxeval = 1000*p)
        for r, point in enumerate(points):
            qaoa_results, optimal_circ = CustomQAOA(operator,
                                                        quantum_instance,
                                                        optimizer,
                                                        reps = p,
                                                        initial_fourier_point= point,
                                                        initial_state = initial_state,
                                                        mixer = mixer,
                                                        construct_circ= construct_circ,
                                                        fourier_parametrise = fourier_parametrise,
                                                        qubo = qubo
                                                        )
            if r == 0:
                if fourier_parametrise:
                    next_point_l = np.zeros(shape = 2*p + 2)
                    next_point_l[0:p] = qaoa_results.optimal_point[0:p]
                    next_point_l[p+1:2*p+1] = qaoa_results.optimal_point[p:2*p]
                else:
                    next_point_l = interp_point(qaoa_results.optimal_point)
            exp_val = qaoa_results.eigenvalue * max_coeff
            exp_vals.append(exp_val)
            
            state_solutions = { item[0][::-1]: item[1:] for item in qaoa_results.eigenstate }
            
            for item in sorted(state_solutions.items(), key = lambda x: x[1][1], reverse = True)[0:5]:
                print_to_file( item )
                
            prob_s = 0
            for string in x_s:
                prob_s += state_solutions[string][1] if string in state_solutions else 0
            prob_s /= len(x_s) #normalise
            results.append((qaoa_results, optimal_circ, prob_s))
            print_to_file("    "+"Point_{}, Exp_val: {}, Prob_s: {}".format(r, exp_val, prob_s))
        minim_index = np.argmin(exp_vals)
        optimal_qaoa_result, optimal_circ, optimal_prob_s = results[minim_index]
        if fourier_parametrise:
            next_point = convert_from_fourier_point( optimal_qaoa_result.optimal_point, 2*p )
            next_point = convert_to_fourier_point( interp_point(next_point), 2*p + 2 )
#             next_point = np.zeros(shape = 2*p + 2)
#             next_point[0:p] = optimal_qaoa_result.optimal_point[0:p]
#             next_point[p+1:2*p+1] = optimal_qaoa_result.optimal_point[p:2*p]
        else:
            next_point = interp_point(optimal_qaoa_result.optimal_point)
        if construct_circ:
            print_to_file(optimal_circ.draw(fold=150))
        minim_exp_val = exp_vals[minim_index]
        approx_ratio = 1.0 - np.abs( (opt_value - minim_exp_val ) / opt_value )
        print_to_file("    "+"Minimum: {}, prob_s: {}, approx_ratio {}".format(minim_exp_val, optimal_prob_s, approx_ratio))
        approx_ratios.append(approx_ratio)
        prob_s_s.append(optimal_prob_s)
    print_to_file("-"*50)
    print_to_file("QAOA terminated")
    print_to_file("-"*50)
    print_to_file("Approximation ratios per layer: {}".format(approx_ratios))
    print_to_file("Prob_success per layer: {}".format(prob_s_s))
    save_results = np.append(approx_ratios, prob_s_s)
    if fourier_parametrise:
        with open('results_{}cars{}routes/RI_F_{}.csv'.format(args["no_cars"], args["no_routes"], args["no_samples"]), 'w') as f:
            np.savetxt(f, save_results, delimiter=',')
        print_to_file("Results saved in results_{}cars{}routes/RI_F_{}.csv".format(args["no_cars"], args["no_routes"], args["no_samples"]))
    else:
        with open('results_{}cars{}routes/RI_NF_{}.csv'.format(args["no_cars"], args["no_routes"], args["no_samples"]), 'w') as f:
            np.savetxt(f, save_results, delimiter=',')
        print_to_file("Results saved in results_{}cars{}routes/RI_NF_{}.csv".format(args["no_cars"], args["no_routes"], args["no_samples"]))
    finish = time()
    print_to_file("Time Taken: {}".format(finish - start))
Esempio n. 10
0
 def __init__(self, qubo, no_cars, no_routes, **kwargs):
     opt_str = kwargs.get('opt_str', "LN_COBYLA")
     self.symmetrise = kwargs.get('symmetrise', False)
     self.customise = kwargs.get('customise', True)
     self.classical_result = kwargs.get("classical_result", None)
     simulator = kwargs.get("simulator", "aer_simulator_matrix_product_state")
     noise_model = kwargs.get("noise_model", None)
     
     if simulator == None:
         simulator = "aer_simulator_matrix_product_state"
     
     #Initializing other algorithm required objects
     var_list = qubo.variables
     if noise_model == None:
         self.quantum_instance = QuantumInstance( backend = Aer.get_backend(simulator), shots = 4096)
     elif noise_model:
         self.quantum_instance = QuantumInstance( backend = Aer.get_backend(simulator),
                                                  shots = 4096, 
                                                  noise_model = noise_model,
                                                  basis_gates = ["cx", "x", "sx", "rz", "id"]
                                                )
                                                 
     self.random_instance = QuantumInstance(backend = Aer.get_backend("aer_simulator_matrix_product_state"), shots = 1000)
     #print backend name
     print("Quantum Instance: {}\n".format(self.quantum_instance.backend_name))
     #print backend name
     
     self.optimizer = NLOPT_Optimizer(opt_str)
     self.optimizer.set_options(max_eval = 1000)
     self.original_qubo = qubo
     self.qubo = qubo
     self.operator, self.offset = qubo.to_ising()
     
     #If no classical result, this will compute the appropriate self.classical_result, else this will simply re-organise already available result    
     self.solve_classically() 
      
     #Setup for variable replacements
     self.replacements = {var.name:None for var in var_list}
     self.no_cars = no_cars
     self.no_routes = no_routes
     self.car_blocks = np.empty(shape = (no_cars,), dtype=object)
     for car_no in range(no_cars):
         self.car_blocks[car_no] = ["X_{}_{}".format(car_no, route_no) for route_no in range(no_routes)]
     
     #Initialize variable placeholders in
     self.qaoa_result = None
     self.initial_state = None
     self.mixer = None
     self.benchmark_energy = None
     self.var_values = {}
     self.prob_s = []
     self.approx_s = []
     self.optimal_point = None
     
     #Random energy
     self.get_random_energy()
     
     #Symmetrise QUBO if required (after getting random energy WITHOUT ancilla)
     if self.symmetrise: 
         self.symmetrise_qubo()
         
     #Custom initial state and mixer if required
     if self.customise:
         if self.symmetrise:
             self.construct_initial_state(ancilla = True)
         else:
             self.construct_initial_state()
         self.construct_mixer()
Esempio n. 11
0
class RQAOA:
    def __init__(self, qubo, no_cars, no_routes, **kwargs):
        opt_str = kwargs.get('opt_str', "LN_COBYLA")
        self.symmetrise = kwargs.get('symmetrise', False)
        self.customise = kwargs.get('customise', True)
        self.classical_result = kwargs.get("classical_result", None)
        simulator = kwargs.get("simulator", "aer_simulator_matrix_product_state")
        noise_model = kwargs.get("noise_model", None)
        
        if simulator == None:
            simulator = "aer_simulator_matrix_product_state"
        
        #Initializing other algorithm required objects
        var_list = qubo.variables
        if noise_model == None:
            self.quantum_instance = QuantumInstance( backend = Aer.get_backend(simulator), shots = 4096)
        elif noise_model:
            self.quantum_instance = QuantumInstance( backend = Aer.get_backend(simulator),
                                                     shots = 4096, 
                                                     noise_model = noise_model,
                                                     basis_gates = ["cx", "x", "sx", "rz", "id"]
                                                   )
                                                    
        self.random_instance = QuantumInstance(backend = Aer.get_backend("aer_simulator_matrix_product_state"), shots = 1000)
        #print backend name
        print("Quantum Instance: {}\n".format(self.quantum_instance.backend_name))
        #print backend name
        
        self.optimizer = NLOPT_Optimizer(opt_str)
        self.optimizer.set_options(max_eval = 1000)
        self.original_qubo = qubo
        self.qubo = qubo
        self.operator, self.offset = qubo.to_ising()
        
        #If no classical result, this will compute the appropriate self.classical_result, else this will simply re-organise already available result    
        self.solve_classically() 
         
        #Setup for variable replacements
        self.replacements = {var.name:None for var in var_list}
        self.no_cars = no_cars
        self.no_routes = no_routes
        self.car_blocks = np.empty(shape = (no_cars,), dtype=object)
        for car_no in range(no_cars):
            self.car_blocks[car_no] = ["X_{}_{}".format(car_no, route_no) for route_no in range(no_routes)]
        
        #Initialize variable placeholders in
        self.qaoa_result = None
        self.initial_state = None
        self.mixer = None
        self.benchmark_energy = None
        self.var_values = {}
        self.prob_s = []
        self.approx_s = []
        self.optimal_point = None
        
        #Random energy
        self.get_random_energy()
        
        #Symmetrise QUBO if required (after getting random energy WITHOUT ancilla)
        if self.symmetrise: 
            self.symmetrise_qubo()
            
        #Custom initial state and mixer if required
        if self.customise:
            if self.symmetrise:
                self.construct_initial_state(ancilla = True)
            else:
                self.construct_initial_state()
            self.construct_mixer()
        
    
    def construct_initial_state(self, ancilla=False):
        qc = QuantumCircuit()
        for car_no in range(self.no_cars):
            car_block = self.car_blocks[car_no]
            R = len(car_block)
            if R != 0 and R != 1:
                num_qubits = qc.num_qubits
                q_regs = QuantumRegister(R, 'car_{}'.format((car_no)))
                qc.add_register(q_regs)
                w_one = QuantumCircuit(q_regs)
                for r in range(0,R-1):
                    if r == 0:
                        w_one.ry(2*np.arccos(1/np.sqrt(R-r)),r)
                    elif r != R-1:
                        w_one.cry(2*np.arccos(1/np.sqrt(R-r)), r-1, r)
                for r in range(1,R):
                    w_one.cx(R-r-1,R-r)
                w_one.x(0)
                qc.append(w_one, range(num_qubits, num_qubits+R))
            elif R == 1:
                num_qubits = qc.num_qubits
                q_regs = QuantumRegister(R, 'car_{}'.format((car_no)))
                qc.add_register(q_regs)
                w_one = QuantumCircuit(q_regs)
                w_one.h(0)
                qc.append(w_one, range(num_qubits, num_qubits+R))
            else:
                continue
        qc = qc.decompose()
        self.initial_state = qc

        if ancilla:
            ancilla_reg = QuantumRegister(1, 'ancilla')
            self.initial_state.add_register(ancilla_reg)
            self.initial_state.h(ancilla_reg[0])    
    
    def symmetrise_qubo(self):
        new_operator = []
        operator, _ = self.qubo.to_ising()
        for op_1 in operator:
            coeff, op_1 = op_1.to_pauli_op().coeff, op_1.to_pauli_op().primitive
            op_1_str = op_1.to_label()
            Z_counts = op_1_str.count('Z')
            if Z_counts == 1:
                op_1_str = "Z" + op_1_str  #Add a Z in the last qubit to single Z terms
            else:
                op_1_str = "I" + op_1_str #Add an I in the last qubit to ZZ terms (no change in operator)
            pauli = PauliOp( primitive = Pauli(op_1_str), coeff = coeff )
            new_operator.append(pauli)
        symmetrised_qubo = QuadraticProgram()
        symmetrised_operator = sum(new_operator)
        symmetrised_qubo.from_ising(symmetrised_operator, self.offset, linear=True)
        self.qubo = symmetrised_qubo
        self.rename_qubo_variables()
        operator, _ = self.qubo.to_ising()
        self.operator = operator
        
    def rename_qubo_variables(self):
        original_qubo = self.original_qubo
        qubo = self.qubo
        variable_names = [ variable.name for variable in qubo.variables ]
        original_variable_names = [ (variable.name, 1) for variable in original_qubo.variables ]
        new_variable_names = original_variable_names + [("X_anc", 1)] if self.symmetrise else original_variable_names
        variables_dict = dict(zip(variable_names, new_variable_names))
        for new_variable_name in variables_dict.values():
            qubo.binary_var(name = new_variable_name[0])
        qubo = qubo.substitute_variables(variables = variables_dict)
        if qubo.status == QuadraticProgram.Status.INFEASIBLE:
            raise QiskitOptimizationError('Infeasible due to variable substitution')
        self.qubo = qubo

    def construct_mixer(self):
        from qiskit.circuit.parameter import Parameter
        initial_state = self.initial_state 
        no_qubits = initial_state.num_qubits
        t = Parameter('t')
        mixer = QuantumCircuit(no_qubits)
        mixer.append(initial_state.inverse(), range(no_qubits))
        mixer.rz(2*t, range(no_qubits))
        mixer.append(initial_state, range(no_qubits))
        self.mixer = mixer
    
    def solve_classically(self):
        if self.classical_result:
            print("There is an existing classical result. Using this as code proceeds.")
            print(self.classical_result)
            self.opt_value = self.classical_result.fval
        else:
            print("No classical result already available.")
            print("Now solving classically")
            _, opt_value, classical_result, _ = find_all_ground_states(self.original_qubo)
            self.classical_result = classical_result
            self.opt_value = opt_value
    
    def get_random_energy(self):
        #Get random benchmark energy for 0 layer QAOA (achieved by using layer 1 QAOA with [0,0] angles) and with only feasible states (custom initial state)
        self.construct_initial_state()
        self.construct_mixer()
        random_energy, _ = CustomQAOA(operator = self.operator,
                    quantum_instance = self.random_instance,
                    optimizer = self.optimizer,
                    reps = 1,
                    initial_state = self.initial_state,
                    mixer = self.mixer,
                    solve = False,
                    )
        #Remove custom initial state if using BASE QAOA
        self.initial_state = None
        self.mixer = None
        temp = random_energy
        self.random_energy = temp + self.offset
        if np.round( self.random_energy - self.opt_value, 6 ) < 1e-7:
            print("0 layer QAOA converged to exact solution. Shifting value up by |exact_ground_energy| instead to avoid dividing by 0 in approx quality.")
            self.random_energy += np.abs(self.random_energy)
        self.benchmark_energy = self.random_energy
        print("random energy: {}\n".format(self.random_energy))
    
    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 print_state(self, eigenstate):
            header = '|'
            for var in self.qubo.variables:
                var_name = var.name
                header += '{:<5}|'.format(var_name.replace("_", ""))
            header += '{:<6}|'.format('Cost')
            header += '{:<5}|'.format("Prob")
            print("-"*len(header))
            print(header)
            print("-"*len(header))
            
            count = 0
            for item in eigenstate:
                string = '|'
                for binary_var in item[0]:
                    string += '{:<5} '.format(binary_var) #Binary string
                string += '{:<6} '.format(np.round(item[1], 2)) #Cost
                string += '{:<5}|'.format(np.round(item[2], 3)) #Prob
                print(string)
                
                #Print only first 20 states of lowest energy
                count += 1
                if count == 20: 
                    break
            
            print("-"*len(header))

    def perform_substitution_from_qaoa_results(self, qaoa_results, update_benchmark_energy=True, biased = True):
        
        correlations = self.get_biased_correlations(qaoa_results.eigenstate) if biased else self.get_correlations(qaoa_results.eigenstate)
        i, j = self.find_strongest_correlation(correlations)
        correlation = correlations[i, j]
        new_qubo = deepcopy(self.qubo)
        x_i, x_j = new_qubo.variables[i].name, new_qubo.variables[j].name
        if x_i == "X_anc":
            print("X_i was ancilla. Swapped")
            x_i, x_j = x_j, x_i #So ancilla qubit is never substituted out
            i, j = j, i #Also swap i and j
        print( "\nCorrelation: < {} {} > = {}".format(x_i.replace("_", ""), x_j.replace("_", ""), correlation)) 
        
        car_block = int(x_i[2])
#         #If same car_block and x_i = x_j, then both must be 0 since only one 1 in a car block
#         if x_i[2] == x_j[2] and correlation > 0 and len(self.car_blocks[car_block]) > 2: 
#             # set x_i = x_j = 0
#             new_qubo = new_qubo.substitute_variables({x_i: 0, x_j:0})
#             if new_qubo.status == QuadraticProgram.Status.INFEASIBLE:
#                 raise QiskitOptimizationError('Infeasible due to variable substitution {} = {} = 0'.format(x_i, x_j))
#             self.var_values[x_i] = 0
#             self.var_values[x_j] = 0
#             self.car_blocks[car_block].remove(x_i)
#             self.car_blocks[car_block].remove(x_j)
#             print("Two variable substitutions were performed due to extra information from constraints.")                    
        if correlation > 0: 
            # set x_i = x_j
            new_qubo = new_qubo.substitute_variables(variables={x_i: (x_j, 1)})
            if new_qubo.status == QuadraticProgram.Status.INFEASIBLE:
                raise QiskitOptimizationError('Infeasible due to variable substitution {} = {}'.format(x_i, x_j))            
            self.replacements[x_i] = (x_j, 1)
            self.car_blocks[car_block].remove(x_i)
        else :
            # set x_i = 1 - x_j, this is done in two steps:
            # 1. set x_i = 1 + x_i
            # 2. set x_i = -x_j

            # 1a. get additional offset from the 1 on the RHS of (xi -> 1+xi)
            constant = new_qubo.objective.constant
            constant += new_qubo.objective.linear[i]
            constant += new_qubo.objective.quadratic[i, i]
            new_qubo.objective.constant = constant

            #1b get additional linear part from quadratic terms becoming linear due to the same 1 in the 1+xi as above
            for k in range(new_qubo.get_num_vars()):
                coeff = new_qubo.objective.linear[k]
                if k == i:
                    coeff += 2*new_qubo.objective.quadratic[i, k]
                else:
                    coeff += new_qubo.objective.quadratic[i, k]

                # set new coefficient if not too small
                if np.abs(coeff) > 1e-10:
                    new_qubo.objective.linear[k] = coeff
                else:
                    new_qubo.objective.linear[k] = 0

            #2 set xi = -xj
            new_qubo = new_qubo.substitute_variables(variables={x_i: (x_j, -1)})
            if new_qubo.status == QuadraticProgram.Status.INFEASIBLE:
                raise QiskitOptimizationError('Infeasible due to variable substitution {} = -{}'.format(x_i, x_j))
            self.replacements[x_i] = (x_j, -1)
            self.car_blocks[car_block].remove(x_i)                
        
        self.qubo = new_qubo
        op, offset = new_qubo.to_ising() 
        self.operator = op
        self.offset = offset
        if self.customise:
            if self.symmetrise:
                self.construct_initial_state(ancilla = True)
            else:
                self.construct_initial_state()
            self.construct_mixer()
        
        
    def get_correlations(self, states) -> np.ndarray:
        """
        Get <Zi x Zj> correlation matrix from the eigenstate(state: Dict).

        Returns:
            A correlation matrix.
        """
        x, _, prob = states[0]
        n = len(x)
        correlations = np.zeros((n, n))
        for x, cost, prob in states:
            for i in range(n):
                for j in range(i):
                    if x[i] == x[j]:
                        correlations[i, j] += prob
                    else:
                        correlations[i, j] -= prob
        return correlations
    
    def get_z_zz(self, states) -> np.ndarray:
        """
        Get <Zi x Zj> correlation matrix from the eigenstate(state: Dict) along with < Zi > on the diagonal 

        Returns:
            A correlation matrix.
        """
        x, _, prob = states[0]
        n = len(x)
        z_zz_matrix = np.zeros((n, n))
        for x, cost, prob in states:
            for i in range(n):
                #Diagonal entries
                if x[i] == '0':
                    z_zz_matrix[i, i] += prob
                elif x[i] == '1':
                    z_zz_matrix[i, i] -= prob
                
                #Lower triangular entries
                for j in range(i):
                        if x[i] == x[j]:
                            z_zz_matrix[i, j] += prob
                        else:
                            z_zz_matrix[i, j] -= prob
                            
        return z_zz_matrix

    def find_strongest_z_or_zz(self, z_zz_matrix):

        # get absolute values and set diagonal to -1 to make sure maximum is always on off-diagonal
        abs_z_zz = np.abs(z_zz_matrix)

        # get index of maximum (by construction on off-diagonal)
        m_max = np.argmax(abs_z_zz.flatten())

        # translate back to indices
        i = int(m_max // len(z_zz_matrix))
        j = int(m_max - i*len(z_zz_matrix))

        return (i, j)

    def perform_substitution_from_qaoa_results_z(self, qaoa_results):
        
        z_zz_matrix = self.get_z_zz(qaoa_results.eigenstate)
        i, j = self.find_strongest_z_or_zz(z_zz_matrix)
        sign = z_zz_matrix[i, j]
        new_qubo = deepcopy(self.qubo)
        x_i, x_j = new_qubo.variables[i].name, new_qubo.variables[j].name
        if x_i == "X_anc":
            print("X_i was ancilla. Swapped")
            x_i, x_j = x_j, x_i #So ancilla qubit is never substituted out
            i, j = j, i #Also swap i and j
        print(z_zz_matrix)
        print( "\nMaximum: < {} {} > = {}".format(x_i.replace("_", ""), x_j.replace("_", ""), sign)) 
        print(new_qubo)
        
        car_block = int(x_i[2])            
        if i != j:
            if sign > 0: 
                # set x_i = x_j
                new_qubo = new_qubo.substitute_variables(variables={x_i: (x_j, 1)})
                if new_qubo.status == QuadraticProgram.Status.INFEASIBLE:
                    raise QiskitOptimizationError('Infeasible due to variable substitution {} = {}'.format(x_i, x_j))            
                self.replacements[x_i] = (x_j, 1)
                self.car_blocks[car_block].remove(x_i)
            else:
                # set x_i = 1 - x_j, this is done in two steps:
                # 1. set x_i = 1 + x_i
                # 2. set x_i = -x_j

                # 1a. get additional offset from the 1 on the RHS of (xi -> 1+xi)
                constant = new_qubo.objective.constant
                constant += new_qubo.objective.linear[i]
                constant += new_qubo.objective.quadratic[i, i]
                new_qubo.objective.constant = constant

                #1b get additional linear part from quadratic terms becoming linear due to the same 1 in the 1+xi as above
                for k in range(new_qubo.get_num_vars()):
                    coeff = new_qubo.objective.linear[k]
                    if k == i:
                        coeff += 2*new_qubo.objective.quadratic[i, k]
                    else:
                        coeff += new_qubo.objective.quadratic[i, k]

                    # set new coefficient if not too small
                    if np.abs(coeff) > 1e-10:
                        new_qubo.objective.linear[k] = coeff
                    else:
                        new_qubo.objective.linear[k] = 0

                #2 set xi = -xj
                new_qubo = new_qubo.substitute_variables(variables={x_i: (x_j, -1)})
                if new_qubo.status == QuadraticProgram.Status.INFEASIBLE:
                    raise QiskitOptimizationError('Infeasible due to variable substitution {} = -{}'.format(x_i, x_j))
                self.replacements[x_i] = (x_j, -1)
                self.car_blocks[car_block].remove(x_i)    
        else:
            if sign > 0: 
                # set x_i = 0
                try:
                    new_qubo = new_qubo.substitute_variables(constants={x_i: 0.0})
                except Exception as e:
                    print(e)
#                 if new_qubo.status == QuadraticProgram.Status.INFEASIBLE:
#                     raise QiskitOptimizationError('Infeasible due to variable substitution {} = {}'.format(x_i, 0))            
                self.replacements[x_i] = None
                self.var_values[x_i] = 0
                self.car_blocks[car_block].remove(x_i)
            else:
                #set x_i = 1
                try:
                    new_qubo = new_qubo.substitute_variables(constants={x_i: 1.0})
                except Exception as e:
                    print(e)
#                 if new_qubo.status == QuadraticProgram.Status.INFEASIBLE:
#                     raise QiskitOptimizationError('Infeasible due to variable substitution {} = {}'.format(x_i, 1))
                self.replacements[x_i] = None
                self.var_values[x_i] = 1
                self.car_blocks[car_block].remove(x_i)    
        
        self.qubo = new_qubo
        op, offset = new_qubo.to_ising() 
        self.operator = op
        self.offset = offset
        if self.customise:
            if self.symmetrise:
                self.construct_initial_state(ancilla = True)
            else:
                self.construct_initial_state()
            self.construct_mixer()    
              
    def get_biased_correlations(self, states) -> np.ndarray:
        """
        Get <Zi x Zj> correlation matrix from the eigenstate(states: Dict).

        Returns:
            A correlation matrix.
        """
        x, _, prob = states[0]
        n = len(x)
        correlations = np.zeros((n, n))
        for x, cost, prob in states:
            scaled_approx_quality = 1 / ( 1 + 2 ** (-self.benchmark_energy + cost) )
            for i in range(n):
                for j in range(i):
                    if x[i] == x[j]:
                        correlations[i, j] += scaled_approx_quality * prob
                    else:
                        correlations[i, j] -= scaled_approx_quality * prob
        return correlations

    def find_strongest_correlation(self, correlations):

        # get absolute values and set diagonal to -1 to make sure maximum is always on off-diagonal
        abs_correlations = np.abs(correlations)
        diagonal = np.diag( np.ones(len(correlations)) )
        abs_correlations = abs_correlations - diagonal

        # get index of maximum (by construction on off-diagonal)
        m_max = np.argmax(abs_correlations.flatten())

        # translate back to indices
        i = int(m_max // len(correlations))
        j = int(m_max - i*len(correlations))

        return (i, j)
Esempio n. 12
0
def main(args=None):
    start = time()
    if args == None:
        args = parse()

    prob_s_s = []
    qubo_no = args["no_samples"]
    print("__"*50, "\nQUBO NO: {}\n".format(qubo_no), "__"*50)

    #Load generated qubo_no
    with open('qubos_{}_car_{}_routes/qubo_{}.pkl'.format(args["no_cars"], args["no_routes"], qubo_no), 'rb') as f:
        qubo, max_coeff, operator, offset, routes = pkl.load(f)
    # print(operator)
    classical_result = solve_classically(qubo)
    print(classical_result)
    x_arr = classical_result.x
    optimal_value = qubo.objective.evaluate(x_arr)
    # print(optimal_value)
    x_str = arr_to_str(x_arr)
    sort_values = get_costs(qubo)
    # print("_"*50)
    # up_to = 27
    # print("{} lowest states:".format(up_to))
    # avg = 0
    # for i in range(up_to):
    #     print(sort_values[i])
    #     avg += sort_values[i][1]
    # print("_"*50)
    # print("Avg: {}".format(avg/up_to))


    #Remake QUBO to introduce only ZiZj terms
    op, offset = qubo.to_ising()
    new_operator = []
    for i, op_1 in enumerate(op):
        coeff, op_1 = op_1.to_pauli_op().coeff, op_1.to_pauli_op().primitive
        op_1_str = op_1.to_label()
        Z_counts = op_1_str.count('Z')
        if Z_counts == 1:
            op_1_str = 'Z' + op_1_str
        else:
            op_1_str = 'I' + op_1_str
        pauli = PauliOp( primitive = Pauli(op_1_str), coeff = coeff )
        new_operator.append(pauli)
    qubo_2 = QuadraticProgram()
    operator = sum(new_operator)
    qubo_2.from_ising(operator, offset, linear=True)

    print("Solving with QAOA...")
    no_shots = 10000
    backend = Aer.get_backend('statevector_simulator')
    quantum_instance = QuantumInstance(backend, shots = no_shots)
    optimizer_method = "LN_SBPLX"
    optimizer = NLOPT_Optimizer(method = optimizer_method)
    print("_"*50,"\n"+optimizer.__class__.__name__)
    print("_"*50)


    p = 1
    delta_points = []
    deltas = np.arange(0.75, 0.76, 0.05)
    print("delta_t's: ", deltas)
    original_point = np.append([], [[(i+1)/p, 1-(i+1)/p] for i in range(p)])
    for delta in deltas:
        delta_points.append(delta*original_point)
    
    # point = [ 0.60081404,  0.11785113,  0.02330747,  1.10006101,  0.46256391,
    #    -0.96823671]
    draw_circuit = True
    initial_state = construct_initial_state_2(args["no_routes"], args["no_cars"])
    mixer = n_qbit_mixer(initial_state)
    quantum_instance = QuantumInstance(backend)
    results = []
    exp_vals = []
    for point in delta_points:
        point = QAOAEx.convert_to_fourier_point(point, 2*p)
        qaoa_results, _ = Fourier_QAOA(operator,
                                                    quantum_instance,
                                                    optimizer,
                                                    reps = p,
                                                    initial_fourier_point=point,
                                                    initial_state = initial_state,
                                                    mixer = mixer,
                                                    construct_circ=draw_circuit,
                                                    fourier_parametrise = True
                                                    )
        # print(optimal_circ.draw())
        results.append(qaoa_results)
        exp_val = qaoa_results.eigenvalue + offset
        print("ExpVal {}".format(exp_val))
        exp_vals.append(exp_val)
    minim_index = np.argmin(exp_vals)
    qaoa_results = results[minim_index]
    sort_states = sorted(qaoa_results.eigenstate.items(), key=lambda x: x[1], reverse=True)
    correlations = get_correlations(sort_states)
    # print(correlations)
    i,j = find_strongest_correlation(correlations)
    if correlations[i,j] > 0:
        condition = "Z_{i} = Z_{j}".format(i=i,j=j)
    else:
        condition = "Z_{i} = - Z_{j}".format(i=i,j=j)
    print("Max: <Z_{i}Z_{j}>={maxim}, condition: ".format(i=i, j=j, maxim = correlations[i,j])+condition)
    ground_energy = sort_values[0][1]
    x_s = [x_str]
    for i in range(0,10):
        if sort_values[i][0] == x_str or np.round(sort_values[i][1], 4) != np.round(ground_energy, 4):
            continue
        else:
            print("Other ground state(s) found: '{}'".format(sort_values[i][0]))
            x_s.append(sort_values[i][0])
    x_s_2 = []
    print("All the possible solutions were originally: ")
    print("[Z0 Z1 Z2 Z3 Z4 Z5 Z6 Z7 Z8 Z9]")
    for string in x_s:
        x = '0' + string
        x_arr = np.array(str_to_arr(x))
        # print("f({x})={obj}".format(x=x, obj = qubo_2.objective.evaluate(x_arr)))
        x_s_2.append(x_arr)
        formatted_arr = ["{} ".format(item) for item in x_arr]
        formatted_arr = ' '.join(formatted_arr)
        print("[{}]".format(formatted_arr))


    finish = time()
    print("Time taken {time}s".format(time=finish-start))