funcprops 0.1.0__py3-none-any.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 (56) hide show
  1. funcprops/__init__.py +234 -0
  2. funcprops/_semialg.py +246 -0
  3. funcprops/api.py +70 -0
  4. funcprops/branchcuts/__init__.py +141 -0
  5. funcprops/branchcuts/computation.py +664 -0
  6. funcprops/branchcuts/core.py +1204 -0
  7. funcprops/branchcuts/post_simplify.py +461 -0
  8. funcprops/conditions.py +124 -0
  9. funcprops/data/catalogue.json +127449 -0
  10. funcprops/data/dlmf_audit.json +3382 -0
  11. funcprops/dlmf.py +29 -0
  12. funcprops/engine.py +324 -0
  13. funcprops/exceptions.py +29 -0
  14. funcprops/proof_cache.py +373 -0
  15. funcprops/real_analysis/__init__.py +52 -0
  16. funcprops/real_analysis/advanced_convexity.py +217 -0
  17. funcprops/real_analysis/boundedness.py +168 -0
  18. funcprops/real_analysis/common.py +244 -0
  19. funcprops/real_analysis/convexity.py +698 -0
  20. funcprops/real_analysis/differentiability.py +112 -0
  21. funcprops/real_analysis/domain.py +188 -0
  22. funcprops/real_analysis/mapping.py +374 -0
  23. funcprops/real_analysis/monotonic_certificates.py +867 -0
  24. funcprops/real_analysis/monotonicity.py +673 -0
  25. funcprops/real_analysis/oscillation.py +151 -0
  26. funcprops/real_analysis/piecewise.py +174 -0
  27. funcprops/real_analysis/range.py +552 -0
  28. funcprops/real_analysis/sign.py +561 -0
  29. funcprops/real_analysis/smoothness.py +268 -0
  30. funcprops/real_analysis/special_functions.py +515 -0
  31. funcprops/real_analysis/structural_properties.py +558 -0
  32. funcprops/real_analysis/zeros.py +327 -0
  33. funcprops/registry.py +274 -0
  34. funcprops/results.py +239 -0
  35. funcprops/singularities.py +1767 -0
  36. funcprops/stratification.py +494 -0
  37. funcprops/sympy_compare.py +63 -0
  38. funcprops/transcendental/__init__.py +140 -0
  39. funcprops/transcendental/algebraize.py +201 -0
  40. funcprops/transcendental/analytic.py +261 -0
  41. funcprops/transcendental/complex_roots.py +542 -0
  42. funcprops/transcendental/exp_fourier.py +221 -0
  43. funcprops/transcendental/extrema.py +821 -0
  44. funcprops/transcendental/periodic.py +222 -0
  45. funcprops/transcendental/proof.py +104 -0
  46. funcprops/transcendental/real_interval.py +128 -0
  47. funcprops/transcendental/real_sign.py +117 -0
  48. funcprops/transcendental/reduce.py +288 -0
  49. funcprops/transcendental/roots.py +1380 -0
  50. funcprops/transcendental/zero_geometry.py +1197 -0
  51. funcprops/verification.py +410 -0
  52. funcprops-0.1.0.dist-info/METADATA +208 -0
  53. funcprops-0.1.0.dist-info/RECORD +56 -0
  54. funcprops-0.1.0.dist-info/WHEEL +5 -0
  55. funcprops-0.1.0.dist-info/licenses/LICENSE +674 -0
  56. funcprops-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,494 @@
