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.
- funcprops/__init__.py +234 -0
- funcprops/_semialg.py +246 -0
- funcprops/api.py +70 -0
- funcprops/branchcuts/__init__.py +141 -0
- funcprops/branchcuts/computation.py +664 -0
- funcprops/branchcuts/core.py +1204 -0
- funcprops/branchcuts/post_simplify.py +461 -0
- funcprops/conditions.py +124 -0
- funcprops/data/catalogue.json +127449 -0
- funcprops/data/dlmf_audit.json +3382 -0
- funcprops/dlmf.py +29 -0
- funcprops/engine.py +324 -0
- funcprops/exceptions.py +29 -0
- funcprops/proof_cache.py +373 -0
- funcprops/real_analysis/__init__.py +52 -0
- funcprops/real_analysis/advanced_convexity.py +217 -0
- funcprops/real_analysis/boundedness.py +168 -0
- funcprops/real_analysis/common.py +244 -0
- funcprops/real_analysis/convexity.py +698 -0
- funcprops/real_analysis/differentiability.py +112 -0
- funcprops/real_analysis/domain.py +188 -0
- funcprops/real_analysis/mapping.py +374 -0
- funcprops/real_analysis/monotonic_certificates.py +867 -0
- funcprops/real_analysis/monotonicity.py +673 -0
- funcprops/real_analysis/oscillation.py +151 -0
- funcprops/real_analysis/piecewise.py +174 -0
- funcprops/real_analysis/range.py +552 -0
- funcprops/real_analysis/sign.py +561 -0
- funcprops/real_analysis/smoothness.py +268 -0
- funcprops/real_analysis/special_functions.py +515 -0
- funcprops/real_analysis/structural_properties.py +558 -0
- funcprops/real_analysis/zeros.py +327 -0
- funcprops/registry.py +274 -0
- funcprops/results.py +239 -0
- funcprops/singularities.py +1767 -0
- funcprops/stratification.py +494 -0
- funcprops/sympy_compare.py +63 -0
- funcprops/transcendental/__init__.py +140 -0
- funcprops/transcendental/algebraize.py +201 -0
- funcprops/transcendental/analytic.py +261 -0
- funcprops/transcendental/complex_roots.py +542 -0
- funcprops/transcendental/exp_fourier.py +221 -0
- funcprops/transcendental/extrema.py +821 -0
- funcprops/transcendental/periodic.py +222 -0
- funcprops/transcendental/proof.py +104 -0
- funcprops/transcendental/real_interval.py +128 -0
- funcprops/transcendental/real_sign.py +117 -0
- funcprops/transcendental/reduce.py +288 -0
- funcprops/transcendental/roots.py +1380 -0
- funcprops/transcendental/zero_geometry.py +1197 -0
- funcprops/verification.py +410 -0
- funcprops-0.1.0.dist-info/METADATA +208 -0
- funcprops-0.1.0.dist-info/RECORD +56 -0
- funcprops-0.1.0.dist-info/WHEEL +5 -0
- funcprops-0.1.0.dist-info/licenses/LICENSE +674 -0
- funcprops-0.1.0.dist-info/top_level.txt +1 -0
funcprops/__init__.py
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"""Symbolic function properties for SymPy expressions."""
|
|
2
|
+
|
|
3
|
+
from .api import (
|
|
4
|
+
function_analytic,
|
|
5
|
+
function_bijective,
|
|
6
|
+
function_boundedness,
|
|
7
|
+
function_branchcuts,
|
|
8
|
+
function_branchpoints,
|
|
9
|
+
function_complex_zeros,
|
|
10
|
+
function_continuous,
|
|
11
|
+
function_convexity,
|
|
12
|
+
function_differentiability,
|
|
13
|
+
function_discontinuities,
|
|
14
|
+
function_domain,
|
|
15
|
+
function_homogeneity,
|
|
16
|
+
function_injective,
|
|
17
|
+
function_meromorphic,
|
|
18
|
+
function_monotonicity,
|
|
19
|
+
function_oscillation,
|
|
20
|
+
function_periodicity,
|
|
21
|
+
function_poles,
|
|
22
|
+
function_range,
|
|
23
|
+
function_sign,
|
|
24
|
+
function_singularities,
|
|
25
|
+
function_singularity_analysis,
|
|
26
|
+
function_smoothness,
|
|
27
|
+
function_surjective,
|
|
28
|
+
function_symmetry,
|
|
29
|
+
function_zeros,
|
|
30
|
+
)
|
|
31
|
+
from .dlmf import DLMFFact, dlmf_audit_report, dlmf_facts
|
|
32
|
+
from .real_analysis.advanced_convexity import ConvexityCertificate
|
|
33
|
+
from .real_analysis.monotonic_certificates import (
|
|
34
|
+
IndexedMonotonicPartition,
|
|
35
|
+
InverseSolutionCertificate,
|
|
36
|
+
MonotonicDecomposition,
|
|
37
|
+
MonotonicInterval,
|
|
38
|
+
MonotonicPartition,
|
|
39
|
+
NonMonotonicityWitness,
|
|
40
|
+
PartitionRange,
|
|
41
|
+
certified_monotonic_inverse,
|
|
42
|
+
clear_monotonic_partition_cache,
|
|
43
|
+
injectivity_from_monotonic_partition,
|
|
44
|
+
monotonic_decomposition,
|
|
45
|
+
monotonic_partition,
|
|
46
|
+
range_from_monotonic_partition,
|
|
47
|
+
solve_monotonic_inverse,
|
|
48
|
+
)
|
|
49
|
+
from .real_analysis.oscillation import OscillationCertificate
|
|
50
|
+
from .real_analysis.smoothness import SmoothnessResult
|
|
51
|
+
from .real_analysis.structural_properties import (
|
|
52
|
+
HomogeneityResult,
|
|
53
|
+
PeriodicityResult,
|
|
54
|
+
SymmetryResult,
|
|
55
|
+
)
|
|
56
|
+
from .registry import Family, FunctionRegistry, RuleCase, registry
|
|
57
|
+
from .results import *
|
|
58
|
+
from .singularities import SingularityAnalysis
|
|
59
|
+
from .transcendental import (
|
|
60
|
+
AlgebraizationResult,
|
|
61
|
+
AnalyticNeighborhoodCertificate,
|
|
62
|
+
ArgumentPrincipleCertificate,
|
|
63
|
+
CertifiedComplexRootRectangle,
|
|
64
|
+
CertifiedIntegerBracket,
|
|
65
|
+
CertifiedRootInterval,
|
|
66
|
+
ComplexRectangle,
|
|
67
|
+
ComplexRootIsolationResult,
|
|
68
|
+
ExponentialFourierCount,
|
|
69
|
+
ExtremumEnvelope,
|
|
70
|
+
ExtremumFamilyCertificate,
|
|
71
|
+
ExtremumOrderingTheorem,
|
|
72
|
+
ExtremumRange,
|
|
73
|
+
IndexedExtremumValue,
|
|
74
|
+
IndexedTailIndex,
|
|
75
|
+
IndexedTailInversionCertificate,
|
|
76
|
+
IndexedZero,
|
|
77
|
+
IntervalPropertyCell,
|
|
78
|
+
ParametricZeroFamily,
|
|
79
|
+
PeriodicCondition,
|
|
80
|
+
PeriodicSet,
|
|
81
|
+
PropertyIntervalDecomposition,
|
|
82
|
+
RootCertificate,
|
|
83
|
+
RootIsolationResult,
|
|
84
|
+
UnivariateReductionResult,
|
|
85
|
+
ZeroGeometryCertificate,
|
|
86
|
+
ZeroInterlacing,
|
|
87
|
+
ZeroOrdering,
|
|
88
|
+
algebraize,
|
|
89
|
+
analytic_neighborhood,
|
|
90
|
+
argument_principle_count,
|
|
91
|
+
exponential_fourier_count,
|
|
92
|
+
exponential_polynomial_terms,
|
|
93
|
+
extremum_geometry,
|
|
94
|
+
extremum_ordering_theorems,
|
|
95
|
+
holomorphic_on_rectangle,
|
|
96
|
+
indexed_tail_integer_bracket,
|
|
97
|
+
indexed_tail_inversion,
|
|
98
|
+
isolate_complex_roots,
|
|
99
|
+
property_interval_decomposition,
|
|
100
|
+
reduce_univariate_condition,
|
|
101
|
+
robust_real_sign,
|
|
102
|
+
simplify_extremum_range,
|
|
103
|
+
symbolic_extremum_range,
|
|
104
|
+
transcendental_roots,
|
|
105
|
+
zero_geometry,
|
|
106
|
+
zero_ordering_bounds,
|
|
107
|
+
)
|
|
108
|
+
from .verification import (
|
|
109
|
+
verify_certificate,
|
|
110
|
+
verify_convexity_certificate,
|
|
111
|
+
verify_extremum_family_certificate,
|
|
112
|
+
verify_extremum_ordering_theorem,
|
|
113
|
+
verify_extremum_range,
|
|
114
|
+
verify_indexed_tail_inversion,
|
|
115
|
+
verify_integer_bracket,
|
|
116
|
+
verify_monotonic_decomposition,
|
|
117
|
+
verify_monotonic_partition,
|
|
118
|
+
verify_root_certificate,
|
|
119
|
+
verify_zero_geometry,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
__all__ = [
|
|
123
|
+
name
|
|
124
|
+
for name in globals()
|
|
125
|
+
if name.startswith("function_")
|
|
126
|
+
or name
|
|
127
|
+
in {
|
|
128
|
+
"registry",
|
|
129
|
+
"FunctionRegistry",
|
|
130
|
+
"Family",
|
|
131
|
+
"RuleCase",
|
|
132
|
+
"Analyticity",
|
|
133
|
+
"Meromorphicity",
|
|
134
|
+
"Monotonicity",
|
|
135
|
+
"Convexity",
|
|
136
|
+
"Sign",
|
|
137
|
+
"Boundedness",
|
|
138
|
+
"Continuity",
|
|
139
|
+
"Differentiability",
|
|
140
|
+
"TruthValue",
|
|
141
|
+
"Periodicity",
|
|
142
|
+
"Symmetry",
|
|
143
|
+
"Homogeneity",
|
|
144
|
+
"PropertyResult",
|
|
145
|
+
"DetailedPropertyResult",
|
|
146
|
+
"SupportsDetailedPropertyResult",
|
|
147
|
+
"as_detailed_result",
|
|
148
|
+
"ConditionalValue",
|
|
149
|
+
"Singularity",
|
|
150
|
+
"SingularityKind",
|
|
151
|
+
"SingularityAnalysis",
|
|
152
|
+
"PeriodicSet",
|
|
153
|
+
"PeriodicCondition",
|
|
154
|
+
"RootCertificate",
|
|
155
|
+
"CertifiedRootInterval",
|
|
156
|
+
"ComplexRectangle",
|
|
157
|
+
"ArgumentPrincipleCertificate",
|
|
158
|
+
"CertifiedComplexRootRectangle",
|
|
159
|
+
"ComplexRootIsolationResult",
|
|
160
|
+
"ParametricZeroFamily",
|
|
161
|
+
"RootIsolationResult",
|
|
162
|
+
"IndexedZero",
|
|
163
|
+
"ZeroOrdering",
|
|
164
|
+
"ZeroInterlacing",
|
|
165
|
+
"ZeroGeometryCertificate",
|
|
166
|
+
"IntervalPropertyCell",
|
|
167
|
+
"PropertyIntervalDecomposition",
|
|
168
|
+
"zero_geometry",
|
|
169
|
+
"property_interval_decomposition",
|
|
170
|
+
"zero_ordering_bounds",
|
|
171
|
+
"IndexedExtremumValue",
|
|
172
|
+
"ExtremumEnvelope",
|
|
173
|
+
"ExtremumFamilyCertificate",
|
|
174
|
+
"ExtremumOrderingTheorem",
|
|
175
|
+
"ExtremumRange",
|
|
176
|
+
"ExponentialFourierCount",
|
|
177
|
+
"extremum_geometry",
|
|
178
|
+
"extremum_ordering_theorems",
|
|
179
|
+
"simplify_extremum_range",
|
|
180
|
+
"symbolic_extremum_range",
|
|
181
|
+
"AnalyticNeighborhoodCertificate",
|
|
182
|
+
"UnivariateReductionResult",
|
|
183
|
+
"PeriodicityResult",
|
|
184
|
+
"SymmetryResult",
|
|
185
|
+
"HomogeneityResult",
|
|
186
|
+
"MonotonicDecomposition",
|
|
187
|
+
"MonotonicInterval",
|
|
188
|
+
"IndexedMonotonicPartition",
|
|
189
|
+
"MonotonicPartition",
|
|
190
|
+
"NonMonotonicityWitness",
|
|
191
|
+
"InverseSolutionCertificate",
|
|
192
|
+
"PartitionRange",
|
|
193
|
+
"OscillationCertificate",
|
|
194
|
+
"monotonic_decomposition",
|
|
195
|
+
"monotonic_partition",
|
|
196
|
+
"solve_monotonic_inverse",
|
|
197
|
+
"range_from_monotonic_partition",
|
|
198
|
+
"injectivity_from_monotonic_partition",
|
|
199
|
+
"certified_monotonic_inverse",
|
|
200
|
+
"clear_monotonic_partition_cache",
|
|
201
|
+
"IndexedTailIndex",
|
|
202
|
+
"IndexedTailInversionCertificate",
|
|
203
|
+
"CertifiedIntegerBracket",
|
|
204
|
+
"indexed_tail_inversion",
|
|
205
|
+
"indexed_tail_integer_bracket",
|
|
206
|
+
"SmoothnessResult",
|
|
207
|
+
"ConvexityCertificate",
|
|
208
|
+
"DLMFFact",
|
|
209
|
+
"dlmf_facts",
|
|
210
|
+
"dlmf_audit_report",
|
|
211
|
+
"verify_certificate",
|
|
212
|
+
"verify_monotonic_decomposition",
|
|
213
|
+
"verify_monotonic_partition",
|
|
214
|
+
"verify_zero_geometry",
|
|
215
|
+
"verify_convexity_certificate",
|
|
216
|
+
"verify_extremum_ordering_theorem",
|
|
217
|
+
"verify_indexed_tail_inversion",
|
|
218
|
+
"verify_root_certificate",
|
|
219
|
+
"verify_extremum_family_certificate",
|
|
220
|
+
"verify_extremum_range",
|
|
221
|
+
"verify_integer_bracket",
|
|
222
|
+
"AlgebraizationResult",
|
|
223
|
+
"transcendental_roots",
|
|
224
|
+
"robust_real_sign",
|
|
225
|
+
"exponential_fourier_count",
|
|
226
|
+
"exponential_polynomial_terms",
|
|
227
|
+
"reduce_univariate_condition",
|
|
228
|
+
"algebraize",
|
|
229
|
+
"argument_principle_count",
|
|
230
|
+
"isolate_complex_roots",
|
|
231
|
+
"holomorphic_on_rectangle",
|
|
232
|
+
"analytic_neighborhood",
|
|
233
|
+
}
|
|
234
|
+
]
|
funcprops/_semialg.py
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"""Adapter to semialg's public exact real-function API.
|
|
2
|
+
|
|
3
|
+
funcprops owns the public property vocabulary and evidence objects. semialg
|
|
4
|
+
provides the exact backend for semialgebraic functions on semialgebraic real
|
|
5
|
+
domains, including domain, range, sign, monotonicity, convexity, smoothness,
|
|
6
|
+
and mapping-property calculations. Imports remain lazy so catalogue and
|
|
7
|
+
complex-analysis queries do not load the backend until a real-domain proof
|
|
8
|
+
needs it.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from collections.abc import Callable
|
|
14
|
+
from functools import cache
|
|
15
|
+
|
|
16
|
+
import sympy as sp
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SemialgUnavailable(ImportError):
|
|
20
|
+
"""Raised when the required semialg public API cannot be loaded."""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@cache
|
|
24
|
+
def _public_function(name: str) -> Callable:
|
|
25
|
+
"""Return one function from semialg's documented root API."""
|
|
26
|
+
try:
|
|
27
|
+
import semialg
|
|
28
|
+
except ImportError as exc:
|
|
29
|
+
raise SemialgUnavailable(
|
|
30
|
+
"funcprops real-analysis queries require semialg>=1.1.0"
|
|
31
|
+
) from exc
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
function = getattr(semialg, name)
|
|
35
|
+
except AttributeError as exc:
|
|
36
|
+
raise SemialgUnavailable(
|
|
37
|
+
f"semialg>=1.1.0 does not provide the required public function {name!r}"
|
|
38
|
+
) from exc
|
|
39
|
+
if not callable(function):
|
|
40
|
+
raise SemialgUnavailable(f"semialg public attribute {name!r} is not callable")
|
|
41
|
+
return function
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def function_domain(expr, variable):
|
|
45
|
+
"""Return semialg's recognized real-domain condition."""
|
|
46
|
+
function = _public_function("function_domain")
|
|
47
|
+
return function(sp.sympify(expr), [variable])
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def function_range(
|
|
51
|
+
expr, variable, value_symbol, *, constraints=True, method="qe", **kwargs
|
|
52
|
+
):
|
|
53
|
+
"""Return an exact real-range condition from semialg."""
|
|
54
|
+
function = _public_function("function_range")
|
|
55
|
+
return function(
|
|
56
|
+
sp.sympify(expr),
|
|
57
|
+
constraints,
|
|
58
|
+
[variable],
|
|
59
|
+
value_symbol=value_symbol,
|
|
60
|
+
method=method,
|
|
61
|
+
**kwargs,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def function_convexity(
|
|
66
|
+
expr, variable, *, domain=True, strict_inequalities=True, **kwargs
|
|
67
|
+
):
|
|
68
|
+
"""Return semialg's strongest certified convexity classification."""
|
|
69
|
+
properties = kwargs.pop("properties", "primary")
|
|
70
|
+
return_result = kwargs.pop("return_result", False)
|
|
71
|
+
parameters = kwargs.pop("parameters", None)
|
|
72
|
+
if kwargs:
|
|
73
|
+
names = ", ".join(sorted(kwargs))
|
|
74
|
+
raise TypeError(f"unsupported semialg convexity options: {names}")
|
|
75
|
+
|
|
76
|
+
# semialg computes the strongest exact class. funcprops weakens the class
|
|
77
|
+
# afterwards when callers request non-strict semantics.
|
|
78
|
+
del strict_inequalities
|
|
79
|
+
function = _public_function("function_convexity")
|
|
80
|
+
return function(
|
|
81
|
+
sp.sympify(expr),
|
|
82
|
+
[variable],
|
|
83
|
+
domain=domain,
|
|
84
|
+
parameters=parameters,
|
|
85
|
+
return_result=return_result,
|
|
86
|
+
properties=properties,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def function_monotonicity(expr, variable, *, domain=True, return_result=False):
|
|
91
|
+
"""Return semialg's exact monotonicity classification."""
|
|
92
|
+
function = _public_function("function_monotonicity")
|
|
93
|
+
return function(
|
|
94
|
+
sp.sympify(expr),
|
|
95
|
+
variable,
|
|
96
|
+
domain=domain,
|
|
97
|
+
return_result=return_result,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def function_smoothness(expr, variable, *, domain=True, max_order=6):
|
|
102
|
+
"""Return semialg's exact regularity result on a semialgebraic domain."""
|
|
103
|
+
function = _public_function("function_smoothness")
|
|
104
|
+
return function(
|
|
105
|
+
sp.sympify(expr),
|
|
106
|
+
variable,
|
|
107
|
+
domain=domain,
|
|
108
|
+
max_order=max_order,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def function_mapping_properties(
|
|
113
|
+
expr, variable, *, domain=True, codomain=True, image_variable=None
|
|
114
|
+
):
|
|
115
|
+
"""Return semialg's exact mapping properties for a semialgebraic map."""
|
|
116
|
+
function = _public_function("function_mapping_properties")
|
|
117
|
+
image_variables = None if image_variable is None else (image_variable,)
|
|
118
|
+
return function(
|
|
119
|
+
expr,
|
|
120
|
+
variable,
|
|
121
|
+
domain=domain,
|
|
122
|
+
codomain=codomain,
|
|
123
|
+
image_variables=image_variables,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def eligible_problem(expr, variable, *, domain=True) -> bool:
|
|
128
|
+
"""Return whether an expression/domain pair is safe for semialg delegation."""
|
|
129
|
+
from .real_analysis.common import (
|
|
130
|
+
is_semialgebraic_condition,
|
|
131
|
+
is_semialgebraic_expression,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
return is_semialgebraic_expression(expr, variable) and is_semialgebraic_condition(
|
|
135
|
+
domain, variable
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def is_satisfiable(condition, variables):
|
|
140
|
+
"""Decide satisfiability of a semialgebraic parameter guard exactly."""
|
|
141
|
+
function = _public_function("is_satisfiable")
|
|
142
|
+
normalized = tuple(sp.sympify(v) for v in variables)
|
|
143
|
+
return bool(function(sp.sympify(condition), normalized))
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def function_sign(expr, variable, *, assumptions=True, return_result=False):
|
|
147
|
+
"""Return semialg's canonical exact sign classification."""
|
|
148
|
+
function = _public_function("function_sign")
|
|
149
|
+
return function(
|
|
150
|
+
sp.sympify(expr),
|
|
151
|
+
[variable],
|
|
152
|
+
assumptions=assumptions,
|
|
153
|
+
return_result=return_result,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def function_sign_partition(expr, variable, *, domain=True):
|
|
158
|
+
"""Return semialg's exact univariate sign partition."""
|
|
159
|
+
function = _public_function("function_sign_partition")
|
|
160
|
+
return function(sp.sympify(expr), variable, domain=domain)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def normalize_sign(value):
|
|
164
|
+
"""Translate a semialg sign classification into funcprops' Sign enum."""
|
|
165
|
+
from .results import Sign
|
|
166
|
+
|
|
167
|
+
classification = getattr(value, "classification", value)
|
|
168
|
+
return {
|
|
169
|
+
"positive": Sign.POSITIVE,
|
|
170
|
+
"negative": Sign.NEGATIVE,
|
|
171
|
+
"zero": Sign.ZERO,
|
|
172
|
+
"nonnegative": Sign.NONNEGATIVE,
|
|
173
|
+
"nonpositive": Sign.NONPOSITIVE,
|
|
174
|
+
"mixed": Sign.SIGN_CHANGING,
|
|
175
|
+
}.get(str(classification), Sign.UNKNOWN)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def normalize_convexity(value):
|
|
179
|
+
"""Translate semialg convexity vocabulary into funcprops' enum."""
|
|
180
|
+
from .results import Convexity
|
|
181
|
+
|
|
182
|
+
if isinstance(value, Convexity):
|
|
183
|
+
return value
|
|
184
|
+
if hasattr(value, "primary"):
|
|
185
|
+
value = value.primary
|
|
186
|
+
if isinstance(value, int):
|
|
187
|
+
return {
|
|
188
|
+
1: Convexity.STRICTLY_CONVEX,
|
|
189
|
+
2: Convexity.CONVEX,
|
|
190
|
+
-1: Convexity.STRICTLY_CONCAVE,
|
|
191
|
+
-2: Convexity.CONCAVE,
|
|
192
|
+
}.get(int(value), Convexity.UNKNOWN)
|
|
193
|
+
key = str(getattr(value, "name", value)).lower().replace("_", "").replace(" ", "")
|
|
194
|
+
return {
|
|
195
|
+
"stronglyconvex": Convexity.STRONGLY_CONVEX,
|
|
196
|
+
"strictlyconvex": Convexity.STRICTLY_CONVEX,
|
|
197
|
+
"convex": Convexity.CONVEX,
|
|
198
|
+
"affine": Convexity.AFFINE,
|
|
199
|
+
"strictlyconcave": Convexity.STRICTLY_CONCAVE,
|
|
200
|
+
"concave": Convexity.CONCAVE,
|
|
201
|
+
"stronglyconcave": Convexity.STRONGLY_CONCAVE,
|
|
202
|
+
"neither": Convexity.NEITHER,
|
|
203
|
+
}.get(key, Convexity.UNKNOWN)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def normalize_monotonicity(value):
|
|
207
|
+
"""Translate semialg monotonicity vocabulary into funcprops' enum."""
|
|
208
|
+
from .results import Monotonicity
|
|
209
|
+
|
|
210
|
+
classification = getattr(value, "classification", value)
|
|
211
|
+
return {
|
|
212
|
+
"strictly_increasing": Monotonicity.STRICTLY_INCREASING,
|
|
213
|
+
"increasing": Monotonicity.NONDECREASING,
|
|
214
|
+
"constant": Monotonicity.CONSTANT,
|
|
215
|
+
"decreasing": Monotonicity.NONINCREASING,
|
|
216
|
+
"strictly_decreasing": Monotonicity.STRICTLY_DECREASING,
|
|
217
|
+
"nonmonotonic": Monotonicity.NOT_MONOTONE,
|
|
218
|
+
}.get(str(classification), Monotonicity.UNKNOWN)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def normalize_truth(value):
|
|
222
|
+
"""Translate a backend boolean into funcprops' three-valued truth type."""
|
|
223
|
+
from .results import TruthValue
|
|
224
|
+
|
|
225
|
+
if value is True:
|
|
226
|
+
return TruthValue.TRUE
|
|
227
|
+
if value is False:
|
|
228
|
+
return TruthValue.FALSE
|
|
229
|
+
return TruthValue.UNKNOWN
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def prove_sign(expr, variable, relation, *, assumptions=True):
|
|
233
|
+
"""Prove one exact sign relation with semialg's public sign provers."""
|
|
234
|
+
prover_names = {
|
|
235
|
+
"positive": "prove_positive",
|
|
236
|
+
"negative": "prove_negative",
|
|
237
|
+
"nonnegative": "prove_nonnegative",
|
|
238
|
+
"nonpositive": "prove_nonpositive",
|
|
239
|
+
}
|
|
240
|
+
try:
|
|
241
|
+
prover_name = prover_names[relation]
|
|
242
|
+
except KeyError as exc:
|
|
243
|
+
raise ValueError(f"unknown sign relation: {relation!r}") from exc
|
|
244
|
+
|
|
245
|
+
prover = _public_function(prover_name)
|
|
246
|
+
return prover(sp.sympify(expr), [variable], assumptions=assumptions)
|
funcprops/api.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Public routing layer for function-property queries.
|
|
2
|
+
|
|
3
|
+
Each public query has a single implementation in either :mod:`funcprops.singularities`
|
|
4
|
+
or :mod:`funcprops.real_analysis`. This module only re-exports those implementations
|
|
5
|
+
so callers have one stable import surface without duplicating property logic.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from .real_analysis.boundedness import function_boundedness
|
|
11
|
+
from .real_analysis.convexity import function_convexity
|
|
12
|
+
from .real_analysis.differentiability import function_differentiability
|
|
13
|
+
from .real_analysis.domain import function_domain
|
|
14
|
+
from .real_analysis.mapping import (
|
|
15
|
+
function_bijective,
|
|
16
|
+
function_injective,
|
|
17
|
+
function_surjective,
|
|
18
|
+
)
|
|
19
|
+
from .real_analysis.monotonicity import function_monotonicity
|
|
20
|
+
from .real_analysis.oscillation import function_oscillation
|
|
21
|
+
from .real_analysis.range import function_range
|
|
22
|
+
from .real_analysis.sign import function_sign
|
|
23
|
+
from .real_analysis.smoothness import function_smoothness
|
|
24
|
+
from .real_analysis.structural_properties import (
|
|
25
|
+
function_homogeneity,
|
|
26
|
+
function_periodicity,
|
|
27
|
+
function_symmetry,
|
|
28
|
+
)
|
|
29
|
+
from .real_analysis.zeros import function_zeros
|
|
30
|
+
from .singularities import (
|
|
31
|
+
function_analytic,
|
|
32
|
+
function_branchcuts,
|
|
33
|
+
function_branchpoints,
|
|
34
|
+
function_continuous,
|
|
35
|
+
function_discontinuities,
|
|
36
|
+
function_meromorphic,
|
|
37
|
+
function_poles,
|
|
38
|
+
function_singularities,
|
|
39
|
+
function_singularity_analysis,
|
|
40
|
+
)
|
|
41
|
+
from .transcendental.complex_roots import function_complex_zeros
|
|
42
|
+
|
|
43
|
+
__all__ = [
|
|
44
|
+
"function_analytic",
|
|
45
|
+
"function_bijective",
|
|
46
|
+
"function_boundedness",
|
|
47
|
+
"function_branchcuts",
|
|
48
|
+
"function_branchpoints",
|
|
49
|
+
"function_complex_zeros",
|
|
50
|
+
"function_continuous",
|
|
51
|
+
"function_convexity",
|
|
52
|
+
"function_differentiability",
|
|
53
|
+
"function_discontinuities",
|
|
54
|
+
"function_domain",
|
|
55
|
+
"function_homogeneity",
|
|
56
|
+
"function_injective",
|
|
57
|
+
"function_meromorphic",
|
|
58
|
+
"function_monotonicity",
|
|
59
|
+
"function_oscillation",
|
|
60
|
+
"function_periodicity",
|
|
61
|
+
"function_poles",
|
|
62
|
+
"function_range",
|
|
63
|
+
"function_sign",
|
|
64
|
+
"function_singularities",
|
|
65
|
+
"function_singularity_analysis",
|
|
66
|
+
"function_smoothness",
|
|
67
|
+
"function_surjective",
|
|
68
|
+
"function_symmetry",
|
|
69
|
+
"function_zeros",
|
|
70
|
+
]
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""Symbolic principal-branch cuts, boundary jumps, and restrictions.
|
|
2
|
+
|
|
3
|
+
The low-level API computes cut conditions and jumps. The property-level
|
|
4
|
+
:func:`funcprops.function_branchcuts` query converts those loci into the common
|
|
5
|
+
funcprops singularity vocabulary.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from ..engine import simplify_condition
|
|
9
|
+
from .computation import (
|
|
10
|
+
branch_cut_jumps,
|
|
11
|
+
branch_cut_jumps_pairs,
|
|
12
|
+
condition_false_q,
|
|
13
|
+
condition_simplify,
|
|
14
|
+
differentiate_pairs,
|
|
15
|
+
jump_pairs_from_table,
|
|
16
|
+
numeric_branch_cut_jumps,
|
|
17
|
+
unsatisfiable_q,
|
|
18
|
+
)
|
|
19
|
+
from .core import (
|
|
20
|
+
IMPLEMENTED_RULE_NAMES,
|
|
21
|
+
BranchRule,
|
|
22
|
+
ConditionalJump,
|
|
23
|
+
JumpTerm,
|
|
24
|
+
RestrictedExpr,
|
|
25
|
+
Undefined,
|
|
26
|
+
apply_restrictions,
|
|
27
|
+
branch_jump,
|
|
28
|
+
get_branch_rule,
|
|
29
|
+
propagate_undefined,
|
|
30
|
+
restrict,
|
|
31
|
+
simplify_restricted,
|
|
32
|
+
)
|
|
33
|
+
from .post_simplify import (
|
|
34
|
+
merge_duplicate_pairs,
|
|
35
|
+
post_simplify_pairs,
|
|
36
|
+
post_simplify_restricted,
|
|
37
|
+
rewrite_branch_expr,
|
|
38
|
+
simplify_branch_condition,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def branch_cut_conditions(expr, variable, *, assumptions=True):
|
|
43
|
+
"""Return unique symbolic principal-cut conditions for ``expr``.
|
|
44
|
+
|
|
45
|
+
This property query is intentionally cheaper than full jump propagation.
|
|
46
|
+
It pulls primitive cut rules back through arguments and recursively includes
|
|
47
|
+
inner cuts, but does not form plus/minus boundary-value products.
|
|
48
|
+
"""
|
|
49
|
+
from sympy import Dummy, S, sympify
|
|
50
|
+
|
|
51
|
+
expr = sympify(expr)
|
|
52
|
+
variable = sympify(variable)
|
|
53
|
+
out = []
|
|
54
|
+
seen = set()
|
|
55
|
+
|
|
56
|
+
def add(cond):
|
|
57
|
+
cond = simplify_condition(sympify(cond), assumptions=assumptions)
|
|
58
|
+
if cond is S.false:
|
|
59
|
+
return
|
|
60
|
+
key = repr(cond)
|
|
61
|
+
if key not in seen:
|
|
62
|
+
seen.add(key)
|
|
63
|
+
out.append(cond)
|
|
64
|
+
|
|
65
|
+
def visit(e):
|
|
66
|
+
e = sympify(e)
|
|
67
|
+
if not e.has(variable):
|
|
68
|
+
return
|
|
69
|
+
# Exact primitive rule first.
|
|
70
|
+
try:
|
|
71
|
+
rule = get_branch_rule(e, variable)
|
|
72
|
+
except (NotImplementedError, ValueError, TypeError, AttributeError):
|
|
73
|
+
rule = None
|
|
74
|
+
if rule is not None:
|
|
75
|
+
for term in rule.jump_terms:
|
|
76
|
+
add(term.cond)
|
|
77
|
+
else:
|
|
78
|
+
# Pull a one-variable primitive rule back through each varying argument.
|
|
79
|
+
for i, arg in enumerate(getattr(e, "args", ())):
|
|
80
|
+
if not getattr(arg, "has", lambda *_: False)(variable):
|
|
81
|
+
continue
|
|
82
|
+
u = Dummy("funcprops_branch_arg")
|
|
83
|
+
args = list(e.args)
|
|
84
|
+
args[i] = u
|
|
85
|
+
try:
|
|
86
|
+
template = e.func(*args)
|
|
87
|
+
rule = get_branch_rule(template, u)
|
|
88
|
+
except (NotImplementedError, ValueError, TypeError, AttributeError):
|
|
89
|
+
continue
|
|
90
|
+
for term in rule.jump_terms:
|
|
91
|
+
add(term.cond.xreplace({u: arg}))
|
|
92
|
+
# Branches inside arguments remain branches of the composite expression.
|
|
93
|
+
for arg in getattr(e, "args", ()):
|
|
94
|
+
if getattr(arg, "has", lambda *_: False)(variable):
|
|
95
|
+
visit(arg)
|
|
96
|
+
|
|
97
|
+
visit(expr)
|
|
98
|
+
return tuple(out)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def coverage_summary():
|
|
102
|
+
"""Return BranchCuts-rule coverage against the funcprops catalogue."""
|
|
103
|
+
from ..registry import registry
|
|
104
|
+
|
|
105
|
+
families = list(registry())
|
|
106
|
+
return {
|
|
107
|
+
"implemented_rules": {"total": len(IMPLEMENTED_RULE_NAMES)},
|
|
108
|
+
"sympy": {"total": sum(bool(f.sympy_names) for f in families)},
|
|
109
|
+
"mpmath": {"total": sum(bool(f.mpmath_names) for f in families)},
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
__all__ = [
|
|
114
|
+
"IMPLEMENTED_RULE_NAMES",
|
|
115
|
+
"BranchRule",
|
|
116
|
+
"ConditionalJump",
|
|
117
|
+
"JumpTerm",
|
|
118
|
+
"RestrictedExpr",
|
|
119
|
+
"Undefined",
|
|
120
|
+
"apply_restrictions",
|
|
121
|
+
"branch_cut_conditions",
|
|
122
|
+
"branch_cut_jumps",
|
|
123
|
+
"branch_cut_jumps_pairs",
|
|
124
|
+
"branch_jump",
|
|
125
|
+
"condition_false_q",
|
|
126
|
+
"condition_simplify",
|
|
127
|
+
"coverage_summary",
|
|
128
|
+
"differentiate_pairs",
|
|
129
|
+
"get_branch_rule",
|
|
130
|
+
"jump_pairs_from_table",
|
|
131
|
+
"merge_duplicate_pairs",
|
|
132
|
+
"numeric_branch_cut_jumps",
|
|
133
|
+
"post_simplify_pairs",
|
|
134
|
+
"post_simplify_restricted",
|
|
135
|
+
"propagate_undefined",
|
|
136
|
+
"restrict",
|
|
137
|
+
"rewrite_branch_expr",
|
|
138
|
+
"simplify_branch_condition",
|
|
139
|
+
"simplify_restricted",
|
|
140
|
+
"unsatisfiable_q",
|
|
141
|
+
]
|