QuLab 2.10.10__cp313-cp313-win_amd64.whl

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.
Files changed (107) hide show
  1. qulab/__init__.py +33 -0
  2. qulab/__main__.py +4 -0
  3. qulab/cli/__init__.py +0 -0
  4. qulab/cli/commands.py +30 -0
  5. qulab/cli/config.py +170 -0
  6. qulab/cli/decorators.py +28 -0
  7. qulab/dicttree.py +523 -0
  8. qulab/executor/__init__.py +5 -0
  9. qulab/executor/analyze.py +188 -0
  10. qulab/executor/cli.py +434 -0
  11. qulab/executor/load.py +563 -0
  12. qulab/executor/registry.py +185 -0
  13. qulab/executor/schedule.py +543 -0
  14. qulab/executor/storage.py +615 -0
  15. qulab/executor/template.py +259 -0
  16. qulab/executor/utils.py +194 -0
  17. qulab/expression.py +827 -0
  18. qulab/fun.cp313-win_amd64.pyd +0 -0
  19. qulab/monitor/__init__.py +1 -0
  20. qulab/monitor/__main__.py +8 -0
  21. qulab/monitor/config.py +41 -0
  22. qulab/monitor/dataset.py +77 -0
  23. qulab/monitor/event_queue.py +54 -0
  24. qulab/monitor/mainwindow.py +234 -0
  25. qulab/monitor/monitor.py +115 -0
  26. qulab/monitor/ploter.py +123 -0
  27. qulab/monitor/qt_compat.py +16 -0
  28. qulab/monitor/toolbar.py +265 -0
  29. qulab/scan/__init__.py +2 -0
  30. qulab/scan/curd.py +221 -0
  31. qulab/scan/models.py +554 -0
  32. qulab/scan/optimize.py +76 -0
  33. qulab/scan/query.py +387 -0
  34. qulab/scan/record.py +603 -0
  35. qulab/scan/scan.py +1166 -0
  36. qulab/scan/server.py +450 -0
  37. qulab/scan/space.py +213 -0
  38. qulab/scan/utils.py +234 -0
  39. qulab/storage/__init__.py +0 -0
  40. qulab/storage/__main__.py +51 -0
  41. qulab/storage/backend/__init__.py +0 -0
  42. qulab/storage/backend/redis.py +204 -0
  43. qulab/storage/base_dataset.py +352 -0
  44. qulab/storage/chunk.py +60 -0
  45. qulab/storage/dataset.py +127 -0
  46. qulab/storage/file.py +273 -0
  47. qulab/storage/models/__init__.py +22 -0
  48. qulab/storage/models/base.py +4 -0
  49. qulab/storage/models/config.py +28 -0
  50. qulab/storage/models/file.py +89 -0
  51. qulab/storage/models/ipy.py +58 -0
  52. qulab/storage/models/models.py +88 -0
  53. qulab/storage/models/record.py +161 -0
  54. qulab/storage/models/report.py +22 -0
  55. qulab/storage/models/tag.py +93 -0
  56. qulab/storage/storage.py +95 -0
  57. qulab/sys/__init__.py +2 -0
  58. qulab/sys/chat.py +688 -0
  59. qulab/sys/device/__init__.py +3 -0
  60. qulab/sys/device/basedevice.py +255 -0
  61. qulab/sys/device/loader.py +86 -0
  62. qulab/sys/device/utils.py +79 -0
  63. qulab/sys/drivers/FakeInstrument.py +68 -0
  64. qulab/sys/drivers/__init__.py +0 -0
  65. qulab/sys/ipy_events.py +125 -0
  66. qulab/sys/net/__init__.py +0 -0
  67. qulab/sys/net/bencoder.py +205 -0
  68. qulab/sys/net/cli.py +169 -0
  69. qulab/sys/net/dhcp.py +543 -0
  70. qulab/sys/net/dhcpd.py +176 -0
  71. qulab/sys/net/kad.py +1142 -0
  72. qulab/sys/net/kcp.py +192 -0
  73. qulab/sys/net/nginx.py +194 -0
  74. qulab/sys/progress.py +190 -0
  75. qulab/sys/rpc/__init__.py +0 -0
  76. qulab/sys/rpc/client.py +0 -0
  77. qulab/sys/rpc/exceptions.py +96 -0
  78. qulab/sys/rpc/msgpack.py +1052 -0
  79. qulab/sys/rpc/msgpack.pyi +41 -0
  80. qulab/sys/rpc/router.py +35 -0
  81. qulab/sys/rpc/rpc.py +412 -0
  82. qulab/sys/rpc/serialize.py +139 -0
  83. qulab/sys/rpc/server.py +29 -0
  84. qulab/sys/rpc/socket.py +29 -0
  85. qulab/sys/rpc/utils.py +25 -0
  86. qulab/sys/rpc/worker.py +0 -0
  87. qulab/sys/rpc/zmq_socket.py +227 -0
  88. qulab/tools/__init__.py +0 -0
  89. qulab/tools/connection_helper.py +39 -0
  90. qulab/typing.py +2 -0
  91. qulab/utils.py +95 -0
  92. qulab/version.py +1 -0
  93. qulab/visualization/__init__.py +188 -0
  94. qulab/visualization/__main__.py +71 -0
  95. qulab/visualization/_autoplot.py +464 -0
  96. qulab/visualization/plot_circ.py +319 -0
  97. qulab/visualization/plot_layout.py +408 -0
  98. qulab/visualization/plot_seq.py +242 -0
  99. qulab/visualization/qdat.py +152 -0
  100. qulab/visualization/rot3d.py +23 -0
  101. qulab/visualization/widgets.py +86 -0
  102. qulab-2.10.10.dist-info/METADATA +110 -0
  103. qulab-2.10.10.dist-info/RECORD +107 -0
  104. qulab-2.10.10.dist-info/WHEEL +5 -0
  105. qulab-2.10.10.dist-info/entry_points.txt +2 -0
  106. qulab-2.10.10.dist-info/licenses/LICENSE +21 -0
  107. qulab-2.10.10.dist-info/top_level.txt +1 -0
