Skip to content

Commit aafc0df

Browse files
Release/1.3 (#189)
- optimize polynomial data structure in the compilation - optimize the construction of sum expressions - optimize the to_qubo method to run faster - fix hash collision to avoid a crash in robin_hood
1 parent 4e5c543 commit aafc0df

29 files changed

+1389
-1357
lines changed

.circleci/config.yml

-649
This file was deleted.

.github/workflows/build_and_upolad.yaml

+1-2
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,6 @@ jobs:
172172
path: dist
173173
- uses: pypa/gh-action-pypi-publish@release/v1
174174
with:
175-
user: __token__
176175
password: ${{ secrets.PYPI_PASSWORD }}
177176

178177
upload_test_pypi:
@@ -185,7 +184,7 @@ jobs:
185184
path: dist
186185
- uses: pypa/gh-action-pypi-publish@release/v1
187186
with:
188-
user: __token__
189187
password: ${{ secrets.PYPI_TEST_PASSWORD }}
190188
repository_url: https://test.pypi.org/legacy/
191189
skip_existing: true
190+
verbose: true

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ __pycache__/
4141
# Distribution / packaging
4242
.Python
4343
build/
44+
build_test/
4445
develop-eggs/
4546
dist/
4647
downloads/

CMakeLists.txt

+5-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ include(external/eigen.cmake)
1818
include(external/pybind11.cmake)
1919
include(external/robin_hood.cmake)
2020

21-
pybind11_add_module(cpp_pyqubo src/main.cpp)
21+
if(DEFINED ENV{build_test})
22+
add_executable(cpp_pyqubo src/test.cpp)
23+
else()
24+
pybind11_add_module(cpp_pyqubo src/main.cpp)
25+
endif()
2226

2327
target_compile_definitions(cpp_pyqubo PRIVATE VERSION_INFO=${PYQUBO_VERSION_INFO})
2428
target_compile_features(cpp_pyqubo PRIVATE cxx_std_17)

README.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ By calling ``model.to_qubo()``, we get the resulting QUBO.
4545
>>> H = (4*s1 + 2*s2 + 7*s3 + s4)**2
4646
>>> model = H.compile()
4747
>>> qubo, offset = model.to_qubo()
48-
>>> pprint(qubo)
48+
>>> pprint(qubo) # doctest: +SKIP
4949
{('s1', 's1'): -160.0,
5050
('s1', 's2'): 64.0,
5151
('s1', 's3'): 224.0,

benchmark/benchmark.py

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
from pyqubo import Array, Constraint, Placeholder
2+
import logging
3+
import time
4+
import argparse
5+
import numpy as np
6+
from timeout_decorator import timeout, TimeoutError
7+
from memory_profiler import memory_usage
8+
9+
10+
parser = argparse.ArgumentParser()
11+
12+
logging.basicConfig(level=logging.INFO)
13+
logger = logging.getLogger("benchmark_tsp")
14+
15+
16+
def number_partition_with_timeout(n, timeout_sec):
17+
18+
@timeout(timeout_sec)
19+
def number_partition(n):
20+
t0 = time.time()
21+
s = Array.create('s', n, 'SPIN')
22+
numbers = np.random.randint(0, 10, n)
23+
H = sum([si * ni for si, ni in zip(s, numbers)])**2
24+
25+
# Compile model
26+
t1 = time.time()
27+
model = H.compile()
28+
t2 = time.time()
29+
qubo, offset = model.to_qubo(index_label=False)
30+
t3 = time.time()
31+
print("len(qubo)", len(qubo))
32+
33+
return t1-t0, t2-t1, t3-t2
34+
35+
return number_partition(n)
36+
37+
def tsp_with_timeout(n_city, timeout_sec):
38+
39+
@timeout(timeout_sec)
40+
def tsp(n_city):
41+
t0 = time.time()
42+
x = Array.create('c', (n_city, n_city), 'BINARY')
43+
use_for_loop=False
44+
45+
# Constraint not to visit more than two cities at the same time.
46+
time_const = 0.0
47+
for i in range(n_city):
48+
# If you wrap the hamiltonian by Const(...), this part is recognized as constraint
49+
time_const += Constraint((sum(x[i, j] for j in range(n_city)) - 1)**2, label="time{}".format(i))
50+
51+
# Constraint not to visit the same city more than twice.
52+
city_const = 0.0
53+
for j in range(n_city):
54+
city_const += Constraint((sum(x[i, j] for i in range(n_city)) - 1)**2, label="city{}".format(j))
55+
56+
# distance of route
57+
feed_dict = {}
58+
59+
if use_for_loop:
60+
distance = 0.0
61+
for i in range(n_city):
62+
for j in range(n_city):
63+
for k in range(n_city):
64+
# we set the constant distance
65+
d_ij = 10
66+
distance += d_ij * x[k, i] * x[(k + 1) % n_city, j]
67+
68+
else:
69+
distance = []
70+
for i in range(n_city):
71+
for j in range(n_city):
72+
for k in range(n_city):
73+
# we set the constant distance
74+
d_ij = 10
75+
distance.append(d_ij * x[k, i] * x[(k + 1) % n_city, j])
76+
distance = sum(distance)
77+
78+
print("express done")
79+
80+
# Construct hamiltonian
81+
A = Placeholder("A")
82+
H = distance
83+
84+
feed_dict["A"] = 1.0
85+
86+
# Compile model
87+
t1 = time.time()
88+
model = H.compile()
89+
t2 = time.time()
90+
qubo, offset = model.to_qubo(index_label=False, feed_dict=feed_dict)
91+
t3 = time.time()
92+
93+
print("len(qubo)", len(qubo))
94+
95+
return t1-t0, t2-t1, t3-t2
96+
97+
return tsp(n_city)
98+
99+
100+
101+
def measure(problem, step, init_size, max_size, timeout):
102+
if problem == "tsp":
103+
f = tsp_with_timeout
104+
elif problem == "number_partition":
105+
f = number_partition_with_timeout
106+
107+
for n in range(init_size, max_size+step, step):
108+
try:
109+
max_memory, (express_time, compile_time, to_qubo_time) = memory_usage((f, (n, timeout)), max_usage=True, retval=True)
110+
logger.info("Memory usage is {} MB for n={}".format(max_memory, n))
111+
logger.info("Elapsed time is {} sec (expression: {} sec, compile: {} sec, to_qubo {} sec), for n={}".format(
112+
express_time+compile_time+to_qubo_time, express_time, compile_time, to_qubo_time, n))
113+
114+
except TimeoutError as e:
115+
logger.error("TimeoutError: Elapsed time exceeded {} sec for n_city={}".format(timeout, n))
116+
break
117+
118+
119+
if __name__=="__main__":
120+
parser.add_argument('-p', '--problem', type=str)
121+
parser.add_argument('-m', '--max_size', type=int)
122+
parser.add_argument('-i', '--init_size', type=int)
123+
parser.add_argument('-s', '--step', type=int)
124+
parser.add_argument('-t', '--timeout', type=int)
125+
args = parser.parse_args()
126+
#number_partition_with_timeout(2000, timeout_sec=10)
127+
measure(args.problem, args.step, args.init_size, args.max_size, args.timeout)

benchmark/benchmark_tsp.py

-75
This file was deleted.

docs/reference/model.rst

+3-3
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ Model
3838
>>> model.to_qubo(index_label=True) # doctest: +SKIP
3939
({(2, 2): 3.0, (1, 1): 2.0, (0, 0): 1.0}, 0.0)
4040
>>> model.variables
41-
['a', 'b', 'c']
41+
['c', 'a', 'b']
4242

4343
This indicaretes the mapping of indices and labels as 'c'->0, 'a'->1, 'b'->2
4444

@@ -96,7 +96,7 @@ Model
9696
>>> pprint(model.to_qubo(index_label=True)) # doctest: +SKIP
9797
({(0, 0): 3.0, (0, 2): 1.0, (1, 1): 0.0, (1, 2): 1.0, (2, 2): 0.0}, 0.0)
9898
>>> model.variables
99-
['x', 'y', 'z']
99+
['z', 'x', 'y']
100100

101101

102102
.. py:method:: to_ising(index_label=False, feed_dict=None)
@@ -129,7 +129,7 @@ Model
129129
>>> pprint(model.to_ising(index_label=True)) # doctest: +SKIP
130130
({0: 1.75, 1: 0.25, 2: 0.5}, {(0, 2): 0.25, (1, 2): 0.25}, 2.0)
131131
>>> model.variables
132-
['x', 'y', 'z']
132+
['z', 'x', 'y']
133133

134134

135135
.. py:method:: to_bqm(index_label=False, feed_dict=None)

external/robin_hood.cmake

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ include(FetchContent)
33
FetchContent_Declare(
44
robin_hood
55
GIT_REPOSITORY https://github.com/martinus/robin-hood-hashing
6-
GIT_TAG 3.11.2
6+
GIT_TAG 3.11.3
77
)
88

99
FetchContent_GetProperties(robin_hood)

pyqubo/array.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class Array:
6464
6565
>>> array[:, 1] # = array[(slice(None), 1)]
6666
Array([Binary('x1'), Binary('x3')])
67-
>>> sum(array[:, 1])
67+
>>> sum(array[:, 1]) # doctest: +SKIP
6868
(Binary('x1') + Binary('x3'))
6969
7070
Use list or tuple to select a subset of the array.

pyqubo/integer/one_hot_enc_integer.py

+2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ def __init__(self, label, value_range, strength):
6565
express = SubH(lower + sum(i*x for i, x in enumerate(self.array)), label=label)
6666
penalty = self.constraint * strength
6767

68+
self._express = express
69+
6870
super().__init__(
6971
label=label,
7072
value_range=value_range,

pyqubo/package_info.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# (major, minor, patch, prerelease)
22

3-
VERSION = (1, 2, 0, "")
3+
VERSION = (1, 3, 0, "")
44
__shortversion__ = '.'.join(map(str, VERSION[:3]))
55
__version__ = '.'.join(map(str, VERSION[:3])) + "".join(VERSION[3:])
66

77
__package_name__ = 'pyqubo'
8-
__contact_names__ = 'Recruit Communications Co., Ltd.'
8+
__contact_names__ = 'Recruit Co., Ltd.'
99
__contact_emails__ = 'rco_pyqubo@ml.cocorou.jp'
1010
__homepage__ = 'https://pyqubo.readthedocs.io/en/latest/'
1111
__repository_url__ = 'https://github.com/recruit-communications/pyqubo'

0 commit comments

Comments
 (0)