Skip to content

Commit c8ef4f1

Browse files
committed
enh: performance optimalisation
enh: add more test fix: nunber stats eng: print number stats enh: number factory next enh: resolver with better recognition of dead branches enh: crossmath with more customization
1 parent eb4fdf4 commit c8ef4f1

6 files changed

+251
-99
lines changed

README.md

+39-31
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,45 @@
11
# Crossmath algorithm prototype in Python
22

33
```
4-
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
5-
0 4.9 - -10.1 = 15.0 6.3 + -27.3 = -21.0 3.2 + 5.2 = 8.4
6-
1 + + - -
7-
2 2.3 + 8.0 = 10.3 4.2 / 0.1 = 42.0 9.7 - 4.7 = 5.0 0.7 + 7.5 = 8.2
8-
3 / = - + = = =
9-
4 2.3 + -2.1 = 0.2 2.1 / 0.1 = 21.0 2.0 * 0.1 = 0.2 7.7 - 3.0 = 4.7
10-
5 = = = -
11-
6 1.0 10.1 - 6.3 = 3.8 6.0 * 4.8 = 28.8 3.7 + 0.9 = 4.6
12-
7 / + = +
13-
8 8.5 - 0.1 = 8.4 9.2 - 1.8 = 7.4 -28.7 - -28.7 = 0.0
14-
9 / = - + = = *
15-
10 8.5 38.0 + 2.6 = 40.6 7.8 - 0.3 = 7.5 -25.0 * 1.9 = -47.5
16-
11 = = = =
17-
12 1.0 + 4.8 = 5.8 49.8 - 30.5 = 19.3 0.0 + 0.4 = 0.4
18-
13 - + /
19-
14 1.9 + 2.5 = 4.4 -26.4 - 19.3 = -45.7
20-
15 + = = = +
21-
16 5.4 2.3 - 0.2 = 2.1 4.1 / 1.0 = 4.1
22-
17 = / =
23-
18 7.3 + -7.2 = 0.1 8.6 + -50.2 = -41.6
24-
19 - = +
25-
20 -7.3 2.0 - 1.0 = 1.0 7.0 * 1.8 = 12.6
26-
21 = - = / +
27-
22 0.1 4.9 + 0.9 = 5.8 -43.2 + 1.8 = -41.4
28-
23 / = - = =
29-
24 0.2 - 0.1 = 0.1 -28.8 * 1.0 = -28.8
30-
25 = = -
31-
26 24.5 5.7 -28.8
32-
27 =
33-
28 0.0
34-
29
4+
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
5+
0 -7.0 2.5 - -8.5 = 11.0 -10.5 * -6.0 = 63.0
6+
1 + + / +
7+
2 11.0 - -15.0 = 26.0 -3.5 / -3.5 = 1.0 -92.5
8+
3 - = = * - = =
9+
4 -16.5 4.0 -22.0 28.5 + -15.5 = 13.0 -35.5 - -6.0 = -29.5
10+
5 - = = =
11+
6 10.0 * 7.0 = 70.0 -13.5 - -45.5 = 32.0 80.0 / -20.0 = -4.0 1.5 / -3.0 = -0.5
12+
7 = + + + +
13+
8 -26.5 16.0 - 2.5 = 13.5 13.0 / -13.0 = -1.0 -3.0 + 14.0 = 11.0 -43.5 + 3.0 = -40.5
14+
9 = + - - + - = = =
15+
10 17.5 86.0 -18.0 * -2.5 = 45.0 15.5 -12.5 - -8.0 = -4.5 -6.0 * 7.0 = -42.0
16+
11 * = = = = - + =
17+
12 -9.5 1.0 31.5 + -15.5 = 16.0 -32.0 + 2.5 = -29.5 -21.5 1.5 - -8.0 = 9.5
18+
13 - = + = = +
19+
14 17.0 - 17.5 = -0.5 30.0 0.5 -12.5 - 17.0 = -29.5 8.5 * -11.0 = -93.5
20+
15 = = + - + = *
21+
16 -26.5 31.0 + -7.0 = 24.0 -20.0 / -10.0 = 2.0 18.0 - 6.5 = 11.5
22+
17 = = / = =
23+
18 -4.0 -0.5 - 23.0 = -23.5 -20.0 - -22.5 = 2.5 10.5 - 5.5 = 5.0 19.5 + -71.5 = -52.0
24+
19 * + = - *
25+
20 0.0 + 1.5 = 1.5 1.0 * 15.0 = 15.0 6.5 + -6.0 = 0.5
26+
21 / = = + / + = =
27+
22 7.0 + -6.0 = 1.0 -11.0 15.0 + -3.5 = 11.5 2.5 + 17.0 = 19.5
28+
23 = = = = / -
29+
24 0.0 - -19.0 = 19.0 4.0 - 1.0 = 3.0 17.0 - 7.5 = 9.5
30+
25 / - = =
31+
26 -19.0 1.5 1.0 12.0
32+
27 = =
33+
28 1.0 17.5
34+
29
35+
Number factory statistic:
36+
7 -> 1.0
37+
5 -> -6.0, 1.5, 2.5
38+
4 -> 17.0
39+
3 -> -29.5, -20.0, -3.5, -0.5, 7.0, 11.0, 15.0, 17.5
40+
2 -> -26.5, -19.0, -15.5, -12.5, -11.0, -8.0, -7.0, -4.0, -3.0, 0.0, 0.5, 3.0, 4.0, 6.5, 9.5, 11.5, 13.0, 16.0, 19.5
41+
1 -> -93.5, -92.5, -71.5, -52.0, -45.5, -43.5, -42.0, -40.5, -35.5, -32.0, -23.5, -22.5, -22.0, -21.5, -18.0, -16.5, -15.0, -13.5, -13.0, -10.5, -10.0, -9.5, -8.5, -4.5, -2.5, -1.0, 2.0, 5.0, 5.5, 7.5, 8.5, 10.0, 10.5, 12.0, 13.5, 14.0, 15.5, 18.0, 19.0, 23.0, 24.0, 26.0, 28.5, 30.0, 31.0, 31.5, 32.0, 45.0, 63.0, 70.0, 80.0, 86.0
42+
3543
```
3644

