Skip to content

Commit cbd73f4

Browse files
authored
Merge pull request #3 from psrenergy/pr/update
Update for Qiskit 1.x
2 parents 063363c + 49c1b54 commit cbd73f4

File tree

7 files changed

+323
-207
lines changed

7 files changed

+323
-207
lines changed

CondaPkg.toml

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
channels = ["anaconda", "conda-forge"]
22

3+
[deps]
4+
python = ">=3.7,<=3.11"
5+
36
[pip.deps]
4-
qiskit = "==0.39.4"
5-
qiskit-optimization = "==0.4.0"
7+
qiskit = "==1.1.0"
8+
qiskit-optimization = "==0.6.1"
9+
qiskit-ibm-runtime = "==0.23.0"
10+
qiskit-algorithms = "==0.3.0"
11+
qiskit-aer = "==0.14.1"
12+
scipy = "==1.13.0"
13+
numpy = "==1.26.0"

Project.toml

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
name = "QiskitOpt"
22
uuid = "81b20daf-e62b-4502-a0d1-aa084de80e33"
33
authors = ["pedromxavier <[email protected]>", "pedroripper <[email protected]>"]
4-
version = "0.2.0"
4+
version = "0.3.0"
55

66
[deps]
7+
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
78
PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d"
8-
QUBODrivers = "a3f166f7-2cd3-47b6-9e1e-6fbfe0449eb0"
9+
QUBO = "ce8c2e91-a970-4681-856b-16178c24a30c"
910
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
1011

1112
[compat]
12-
PythonCall = "0.9.12"
13-
QUBODrivers = "0.1"
14-
julia = "1.6"
13+
PythonCall = "0.9.20"
14+
julia = "1.9"
15+
QUBO = "0.3.0"

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ end
4545
To access IBM's Quantum Computers, it is necessary to create an account at [IBM Q](https://quantum-computing.ibm.com/) to obtain an API Token and run the following python code:
4646

4747
```python
48-
from qiskit import IBMQ
48+
from qiskit_ibm_runtime import QiskitRuntimeService
4949

50-
IBMQ.save_account("YOUR_TOKEN_HERE")
50+
QiskitRuntimeService.save_account(channel='ibm_quantum', token="YOUR_TOKEN_HERE")
5151
```
5252

5353
Another option is to set the `IBMQ_API_TOKEN` environment variable before loading `QiskitOpt.jl`:

src/QAOA.jl

+140-75
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,178 @@
11
module QAOA
22

3-
using Random
4-
using PythonCall: pyconvert
3+
using LinearAlgebra
4+
using PythonCall: pyconvert, pylist, pydict, pyint, pytuple, pybool, @pyexec
55
using ..QiskitOpt:
66
qiskit,
7-
qiskit_optimization_algorithms,
8-
qiskit_optimization_runtime,
9-
quadratic_program
10-
import QUBODrivers:
11-
MOI,
12-
QUBODrivers,
13-
QUBOTools,
14-
Sample,
15-
SampleSet,
16-
@setup,
17-
sample
18-
19-
@setup Optimizer begin
20-
name = "IBM Qiskit QAOA"
21-
sense = :min
22-
domain = :bool
23-
version = v"0.4.0"
7+
qiskit_ibm_runtime,
8+
qiskit_algorithms,
9+
qiskit_aer,
10+
quadratic_program,
11+
scipy,
12+
numpy
13+
14+
using QUBO
15+
MOI = QUBODrivers.MOI
16+
Sample = QUBODrivers.Sample
17+
SampleSet = QUBODrivers.SampleSet
18+
19+
QUBODrivers.@setup Optimizer begin
20+
name = "QAOA @ IBMQ"
2421
attributes = begin
25-
NumberOfReads["num_reads"]::Integer = 1_000
26-
RandomSeed["seed"]::Union{Integer,Nothing} = nothing
27-
IBMBackend["ibm_backend"]::String = "ibmq_qasm_simulator"
22+
MaximumIterations["max_iter"]::Integer = 15
23+
NumberOfReads["num_reads"]::Integer = 100
24+
NumberOfLayers["num_layers"]::Integer = 1
25+
InitialParameters["initial_parameters"]::Union{Vector{Float64}, Nothing} = nothing
26+
IBMFakeBackend["ibm_fake_backend"] = qiskit_ibm_runtime.fake_provider.FakeAlgiers
27+
IBMBackend["ibm_backend"]::Union{String, Nothing} = nothing
28+
IsLocal["is_local"]::Bool = false
29+
Entanglement["entanglement"]::String = "linear"
30+
Channel["channel"]::String = "ibm_quantum"
31+
Instance["instance"]::String = "ibm-q/open/main"
2832
end
2933
end
3034

