# pip version of CPLEX is not available for Apple silicon
%pip install cplex

import numpy as np
from qiskit_optimization import QuadraticProgram
from qiskit_optimization.algorithms import GurobiOptimizer

prob = QuadraticProgram()
n = 2001
prob.binary_var_list(n)
prob.minimize(linear=np.ones(n))
print(prob.prettyprint())

GurobiOptimizer().solve(prob)

from qiskit_optimization.applications import Knapsack

prob = Knapsack(
    values = [1, 2, 3, 4],
    weights= [5, 6, 7, 8],
    max_weight=20
).to_quadratic_program()

print(prob.prettyprint())

from qiskit_optimization.algorithms import ScipyMilpOptimizer

milp = ScipyMilpOptimizer()
result = milp.solve(prob)
print(result)

from qiskit_optimization.applications import Maxcut
import networkx as nx

graph = nx.random_regular_graph(d=3, n=8, seed=123)
maxcut = Maxcut(graph)
pos = nx.spring_layout(graph, seed=123)
maxcut.draw(pos=pos)

prob = maxcut.to_quadratic_program()
print(prob.prettyprint())

result = milp.solve(prob)

def quadratic_to_linear(problem: QuadraticProgram) -> QuadraticProgram:
    new_prob = QuadraticProgram(problem.name)
    for x in problem.variables:
        new_prob._add_variable(x.lowerbound, x.upperbound, x.vartype, x.name)
    obj = problem.objective
    lin = obj.linear.to_dict(use_name=True)
    quad = obj.quadratic.to_dict(use_name=True)
    prod = {}
    for (x, y), coeff in quad.items():
        name = f"{x}_AND_{y}"
        prod[x, y] = name
        new_prob.continuous_var(0, 1, name)
        lin[name] = coeff
    for (x, y), name in prod.items():
        new_prob.linear_constraint({name: 1, x: -1}, "<=", 0)
        new_prob.linear_constraint({name: 1, y: -1}, "<=", 0)
        new_prob.linear_constraint({name: 1, x: -1, y: -1}, ">=", -1)
    if obj.sense == obj.sense.MAXIMIZE:
        new_prob.maximize(obj.constant, lin)
    else:
        new_prob.minimize(obj.constant, lin)
    return new_prob

new_prob = quadratic_to_linear(prob)
print(new_prob.prettyprint())

result = milp.solve(new_prob)
print(result)

maxcut.draw(result.x[:prob.get_num_vars()], pos)

result = GurobiOptimizer().solve(prob)
print(result)

maxcut.draw(result, pos)