3745
## Author

crossmath.py

+2-7
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,10 @@ def clear(self):
4040

4141

4242
class CrossMath:
43-
def __init__(self, exp_map: ExpressionMap, number_factory: NumberFactory):
43+
def __init__(self, exp_map: ExpressionMap, number_factory: NumberFactory, expression_resolver : ExpressionResolver):
4444
self._map = exp_map
4545
self._number_factory = number_factory
46-
self._expression_resolver = ExpressionResolver(
47-
validator=ExpressionValidator(
48-
number_factory=number_factory, minimum=-100, maximum=50
49-
),
50-
number_factory=number_factory,
51-
)
46+
self._expression_resolver = expression_resolver
5247
self._dead_points = DeadPoints()
5348

5449
def _find_potential_positions(self) -> list[Tuple[int, int]]:

expression_resolver.py

+63-37
Original file line numberDiff line numberDiff line change
@@ -10,39 +10,45 @@ def __init__(
1010
self,
1111
message: str = "Expression resolver exception",
1212
expression: Expression | None = None,
13+
parent: Exception | None = None,
1314
):
14-
super().__init__(f"{message} (expression={expression})")
15+
super().__init__(f"{message} (expression={expression}, parent={parent})")
16+
self._parent = parent
1517

1618

1719
class ExpressionResolverNotResolvable(ExpressionResolverException):
1820
def __init__(
1921
self,
2022
message: str = "Expression is not resolvable",
2123
expression: Expression | None = None,
24+
parent: Exception | None = None,
2225
):
23-
super().__init__(message, expression=expression)
26+
super().__init__(message, expression=expression, parent=parent)
2427

2528

2629
class ExpressionResolverMaybeNotResolvable(ExpressionResolverException):
2730
def __init__(
2831
self,
2932
message: str = "Expression is maybe not resolvable",
3033
expression: Expression | None = None,
34+
parent: Exception | None = None,
3135
):
32-
super().__init__(message, expression=expression)
36+
super().__init__(message, expression=expression, parent=parent)
3337

3438

3539
class ExpressionResolver:
3640
def __init__(self, validator: ExpressionValidator, number_factory: NumberFactory):
3741
self._validator = validator
3842
self._number_factory = number_factory
39-
pass
43+
self._maximum_resolve_time_sec = 0.1
4044

41-
def _fly_back(self, expression: Expression):
42-
self._number_factory.fly_back(expression.operand1)
43-
self._number_factory.fly_back(expression.operand2)
44-
self._number_factory.fly_back(expression.result)
45-
pass
45+
def _fly_back(self, base: Expression, result: Expression):
46+
if base.operand1 is None:
47+
self._number_factory.fly_back(result.operand1)
48+
if base.operand2 is None:
49+
self._number_factory.fly_back(result.operand2)
50+
if base.result is None:
51+
self._number_factory.fly_back(result.result)
4652

