pyqbpp 2025.5.11__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,18 @@
1
+ LICENSE
2
+
3
+ Copyright (c) 2025 Koji Nakano
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to use,
7
+ copy, and modify the Software for non-commercial and non-industrial purposes only.
8
+
9
+ Commercial use, including but not limited to any business use, service offering,
10
+ or incorporation into a commercial product, is prohibited without a separate
11
+ written agreement with the copyright holder.
12
+
13
+ The Software is provided "as is", without warranty of any kind, express or implied,
14
+ including but not limited to the warranties of merchantability, fitness for a particular
15
+ purpose, and noninfringement. In no event shall the authors or copyright holders
16
+ be liable for any claim, damages, or other liability, whether in an action of contract,
17
+ tort, or otherwise, arising from, out of, or in connection with the Software or the use
18
+ or other dealings in the Software.
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyqbpp
3
+ Version: 2025.5.11
4
+ Summary: Python version of QUBO++
5
+ Author-email: Koji Nakano <nakano@hiroshima-u.ac.jp>
6
+ License-Expression: LicenseRef-PYQBPPLicense
7
+ Requires-Python: >=3.6
8
+ License-File: LICENSE
9
+ Dynamic: license-file
@@ -0,0 +1,18 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pyqbpp"
7
+ version = "2025.05.11"
8
+ description = "Python version of QUBO++"
9
+ authors = [
10
+ { name = "Koji Nakano", email = "nakano@hiroshima-u.ac.jp" }
11
+ ]
12
+ license = "LicenseRef-PYQBPPLicense"
13
+ license-files = ["LICENSE"]
14
+ requires-python = ">=3.6"
15
+ dependencies = []
16
+
17
+ [tool.setuptools]
18
+ py-modules = ["pyqbpp"]
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyqbpp
3
+ Version: 2025.5.11
4
+ Summary: Python version of QUBO++
5
+ Author-email: Koji Nakano <nakano@hiroshima-u.ac.jp>
6
+ License-Expression: LicenseRef-PYQBPPLicense
7
+ Requires-Python: >=3.6
8
+ License-File: LICENSE
9
+ Dynamic: license-file
@@ -0,0 +1,7 @@
1
+ LICENSE
2
+ pyproject.toml
3
+ pyqbpp.py
4
+ pyqbpp.egg-info/PKG-INFO
5
+ pyqbpp.egg-info/SOURCES.txt
6
+ pyqbpp.egg-info/dependency_links.txt
7
+ pyqbpp.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ pyqbpp
@@ -0,0 +1,361 @@
1
+ # @file pyqbpp.py
2
+ # @brief Python version of QUBO++
3
+ # @details This file contains the implementation of a Python version of QUBO++.
4
+ # @copyright Copyright (c) 2025 Koji Nakano
5
+ # @license Following the QUBO++ license
6
+ # @author Koji Nakano
7
+ # @version 2025.05.11
8
+ # @note This program is under development and compatibility with QUBO++ is limited.
9
+ # @date 2025-05-11
10
+
11
+
12
+ class Var:
13
+ num = 0
14
+ unnamed_num = 0
15
+
16
+ def __init__(self, *args):
17
+ if len(args) == 0:
18
+ self.name = f"{{{Var.num}}}"
19
+ self.index = Var.num
20
+ self.unnamed_index = Var.unnamed_num
21
+ Var.num += 1
22
+ Var.unnamed_num += 1
23
+ elif len(args) == 1:
24
+ self.name = args[0]
25
+ self.index = Var.num
26
+ Var.num += 1
27
+ else:
28
+ raise ValueError("Var has at most one argument")
29
+
30
+ def __eq__(self, other):
31
+ return isinstance(other, Var) and self.index == other.index
32
+
33
+ def __hash__(self):
34
+ return hash(self.index)
35
+
36
+ def __str__(self):
37
+ return self.name
38
+
39
+ def __repr__(self):
40
+ return str(self)
41
+
42
+ def __add__(self, other):
43
+ return Expr(self, other)
44
+
45
+ def __radd__(self, other):
46
+ return Expr(other, self)
47
+
48
+ def __sub__(self, other):
49
+ return Expr(self, Term(-1, other))
50
+
51
+ def __rsub__(self, other):
52
+ return Expr(other, Term(-1, self))
53
+
54
+ def __mul__(self, other):
55
+ if isinstance(other, int):
56
+ return Term(other, self)
57
+ elif isinstance(other, Var):
58
+ return Term(self, other)
59
+ elif isinstance(other, Term):
60
+ return Term(*([self] + other.var), other.coeff)
61
+ else:
62
+ return NotImplemented
63
+
64
+ def __rmul__(self, other):
65
+ return self.__mul__(other)
66
+
67
+ def __lt__(self, other):
68
+ return self.index < other.index
69
+
70
+ def __str__(self):
71
+ return self.name
72
+
73
+
74
+ def var(*args):
75
+ if len(args) == 0:
76
+ return Var()
77
+
78
+ if isinstance(args[0], str):
79
+ var_name = args[0]
80
+ dims = args[1:]
81
+ else:
82
+ var_name = None
83
+ dims = args
84
+
85
+ if len(dims) == 0:
86
+ return Var(var_name)
87
+
88
+ return Vector([
89
+ var(
90
+ f"{var_name}[{i}]" if var_name is not None else None,
91
+ *dims[1:]
92
+ )
93
+ for i in range(dims[0])
94
+ ])
95
+
96
+
97
+ class Term:
98
+ def __init__(self, *args):
99
+ self.coeff = 1
100
+ self.vars = []
101
+ for arg in args:
102
+ if isinstance(arg, int):
103
+ self.coeff *= arg
104
+ elif isinstance(arg, Var):
105
+ self.vars.append(arg)
106
+ else:
107
+ raise ValueError("Term can only have int or Var arguments")
108
+
109
+ def __str__(self):
110
+ if not self.vars:
111
+ return str(self.coeff)
112
+ var_str = "*".join(str(v) for v in self.vars)
113
+ if self.coeff == 1:
114
+ return var_str
115
+ elif self.coeff == -1:
116
+ return "-" + var_str
117
+ else:
118
+ return str(self.coeff) + "*" + var_str
119
+
120
+ def __repr__(self):
121
+ return str(self)
122
+
123
+ def __add__(self, other):
124
+ return Expr(self, other)
125
+
126
+ def __radd__(self, other):
127
+ return Expr(other, self)
128
+
129
+ def __sub__(self, other):
130
+ if isinstance(other, Term):
131
+ return Expr(self, Term(-other.coeff, *other.var))
132
+ return Expr(self, Term(-1, other))
133
+
134
+ def __rsub__(self, other):
135
+ return Expr(other, Term(-1, *self.vars)).__add__(Term(-self.coeff + 1))
136
+
137
+ def __mul__(self, scalar):
138
+ if not isinstance(scalar, int):
139
+ return NotImplemented
140
+ return Term(self.coeff * scalar, *self.vars)
141
+
142
+ def __rmul__(self, scalar):
143
+ return self.__mul__(scalar)
144
+
145
+ def __lt__(self, other):
146
+ if len(self.vars) != len(other.vars):
147
+ return len(self.vars) < len(other.vars)
148
+ else:
149
+ return self.vars < other.vars
150
+
151
+ def simplify_as_binary(self):
152
+ if len(self.vars) <= 1:
153
+ return self
154
+ else:
155
+ seen = set()
156
+ unique_vars = []
157
+ for v in sorted(self.vars, key=lambda v: v.index):
158
+ if v.index not in seen:
159
+ seen.add(v.index)
160
+ unique_vars.append(v)
161
+ return Term(self.coeff, *unique_vars)
162
+
163
+
164
+ class Expr:
165
+ def __init__(self, *args):
166
+ self.const = 0
167
+ self.terms = []
168
+ for arg in args:
169
+ if isinstance(arg, int):
170
+ self.const += arg
171
+ elif isinstance(arg, Term):
172
+ self.terms.append(arg)
173
+ elif isinstance(arg, Var):
174
+ self.terms.append(Term(arg))
175
+ elif isinstance(arg, Expr):
176
+ self.const += arg.const
177
+ self.terms += arg.terms
178
+ else:
179
+ raise ValueError(
180
+ "Expr can only have int, Var, Term, or Expr arguments")
181
+
182
+ def __add__(self, other):
183
+ return Expr(self, other)
184
+
185
+ def __radd__(self, other):
186
+ return Expr(other, self)
187
+
188
+ def __str__(self):
189
+ first = True
190
+ string = ""
191
+ if self.const != 0:
192
+ string = str(self.const)
193
+ first = False
194
+ elif len(self.terms) == 0:
195
+ return "0"
196
+ for term in self.terms:
197
+ if first:
198
+ string += str(term)
199
+ first = False
200
+ else:
201
+ if (term.coeff < 0):
202
+ string += " " + str(term)
203
+ else:
204
+ string += " +" + str(term)
205
+ return string
206
+
207
+ def __sub__(self, other):
208
+ if isinstance(other, Expr):
209
+ negated = Expr()
210
+ negated.const = -other.const
211
+ negated.terms = [Term(-t.coeff, *t.vars) for t in other.terms]
212
+ return Expr(self, negated)
213
+ elif isinstance(other, Term):
214
+ return Expr(self, Term(-other.coeff, *other.vars))
215
+ elif isinstance(other, Var):
216
+ return Expr(self, Term(-1, other))
217
+ elif isinstance(other, int):
218
+ return Expr(self, -other)
219
+ else:
220
+ return NotImplemented
221
+
222
+ def __rsub__(self, other):
223
+ return Expr(other).__sub__(self)
224
+
225
+ def __mul__(self, other):
226
+ if isinstance(other, int):
227
+ return Expr(self.const * other,
228
+ *[Term(t.coeff * other, *t.vars) for t in self.terms])
229
+
230
+ elif isinstance(other, Expr):
231
+ result_terms = []
232
+
233
+ const_part = self.const * other.const
234
+
235
+ for t in other.terms:
236
+ result_terms.append(Term(t.coeff * self.const, *t.vars))
237
+ for t in self.terms:
238
+ result_terms.append(Term(t.coeff * other.const, *t.vars))
239
+
240
+ for t1 in self.terms:
241
+ for t2 in other.terms:
242
+ coeff = t1.coeff * t2.coeff
243
+ vars_combined = t1.vars + t2.vars
244
+ result_terms.append(Term(coeff, *vars_combined))
245
+
246
+ return Expr(const_part, *result_terms)
247
+
248
+ elif isinstance(other, Term):
249
+ return self * Expr(other)
250
+
251
+ elif isinstance(other, Var):
252
+ return self * Expr(other)
253
+
254
+ else:
255
+ return NotImplemented
256
+
257
+ def __rmul__(self, scalar):
258
+ return self.__mul__(scalar)
259
+
260
+ def __eq__(self, other):
261
+ return sqr(self - other)
262
+
263
+ def __repr__(self):
264
+ return str(self)
265
+
266
+ def simplify_as_binary(self):
267
+ simplified_terms = sorted(
268
+ [term.simplify_as_binary() for term in self.terms if term.coeff != 0])
269
+ new_terms = []
270
+ for i in range(len(simplified_terms)):
271
+ if len(simplified_terms[i].vars) == 0:
272
+ self.const += simplified_terms[i].coeff
273
+ elif len(new_terms) == 0 or new_terms[-1].vars != simplified_terms[i].vars:
274
+ new_terms.append(simplified_terms[i])
275
+ else:
276
+ new_terms[-1].coeff += simplified_terms[i].coeff
277
+ self.terms = new_terms
278
+ return self
279
+
280
+
281
+ def sqr(arg):
282
+ if isinstance(arg, Vector):
283
+ return Vector([sqr(item) for item in arg])
284
+ else:
285
+ return arg * arg
286
+
287
+
288
+ def is_nested_list(obj):
289
+ return (
290
+ isinstance(obj, Vector) and
291
+ any(isinstance(item, Vector) for item in obj)
292
+ )
293
+
294
+
295
+ def total_sum(arg):
296
+ if not is_nested_list(arg):
297
+ raise ValueError("Argument must be a nested list")
298
+ return total_sum_impl(arg)
299
+
300
+
301
+ def total_sum_impl(arg):
302
+ result = 0
303
+ for item in arg:
304
+ if isinstance(item, (int, Var, Term, Expr)):
305
+ result += item
306
+ else:
307
+ result += total_sum_impl(item)
308
+ return result
309
+
310
+
311
+ def vector_sum(arg):
312
+ if not is_nested_list(arg):
313
+ raise ValueError("Argument must be a nested list")
314
+ return vector_sum_impl(arg)
315
+
316
+
317
+ def vector_sum_impl(arg):
318
+ if isinstance(arg[0], (int, Var, Term, Expr)):
319
+ return sum(arg)
320
+ else:
321
+ return Vector([vector_sum_impl(item) for item in arg])
322
+
323
+
324
+ def transpose(arg):
325
+ if not is_nested_list(arg):
326
+ raise ValueError("Argument must be a nested list")
327
+ return Vector([
328
+ Vector([arg[i][j] for i in range(len(arg))])
329
+ for j in range(len(arg[0]))
330
+ ])
331
+
332
+
333
+ class Vector:
334
+ def __init__(self, data):
335
+ self.data = [
336
+ Vector(x) if isinstance(
337
+ x, list) and not isinstance(x, Vector) else x
338
+ for x in data
339
+ ] if isinstance(data, list) else data
340
+
341
+ def __getitem__(self, idx):
342
+ return self.data[idx]
343
+
344
+ def __len__(self):
345
+ return len(self.data) if isinstance(self.data, list) else 1
346
+
347
+ def __iter__(self):
348
+ if isinstance(self.data, list):
349
+ return iter(self.data)
350
+ else:
351
+ return iter([self.data])
352
+
353
+ def __eq__(self, other):
354
+
355
+ if isinstance(other, int):
356
+ return [x == other for x in self.data]
357
+ else:
358
+ raise ValueError("Comparison with non-integer is not supported")
359
+
360
+ def __repr__(self):
361
+ return f"Vector({self.data})"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+