Skip to content

Commit

Permalink
bqm feature implementation and testing
Browse files Browse the repository at this point in the history
  • Loading branch information
dakk committed Feb 12, 2024
1 parent 6fd6932 commit b62d27f
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 33 deletions.
3 changes: 3 additions & 0 deletions docs/source/bqm.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@
"source": [
"from qlasskit import qlassf, Qint4\n",
"\n",
"\n",
"@qlassf\n",
"def test_qubo(a: Qint4):\n",
" return Qint4(12) - a\n",
"\n",
"\n",
"bqm = test_qubo.to_bqm()"
]
},
Expand All @@ -50,6 +52,7 @@
"outputs": [],
"source": [
"import neal\n",
"\n",
"sa = neal.SimulatedAnnealingSampler()\n",
"sampleset = sa.sample(bqm, num_reads=10)\n",
"decoded_samples = model.decode_sampleset(sampleset)\n",
Expand Down
4 changes: 2 additions & 2 deletions docs/source/exporter.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,8 @@
],
"source": [
"# Disabled on docs for a depencency problem\n",
"#qc = hello_world.export(\"qutip\")\n",
"#qc.gates"
"# qc = hello_world.export(\"qutip\")\n",
"# qc.gates"
]
}
],
Expand Down
10 changes: 7 additions & 3 deletions docs/source/parameters.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,14 @@
}
],
"source": [
"from qlasskit import qlassf, Parameter \n",
"from qlasskit import qlassf, Parameter\n",
"\n",
"\n",
"@qlassf\n",
"def test(a: Parameter[bool], b: bool) -> bool:\n",
" return a and b\n",
"\n",
"\n",
"qf = test.bind(a=True)\n",
"qf.expressions"
]
Expand Down Expand Up @@ -81,14 +83,16 @@
"source": [
"from qlasskit import Qlist, Qint2, Qint4\n",
"\n",
"\n",
"@qlassf\n",
"def test(a: Parameter[Qlist[Qint2, 4]], b: Qint4) -> Qint4:\n",
" s = Qint4(0)\n",
" for n in a:\n",
" s += n\n",
" return s + b\n",
"\n",
"qf = test.bind(a=[2,1,0,3])\n",
"\n",
"qf = test.bind(a=[2, 1, 0, 3])\n",
"qf.expressions"
]
},
Expand All @@ -110,7 +114,7 @@
}
],
"source": [
"qf.circuit().export().draw('mpl')"
"qf.circuit().export().draw(\"mpl\")"
]
}
],
Expand Down
86 changes: 72 additions & 14 deletions qlasskit/bqm.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,91 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from sympy import Symbol
from sympy.logic import Not, And, Xor, Or
from typing import Literal, get_args

BQMFormat = Literal["bqm", "ising", "qubo", "pq_model"]
BQMFormats = list(get_args(BQMFormat))

def to_bqm(
args,
returns,
exprs,
fmt: BQMFormat
):
class SympyToBQM:
def __init__(self, a_vars):
self.a_vars = a_vars

def visit(self, e):
import pyqubo

if isinstance(e, Symbol):
return self.a_vars[e.name]
elif isinstance(e, Not):
args = [ self.visit(a) for a in e.args ]
return pyqubo.Not(*args)
elif isinstance(e, And):
args = [ self.visit(a) for a in e.args ]
return pyqubo.And(*args)
elif isinstance(e, Xor):
args = [ self.visit(a) for a in e.args ]
return pyqubo.Xor(*args)
elif isinstance(e, Or):
args = [ self.visit(a) for a in e.args ]
return pyqubo.Or(*args)
else:
raise Exception(f'{e}: not handled')


def to_bqm(args, returns, exprs, fmt: BQMFormat):
try:
import pyqubo
except:
raise Exception("Library pyqubo not found: run `pip install pyqubo`")

# vars = ['a.0', 'a.1', 'a.2', 'a.3']
from pyqubo import Binary #, NotConst, AndConst, XorConst,

a_vars = {}
for arg in args:
for b in arg.bitvec:
a_vars[b] = Binary(b)

e = None
for (sym, exp) in exprs:
# TODO: capire se questo e' realmente necessario o se serve solo per
# condizioni
# stbqm = SympyToBQM(a_vars)
# if isinstance(exp, Symbol):
# arg = stbqm.visit(exp)
# elif isinstance(exp, Not):
# args = [ stbqm.visit(a) for a in e.args ]
# elif isinstance(exp, Or):
# args = [ stbqm.visit(a) for a in e.args ]
# elif isinstance(exp, Xor):
# args = [ stbqm.visit(a) for a in e.args ]
# elif isinstance(exp, And):
# args = [ stbqm.visit(a) for a in e.args ]