1
+ """Shared parameter-stratification and three-valued proof helpers.
2
+
3
+ This module centralizes parameter-case normalization, semantic-predicate
4
+ lowering, and exact set comparisons so every property engine uses the same
5
+ conditional-result semantics.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import contextlib
11
+ from collections.abc import Callable, Iterable
12
+ from typing import Any
13
+
14
+ import sympy as sp
15
+ from sympy import S
16
+
17
+ from .conditions import (
18
+ AAlgebraicQ,
19
+ AEqualQ,
20
+ AEvenQ,
21
+ AIntegerQ,
22
+ ANegative,
23
+ ANonIntegerQ,
24
+ ANonNegative,
25
+ ANonPositive,
26
+ AOddQ,
27
+ APositive,
28
+ ARationalQ,
29
+ ARealIfDefinedQ,
30
+ ARealQ,
31
+ AUnequalQ,
32
+ )
33
+ from .engine import simplify_condition
34
+ from .exceptions import SIMPLIFICATION_ERRORS, SYMBOLIC_QUERY_ERRORS
35
+ from .results import ConditionalValue, PropertyResult, TruthValue
36
+
37
+
38
+ def semantic_condition(condition):
39
+ """Lower catalogue semantic predicates to ordinary SymPy predicates.
40
+
41
+ This is deliberately one-way. Native inequalities/membership expressions
42
+ are much easier for ``simplify_logic``, ``refine`` and set conversion to
43
+ reason about, while the catalogue parser can continue to preserve source
44
+ semantics internally.
45
+ """
46
+ c = sp.sympify(condition)
47
+
48
+ def repl(node):
49
+ if not isinstance(node, sp.Basic):
50
+ return node
51
+ f = node.func
52
+ a = node.args
53
+ if f is APositive and len(a) == 1:
54
+ return a[0] > 0
55
+ if f is ANegative and len(a) == 1:
56
+ return a[0] < 0
57
+ if f is ANonPositive and len(a) == 1:
58
+ return a[0] <= 0
59
+ if f is ANonNegative and len(a) == 1:
60
+ return a[0] >= 0
61
+ if f is AIntegerQ and len(a) == 1:
62
+ return sp.Contains(a[0], S.Integers)
63
+ if f is ANonIntegerQ and len(a) == 1:
64
+ return sp.Not(sp.Contains(a[0], S.Integers))
65
+ if f is ARationalQ and len(a) == 1:
66
+ return sp.Contains(a[0], S.Rationals)
67
+ if f in (ARealQ, ARealIfDefinedQ) and len(a) == 1:
68
+ return sp.Contains(a[0], S.Reals)
69
+ if f is AAlgebraicQ and len(a) == 1:
70
+ # SymPy has no global Algebraics singleton that behaves as cleanly
71
+ # as Reals/Integers in Boolean simplification. Preserve this one.
72
+ return node
73
+ if f is AEvenQ and len(a) == 1:
74
+ return sp.And(sp.Contains(a[0], S.Integers), sp.Eq(sp.Mod(a[0], 2), 0))
75
+ if f is AOddQ and len(a) == 1:
76
+ return sp.And(sp.Contains(a[0], S.Integers), sp.Eq(sp.Mod(a[0], 2), 1))
77
+ if f is AUnequalQ and len(a) == 2:
78
+ return sp.Ne(a[0], a[1])
79
+ if f is AEqualQ and len(a) == 2:
80
+ return sp.Eq(a[0], a[1])
81
+ return node
82
+
83
+ with contextlib.suppress(SYMBOLIC_QUERY_ERRORS):
84
+ c = c.replace(
85
+ lambda n: (
86
+ isinstance(n, sp.Basic)
87
+ and n.func
88
+ in {
89
+ APositive,
90
+ ANegative,
91
+ ANonPositive,
92
+ ANonNegative,
93
+ AIntegerQ,
94
+ ANonIntegerQ,
95
+ ARationalQ,
96
+ ARealQ,
97
+ ARealIfDefinedQ,
98
+ AAlgebraicQ,
99
+ AEvenQ,
100
+ AOddQ,
101
+ AUnequalQ,
102
+ AEqualQ,
103
+ }
104
+ ),
105
+ repl,
106
+ )
107
+ with contextlib.suppress(SYMBOLIC_QUERY_ERRORS):
108
+ c = sp.refine(c)
109
+ with contextlib.suppress(SYMBOLIC_QUERY_ERRORS):
110
+ c = sp.simplify_logic(c)
111
+ return c
112
+
113
+
114
+ def parameter_symbols(expr, var=None, *extra):
115
+ syms = set(sp.sympify(expr).free_symbols)
116
+ for obj in extra:
117
+ with contextlib.suppress(SYMBOLIC_QUERY_ERRORS):
118
+ syms.update(sp.sympify(obj).free_symbols)
119
+ if var is not None:
120
+ syms.discard(sp.sympify(var))
121
+ return tuple(sorted(syms, key=sp.default_sort_key))
122
+
123
+
124
+ def normalize_cases(
125
+ cases: Iterable[ConditionalValue],
126
+ *,
127
+ source=None,
128
+ primary=None,
129
+ all_properties=(),
130
+ evidence=(),
131
+ ):
132
+ """Canonicalize conditional cases and merge equal-valued strata."""
133
+ merged: list[tuple[Any, sp.Basic]] = []
134
+ for item in cases:
135
+ cond = semantic_condition(simplify_condition(item.condition))
136
+ if cond is S.false:
137
+ continue
138
+ found = False
139
+ for i, (value, old) in enumerate(merged):
140
+ if value == item.value:
141
+ try:
142
+ new = sp.simplify(sp.simplify_logic(sp.Or(old, cond)))
143
+ except SYMBOLIC_QUERY_ERRORS:
144
+ new = sp.Or(old, cond)
145
+ merged[i] = (value, new)
146
+ found = True
147
+ break
148
+ if not found:
149
+ merged.append((item.value, cond))
150
+
151
+ # Truth-valued strata are mutually exclusive claims. If independently
152
+ # derived TRUE and FALSE guards overlap, make that overlap explicitly
153
+ # UNKNOWN instead of letting insertion order decide the answer.
154
+ true_cond = S.false
155
+ false_cond = S.false
156
+ for value, cond in merged:
157
+ if value is TruthValue.TRUE:
158
+ true_cond = sp.Or(true_cond, cond)
159
+ elif value is TruthValue.FALSE:
160
+ false_cond = sp.Or(false_cond, cond)
161
+ conflict = S.false
162
+ if true_cond is not S.false and false_cond is not S.false:
163
+ try:
164
+ conflict = sp.simplify(sp.simplify_logic(sp.And(true_cond, false_cond)))
165
+ except SYMBOLIC_QUERY_ERRORS:
166
+ conflict = sp.And(true_cond, false_cond)
167
+ if conflict is not S.false and not _guard_satisfiable(conflict):
168
+ conflict = S.false
169
+ if conflict is not S.false:
170
+ adjusted = []
171
+ for value, cond in merged:
172
+ if value in (TruthValue.TRUE, TruthValue.FALSE):
173
+ try:
174
+ cond = sp.simplify(
175
+ sp.simplify_logic(sp.And(cond, sp.Not(conflict)))
176
+ )
177
+ except SYMBOLIC_QUERY_ERRORS:
178
+ cond = sp.And(cond, sp.Not(conflict))
179
+ if cond is not S.false:
180
+ adjusted.append((value, cond))
181
+ merged = [(TruthValue.UNKNOWN, conflict), *adjusted]
182
+
183
+ out: list[ConditionalValue] = []
184
+ covered = S.false
185
+ for value, cond in merged:
186
+ try:
187
+ effective = sp.simplify(sp.simplify_logic(sp.And(cond, sp.Not(covered))))
188
+ except SYMBOLIC_QUERY_ERRORS:
189
+ effective = sp.And(cond, sp.Not(covered))
190
+ if effective is S.false:
191
+ continue
192
+ out.append(ConditionalValue(value, effective))
193
+ try:
194
+ covered = sp.simplify(sp.simplify_logic(sp.Or(covered, cond)))
195
+ except SYMBOLIC_QUERY_ERRORS:
196
+ covered = sp.Or(covered, cond)
197
+
198
+ if primary is None:
199
+ for c in out:
200
+ if c.condition is S.true:
201
+ primary = c.value
202
+ break
203
+ return PropertyResult(
204
+ primary=primary,
205
+ cases=tuple(out),
206
+ all_properties=tuple(all_properties),
207
+ source=source,
208
+ evidence=tuple(evidence),
209
+ )
210
+
211
+
212
+ def map_conditional(value, mapper: Callable[[Any], Any], *, source=None):
213
+ if not isinstance(value, PropertyResult):
214
+ return mapper(value)
215
+ cases = [ConditionalValue(mapper(c.value), c.condition) for c in value.cases]
216
+ primary = mapper(value.primary) if value.primary is not None else None
217
+ return normalize_cases(
218
+ cases, primary=primary, source=source or value.source, evidence=value.evidence
219
+ )
220
+
221
+
222
+ def implication_guard(domain, locus, var):
223
+ """Return a parameter-only sufficient guard for ``domain => locus``.
224
+
225
+ ``None`` means that the implication could not be reduced without the
226
+ function variable. The returned guard is exact for the structural forms
227
+ handled here; we never existentially sample parameters.
228
+ """
229
+ var = sp.sympify(var)
230
+ d = semantic_condition(domain)
231
+ locus_cond = semantic_condition(locus)
232
+ try:
233
+ imp = sp.simplify_logic(sp.Implies(d, locus_cond))
234
+ except SYMBOLIC_QUERY_ERRORS:
235
+ imp = sp.Implies(d, locus_cond)
236
+ with contextlib.suppress(SYMBOLIC_QUERY_ERRORS):
237
+ imp = sp.refine(imp, d)
238
+ with contextlib.suppress(SYMBOLIC_QUERY_ERRORS):
239
+ imp = sp.simplify_logic(imp)
240
+ if not getattr(imp, "has", lambda *_: False)(var):
241
+ return imp
242
+
243
+ # Common catalogue form: conjunction of domain facts and parameter facts.
244
+ if isinstance(locus_cond, sp.And):
245
+ residual = []
246
+ for term in locus_cond.args:
247
+ try:
248
+ test = sp.simplify_logic(sp.Implies(d, term))
249
+ except SYMBOLIC_QUERY_ERRORS:
250
+ test = sp.Implies(d, term)
251
+ if test is S.true:
252
+ continue
253
+ if not term.has(var):
254
+ residual.append(term)
255
+ else:
256
+ return None
257
+ return sp.And(*residual) if residual else S.true
258
+ return None
259
+
260
+
261
+ def exact_set_equal(a, b):
262
+ """Three-valued exact set equality test."""
263
+ if a == b:
264
+ return TruthValue.TRUE
265
+ if not isinstance(a, sp.Set) or not isinstance(b, sp.Set):
266
+ return TruthValue.UNKNOWN
267
+ try:
268
+ diff = a.symmetric_difference(b)
269
+ if diff is S.EmptySet or diff == S.EmptySet:
270
+ return TruthValue.TRUE
271
+ # Nonempty finite/interval/union differences are exact disproofs.
272
+ if isinstance(diff, (sp.FiniteSet, sp.Interval)):
273
+ return TruthValue.FALSE
274
+ emp = getattr(diff, "is_empty", None)
275
+ if emp is True:
276
+ return TruthValue.TRUE
277
+ if emp is False:
278
+ return TruthValue.FALSE
279
+ except SYMBOLIC_QUERY_ERRORS:
280
+ pass
281
+ try:
282
+ ab = a.is_subset(b)
283
+ ba = b.is_subset(a)
284
+ if ab is True and ba is True:
285
+ return TruthValue.TRUE
286
+ if ab is False or ba is False:
287
+ return TruthValue.FALSE
288
+ except SYMBOLIC_QUERY_ERRORS:
289
+ pass
290
+ return TruthValue.UNKNOWN
291
+
292
+
293
+ def combine_truth_results(a, b, *, op="and", source="combined"):
294
+ """Combine scalar/conditional TruthValue results without losing strata."""
295
+
296
+ def scalar(x, y):
297
+ if op == "and":
298
+ if x is TruthValue.FALSE or y is TruthValue.FALSE:
299
+ return TruthValue.FALSE
300
+ if x is TruthValue.TRUE and y is TruthValue.TRUE:
301
+ return TruthValue.TRUE
302
+ return TruthValue.UNKNOWN
303
+ if x is TruthValue.TRUE or y is TruthValue.TRUE:
304
+ return TruthValue.TRUE
305
+ if x is TruthValue.FALSE and y is TruthValue.FALSE:
306
+ return TruthValue.FALSE
307
+ return TruthValue.UNKNOWN
308
+
309
+ if not isinstance(a, PropertyResult) and not isinstance(b, PropertyResult):
310
+ return scalar(a, b)
311
+ acases = (
312
+ a.cases if isinstance(a, PropertyResult) else (ConditionalValue(a, S.true),)
313
+ )
314
+ bcases = (
315
+ b.cases if isinstance(b, PropertyResult) else (ConditionalValue(b, S.true),)
316
+ )
317
+ cases = []
318
+ for ca in acases:
319
+ for cb in bcases:
320
+ try:
321
+ cond = sp.simplify_logic(sp.And(ca.condition, cb.condition))
322
+ except SYMBOLIC_QUERY_ERRORS:
323
+ cond = sp.And(ca.condition, cb.condition)
324
+ if cond is not S.false:
325
+ cases.append(ConditionalValue(scalar(ca.value, cb.value), cond))
326
+ return normalize_cases(cases, source=source)
327
+
328
+
329
+ def _guard_satisfiable(guard):
330
+ """Return False only when a parameter guard is certified impossible.
331
+
332
+ SymPy's Boolean satisfier is propositional for inequalities, so polynomial
333
+ real guards are sent to semialg first. Unsupported guards fall back to
334
+ conservative propositional reasoning.
335
+ """
336
+ guard = semantic_condition(guard)
337
+ if guard is S.false:
338
+ return False
339
+ if guard is S.true:
340
+ return True
341
+ variables = tuple(sorted(guard.free_symbols, key=sp.default_sort_key))
342
+ if variables:
343
+ try:
344
+ from . import _semialg
345
+
346
+ return _semialg.is_satisfiable(guard, variables)
347
+ except (
348
+ _semialg.SemialgUnavailable,
349
+ TypeError,
350
+ ValueError,
351
+ NotImplementedError,
352
+ ArithmeticError,
353
+ ):
354
+ pass
355
+ try:
356
+ return sp.satisfiable(guard) is not False
357
+ except SIMPLIFICATION_ERRORS:
358
+ return True
359
+
360
+
361
+ def stratify_boolean_by_parameters(
362
+ condition, var, *, max_atoms=8, source="boolean-parameter-stratification"
363
+ ):
364
+ """Split a Boolean condition into parameter guards and var-only values.
365
+
366
+ This is exact for the finite Boolean atom partition it constructs. It is
367
+ especially useful for catalogue domains stored as a single mixed formula,
368
+ e.g. ``IntegerQ(nu) | x>0 | (nu>=0 & x>=0)``.
369
+ """
370
+ var = sp.sympify(var)
371
+ c = semantic_condition(condition)
372
+ # Parameter-only Boolean atoms. Do not split arbitrary function-valued
373
+ # endpoint expressions such as gamma(a); only predicates/relations matter.
374
+ try:
375
+ atoms = set(c.atoms(sp.core.relational.Relational, sp.Contains))
376
+ except SYMBOLIC_QUERY_ERRORS:
377
+ atoms = set()
378
+ atoms = {a for a in atoms if not a.has(var)}
379
+ atoms = tuple(sorted(atoms, key=sp.default_sort_key))
380
+ if not atoms or len(atoms) > max_atoms:
381
+ return PropertyResult(primary=c, source=source)
382
+ cases = []
383
+ from itertools import product
384
+
385
+ for bits in product((False, True), repeat=len(atoms)):
386
+ guard_terms = []
387
+ repl = {}
388
+ for a, b in zip(atoms, bits, strict=False):
389
+ repl[a] = S.true if b else S.false
390
+ guard_terms.append(a if b else sp.Not(a))
391
+ guard = semantic_condition(sp.And(*guard_terms))
392
+ if not _guard_satisfiable(guard):
393
+ continue
394
+ try:
395
+ value = c.xreplace(repl)
396
+ value = semantic_condition(value)
397
+ with contextlib.suppress(SYMBOLIC_QUERY_ERRORS):
398
+ value = sp.simplify(value)
399
+ except SYMBOLIC_QUERY_ERRORS:
400
+ value = c
401
+ # If parameters remain in predicates, this partition was insufficient.
402
+ params = set(value.free_symbols)
403
+ params.discard(var)
404
+ if params:
405
+ continue
406
+ cases.append(ConditionalValue(value, guard))
407
+ if not cases:
408
+ return PropertyResult(primary=c, source=source)
409
+ return normalize_cases(cases, source=source)
410
+
411
+
412
+ def result_cases(result):
413
+ """Return a uniform conditional-case view of a scalar or PropertyResult."""
414
+ if isinstance(result, PropertyResult):
415
+ if result.cases:
416
+ return result.cases
417
+ if result.primary is not None:
418
+ return (ConditionalValue(result.primary, S.true),)
419
+ return ()
420
+ return (ConditionalValue(result, S.true),)
421
+
422
+
423
+ def guarded_result(result, guard, *, source=None):
424
+ """Attach a parameter guard to every case in a property result."""
425
+ guard = semantic_condition(guard)
426
+ cases = []
427
+ for case in result_cases(result):
428
+ try:
429
+ cond = sp.simplify_logic(sp.And(guard, case.condition))
430
+ except SYMBOLIC_QUERY_ERRORS:
431
+ cond = sp.And(guard, case.condition)
432
+ if cond is not S.false:
433
+ cases.append(ConditionalValue(case.value, cond))
434
+ if not cases:
435
+ return PropertyResult(primary=None, source=source)
436
+ evidence = result.evidence if isinstance(result, PropertyResult) else ()
437
+ exceptions = result.exceptions if isinstance(result, PropertyResult) else ()
438
+ all_props = result.all_properties if isinstance(result, PropertyResult) else ()
439
+ out = normalize_cases(
440
+ cases,
441
+ source=source or getattr(result, "source", None),
442
+ all_properties=all_props,
443
+ evidence=evidence,
444
+ )
445
+ if exceptions:
446
+ return PropertyResult(
447
+ out.primary,
448
+ cases=out.cases,
449
+ all_properties=out.all_properties,
450
+ source=out.source,
451
+ exceptions=exceptions,
452
+ evidence=out.evidence,
453
+ )
454
+ return out
455
+
456
+
457
+ def evaluate_parameter_strata(
458
+ condition,
459
+ var,
460
+ evaluator: Callable[[sp.Basic], Any],
461
+ *,
462
+ source="parameter-stratification",
463
+ max_atoms=8,
464
+ ):
465
+ """Evaluate one property separately on exact parameter-domain strata.
466
+
467
+ ``condition`` may mix the function variable with parameter-only Boolean
468
+ atoms. When the finite parameter partition is exact, ``evaluator`` is
469
+ called on each resulting variable-only domain and nested conditional
470
+ results are flattened. ``None`` means there was no useful split.
471
+ """
472
+ split = stratify_boolean_by_parameters(
473
+ condition, var, max_atoms=max_atoms, source=source
474
+ )
475
+ if not split.cases or (
476
+ len(split.cases) == 1 and split.cases[0].condition is S.true
477
+ ):
478
+ return None
479
+ cases = []
480
+ evidence = []
481
+ for domain_case in split.cases:
482
+ value = evaluator(domain_case.value)
483
+ if isinstance(value, PropertyResult):
484
+ evidence.extend(value.evidence)
485
+ for item in result_cases(value):
486
+ try:
487
+ guard = sp.simplify_logic(sp.And(domain_case.condition, item.condition))
488
+ except SYMBOLIC_QUERY_ERRORS:
489
+ guard = sp.And(domain_case.condition, item.condition)
490
+ if guard is not S.false:
491
+ cases.append(ConditionalValue(item.value, guard))
492
+ if not cases:
493
+ return None
494
+ return normalize_cases(cases, source=source, evidence=tuple(evidence))
@@ -0,0 +1,63 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from enum import Enum
5
+
6
+ from sympy import S
7
+ from sympy.calculus.util import continuous_domain
8
+
9
+ from .conditions import parse_catalogue_expression
10
+ from .exceptions import SYMBOLIC_QUERY_ERRORS
11
+
12
+
13
+ class ComparisonStatus(str, Enum):
14
+ AGREE = "agree"
15
+ DISAGREE = "disagree"
16
+ SYMPY_UNKNOWN = "sympy_unknown"
17
+ NOT_COMPARABLE = "not_comparable"
18
+ ERROR = "error"
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class Comparison:
23
+ status: ComparisonStatus
24
+ registry_value: object
25
+ sympy_value: object = None
26
+ detail: str = ""
27
+
28
+
29
+ def compare_real_domain(expr, var, registry_condition):
30
+ """Compare a catalogue real-domain rule with SymPy when both are expressible.
31
+
32
+ SymPy's continuous_domain returns a Set while catalogue rules are Boolean
33
+ conditions, so exact equality is only asserted for readily convertible cases.
34
+ """
35
+ try:
36
+ s = continuous_domain(expr, var, S.Reals)
37
+ except NotImplementedError as e:
38
+ return Comparison(
39
+ ComparisonStatus.SYMPY_UNKNOWN, registry_condition, None, str(e)
40
+ )
41
+ except SYMBOLIC_QUERY_ERRORS as e:
42
+ return Comparison(ComparisonStatus.ERROR, registry_condition, None, str(e))
43
+ c = (
44
+ parse_catalogue_expression(registry_condition)
45
+ if isinstance(registry_condition, str)
46
+ else registry_condition
47
+ )
48
+ if c is S.true:
49
+ return Comparison(
50
+ ComparisonStatus.AGREE if s == S.Reals else ComparisonStatus.DISAGREE, c, s
51
+ )
52
+ if c is S.false:
53
+ return Comparison(
54
+ ComparisonStatus.AGREE if s == S.EmptySet else ComparisonStatus.DISAGREE,
55
+ c,
56
+ s,
57
+ )
58
+ return Comparison(
59
+ ComparisonStatus.NOT_COMPARABLE,
60
+ c,
61
+ s,
62
+ "Boolean-condition to Set equivalence requires quantifier/set normalization",
63
+ )
@@ -0,0 +1,140 @@
1
+ from .algebraize import (
2
+ AlgebraizationResult,
3
+ algebraize,
4
+ algebraize_common_subexpressions,
5
+ algebraize_exponentials,
6
+ algebraize_trig_circle,
7
+ algebraize_trigonometric,
8
+ rewrite_hyperbolic_to_exp,
9
+ )
10
+ from .analytic import (
11
+ AnalyticNeighborhoodCertificate,
12
+ analytic_neighborhood,
13
+ holomorphic_on_rectangle,
14
+ )
15
+ from .complex_roots import (
16
+ ArgumentPrincipleCertificate,
17
+ CertifiedComplexRootRectangle,
18
+ ComplexRectangle,
19
+ ComplexRootIsolationResult,
20
+ argument_principle_count,
21
+ isolate_complex_roots,
22
+ )
23
+ from .exp_fourier import (
24
+ ExponentialFourierCount,
25
+ exponential_fourier_count,
26
+ exponential_polynomial_terms,
27
+ )
28
+ from .extrema import (
29
+ ExtremumEnvelope,
30
+ ExtremumFamilyCertificate,
31
+ ExtremumOrderingTheorem,
32
+ ExtremumRange,
33
+ IndexedExtremumValue,
34
+ extremum_geometry,
35
+ extremum_ordering_theorems,
36
+ simplify_extremum_range,
37
+ symbolic_extremum_range,
38
+ )
39
+ from .periodic import (
40
+ PeriodicCondition,
41
+ PeriodicSet,
42
+ common_period_values,
43
+ common_positive_period,
44
+ positive_period,
45
+ )
46
+ from .proof import (
47
+ PerformanceGoal,
48
+ ProofPolicy,
49
+ condition_to_intervals,
50
+ extended_real_sign,
51
+ find_parameters,
52
+ )
53
+ from .real_sign import robust_real_sign
54
+ from .reduce import UnivariateReductionResult, reduce_univariate_condition
55
+ from .roots import (
56
+ CertifiedRootInterval,
57
+ ParametricZeroFamily,
58
+ RootCertificate,
59
+ RootIsolationResult,
60
+ transcendental_roots,
61
+ )
62
+ from .zero_geometry import (
63
+ CertifiedIntegerBracket,
64
+ IndexedTailIndex,
65
+ IndexedTailInversionCertificate,
66
+ IndexedZero,
67
+ IntervalPropertyCell,
68
+ PropertyIntervalDecomposition,
69
+ ZeroGeometryCertificate,
70
+ ZeroInterlacing,
71
+ ZeroOrdering,
72
+ indexed_tail_integer_bracket,
73
+ indexed_tail_inversion,
74
+ property_interval_decomposition,
75
+ zero_geometry,
76
+ zero_ordering_bounds,
77
+ )
78
+
79
+ __all__ = [
80
+ "AlgebraizationResult",
81
+ "AnalyticNeighborhoodCertificate",
82
+ "ArgumentPrincipleCertificate",
83
+ "CertifiedComplexRootRectangle",
84
+ "CertifiedIntegerBracket",
85
+ "CertifiedRootInterval",
86
+ "ComplexRectangle",
87
+ "ComplexRootIsolationResult",
88
+ "ExponentialFourierCount",
89
+ "ExtremumEnvelope",
90
+ "ExtremumFamilyCertificate",
91
+ "ExtremumOrderingTheorem",
92
+ "ExtremumRange",
93
+ "IndexedExtremumValue",
94
+ "IndexedTailIndex",
95
+ "IndexedTailInversionCertificate",
96
+ "IndexedZero",
97
+ "IntervalPropertyCell",
98
+ "ParametricZeroFamily",
99
+ "PerformanceGoal",
100
+ "PeriodicCondition",
101
+ "PeriodicSet",
102
+ "ProofPolicy",
103
+ "PropertyIntervalDecomposition",
104
+ "RootCertificate",
105
+ "RootIsolationResult",
106
+ "UnivariateReductionResult",
107
+ "ZeroGeometryCertificate",
108
+ "ZeroInterlacing",
109
+ "ZeroOrdering",
110
+ "algebraize",
111
+ "algebraize_common_subexpressions",
112
+ "algebraize_exponentials",
113
+ "algebraize_trig_circle",
114
+ "algebraize_trigonometric",
115
+ "analytic_neighborhood",
116
+ "argument_principle_count",
117
+ "common_period_values",
118
+ "common_positive_period",
119
+ "condition_to_intervals",
120
+ "exponential_fourier_count",
121
+ "exponential_polynomial_terms",
122
+ "extended_real_sign",
123
+ "extremum_geometry",
124
+ "extremum_ordering_theorems",
125
+ "find_parameters",
126
+ "holomorphic_on_rectangle",
127
+ "indexed_tail_integer_bracket",
128
+ "indexed_tail_inversion",
129
+ "isolate_complex_roots",
130
+ "positive_period",
131
+ "property_interval_decomposition",
132
+ "reduce_univariate_condition",
133
+ "rewrite_hyperbolic_to_exp",
134
+ "robust_real_sign",
135
+ "simplify_extremum_range",
136
+ "symbolic_extremum_range",
137
+ "transcendental_roots",
138
+ "zero_geometry",
139
+ "zero_ordering_bounds",
140
+ ]