4753
def _fix_result(self, expression: Expression):
4854
expression.operand1 = self._number_factory.fix(expression.operand1)
@@ -60,7 +66,7 @@ def _next_operator(self, expression: Expression) -> list[Operator]:
6066
def _resolve_result_is_none(self, expression: Expression) -> Expression:
6167
start = time.time()
6268
operators = self._next_operator(expression)
63-
while True:
69+
while time.time() - start < self._maximum_resolve_time_sec:
6470
exp_result = expression.clone()
6571
if exp_result.operator is None:
6672
try:
@@ -75,15 +81,27 @@ def _resolve_result_is_none(self, expression: Expression) -> Expression:
7581
exp_result.operand1 = self._number_factory.next()
7682
if exp_result.operand2 is None:
7783
if exp_result.operator == Operator.SUB:
78-
exp_result.operand2 = self._number_factory.next(
79-
maximum=exp_result.operand1
80-
)
84+
try:
85+
exp_result.operand2 = self._number_factory.next(
86+
maximum=exp_result.operand1
87+
)
88+
except ValueError as e:
89+
raise ExpressionResolverNotResolvable(
90+
expression=expression, parent=e
91+
)
8192
elif exp_result.operator == Operator.DIV:
82-
exp_result.operand2 = self._number_factory.next(
83-
maximum=exp_result.operand1,
84-
dividable_by=exp_result.operand1,
85-
zero_allowed=False,
86-
)
93+
try:
94+
exp_result.operand2 = self._number_factory.next(
95+
maximum=exp_result.operand1,
96+
dividable_by=exp_result.operand1,
97+
zero_allowed=False,
98+
)
99+
except ValueError as e:
100+
if expression.operator is None:
101+
continue
102+
raise ExpressionResolverNotResolvable(
103+
expression=expression, parent=e
104+
)
87105
else:
88106
exp_result.operand2 = self._number_factory.next()
89107
exp_result.result = self._number_factory.fix(
@@ -95,15 +113,15 @@ def _resolve_result_is_none(self, expression: Expression) -> Expression:
95113
self._check_result(expression, exp_result)
96114
if not self._validator.validate(exp_result):
97115
# TODO time or count limit
98-
time_diff = time.time() - start
99-
if time_diff > 1.0:
100-
raise ExpressionResolverMaybeNotResolvable(
101-
message=f"Too slow: {time_diff:.1f}s", expression=expression
102-
)
103116
continue
104-
self._fly_back(exp_result)
117+
self._fly_back(expression, exp_result)
105118
return exp_result
106119

120+
time_diff = time.time() - start
121+
raise ExpressionResolverMaybeNotResolvable(
122+
message=f"Too slow: {time_diff:.1f}s", expression=expression
123+
)
124+
107125
def _resolve_only_operator_missing(self, expression: Expression) -> Expression:
108126
if (
109127
expression.operand1 is None
@@ -133,13 +151,13 @@ def _resolve_only_operator_missing(self, expression: Expression) -> Expression:
133151
self._check_result(expression, exp_result)
134152
if not self._validator.validate(exp_result):
135153
continue
136-
self._fly_back(exp_result)
154+
self._fly_back(expression, exp_result)
137155
return exp_result
138156
raise ExpressionResolverNotResolvable(expression=expression)
139157

140158
def _resolve_result_is_available(self, expression: Expression) -> Expression:
141159
start = time.time()
142-
while True:
160+
while time.time() - start < self._maximum_resolve_time_sec:
143161
exp_calc = expression.clone()
144162
exp_result = expression.clone()
145163
exp_result.result = expression.result
@@ -174,11 +192,19 @@ def _resolve_result_is_available(self, expression: Expression) -> Expression:
174192
# ? * b = c -> c / b = a
175193
exp_calc.operator = Operator.DIV
176194
if not_has_operands:
177-
exp_result.operand1 = exp_calc.operand1 = self._number_factory.next(
178-
dividable_by=exp_result.result,
179-
zero_allowed=False,
180-
)
181-
print(exp_calc)
195+
try:
196+
exp_result.operand1 = exp_calc.operand1 = (
197+
self._number_factory.next(
198+
dividable_by=exp_result.result,
199+
zero_allowed=False,
200+
)
201+
)
202+
except ValueError as e:
203+
if expression.operator is None:
204+
continue
205+
raise ExpressionResolverNotResolvable(
206+
expression=expression, parent=e
207+
)
182208
elif operator == Operator.DIV:
183209
# a / b = c
184210
if not_has_operands:
@@ -219,15 +245,15 @@ def _resolve_result_is_available(self, expression: Expression) -> Expression:
219245

220246
if not self._validator.validate(exp_result):
221247
# TODO time or count limit
222-
time_diff = time.time() - start
223-
if time_diff > 1.0:
224-
raise ExpressionResolverMaybeNotResolvable(
225-
message=f"Too slow: {time_diff:.1f}s", expression=expression
226-
)
227248
continue
228-
self._fly_back(exp_result)
249+
self._fly_back(expression, exp_result)
229250
return exp_result
230251

252+
time_diff = time.time() - start
253+
raise ExpressionResolverMaybeNotResolvable(
254+
message=f"Too slow: {time_diff:.1f}s", expression=expression
255+
)
256+
231257
def resolve(self, expression: Expression) -> Expression | None:
232258
start_time = time.time()
233259
try:

0 commit comments

Comments
 (0)