new_e = SympyToBQM(a_vars).visit(exp)
if e is None:
e = new_e
else:
e += new_e

# args = ['a.0', 'a.1', 'a.2', 'a.3']
# al = [ Binary(s) for s in vars ]
# e = AndConst(al[0], And(Not(al[1]), And(al[2], Not(al[3]))), Binary("_ret"), '_ret')
# model = e.compile()

if fmt == 'bqm':
model = e.compile()
print(e)

if fmt == "bqm":
return model.to_bqm()
elif fmt == 'ising':
elif fmt == "ising":
return model.to_ising()
elif fmt == 'qubo':
elif fmt == "qubo":
return model.to_qubo()
elif fmt == 'pq_model':
elif fmt == "pq_model":
return model
else:
raise Exception(f"Unknown format `{fmt}")

raise Exception(f"Unknown format `{fmt}")


# TODO: utility for samples decoding
# def decode_samples():
19 changes: 10 additions & 9 deletions qlasskit/qlassfun.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,20 @@
from .boolopt import BoolOptimizerProfile, defaultOptimizer
from .boolopt.bool_optimizer import merge_expressions
from .boolquant import Q # noqa: F403, F401
from .bqm import BQMFormat, to_bqm
from .compiler import SupportedCompiler, to_quantum
from .qcircuit import QCircuitWrapper
from .types import * # noqa: F403, F401
from .types import Qtype, format_outcome, interpret_as_qtype, type_repr
from .bqm import BQMFormat, to_bqm

MAX_TRUTH_TABLE_SIZE = 20


def in_ipynb():
import sys
return 'ipykernel' in sys.modules

return "ipykernel" in sys.modules


class UnboundQlassf:
"""Class representing a qlassf function with unbound parameters"""
Expand Down Expand Up @@ -94,7 +97,7 @@ def to_val(w):

original_f = eval(fun_ast.body[0].name)
else:
print('Warning, I cannot create original_f in python notebooks!')
print("Warning, I cannot create original_f in python notebooks!")
original_f = None

return self._do_translate(fun_ast, original_f)
Expand Down Expand Up @@ -244,13 +247,11 @@ def f(self) -> Callable:

def to_logicfun(self) -> LogicFun:
return copy.deepcopy((self.name, self.args, self.returns, self.expressions))
def to_bqm(self, fmt: BQMFormat = 'bqm'):

def to_bqm(self, fmt: BQMFormat = "bqm"):
return to_bqm(
args=self.args,
returns=self.returns,
exprs=self.expressions,
format=fmt)
args=self.args, returns=self.returns, exprs=self.expressions, fmt=fmt
)

@staticmethod
def from_function(
Expand Down
6 changes: 4 additions & 2 deletions test/qlassf/test_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ def test_bind_qint2(self):

def test_bind_multiple_qint2(self):
uqf = qlassf(
("def test(c: Parameter[Qint2], d: Parameter[Qint2], a: bool) -> Qint2: "
"return c+d if a else c+1"),
(
"def test(c: Parameter[Qint2], d: Parameter[Qint2], a: bool) -> Qint2: "
"return c+d if a else c+1"
),
to_compile=COMPILATION_ENABLED,
)
qf = uqf.bind(c=1, d=2)
Expand Down
23 changes: 20 additions & 3 deletions test/test_qlassf_to_bqm.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,28 @@

import unittest


from qlasskit import Qint2, Qint4, QlassF, exceptions, qlassf



class TestQlassfToBQM(unittest.TestCase):
def test_encode_decode_bool(self):
def test_to_bqm_1(self):
f = "def test(a: bool) -> bool:\n\treturn not a"
qf = qlassf(f, to_compile=False)
qf = qlassf(f, to_compile=False)
bqm = qf.to_bqm()

def test_to_bqm_2(self):
f = "def test(a: Qint2) -> bool:\n\treturn a == 2"
qf = qlassf(f, to_compile=False)
bqm = qf.to_bqm()
print(bqm)

def test_to_bqm_3(self):
f = "def test(a: Qint2) -> Qint2:\n\treturn a + 1"
qf = qlassf(f, to_compile=False)
bqm = qf.to_bqm()

def test_to_bqm_4(self):
f = "def test(a: Qint4) -> Qint4:\n\treturn a + 2"
qf = qlassf(f, to_compile=False)
bqm = qf.to_bqm()

0 comments on commit b62d27f

Please sign in to comment.