31-
function sample(sampler::Optimizer{T}) where {T}
35+
function QUBODrivers.sample(sampler::Optimizer{T}) where {T}
3236
# -*- Retrieve Attributes - *-
33-
seed = MOI.get(sampler, QAOA.RandomSeed())
34-
num_reads = MOI.get(sampler, QAOA.NumberOfReads())
37+
n, L, Q, α, β = QUBOTools.qubo(sampler, :dense)
3538
ibm_backend = MOI.get(sampler, QAOA.IBMBackend())
3639

37-
# -*- Retrieve Model -*- #
38-
qp, α, β = quadratic_program(sampler)
39-
40-
# -*- Instantiate Random Generator -*- #
41-
rng = Random.Xoshiro(seed)
42-
4340
# Results vector
44-
samples = Vector{Sample{T,Int}}(undef, num_reads)
41+
samples = QUBOTools.Sample{T,Int}[]
4542

46-
# Timing Information
43+
# Extra Information
4744
metadata = Dict{String,Any}(
4845
"origin" => "IBMQ QAOA @ $(ibm_backend)",
49-
"time" => Dict{String,Any}(),
46+
"time" => Dict{String,Any}(),
47+
"evals" => Vector{Float64}(),
5048
)
5149

52-
# Connect to IBMQ and get backend
53-
connect(sampler) do client
54-
qaoa = qiskit_optimization_algorithms.MinimumEigenOptimizer(client)
55-
results = qaoa.solve(qp)
56-
57-
Ψ = Vector{Int}[]
58-
ρ = Float64[]
59-
Λ = T[]
50+
retrieve(sampler) do result, sample_results
51+
if MOI.get(sampler, MOI.ObjectiveSense()) == MOI.MAX_SENSE
52+
α = -α
53+
end
6054