qulab/expression.py ADDED
@@ -0,0 +1,827 @@
1
+ from __future__ import annotations
2
+
3
+ import operator
4
+
5
+ import numpy as np
6
+ from pyparsing import (CaselessKeyword, Combine, Forward, Group, Keyword,
7
+ Literal, MatchFirst, Optional, ParserElement,
8
+ ParseResults, Regex, Suppress, Word, ZeroOrMore,
9
+ alphanums, alphas, delimitedList, infixNotation, nums,
10
+ oneOf, opAssoc, pyparsing_common, restOfLine, srange,
11
+ stringEnd, stringStart)
12
+ from scipy import special
13
+
14
+ # 启用 Packrat 优化以提高解析效率
15
+ ParserElement.enablePackrat()
16
+
17
+ # 定义符号(括号、方括号、花括号、点号等)的 Suppress 版
18
+ LPAREN, RPAREN = map(Suppress, "()")
19
+ LBRACK, RBRACK = map(Suppress, "[]")
20
+ LBRACE, RBRACE = map(Suppress, "{}")
21
+ DOT = Suppress(".")
22
+ COMMA = Suppress(",")
23
+
24
+ # 数字定义:整数(十进制、八进制、十六进制)和浮点数
25
+ INT = (Combine(Word("123456789", nums))
26
+ | Literal("0")).setParseAction(lambda t: int(t[0]))
27
+ OCT = Combine("0" + Word("01234567")).setParseAction(lambda t: int(t[0], 8))
28
+ HEX = Combine("0x" + Word("0123456789abcdefABCDEF")).setParseAction(
29
+ lambda t: int(t[0], 16))
30
+ FLOAT = Combine(
31
+ Word(nums) + '.' + Word(nums) | '.' + Word(nums) | Word(nums) + '.'
32
+ | Word(nums) + (Literal('e') | Literal('E')) +
33
+ Word("+-" + nums)).setParseAction(lambda t: float(t[0]))
34
+
35
+
36
+ # 定义标识符,转换为 Symbol 对象(在 Expression 类中已定义)
37
+ def symbol_parse_action(t):
38
+ return Symbol(t[0])
39
+
40
+
41
+ SYMBOL = Word(alphas + "_",
42
+ alphanums + "_").setParseAction(symbol_parse_action)
43
+
44
+
45
+ # 定义查询语法:$a.b.c 或 $a.b 或 $a
46
+ def query_parse_action(t):
47
+ return Query(t[0])
48
+
49
+
50
+ attr_chain = ZeroOrMore(Combine(Literal('.') + SYMBOL))
51
+ dollar_named_chain = Combine(Literal('$') + SYMBOL + attr_chain)
52
+ dollar_dotN_chain = Combine(
53
+ Literal('$') + Regex(r'\.{1,}') + SYMBOL + attr_chain)
54
+ dollar_simple = Combine(Literal('$') + SYMBOL)
55
+
56
+ QUERY = MatchFirst([dollar_dotN_chain, dollar_named_chain,
57
+ dollar_simple]).setParseAction(lambda s, l, t: Query(t[0]))
58
+
59
+ #------------------------------------------------------------------------------
60
+ # 定义运算表达式的解析动作转换函数
61
+
62
+ # 一元运算符映射(注意:此处 ! 使用逻辑非 operator.not_)
63
+ unary_ops = {
64
+ '+': operator.pos,
65
+ '-': operator.neg,
66
+ '~': operator.invert,
67
+ '!': operator.not_
68
+ }
69
+
70
+
71
+ def unary_parse_action(tokens: ParseResults) -> Expression:
72
+ # tokens 形如:[['-', operand]]
73
+ op, operand = tokens[0]
74
+ # operand 已经是 Expression 对象(或常量),构造 UnaryExpression
75
+ return UnaryExpression(operand, unary_ops[op])
76
+
77
+
78
+ # 二元运算符映射
79
+ binary_ops = {
80
+ '+': operator.add,
81
+ '-': operator.sub,
82
+ '*': operator.mul,
83
+ '/': operator.truediv,
84
+ '//': operator.floordiv,
85
+ '%': operator.mod,
86
+ '**': operator.pow,
87
+ '@': operator.matmul,
88
+ '<<': operator.lshift,
89
+ '>>': operator.rshift,
90
+ '&': operator.and_,
91
+ '^': operator.xor,
92
+ '|': operator.or_,
93
+ '<': operator.lt,
94
+ '<=': operator.le,
95
+ '>': operator.gt,
96
+ '>=': operator.ge,
97
+ '==': operator.eq,
98
+ '!=': operator.ne
99
+ }
100
+
101
+
102
+ def binary_parse_action(tokens: ParseResults) -> Expression:
103
+ # tokens[0] 为形如:[operand1, op, operand2, op, operand3, ...]
104
+ t = tokens[0]
105
+ expr = t[0]
106
+ for i in range(1, len(t), 2):
107
+ op = t[i]
108
+ right = t[i + 1]
109
+ expr = BinaryExpression(expr, right, binary_ops[op])
110
+ return expr
111
+
112
+
113
+ expr = Forward()
114
+ #------------------------------------------------------------------------------
115
+ # 构造基元表达式:包括数值、标识符、括号内表达式
116
+ atom = (
117
+ FLOAT | INT | OCT | HEX | SYMBOL | QUERY |
118
+ (LPAREN + expr + RPAREN) # 注意:后面我们将使用递归定义 expr
119
+ )
120
+
121
+
122
+ # 为支持函数调用和属性访问,构造后缀表达式:
123
+ # 例如: func(x,y).attr
124
+ def parse_function_call(expr_obj):
125
+ # 参数列表可能为空,或者用逗号分隔的表达式列表
126
+ arg_list = Optional(delimitedList(expr), default=[])
127
+ return LPAREN + Group(arg_list) + RPAREN
128
+
129
+
130
+ postfix = Forward()
131
+ # 后缀可以是函数调用,也可以是属性访问
132
+ postfix_operation = ((parse_function_call(atom)
133
+ ).setParseAction(lambda t: ('CALL', t[0].asList())) |
134
+ (DOT + SYMBOL).setParseAction(lambda t: ('ATTR', t[0])))
135
+
136
+
137
+ # 定义 factor:先解析 atom,然后再依次处理后缀操作
138
+ def attach_postfix(tokens: ParseResults) -> Expression:
139
+ # tokens[0] 为初始的 atom 对象
140
+ expr_obj = tokens[0]
141
+ # 遍历后缀操作序列
142
+ for op, arg in tokens[1:]:
143
+ if op == 'CALL':
144
+ # 对于函数调用,arg 是参数列表,调用 __call__ 运算符(由 Expression.__call__ 实现)
145
+ expr_obj = expr_obj(*arg)
146
+ elif op == 'ATTR':
147
+ # 对于属性访问,用 ObjectMethod 构造
148
+ expr_obj = ObjectMethod(expr_obj, '__getattr__', arg)
149
+ return expr_obj
150
+
151
+
152
+ # 将 atom 与后缀操作连接
153
+ postfix << (atom + Optional(
154
+ (postfix_operation[...]))).setParseAction(attach_postfix)
155
+
156
+ #------------------------------------------------------------------------------
157
+ # 现在构造整个表达式解析器,利用 infixNotation 建立运算符优先级
158
+ expr <<= infixNotation(
159
+ postfix,
160
+ [
161
+ (oneOf('! ~ + -'), 1, opAssoc.RIGHT, unary_parse_action),
162
+ # 指数运算,右结合
163
+ (Literal("**"), 2, opAssoc.RIGHT, binary_parse_action),
164
+ (oneOf('* / // % @'), 2, opAssoc.LEFT, binary_parse_action),
165
+ (oneOf('+ -'), 2, opAssoc.LEFT, binary_parse_action),
166
+ (oneOf('<< >>'), 2, opAssoc.LEFT, binary_parse_action),
167
+ (oneOf('&'), 2, opAssoc.LEFT, binary_parse_action),
168
+ (oneOf('^'), 2, opAssoc.LEFT, binary_parse_action),
169
+ (oneOf('|'), 2, opAssoc.LEFT, binary_parse_action),
170
+ (oneOf('< <= > >= == !='), 2, opAssoc.LEFT, binary_parse_action),
171
+ ])
172
+
173
+ ConstType = (int, float, complex)
174
+ _empty = object()
175
+
176
+
177
+ class Ref():
178
+ __slots__ = ['name']
179
+
180
+ def __init__(self, name):
181
+ self.name = name
182
+
183
+ def __repr__(self) -> str:
184
+ return f"Ref({self.name!r})"
185
+
186
+
187
+ class Env():
188
+
189
+ def __init__(self):
190
+ self.consts = {}
191
+ self.variables = {}
192
+ self.refs = {}
193
+ self.nested = {}
194
+ self.functions = {
195
+ 'sin': np.sin,
196
+ 'cos': np.cos,
197
+ 'tan': np.tan,
198
+ 'pi': np.pi,
199
+ 'e': np.e,
200
+ 'log': np.log,
201
+ 'log2': np.log2,
202
+ 'log10': np.log10,
203
+ 'exp': np.exp,
204
+ 'sqrt': np.sqrt,
205
+ 'abs': np.abs,
206
+ 'sinh': np.sinh,
207
+ 'cosh': np.cosh,
208
+ 'tanh': np.tanh,
209
+ 'arcsin': np.arcsin,
210
+ 'arccos': np.arccos,
211
+ 'arctan': np.arctan,
212
+ 'arctan2': np.arctan2,
213
+ 'arcsinh': np.arcsinh,
214
+ 'arccosh': np.arccosh,
215
+ 'arctanh': np.arctanh,
216
+ 'sinc': np.sinc,
217
+ 'sign': np.sign,
218
+ 'heaviside': np.heaviside,
219
+ 'erf': special.erf,
220
+ 'erfc': special.erfc,
221
+ }
222
+
223
+ def __contains__(self, key):
224
+ return (key in self.consts or key in self.variables
225
+ or key in self.functions or key in self.refs)
226
+
227
+ def __getitem__(self, key):
228
+ if key in self.consts:
229
+ return self.consts[key]
230
+ if key in self.variables:
231
+ return self.variables[key]
232
+ if key in self.functions:
233
+ return self.functions[key]
234
+ if key in self.refs:
235
+ return self[self.refs[key]]
236
+ raise KeyError(f"Key {key} not found")
237
+
238
+ def __setitem__(self, key, value):
239
+ if key in self.consts:
240
+ raise KeyError(f"Key {key:r} is const")
241
+ if key in self.functions:
242
+ raise KeyError(f"Key {key:r} is function")
243
+ elif isinstance(value, Ref):
244
+ self.create_ref(key, value.name)
245
+ elif key in self.refs:
246
+ self[self.refs[key]] = value
247
+ else:
248
+ self.variables[key] = value
249
+
250
+ def __delitem__(self, key):
251
+ if key in self.consts:
252
+ raise KeyError(f"Key {key:r} is const")
253
+ if key in self.functions:
254
+ raise KeyError(f"Key {key:r} is function")
255
+ elif key in self.refs:
256
+ del self[self.refs[key]]
257
+ else:
258
+ del self.variables[key]
259
+
260
+ def ref(self, key):
261
+ if key in self:
262
+ return Ref(key)
263
+ else:
264
+ raise KeyError(f"Key {key!r} not found")
265
+
266
+ def create_ref(self, key, name):
267
+ if name in self.refs:
268
+ if key in self.refs[name]:
269
+ raise ValueError(f"Key {key!r} already exists in ref {name!r}")
270
+ else:
271
+ self.refs[key] = [name, *self.refs[name]]
272
+ else:
273
+ self.refs[key] = [name]
274
+
275
+ def is_const(self, key):
276
+ return key in self.consts
277
+
278
+
279
+ _default_env = Env()
280
+
281
+
282
+ class Expression():
283
+
284
+ def __init__(self):
285
+ self.cache = _empty
286
+
287
+ def d(self, x: str | Symbol):
288
+ if isinstance(x, Symbol):
289
+ x = x.name
290
+ if x in self.symbols():
291
+ return self.derivative(x)
292
+ else:
293
+ return 0
294
+
295
+ def derivative(self, x):
296
+ raise NotImplementedError
297
+
298
+ def __add__(self, other):
299
+ if isinstance(other, Expression):
300
+ other = other.eval(_default_env)
301
+ if isinstance(other, ConstType) and other == 0:
302
+ return self
303
+ return BinaryExpression(self, other, operator.add)
304
+
305
+ def __radd__(self, other):
306
+ if isinstance(other, Expression):
307
+ other = other.eval(_default_env)
308
+ if isinstance(other, ConstType) and other == 0:
309
+ return self
310
+ return BinaryExpression(other, self, operator.add)
311
+
312
+ def __sub__(self, other):
313
+ if isinstance(other, Expression):
314
+ other = other.eval(_default_env)
315
+ if isinstance(other, ConstType) and other == 0:
316
+ return self
317
+ return BinaryExpression(self, other, operator.sub)
318
+
319
+ def __rsub__(self, other):
320
+ if isinstance(other, Expression):
321
+ other = other.eval(_default_env)
322
+ if isinstance(other, ConstType) and other == 0:
323
+ return -self
324
+ return BinaryExpression(other, self, operator.sub)
325
+
326
+ def __mul__(self, other):
327
+ if isinstance(other, Expression):
328
+ other = other.eval(_default_env)
329
+ if isinstance(other, ConstType) and other == 0:
330
+ return 0
331
+ if isinstance(other, ConstType) and other == 1:
332
+ return self
333
+ if isinstance(other, ConstType) and other == -1:
334
+ return -self
335
+ return BinaryExpression(self, other, operator.mul)
336
+
337
+ def __rmul__(self, other):
338
+ if isinstance(other, Expression):
339
+ other = other.eval(_default_env)
340
+ if isinstance(other, ConstType) and other == 0:
341
+ return 0
342
+ if isinstance(other, ConstType) and other == 1:
343
+ return self
344
+ if isinstance(other, ConstType) and other == -1:
345
+ return -self
346
+ return BinaryExpression(other, self, operator.mul)
347
+
348
+ def __matmul__(self, other):
349
+ if isinstance(other, Expression):
350
+ other = other.eval(_default_env)
351
+ return BinaryExpression(self, other, operator.matmul)
352
+
353
+ def __rmatmul__(self, other):
354
+ if isinstance(other, Expression):
355
+ other = other.eval(_default_env)
356
+ return BinaryExpression(other, self, operator.matmul)
357
+
358
+ def __truediv__(self, other):
359
+ if isinstance(other, Expression):
360
+ other = other.eval(_default_env)
361
+ if isinstance(other, ConstType) and other == 1:
362
+ return self
363
+ if isinstance(other, ConstType) and other == -1:
364
+ return -self
365
+ return BinaryExpression(self, other, operator.truediv)
366
+
367
+ def __rtruediv__(self, other):
368
+ if isinstance(other, Expression):
369
+ other = other.eval(_default_env)
370
+ if isinstance(other, ConstType) and other == 0:
371
+ return 0
372
+ return BinaryExpression(other, self, operator.truediv)
373
+
374
+ def __floordiv__(self, other):
375
+ if isinstance(other, Expression):
376
+ other = other.eval(_default_env)
377
+ if isinstance(other, ConstType) and other == 1:
378
+ return self
379
+ if isinstance(other, ConstType) and other == -1:
380
+ return -self
381
+ return BinaryExpression(self, other, operator.floordiv)
382
+
383
+ def __rfloordiv__(self, other):
384
+ if isinstance(other, Expression):
385
+ other = other.eval(_default_env)
386
+ if isinstance(other, ConstType) and other == 0:
387
+ return 0
388
+ return BinaryExpression(other, self, operator.floordiv)
389
+
390
+ def __mod__(self, other):
391
+ if isinstance(other, Expression):
392
+ other = other.eval(_default_env)
393
+ if isinstance(other, ConstType) and other == 1:
394
+ return 0
395
+ return BinaryExpression(self, other, operator.mod)
396
+
397
+ def __rmod__(self, other):
398
+ if isinstance(other, Expression):
399
+ other = other.eval(_default_env)
400
+ return BinaryExpression(other, self, operator.mod)
401
+
402
+ def __pow__(self, other):
403
+ if isinstance(other, Expression):
404
+ other = other.eval(_default_env)
405
+ if isinstance(other, ConstType) and other == 0:
406
+ return 1
407
+ if isinstance(other, ConstType) and other == 1:
408
+ return self
409
+ return BinaryExpression(self, other, operator.pow)
410
+
411
+ def __rpow__(self, other):
412
+ if isinstance(other, Expression):
413
+ other = other.eval(_default_env)
414
+ if isinstance(other, ConstType) and other == 0:
415
+ return 0
416
+ return BinaryExpression(other, self, operator.pow)
417
+
418
+ def __neg__(self):
419
+ return UnaryExpression(self, operator.neg)
420
+
421
+ def __pos__(self):
422
+ return UnaryExpression(self, operator.pos)
423
+
424
+ def __abs__(self):
425
+ return UnaryExpression(self, operator.abs)
426
+
427
+ def __not__(self):
428
+ return UnaryExpression(self, operator.not_)
429
+
430
+ def __inv__(self):
431
+ return UnaryExpression(self, operator.inv)
432
+
433
+ def __invert__(self):
434
+ return UnaryExpression(self, operator.invert)
435
+
436
+ def __index__(self):
437
+ return UnaryExpression(self, operator.index)
438
+
439
+ def __eq__(self, other):
440
+ if isinstance(other, Expression):
441
+ other = other.eval(_default_env)
442
+ return BinaryExpression(self, other, operator.eq)
443
+
444
+ def __ne__(self, other):
445
+ if isinstance(other, Expression):
446
+ other = other.eval(_default_env)
447
+ return BinaryExpression(self, other, operator.ne)
448
+
449
+ def __lt__(self, other):
450
+ if isinstance(other, Expression):
451
+ other = other.eval(_default_env)
452
+ return BinaryExpression(self, other, operator.lt)
453
+
454
+ def __le__(self, other):
455
+ if isinstance(other, Expression):
456
+ other = other.eval(_default_env)
457
+ return BinaryExpression(self, other, operator.le)
458
+
459
+ def __gt__(self, other):
460
+ if isinstance(other, Expression):
461
+ other = other.eval(_default_env)
462
+ return BinaryExpression(self, other, operator.gt)
463
+
464
+ def __ge__(self, other):
465
+ if isinstance(other, Expression):
466
+ other = other.eval(_default_env)
467
+ return BinaryExpression(self, other, operator.ge)
468
+
469
+ def __and__(self, other):
470
+ if isinstance(other, Expression):
471
+ other = other.eval(_default_env)
472
+ return BinaryExpression(self, other, operator.and_)
473
+
474
+ def __rand__(self, other):
475
+ if isinstance(other, Expression):
476
+ other
477
+ return BinaryExpression(other, self, operator.and_)
478
+
479
+ def __or__(self, other):
480
+ if isinstance(other, Expression):
481
+ other = other.eval(_default_env)
482
+ return BinaryExpression(self, other, operator.or_)
483
+
484
+ def __ror__(self, other):
485
+ if isinstance(other, Expression):
486
+ other = other.eval(_default_env)
487
+ return BinaryExpression(other, self, operator.or_)
488
+
489
+ def __lshift__(self, other):
490
+ if isinstance(other, Expression):
491
+ other = other.eval(_default_env)
492
+ return BinaryExpression(self, other, operator.lshift)
493
+
494
+ def __rlshift__(self, other):
495
+ if isinstance(other, Expression):
496
+ other = other.eval(_default_env)
497
+ return BinaryExpression(other, self, operator.lshift)
498
+
499
+ def __rshift__(self, other):
500
+ if isinstance(other, Expression):
501
+ other = other.eval(_default_env)
502
+ return BinaryExpression(self, other, operator.rshift)
503
+
504
+ def __rrshift__(self, other):
505
+ if isinstance(other, Expression):
506
+ other = other.eval(_default_env)
507
+ return BinaryExpression(other, self, operator.rshift)
508
+
509
+ def __xor__(self, other):
510
+ if isinstance(other, Expression):
511
+ other = other.eval(_default_env)
512
+ return BinaryExpression(self, other, operator.xor)
513
+
514
+ def __rxor__(self, other):
515
+ if isinstance(other, Expression):
516
+ other = other.eval(_default_env)
517
+ return BinaryExpression(other, self, operator.xor)
518
+
519
+ def __getitem__(self, other):
520
+ if isinstance(other, Expression):
521
+ other = other.eval(_default_env)
522
+ return ObjectMethod(self, '__getitem__', other)
523
+
524
+ def __getattr__(self, other):
525
+ if isinstance(other, str):
526
+ if other.startswith('_') or other in self.__dict__:
527
+ return super().__getattr__(other)
528
+ if isinstance(other, Expression):
529
+ other = other.eval(_default_env)
530
+ return ObjectMethod(self, '__getattr__', other)
531
+
532
+ def __call__(self, *args):
533
+ args = [
534
+ o.eval(_default_env) if isinstance(o, Expression) else o
535
+ for o in args
536
+ ]
537
+ return ObjectMethod(self, '__call__', *args)
538
+
539
+ def __round__(self, n=None):
540
+ return self
541
+
542
+ def __bool__(self):
543
+ return True
544
+
545
+ def eval(self, env):
546
+ raise NotImplementedError
547
+
548
+ def symbols(self) -> list[str]:
549
+ raise NotImplementedError
550
+
551
+ def changed(self, env) -> bool:
552
+ return True
553
+
554
+ def is_const(self, env) -> bool:
555
+ return False
556
+
557
+ def value(self, env=_default_env):
558
+ if isinstance(env, dict):
559
+ e = Env()
560
+ e.variables = env
561
+ env = e
562
+ if self.changed(env):
563
+ self.cache = self.eval(env)
564
+ return self.cache
565
+
566
+
567
+ class UnaryExpression(Expression):
568
+
569
+ def __init__(self, a, op):
570
+ super().__init__()
571
+ self.a = a
572
+ self.op = op
573
+
574
+ def __getstate__(self) -> dict:
575
+ return {'a': self.a, 'op': self.op}
576
+
577
+ def __setstate__(self, state: dict):
578
+ self.a = state['a']
579
+ self.op = state['op']
580
+ self.cache = _empty
581
+
582
+ def symbols(self) -> list[str]:
583
+ if isinstance(self.a, Expression):
584
+ return self.a.symbols()
585
+ else:
586
+ return []
587
+
588
+ def changed(self, env) -> bool:
589
+ if isinstance(self.a, ConstType):
590
+ return False
591
+ return self.cache is _empty or isinstance(
592
+ self.a, Expression) and self.a.changed(env)
593
+
594
+ def is_const(self, env) -> bool:
595
+ return isinstance(self.a,
596
+ Expression) and self.a.is_const(env) or isinstance(
597
+ self.a, ConstType)
598
+
599
+ def eval(self, env):
600
+ a = self.a.value(env) if isinstance(self.a, Expression) else self.a
601
+ return self.op(a)
602
+
603
+ def derivative(self, x):
604
+ if isinstance(self.a, Expression):
605
+ return self.op(self.a.d(x))
606
+ else:
607
+ return 0
608
+
609
+ def __repr__(self) -> str:
610
+ return f"{self.op.__name__}({self.a!r})"
611
+
612
+
613
+ class BinaryExpression(Expression):
614
+
615
+ def __init__(self, a, b, op):
616
+ super().__init__()
617
+ self.a = a
618
+ self.b = b
619
+ self.op = op
620
+
621
+ def __getstate__(self) -> dict:
622
+ return {'a': self.a, 'b': self.b, 'op': self.op}
623
+
624
+ def __setstate__(self, state: dict):
625
+ self.a = state['a']
626
+ self.b = state['b']
627
+ self.op = state['op']
628
+ self.cache = _empty
629
+
630
+ def symbols(self) -> list[str]:
631
+ symbs = set()
632
+ if isinstance(self.a, Expression):
633
+ symbs.update(self.a.symbols())
634
+ if isinstance(self.b, Expression):
635
+ symbs.update(self.b.symbols())
636
+ return list(symbs)
637
+
638
+ def eval(self, env):
639
+ a = self.a.value(env) if isinstance(self.a, Expression) else self.a
640
+ b = self.b.value(env) if isinstance(self.b, Expression) else self.b
641
+ return self.op(a, b)
642
+
643
+ def derivative(self, x):
644
+ if isinstance(self.a, Expression):
645
+ da = self.a.d(x)
646
+ else:
647
+ da = 0
648
+ if isinstance(self.b, Expression):
649
+ db = self.b.d(x)
650
+ else:
651
+ db = 0
652
+
653
+ if self.op is operator.add:
654
+ return da + db
655
+ elif self.op is operator.sub:
656
+ return da - db
657
+ elif self.op is operator.mul:
658
+ return self.a * db + da * self.b
659
+ elif self.op is operator.truediv:
660
+ return (da * self.b - self.a * db) / self.b**2
661
+ elif self.op is operator.pow:
662
+ if isinstance(self.a, Expression) and isinstance(
663
+ self.b, Expression):
664
+ return self.a**self.b * (self.b * da / self.a +
665
+ ObjectMethod(np, 'log', self.a) * db)
666
+ elif isinstance(self.a, Expression):
667
+ return self.b * self.a**(self.b - 1) * da
668
+ elif isinstance(self.b, Expression):
669
+ return np.log(self.a) * db * self.a**self.b
670
+ else:
671
+ return 0
672
+ else:
673
+ return 0
674
+
675
+ def __repr__(self) -> str:
676
+ return f"({self.a!r} {self.op.__name__} {self.b!r})"
677
+
678
+
679
+ class ObjectMethod(Expression):
680
+
681
+ def __init__(self, obj, method: str, *args):
682
+ super().__init__()
683
+ self.obj = obj
684
+ self.method = method
685
+ self.args = args
686
+
687
+ def __getstate__(self) -> dict:
688
+ return {'obj': self.obj, 'method': self.method, 'args': self.args}
689
+
690
+ def __setstate__(self, state: dict):
691
+ self.obj = state['obj']
692
+ self.method = state['method']
693
+ self.args = state['args']
694
+ self.cache = _empty
695
+
696
+ def symbols(self) -> list[str]:
697
+ symbs = set()
698
+ if isinstance(self.obj, Expression):
699
+ symbs.update(self.obj.symbols())
700
+ for a in self.args:
701
+ if isinstance(a, Expression):
702
+ symbs.update(a.symbols())
703
+ return list(symbs)
704
+
705
+ def eval(self, env):
706
+ obj = self.obj.value(env) if isinstance(self.obj,
707
+ Expression) else self.obj
708
+ args = [
709
+ a.value(env) if isinstance(a, Expression) else a for a in self.args
710
+ ]
711
+ if isinstance(obj, Expression) or any(
712
+ isinstance(x, Expression) for x in args):
713
+ return ObjectMethod(obj, self.method, *args)
714
+ elif self.method == '__getattr__':
715
+ print(f"getattr {obj} {args}")
716
+ return getattr(
717
+ obj, *[a.name if isinstance(a, Symbol) else a for a in args])
718
+ else:
719
+ return getattr(obj, self.method)(*[
720
+ a.value(env) if isinstance(a, Expression) else a for a in args
721
+ ])
722
+
723
+ def __repr__(self):
724
+ if self.method == '__call__':
725
+ return f"{self.obj!r}({', '.join(map(repr, self.args))})"
726
+ else:
727
+ return f"{self.obj!r}.{self.method}({', '.join(map(repr, self.args))})"
728
+
729
+
730
+ class Symbol(Expression):
731
+
732
+ def __init__(self, name):
733
+ super().__init__()
734
+ self.name = name
735
+
736
+ def __getstate__(self) -> dict:
737
+ return {'name': self.name}
738
+
739
+ def __setstate__(self, state: dict):
740
+ self.name = state['name']
741
+ self.cache = _empty
742
+
743
+ def symbols(self) -> list[str]:
744
+ return [self.name]
745
+
746
+ def eval(self, env):
747
+ if self.name in env:
748
+ value = env[self.name]
749
+ if isinstance(value, Expression):
750
+ return value.eval(env)
751
+ else:
752
+ return value
753
+ else:
754
+ return self
755
+
756
+ def derivative(self, x):
757
+ if x == self.name:
758
+ return 1
759
+ else:
760
+ return 0
761
+
762
+ def __repr__(self) -> str:
763
+ return self.name
764
+
765
+
766
+ class Query(Symbol):
767
+
768
+ def derivative(self, x):
769
+ return 0
770
+
771
+ def eval(self, env):
772
+ return super().eval(env)
773
+
774
+
775
+ sin = Symbol('sin')
776
+ cos = Symbol('cos')
777
+ tan = Symbol('tan')
778
+ pi = Symbol('pi')
779
+ e = Symbol('e')
780
+ log = Symbol('log')
781
+ log2 = Symbol('log2')
782
+ log10 = Symbol('log10')
783
+ exp = Symbol('exp')
784
+ sqrt = Symbol('sqrt')
785
+ abs = Symbol('abs')
786
+ sinh = Symbol('sinh')
787
+ cosh = Symbol('cosh')
788
+ tanh = Symbol('tanh')
789
+ arcsin = Symbol('arcsin')
790
+ arccos = Symbol('arccos')
791
+ arctan = Symbol('arctan')
792
+ arctan2 = Symbol('arctan2')
793
+ arcsinh = Symbol('arcsinh')
794
+ arccosh = Symbol('arccosh')
795
+ arctanh = Symbol('arctanh')
796
+ sinc = Symbol('sinc')
797
+ sign = Symbol('sign')
798
+ heaviside = Symbol('heaviside')
799
+ erf = Symbol('erf')
800
+ erfc = Symbol('erfc')
801
+
802
+
803
+ def calc(exp: str | Expression, env: Env = None, **kwargs) -> Expression:
804
+ """
805
+ Calculate the expression.
806
+
807
+ Parameters
808
+ ----------
809
+ exp : str | Expression
810
+ The expression to be calculated.
811
+ env : Env, optional
812
+ The environment to be used for the calculation. Default is _default_env.
813
+ **kwargs : dict
814
+ Additional arguments to be passed to the expression.
815
+
816
+ Returns
817
+ -------
818
+ Expression
819
+ The calculated expression.
820
+ """
821
+ if env is None:
822
+ env = Env()
823
+ for k, v in kwargs.items():
824
+ env[k] = v
825
+ if isinstance(exp, str):
826
+ exp = expr.parseString(exp, parseAll=True)[0]
827
+ return exp.eval(env)