geometric-function-atlas 0.1.1__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.
@@ -0,0 +1,46 @@
1
+ """Public API for reproducible Geometric Function Atlas computations."""
2
+
3
+ from .catalog import get_generator, list_generators
4
+ from .coefficients import GeneratorSeriesResult, generator_series, taylor_coefficients
5
+ from .contracts import ( # noqa: F401
6
+ RESULT_SCHEMA_VERSION,
7
+ CheckStatus,
8
+ CorruptArtifactError,
9
+ FailureState,
10
+ InvalidInputError,
11
+ LiteratureStatus,
12
+ ResourceLimitError,
13
+ UnresolvedError,
14
+ UnsupportedError,
15
+ VerificationCheck,
16
+ VerificationReport,
17
+ failure_payload,
18
+ load_error_schema,
19
+ load_result_schema,
20
+ validate_error_payload,
21
+ validate_result_payload,
22
+ )
23
+ from .counterexamples import CounterexampleResult, verify_counterexample
24
+ from .fekete_szego import FeketeSzegoResult, fekete_szego
25
+ from .implementation_registry import ( # noqa: F401
26
+ get_trusted_implementation,
27
+ list_trusted_implementations,
28
+ )
29
+ from .models import Generator
30
+ from .models import Z as z
31
+ from .version import __version__
32
+
33
+ __all__ = [
34
+ "CounterexampleResult",
35
+ "FeketeSzegoResult",
36
+ "Generator",
37
+ "GeneratorSeriesResult",
38
+ "__version__",
39
+ "fekete_szego",
40
+ "generator_series",
41
+ "get_generator",
42
+ "list_generators",
43
+ "taylor_coefficients",
44
+ "verify_counterexample",
45
+ "z",
46
+ ]
@@ -0,0 +1,5 @@
1
+ """Support ``python -m geometric_function_atlas``."""
2
+
3
+ from .cli import main
4
+
5
+ raise SystemExit(main())
@@ -0,0 +1,98 @@
1
+ """Curated Ma–Minda generators used by the published radius portfolio."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sympy as sp
6
+
7
+ from .models import Generator, Z
8
+ from .version import GENERATOR_CATALOG_VERSION
9
+
10
+ _GENERATORS = (
11
+ Generator(
12
+ key="starlike",
13
+ name="Classical starlike class",
14
+ expression=(1 + Z) / (1 - Z),
15
+ citation="Classical starlike class; Janowski (1973) with A=1, B=-1",
16
+ ),
17
+ Generator(
18
+ key="bell",
19
+ name="Bell-number starlike class",
20
+ expression=sp.exp(sp.exp(Z) - 1),
21
+ citation="Cho, Kumar, Kumar & Ravichandran (2019)",
22
+ ),
23
+ Generator(
24
+ key="cosh_sqrt",
25
+ name="Cosh-square-root starlike class",
26
+ expression=sp.cosh(sp.sqrt(Z)),
27
+ citation="Mundalia & Swaminathan (2023)",
28
+ ),
29
+ Generator(
30
+ key="crescent",
31
+ name="Crescent (lune) starlike class",
32
+ expression=Z + sp.sqrt(1 + Z**2),
33
+ citation="Raina & Sokół (2015)",
34
+ ),
35
+ Generator(
36
+ key="exponential",
37
+ name="Exponential starlike class",
38
+ expression=sp.exp(Z),
39
+ citation="Mendiratta, Nagpal & Ravichandran (2015)",
40
+ ),
41
+ Generator(
42
+ key="lemniscate",
43
+ name="Lemniscate starlike class",
44
+ expression=sp.sqrt(1 + Z),
45
+ citation="Sokół & Stankiewicz (1996)",
46
+ ),
47
+ Generator(
48
+ key="rational_kr",
49
+ name="Kumar–Ravichandran rational starlike class",
50
+ expression=(
51
+ 1
52
+ + Z
53
+ * ((1 + sp.sqrt(2)) + Z)
54
+ / ((1 + sp.sqrt(2)) * ((1 + sp.sqrt(2)) - Z))
55
+ ),
56
+ citation="Kumar & Ravichandran (2016)",
57
+ ),
58
+ Generator(
59
+ key="sigmoid",
60
+ name="Modified-sigmoid starlike class",
61
+ expression=2 / (1 + sp.exp(-Z)),
62
+ citation="Goel & Kumar (2020)",
63
+ ),
64
+ Generator(
65
+ key="sine",
66
+ name="Sine starlike class",
67
+ expression=1 + sp.sin(Z),
68
+ citation="Cho, Kumar, Kumar & Ravichandran (2019)",
69
+ ),
70
+ )
71
+
72
+ _BY_KEY = {generator.key: generator for generator in _GENERATORS}
73
+
74
+
75
+ def list_generators() -> tuple[Generator, ...]:
76
+ """Return the built-in generators in stable key order."""
77
+
78
+ return tuple(sorted(_GENERATORS, key=lambda generator: generator.key))
79
+
80
+
81
+ def get_generator(key: str) -> Generator:
82
+ """Return a built-in generator or fail with a discoverable error."""
83
+
84
+ try:
85
+ return _BY_KEY[key]
86
+ except KeyError as exc:
87
+ available = ", ".join(sorted(_BY_KEY))
88
+ raise KeyError(f"unknown generator {key!r}; available: {available}") from exc
89
+
90
+
91
+ def generator_artifact_version(generator: Generator) -> str:
92
+ """Return the catalog version or identify a caller-supplied definition."""
93
+
94
+ return (
95
+ GENERATOR_CATALOG_VERSION
96
+ if _BY_KEY.get(generator.key) is generator
97
+ else "user-supplied"
98
+ )
@@ -0,0 +1,282 @@
1
+ """Command-line interface for independent reproduction workflows."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import contextlib
7
+ import io
8
+ import json
9
+ import sys
10
+ from collections.abc import Sequence
11
+ from typing import Any
12
+
13
+ from .catalog import get_generator, list_generators
14
+ from .coefficients import generator_series
15
+ from .contracts import (
16
+ CorruptArtifactError,
17
+ FailureState,
18
+ InvalidInputError,
19
+ ResourceLimitError,
20
+ UnresolvedError,
21
+ UnsupportedError,
22
+ failure_payload,
23
+ validate_error_payload,
24
+ )
25
+ from .counterexamples import verify_counterexample
26
+ from .fekete_szego import fekete_szego
27
+ from .version import __version__
28
+
29
+ EXIT_SUCCESS = 0
30
+ EXIT_INVALID_INPUT = 2
31
+ EXIT_UNSUPPORTED = 3
32
+ EXIT_UNRESOLVED = 4
33
+ EXIT_RESOURCE_LIMIT = 5
34
+ EXIT_CORRUPT_ARTIFACT = 6
35
+
36
+ EXIT_CODES = {
37
+ FailureState.INVALID_INPUT.value: EXIT_INVALID_INPUT,
38
+ FailureState.UNSUPPORTED.value: EXIT_UNSUPPORTED,
39
+ FailureState.UNRESOLVED.value: EXIT_UNRESOLVED,
40
+ FailureState.RESOURCE_LIMIT.value: EXIT_RESOURCE_LIMIT,
41
+ FailureState.CORRUPT_ARTIFACT.value: EXIT_CORRUPT_ARTIFACT,
42
+ }
43
+
44
+
45
+ def _write(payload: Any, *, as_json: bool) -> None:
46
+ if as_json:
47
+ _write_utf8(
48
+ json.dumps(
49
+ payload,
50
+ indent=2,
51
+ ensure_ascii=False,
52
+ allow_nan=False,
53
+ sort_keys=True,
54
+ )
55
+ )
56
+ elif isinstance(payload, list):
57
+ for row in payload:
58
+ print(f"{row['key']}: {row['formula']} — {row['citation']}")
59
+ else:
60
+ for key, value in payload.items():
61
+ print(f"{key}: {value}")
62
+
63
+
64
+ def _write_utf8(text: str, *, stream: Any = None) -> None:
65
+ """Write JSON as UTF-8 even when the console locale is not UTF-8."""
66
+
67
+ target = sys.stdout if stream is None else stream
68
+ buffer = getattr(target, "buffer", None)
69
+ if buffer is not None:
70
+ buffer.write(text.encode("utf-8") + b"\n")
71
+ buffer.flush()
72
+ else:
73
+ target.write(f"{text}\n")
74
+
75
+
76
+ def _generators(args: argparse.Namespace) -> None:
77
+ payload = [
78
+ {
79
+ "key": generator.key,
80
+ "name": generator.name,
81
+ "formula": generator.formula,
82
+ "citation": generator.citation,
83
+ "reference_url": generator.reference_url,
84
+ }
85
+ for generator in list_generators()
86
+ ]
87
+ _write(payload, as_json=args.json)
88
+
89
+
90
+ def _coefficients(args: argparse.Namespace) -> None:
91
+ result = generator_series(get_generator(args.generator), order=args.order)
92
+ _write(result.to_dict(), as_json=args.json)
93
+
94
+
95
+ def _fekete_szego(args: argparse.Namespace) -> None:
96
+ result = fekete_szego(args.generator, mu=args.mu)
97
+ _write(result.to_dict(precision=args.precision), as_json=args.json)
98
+
99
+
100
+ def _comma_separated_numbers(value: str, *, label: str) -> list[float]:
101
+ if len(value) > 4096:
102
+ raise ResourceLimitError(f"{label} input is too long")
103
+ parts = [part.strip() for part in value.split(",")]
104
+ if not parts or any(not part for part in parts):
105
+ raise InvalidInputError(f"{label} must be comma-separated real numbers")
106
+ try:
107
+ return [float(part) for part in parts]
108
+ except ValueError as exc:
109
+ raise InvalidInputError(
110
+ f"{label} must be comma-separated real numbers"
111
+ ) from exc
112
+
113
+
114
+ def _verify_counterexample(args: argparse.Namespace) -> None:
115
+ coefficients = _comma_separated_numbers(
116
+ args.coefficients, label="coefficients"
117
+ )
118
+ point = _comma_separated_numbers(args.point, label="point")
119
+ if len(point) != 2:
120
+ raise InvalidInputError("point must have the form real,imaginary")
121
+ result = verify_counterexample(
122
+ coefficients,
123
+ point=(point[0], point[1]),
124
+ property=args.property,
125
+ )
126
+ if args.json:
127
+ _write(result.to_dict(), as_json=True)
128
+ return
129
+
130
+ if result.certified and result.property == "starlike":
131
+ verdict = "CERTIFIED COUNTEREXAMPLE"
132
+ elif result.certified:
133
+ verdict = "CERTIFIED CRITERION VIOLATION"
134
+ elif result.property == "starlike":
135
+ verdict = "NO COUNTEREXAMPLE CERTIFIED AT THIS POINT"
136
+ else:
137
+ verdict = "NO CRITERION VIOLATION CERTIFIED AT THIS POINT"
138
+ criterion_names = {
139
+ "starlike": "starlikeness",
140
+ "becker_univalent": "Becker criterion",
141
+ "nehari_univalent": "Nehari criterion",
142
+ }
143
+ real, imaginary = result.point
144
+ sign = "+" if imaginary >= 0 else "-"
145
+ comparison = "<=" if result.property == "starlike" else ">"
146
+ lines = [
147
+ verdict,
148
+ f"Criterion: {criterion_names[result.property]}",
149
+ f"Witness: z = {real:g} {sign} {abs(imaginary):g}i",
150
+ (
151
+ "Certified value: "
152
+ f"[{result.interval_lower:g}, {result.interval_upper:g}]"
153
+ ),
154
+ f"Counterexample condition: value {comparison} {result.threshold:g}",
155
+ ]
156
+ _write_utf8("\n".join(lines))
157
+
158
+
159
+ def _parser() -> argparse.ArgumentParser:
160
+ parser = argparse.ArgumentParser(
161
+ prog="geometric-function-atlas",
162
+ description="Reproduce and review Geometric Function Atlas computations.",
163
+ )
164
+ parser.add_argument(
165
+ "--version",
166
+ action="version",
167
+ version=f"%(prog)s {__version__}",
168
+ )
169
+ subparsers = parser.add_subparsers(dest="command", required=True)
170
+
171
+ generators = subparsers.add_parser("generators", help="list built-in generators")
172
+ generators.add_argument("--json", action="store_true", help="emit JSON")
173
+ generators.set_defaults(handler=_generators)
174
+
175
+ coefficients = subparsers.add_parser(
176
+ "coefficients", help="compute exact generator Taylor coefficients"
177
+ )
178
+ coefficients.add_argument("generator")
179
+ coefficients.add_argument("--order", type=int, required=True)
180
+ coefficients.add_argument("--json", action="store_true", help="emit JSON")
181
+ coefficients.set_defaults(handler=_coefficients)
182
+
183
+ fs = subparsers.add_parser(
184
+ "fekete-szego", help="compute an exact Ma-Minda Fekete-Szego constant"
185
+ )
186
+ fs.add_argument("generator")
187
+ fs.add_argument("--mu", required=True, help="real rational value, e.g. 1/2")
188
+ fs.add_argument("--precision", type=int, default=16)
189
+ fs.add_argument("--json", action="store_true", help="emit JSON")
190
+ fs.set_defaults(handler=_fekete_szego)
191
+
192
+ counterexample = subparsers.add_parser(
193
+ "verify-counterexample",
194
+ help="rigorously check a supplied counterexample witness",
195
+ )
196
+ counterexample.add_argument(
197
+ "--coefficients",
198
+ required=True,
199
+ help="comma-separated a2,a3,... for f(z)=z+a2*z^2+...",
200
+ )
201
+ counterexample.add_argument(
202
+ "--point",
203
+ required=True,
204
+ help="witness point as real,imaginary; use --point=-0.75,0 for negatives",
205
+ )
206
+ counterexample.add_argument("--property", default="starlike")
207
+ counterexample.add_argument("--json", action="store_true", help="emit JSON")
208
+ counterexample.set_defaults(handler=_verify_counterexample)
209
+
210
+ return parser
211
+
212
+
213
+ def main(argv: Sequence[str] | None = None) -> int:
214
+ parser = _parser()
215
+ command_line = list(sys.argv[1:] if argv is None else argv)
216
+ wants_json = "--json" in command_line
217
+ if wants_json:
218
+ parse_errors = io.StringIO()
219
+ try:
220
+ with contextlib.redirect_stderr(parse_errors):
221
+ args = parser.parse_args(command_line)
222
+ except SystemExit as exc:
223
+ if exc.code == 0:
224
+ raise
225
+ payload = failure_payload(
226
+ FailureState.INVALID_INPUT,
227
+ parse_errors.getvalue().strip() or "invalid command-line arguments",
228
+ )
229
+ validate_error_payload(payload)
230
+ _write_utf8(
231
+ json.dumps(
232
+ payload,
233
+ indent=2,
234
+ ensure_ascii=False,
235
+ allow_nan=False,
236
+ sort_keys=True,
237
+ )
238
+ )
239
+ return EXIT_INVALID_INPUT
240
+ else:
241
+ args = parser.parse_args(command_line)
242
+ try:
243
+ args.handler(args)
244
+ except (
245
+ CorruptArtifactError,
246
+ UnresolvedError,
247
+ UnsupportedError,
248
+ ResourceLimitError,
249
+ InvalidInputError,
250
+ KeyError,
251
+ TypeError,
252
+ ValueError,
253
+ ) as exc:
254
+ state = _failure_state(exc)
255
+ if getattr(args, "json", False):
256
+ payload = failure_payload(state, str(exc))
257
+ validate_error_payload(payload)
258
+ _write_utf8(
259
+ json.dumps(
260
+ payload,
261
+ indent=2,
262
+ ensure_ascii=False,
263
+ allow_nan=False,
264
+ sort_keys=True,
265
+ )
266
+ )
267
+ return EXIT_CODES[state]
268
+ print(f"{parser.prog}: error: {exc}", file=sys.stderr)
269
+ return EXIT_CODES[state]
270
+ return EXIT_SUCCESS
271
+
272
+
273
+ def _failure_state(exc: Exception) -> str:
274
+ if isinstance(exc, CorruptArtifactError):
275
+ return FailureState.CORRUPT_ARTIFACT.value
276
+ if isinstance(exc, UnresolvedError):
277
+ return FailureState.UNRESOLVED.value
278
+ if isinstance(exc, UnsupportedError):
279
+ return FailureState.UNSUPPORTED.value
280
+ if isinstance(exc, ResourceLimitError):
281
+ return FailureState.RESOURCE_LIMIT.value
282
+ return FailureState.INVALID_INPUT.value
@@ -0,0 +1,206 @@
1
+ """Exact coefficient operations for Ma–Minda generators."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from typing import Any
7
+
8
+ import sympy as sp
9
+
10
+ from .catalog import generator_artifact_version, get_generator
11
+ from .contracts import (
12
+ CheckStatus,
13
+ FailureState,
14
+ InvalidInputError,
15
+ ResourceLimitError,
16
+ VerificationCheck,
17
+ VerificationReport,
18
+ build_result_payload,
19
+ )
20
+ from .models import Generator, canonical_expression_dag, validate_exact_expression
21
+ from .version import GENERATOR_FIXTURE_ID, SOURCE_ARTIFACT_COMMIT
22
+
23
+ MAX_TAYLOR_ORDER = 64
24
+
25
+
26
+ @dataclass(frozen=True, slots=True)
27
+ class GeneratorSeriesResult:
28
+ """Exact generator coefficients with reproducibility metadata."""
29
+
30
+ generator: Generator
31
+ order: int
32
+ coefficients: tuple[sp.Expr, ...]
33
+ method: str = "exact_symbolic_taylor_series"
34
+
35
+ def __post_init__(self) -> None:
36
+ if isinstance(self.order, bool) or not isinstance(self.order, int):
37
+ raise TypeError("order must be a positive integer")
38
+ if self.order < 1:
39
+ raise InvalidInputError("order must be at least 1")
40
+ if self.order > MAX_TAYLOR_ORDER:
41
+ raise ResourceLimitError(f"order must be at most {MAX_TAYLOR_ORDER}")
42
+ if not isinstance(self.generator, Generator):
43
+ raise TypeError("generator must be a Generator")
44
+ if self.method != "exact_symbolic_taylor_series":
45
+ raise ValueError("method is a closed package-owned value")
46
+ normalized: list[sp.Expr] = []
47
+ for value in self.coefficients:
48
+ if isinstance(value, (bool, float)):
49
+ raise TypeError("coefficients must be exact symbolic values")
50
+ exact = sp.Integer(value) if isinstance(value, int) else value
51
+ if not isinstance(exact, sp.Expr):
52
+ raise TypeError("coefficients must be exact symbolic values")
53
+ validate_exact_expression(exact)
54
+ normalized.append(exact)
55
+ if len(normalized) != self.order:
56
+ raise ValueError("coefficients length must equal order")
57
+ object.__setattr__(self, "coefficients", tuple(normalized))
58
+
59
+ @property
60
+ def evidence_status(self) -> str:
61
+ """Return the package-derived computational evidence state."""
62
+
63
+ if not self.verification_report.success:
64
+ return FailureState.UNRESOLVED.value
65
+ return "proven_exact_under_declared_assumptions"
66
+
67
+ @property
68
+ def verification_report(self) -> VerificationReport:
69
+ normalized = sp.simplify(
70
+ self.generator.expression.subs(self.generator.variable, 0)
71
+ )
72
+ expected: Any
73
+ try:
74
+ expected_coefficients = taylor_coefficients(
75
+ self.generator, order=self.order
76
+ )
77
+ coefficients_ok = expected_coefficients == self.coefficients
78
+ expected = [str(value) for value in expected_coefficients]
79
+ reason = "stored coefficients do not match the exact expansion"
80
+ except (TypeError, ValueError):
81
+ expected = "unavailable (invalid or over-limit order)"
82
+ coefficients_ok = False
83
+ reason = "exact expansion could not be recomputed"
84
+ return VerificationReport(
85
+ checks=(
86
+ VerificationCheck(
87
+ name="generator_normalization",
88
+ checked="generator value at z = 0",
89
+ expected="1",
90
+ observed=str(normalized),
91
+ status=CheckStatus.PASS if normalized == 1 else CheckStatus.FAIL,
92
+ scope="generator definition",
93
+ failure_reason=(
94
+ None
95
+ if normalized == 1
96
+ else "generator is not normalized at the origin"
97
+ ),
98
+ ),
99
+ VerificationCheck(
100
+ name="exact_taylor_coefficients",
101
+ checked=f"Taylor coefficients through order {self.order}",
102
+ expected=expected,
103
+ observed=[str(value) for value in self.coefficients],
104
+ status=(
105
+ CheckStatus.PASS if coefficients_ok else CheckStatus.FAIL
106
+ ),
107
+ scope="exact symbolic expansion",
108
+ failure_reason=None if coefficients_ok else reason,
109
+ ),
110
+ )
111
+ )
112
+
113
+ def to_dict(self) -> dict[str, Any]:
114
+ return build_result_payload(
115
+ result_type="generator_series",
116
+ canonical_inputs={"generator": self.generator.key, "order": self.order},
117
+ exact_expressions={
118
+ "generator": self.generator.formula,
119
+ "coefficients": [str(value) for value in self.coefficients],
120
+ },
121
+ method=self.method,
122
+ evidence_status=self.evidence_status,
123
+ assumptions=("phi(0) = 1", "expression uses only the variable z"),
124
+ source_references=(self.generator.citation,),
125
+ artifact_versions={
126
+ "generator_catalog": generator_artifact_version(self.generator),
127
+ "source_commit": SOURCE_ARTIFACT_COMMIT
128
+ if generator_artifact_version(self.generator) != "user-supplied"
129
+ else "caller-supplied",
130
+ "fixture_or_proof": GENERATOR_FIXTURE_ID,
131
+ },
132
+ verification=self.verification_report,
133
+ exact_expression_dag=canonical_expression_dag(
134
+ {
135
+ "generator": self.generator.expression,
136
+ "coefficients": self.coefficients,
137
+ }
138
+ ),
139
+ provenance=(
140
+ "caller_supplied"
141
+ if generator_artifact_version(self.generator) == "user-supplied"
142
+ else "built_in"
143
+ ),
144
+ legacy_fields={
145
+ "generator": self.generator.key,
146
+ "generator_formula": self.generator.formula,
147
+ "generator_citation": self.generator.citation,
148
+ "order": self.order,
149
+ "coefficients": [str(value) for value in self.coefficients],
150
+ },
151
+ )
152
+
153
+
154
+ def _resolve_generator(generator: str | Generator) -> Generator:
155
+ if isinstance(generator, str):
156
+ return get_generator(generator)
157
+ if not isinstance(generator, Generator):
158
+ raise TypeError("generator must be a built-in key or Generator")
159
+ return generator
160
+
161
+
162
+ def taylor_coefficients(
163
+ generator: str | Generator,
164
+ *,
165
+ order: int,
166
+ ) -> tuple[sp.Expr, ...]:
167
+ """Return exact ``B_1, …, B_order`` for ``phi(z) = 1 + Σ B_n z^n``.
168
+
169
+ Custom :class:`Generator` objects are accepted so reproductions are not
170
+ limited to the built-in catalog. The normalization ``phi(0) = 1`` is checked
171
+ exactly before coefficients are returned.
172
+ """
173
+
174
+ if isinstance(order, bool) or not isinstance(order, int):
175
+ raise TypeError("order must be a positive integer")
176
+ if order < 1:
177
+ raise InvalidInputError("order must be at least 1")
178
+ if order > MAX_TAYLOR_ORDER:
179
+ raise ResourceLimitError(f"order must be at most {MAX_TAYLOR_ORDER}")
180
+
181
+ resolved = _resolve_generator(generator)
182
+ expression = resolved.expression
183
+ variable = resolved.variable
184
+ if sp.simplify(expression.subs(variable, 0) - 1) != 0:
185
+ raise InvalidInputError(f"generator {resolved.key!r}: phi(0) must equal 1")
186
+
187
+ polynomial = sp.series(expression, variable, 0, order + 1).removeO().expand()
188
+ return tuple(
189
+ sp.simplify(polynomial.coeff(variable, degree))
190
+ for degree in range(1, order + 1)
191
+ )
192
+
193
+
194
+ def generator_series(
195
+ generator: str | Generator,
196
+ *,
197
+ order: int,
198
+ ) -> GeneratorSeriesResult:
199
+ """Return exact coefficients plus a structured provenance record."""
200
+
201
+ resolved = _resolve_generator(generator)
202
+ return GeneratorSeriesResult(
203
+ generator=resolved,
204
+ order=order,
205
+ coefficients=taylor_coefficients(resolved, order=order),
206
+ )