61-
for sample in results.samples
55+
for key in sample_results.keys()
56+
state = reverse(parse.(Int,split(pyconvert.(String, key),"")))
57+
sample = QUBOTools.Sample{T,Int}(
6258
# state:
63-
push!(Ψ, pyconvert.(Int, sample.x))
59+
state,
60+
# energy:
61+
α * (state'* (Q+Diagonal(L)) * state + β),
6462
# reads:
65-
push!(ρ, pyconvert(Float64, sample.probability))
66-
# value:
67-
push!(Λ, α * (pyconvert(T, sample.fval) + β))
63+
pyconvert(Int, sample_results[key])
64+
)
65+
push!(samples, sample)
6866
end
6967

70-
P = cumsum(ρ)
71-
72-
for i = 1:num_reads
73-
p = rand(rng)
74-
j = first(searchsorted(P, p))
75-
76-
samples[i] = Sample{T}(Ψ[j], Λ[j])
77-
end
78-
79-
metadata["time"]["effective"] = pyconvert(
80-
Float64,
81-
results.min_eigen_solver_result.optimizer_time,
82-
)
83-
8468
return nothing
8569
end
8670

8771
return SampleSet{T}(samples, metadata)
8872
end
8973

90-
function connect(
74+
function retrieve(
9175
callback::Function,
92-
sampler::Optimizer,
93-
)
76+
sampler::Optimizer{T},
77+
) where {T}
9478
# -*- Retrieve Attributes -*- #
95-
ibm_backend = MOI.get(sampler, QAOA.IBMBackend())
79+
max_iter = MOI.get(sampler, QAOA.MaximumIterations())
80+
num_reads = MOI.get(sampler, QAOA.NumberOfReads())
81+
num_layers = MOI.get(sampler, QAOA.NumberOfLayers())
82+
ibm_backend = MOI.get(sampler, QAOA.IBMBackend())
83+
ibm_fake_backend = MOI.get(sampler, QAOA.IBMFakeBackend())
84+
channel = MOI.get(sampler, QAOA.Channel())
85+
instance = MOI.get(sampler, QAOA.Instance())
86+
initial_parameters = MOI.get(sampler, QAOA.InitialParameters())
87+
is_local = MOI.get(sampler, QAOA.IsLocal())
88+
89+
@pyexec """
90+
def cost_function(params, ansatz, hamiltonian, estimator):
91+
pub = (ansatz, [hamiltonian], [params])
92+
result = estimator.run(pubs=[pub]).result()
93+
energy = result[0].data.evs[0]
94+
return energy
95+
""" => cost_function
96+
97+
service = qiskit_ibm_runtime.QiskitRuntimeService(
98+
channel = channel,
99+
instance = instance,
100+
)
101+
102+
backend = if !isnothing(ibm_backend)
103+
_backend = service.get_backend(ibm_backend)
104+
if is_local && ibm_backend != "ibmq_qasm_simulator"
105+
qiskit_aer.AerSimulator.from_backend(_backend)
106+
else
107+
_backend
108+
end
109+
else
110+
_backend = ibm_fake_backend()
111+
is_local = true
112+
ibm_backend = _backend.backend_name
113+
_backend
114+
end
96115

97-
# -*- Load Credentials -*- #
98-
qiskit.IBMQ.load_account()
116+
if is_local && ibm_backend != "ibmq_qasm_simulator"
117+
backend = qiskit_aer.AerSimulator.from_backend(backend)
118+
end
99119

100-
# -*- Connect to provider -*- #
101-
provider = qiskit.IBMQ.get_provider()
102-
backend = provider.get_backend(ibm_backend)
120+
ising_qp = quadratic_program(sampler)
121+
ising_hamiltonian = ising_qp[0]
122+
ansatz = qiskit.circuit.library.QAOAAnsatz(
123+
ising_hamiltonian,
124+
reps = num_layers
125+
)
126+
127+
# pass manager for the quantum circuit (optimize the circuit for the target device)
128+
pass_manager = qiskit.transpiler.preset_passmanagers.generate_preset_pass_manager(
129+
target = backend.target,
130+
optimization_level = 3
131+
)
132+
133+
134+
# Ansatz and Hamiltonian to ISA (Instruction Set Architecture)
135+
ansatz_isa = pass_manager.run(ansatz)
136+
ising_hamiltonian = ising_hamiltonian.apply_layout(layout = ansatz_isa.layout)
137+
138+
139+
if isnothing(initial_parameters)
140+
initial_parameters = numpy.empty([ansatz_isa.num_parameters])
141+
for i in 1:pyconvert(Int, ansatz_isa.num_parameters)
142+
initial_parameters[i-1] = numpy.random.rand()
143+
end
144+
end
145+
146+
estimator = if is_local || ibm_backend == "ibmq_qasm_simulator"
147+
qiskit_ibm_runtime.EstimatorV2(backend = backend)
148+
else
149+
session = qiskit_ibm_runtime.Session(service=service, backend=backend)
150+
qiskit_ibm_runtime.EstimatorV2(session=session)
151+
end
152+
if !is_local
153+
estimator.options.default_shots = num_reads
154+
end
103155

104-
# -*- Setup QAOA Client -*- #
105-
client = qiskit_optimization_runtime.QAOAClient(
106-
provider = provider,
107-
backend = backend,
156+
println("Running QAOA on $(ibm_backend)...")
157+
scipy_options = pydict()
158+
scipy_options["maxiter"] = max_iter
159+
result = scipy.optimize.minimize(
160+
cost_function,
161+
initial_parameters,
162+
args = (ansatz_isa, ising_hamiltonian, estimator),
163+
method = "cobyla",
164+
options = scipy_options
108165
)
109166

110-
callback(client)
167+
qc = ansatz.assign_parameters(result.x)
168+
qc.measure_all()
169+
optimized_qc = pass_manager.run(qc)
170+
171+
qiskit_sampler = qiskit.primitives.StatevectorSampler(default_shots = pyint(num_reads))
172+
sampling_result = qiskit_sampler.run(pylist([optimized_qc])).result()[0]
173+
samples = sampling_result.data.meas.get_counts()
174+
175+
callback(result, samples)
111176

112177
return nothing
113178
end

src/QiskitOpt.jl

+27-25
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,52 @@
11
module QiskitOpt
22

33
using PythonCall
4-
import QUBODrivers: MOI, QUBODrivers, QUBOTools
4+
using QUBO
5+
MOI = QUBODrivers.MOI
56

67
# :: Python Qiskit Modules ::
7-
const qiskit = PythonCall.pynew()
8-
const qiskit_algorithms = PythonCall.pynew()
9-
const qiskit_optimization = PythonCall.pynew()
10-
const qiskit_optimization_algorithms = PythonCall.pynew()
11-
const qiskit_optimization_runtime = PythonCall.pynew()
12-
const qiskit_utils = PythonCall.pynew()
8+
const qiskit = PythonCall.pynew()
9+
const qiskit_optimization = PythonCall.pynew()
10+
const qiskit_ibm_runtime = PythonCall.pynew()
11+
const qiskit_algorithms = PythonCall.pynew()
12+
const qiskit_aer = PythonCall.pynew()
13+
const scipy = PythonCall.pynew()
14+
const numpy = PythonCall.pynew()
1315

1416
function __init__()
1517
# Load Python Packages
1618
PythonCall.pycopy!(qiskit, pyimport("qiskit"))
17-
PythonCall.pycopy!(qiskit_algorithms, pyimport("qiskit.algorithms"))
1819
PythonCall.pycopy!(qiskit_optimization, pyimport("qiskit_optimization"))
19-
PythonCall.pycopy!(
20-
qiskit_optimization_algorithms,
21-
pyimport("qiskit_optimization.algorithms"),
22-
)
23-
PythonCall.pycopy!(qiskit_optimization_runtime, pyimport("qiskit_optimization.runtime"))
24-
PythonCall.pycopy!(qiskit_utils, pyimport("qiskit.utils"))
20+
PythonCall.pycopy!(qiskit_ibm_runtime, pyimport("qiskit_ibm_runtime"))
21+
PythonCall.pycopy!(qiskit_algorithms, pyimport("qiskit_algorithms"))
22+
PythonCall.pycopy!(qiskit_aer, pyimport("qiskit_aer"))
23+
PythonCall.pycopy!(scipy, pyimport("scipy"))
24+
PythonCall.pycopy!(numpy, pyimport("numpy"))
2525

2626
# IBMQ Credentials
2727
IBMQ_API_TOKEN = get(ENV, "IBMQ_API_TOKEN", nothing)
28+
IBMQ_INSTANCE = get(ENV, "IBMQ_INSTANCE", "ibm-q/open/main")
2829

2930
if !isnothing(IBMQ_API_TOKEN)
30-
qiskit.IBMQ.save_account(IBMQ_API_TOKEN)
31+
qiskit_ibm_runtime.QiskitRuntimeService.save_account(channel=pystr("ibm_quantum"), instance = pystr(IBMQ_INSTANCE), token=pystr(IBMQ_API_TOKEN))
3132
end
3233
end
3334

3435
function quadratic_program(sampler::QUBODrivers.AbstractSampler{T}) where {T}
3536
# Retrieve Model
36-
Q, α, β = QUBODrivers.qubo(sampler, Dict)
37+
n, L, Q, α, β = QUBOTools.qubo(sampler, :dense)
3738

3839
# Build Qiskit Model
3940
linear = PythonCall.pydict()
4041
quadratic = PythonCall.pydict()
4142

42-
for ((i, j), q) in Q
43-
if i == j
44-
linear[string(i)] = q
45-
else
46-
quadratic[string(i), string(j)] = q
47-
end
43+
sense = MOI.get(sampler, MOI.ObjectiveSense())
44+
45+
for i in 1:n
46+
linear[string(i)] = L[i] * (sense == MOI.MIN_SENSE ? 1 : -1)
47+
end
48+
for i in 1:n, j in 1:n
49+
quadratic[string(i), string(j)] = Q[i,j] * (sense == MOI.MIN_SENSE ? 1 : -1)
4850
end
4951

5052
qp = qiskit_optimization.QuadraticProgram()
@@ -54,11 +56,11 @@ function quadratic_program(sampler::QUBODrivers.AbstractSampler{T}) where {T}
5456
end
5557

5658
qp.minimize(linear = linear, quadratic = quadratic)
57-
58-
return (qp, α, β)
59+
60+
return qp.to_ising()
5961
end
6062

61-
export QAOA, VQE
63+
export VQE, QAOA
6264

6365
include("QAOA.jl")
6466
include("VQE.jl")

0 commit comments

Comments
 (0)