"""Python helpers for FuncCraft specs.
The C++ bindings expose the actual plain-data spec structs. This module adds
readable ``make_*`` factory functions and dict-to-spec conversion so Python
users can construct FuncCraft functions without knowing the C++ headers.
Terminology
-----------
``DomainSpec``
Search bounds in the generated coordinates.
``CoordinateTransformSpec``
Moves from generated/search coordinates into the component's primitive
coordinates. ``input_dimension`` is the parent/search dimension,
``output_dimension`` is the component dimension after transformation, and
``assigned_xopt`` is output-dimensional. The primitive optimum target is
resolved internally from the selected base function and domain scaling.
``ValueTransformSpec``
Optional value-shape transform applied to a component output.
``ComponentSpec``
One component in a function. A component is either a basic function such as
``"Rastrigin"`` or a nested composed function via ``composed_function``.
``CompositionSpec``
Combines component values. ``"none"`` is the identity/single-component
case, ``"cpm-..."`` choices are continuous composition methods, and
``"dpm-..."`` choices are deceptive composition methods. DPM centers and
biases live on the composition spec because they define deceptive local
traps.
``FunctionSpec``
Complete description of one benchmark function, including assigned global
optimum location/value and scale factor.
``SuiteSpec``
Sampling rules for generating many distinct function specs.
Examples
--------
Create a single 2D function and evaluate it through ``BenchmarkFunction``::
from funccraft import (
BenchmarkFunction,
make_component,
make_coordinate_transform,
make_domain,
make_function_spec,
)
spec = make_function_spec(
dimension=2,
domain=make_domain(2, -10.0, 10.0),
components=[
make_component(
base_function="Sphere",
coordinate_transform=make_coordinate_transform(
"rotation",
assigned_xopt=[1.0, -1.0],
seed=1,
),
),
],
assigned_xopt=[1.0, -1.0],
assigned_fopt=0.0,
scale_factor=1.0,
seed=10,
)
f = BenchmarkFunction(spec)
y = f([[1.0, -1.0], [0.0, 0.0]])
Create a small deceptive composition from two base functions::
spec = make_function_spec(
dimension=2,
domain=make_domain(2, -10.0, 10.0),
components=[
make_component(base_function="Rastrigin"),
make_component(base_function="Ackley"),
],
composition=make_composition(
"dpm-softmax",
parameters=[0.01],
biases=[0.0, 20.0],
),
assigned_xopt=[2.0, -3.0],
scale_factor=1.0,
seed=11,
)
Create a suite spec. Choice probabilities in each choice table are fractions
and should sum to one::
suite_spec = make_suite_spec(
requested_number_of_functions=500,
max_components=4,
master_seed=1,
compositions=[
make_composition_choice("cpm-wsum", 0.25),
make_composition_choice("cpm-power-mean", 0.25),
make_composition_choice("dpm-softmax", 0.25, [0.01]),
make_composition_choice("dpm-bgsoftmax", 0.25, [0.01, 1.0, 0.01]),
],
)
"""
from __future__ import annotations
from collections.abc import Mapping
from . import _funccraft
BasicFunctionId = _funccraft.BasicFunctionId
CoordinateTransformKind = _funccraft.CoordinateTransformKind
ValueTransformKind = _funccraft.ValueTransformKind
CompositionKind = _funccraft.CompositionKind
DomainSpec = _funccraft.DomainSpec
CoordinateTransformSpec = _funccraft.CoordinateTransformSpec
ValueTransformSpec = _funccraft.ValueTransformSpec
ComponentSpec = _funccraft.ComponentSpec
CompositionSpec = _funccraft.CompositionSpec
FunctionSpec = _funccraft.FunctionSpec
SuiteSpec = _funccraft.SuiteSpec
CoordinateTransformChoice = _funccraft.CoordinateTransformChoice
ValueTransformChoice = _funccraft.ValueTransformChoice
CompositionChoice = _funccraft.CompositionChoice
Domain = DomainSpec
TransformSpec = CoordinateTransformSpec
ChoiceSpec = CompositionChoice
[docs]
def make_domain(dimension, lower_bound=-100.0, upper_bound=100.0):
"""Create a native ``DomainSpec``.
``lower_bound`` and ``upper_bound`` may be scalars or length-``dimension``
sequences.
Examples
--------
Uniform bounds::
domain = make_domain(10, -100.0, 100.0)
Per-coordinate bounds::
domain = make_domain(2, lower_bound=[-5.0, -1.0], upper_bound=[5.0, 1.0])
"""
spec = DomainSpec()
spec.dimension = int(dimension)
if isinstance(lower_bound, (int, float)):
spec.lower_bound = [float(lower_bound)] * spec.dimension
else:
spec.lower_bound = _list(lower_bound)
if isinstance(upper_bound, (int, float)):
spec.upper_bound = [float(upper_bound)] * spec.dimension
else:
spec.upper_bound = _list(upper_bound)
return spec
[docs]
def make_component(
base_function=None,
composed_function=None,
coordinate_transform=None,
value_transform=None,
seed=0,
):
"""Create a native ``ComponentSpec``.
``base_function`` accepts a ``BasicFunctionId``, integer id, or name.
It is required only when ``composed_function`` is not set. A composed
component may be a ``FunctionSpec``, compatible dict, or object exposing
a ``spec`` attribute such as ``BenchmarkFunction``.
``coordinate_transform`` and ``value_transform`` may be native specs or
dictionaries with the same field names.
Examples
--------
Basic-function component::
component = make_component(
base_function="Rosenbrock",
coordinate_transform=make_coordinate_transform("rotation", seed=5),
value_transform=make_value_transform("none"),
)
Nested composed-function component::
child = make_function_spec(
dimension=2,
components=[make_component(base_function="Sphere")],
assigned_xopt=[0.0, 0.0],
scale_factor=1.0,
)
component = make_component(composed_function=child)
"""
spec = ComponentSpec()
if composed_function is not None:
spec.composed_function = _owned_function_spec(composed_function)
else:
if base_function is None:
raise ValueError("basic component requires base_function")
spec.base_function = basic_function_id(base_function)
spec.coordinate_transform = coordinate_transform_spec(coordinate_transform or {})
spec.value_transform = value_transform_spec(value_transform or {})
spec.seed = int(seed)
return spec
[docs]
def make_composition(kind="none", parameters=None, biases=None, centers=None):
"""Create a native ``CompositionSpec``.
Parameters
----------
kind:
``"none"`` for a single identity component, ``"cpm-wsum"``,
``"cpm-power-mean"``, ``"cpm-level-well"``, ``"dpm-softmax"``, or
``"dpm-bgsoftmax"``.
parameters:
Composition-specific numeric parameters.
biases:
DPM-only component biases used to create deceptive local traps.
centers:
DPM-only full-dimensional component centers. Users normally omit this;
exported materialized specs include resolved centers.
Examples
--------
Identity/single-component composition::
composition = make_composition("none")
DPM composition with two component biases::
composition = make_composition(
"dpm-softmax",
parameters=[0.01],
biases=[0.0, 25.0],
)
"""
spec = CompositionSpec()
kind = composition_kind(kind)
bias_values = _list(biases)
if bias_values and kind not in (CompositionKind.DpmSoftmax, CompositionKind.DpmBgSoftmax):
raise ValueError("composition biases are only valid for DPM compositions")
center_values = _matrix(centers)
if center_values and kind not in (CompositionKind.DpmSoftmax, CompositionKind.DpmBgSoftmax):
raise ValueError("composition centers are only valid for DPM compositions")
spec.kind = kind
spec.parameters = _list(parameters)
spec.biases = bias_values
spec.centers = center_values
return spec
[docs]
def make_function_spec(
dimension,
domain=None,
components=None,
composition=None,
assigned_xopt=None,
assigned_fopt=0.0,
scale_factor=None,
seed=0,
label="",
metadata=None,
):
"""Create a native ``FunctionSpec``.
``components`` may contain native ``ComponentSpec`` objects or dictionaries.
``scale_factor=None`` lets FuncCraft choose the scale internally.
Important fields
----------------
``assigned_xopt``
Desired global minimizer in generated/search coordinates. If omitted,
FuncCraft generates it from ``seed``.
``assigned_fopt``
Desired objective value at the assigned global minimizer.
``scale_factor``
Multiplicative value scale. Use ``None`` for internal estimation, or a
positive value when you want exact control.
``seed``
Function-level seed used for generated runtime details such as missing
assigned optima and transform parameters.
Examples
--------
A single shifted Sphere::
spec = make_function_spec(
dimension=3,
domain=make_domain(3, -5.0, 5.0),
components=[make_component(base_function="Sphere")],
assigned_xopt=[1.0, 2.0, 3.0],
assigned_fopt=-100.0,
scale_factor=1.0,
)
A composition with nested components can be built by passing a
``FunctionSpec`` to ``make_component(composed_function=...)``.
"""
spec = FunctionSpec()
spec.dimension = int(dimension)
spec.domain = domain_spec(domain or {})
spec.components = [component_spec(item) for item in (components or [])]
spec.composition = composition_spec(composition or {})
spec.assigned_xopt = _list(assigned_xopt)
spec.assigned_fopt = float(assigned_fopt)
spec.scale_factor = scale_factor
spec.seed = int(seed)
spec.label = str(label)
spec.metadata = _list(metadata)
return spec
[docs]
def make_composition_choice(kind, probability=1.0, parameters=None):
"""Create a weighted composition choice for ``SuiteSpec``.
Choice probabilities in the same table are interpreted as fractions and
should sum to one.
"""
spec = CompositionChoice()
spec.kind = composition_kind(kind)
spec.probability = float(probability)
spec.parameters = _list(parameters)
return spec
[docs]
def make_choice(kind, probability=1.0, parameters=None):
"""Alias for ``make_composition_choice``.
Use the explicit ``make_coordinate_transform_choice`` or
``make_value_transform_choice`` when constructing those choice tables.
"""
return make_composition_choice(kind, probability, parameters)
[docs]
def make_suite_spec(
supported_dimensions=None,
base_functions=None,
composition_base_functions=None,
coordinate_transforms=None,
value_transforms=None,
compositions=None,
min_components=None,
max_components=None,
max_nested_composition_depth=None,
nested_probability=None,
requested_number_of_functions=None,
max_number_of_functions=None,
master_seed=None,
lower_bound=None,
upper_bound=None,
assigned_fopt=None,
xopt_domain_shrink_factor=None,
suite_label=None,
):
"""Create a native ``SuiteSpec``.
Omitted fields keep the C++ defaults. Choice tables accept native choice
objects or dictionaries with ``kind``, ``probability``, and ``parameters``.
``max_nested_composition_depth=0`` means composed suite functions use only
primitive components. Larger values allow composed functions as components;
``nested_probability`` controls how often each component is nested.
Examples
--------
Generate 1,000 functions using default choices except for dimensions,
component count, and nesting::
spec = make_suite_spec(
supported_dimensions="2,5,10,20",
requested_number_of_functions=1000,
min_components=2,
max_components=5,
max_nested_composition_depth=2,
nested_probability=0.25,
master_seed=2026,
)
Replace the composition choice table explicitly. Probabilities should sum
to one::
spec = make_suite_spec(
requested_number_of_functions=200,
compositions=[
make_composition_choice("cpm-wsum", 0.4),
make_composition_choice("cpm-level-well", 0.2),
make_composition_choice("dpm-softmax", 0.4, [0.01]),
],
)
"""
spec = SuiteSpec()
if supported_dimensions is not None:
spec.supported_dimensions = str(supported_dimensions)
if base_functions is not None:
spec.base_functions = [basic_function_id(item) for item in base_functions]
if composition_base_functions is not None:
spec.composition_base_functions = [
basic_function_id(item) for item in composition_base_functions
]
if coordinate_transforms is not None:
spec.coordinate_transforms = [
coordinate_transform_choice(item) for item in coordinate_transforms
]
if value_transforms is not None:
spec.value_transforms = [value_transform_choice(item) for item in value_transforms]
if compositions is not None:
spec.compositions = [composition_choice(item) for item in compositions]
if min_components is not None:
spec.min_components = int(min_components)
if max_components is not None:
spec.max_components = int(max_components)
if max_nested_composition_depth is not None:
spec.max_nested_composition_depth = int(max_nested_composition_depth)
if nested_probability is not None:
spec.nested_probability = float(nested_probability)
if requested_number_of_functions is not None:
spec.requested_number_of_functions = int(requested_number_of_functions)
if max_number_of_functions is not None:
spec.max_number_of_functions = int(max_number_of_functions)
if master_seed is not None:
spec.master_seed = int(master_seed)
if lower_bound is not None:
spec.lower_bound = float(lower_bound)
if upper_bound is not None:
spec.upper_bound = float(upper_bound)
if assigned_fopt is not None:
spec.assigned_fopt = float(assigned_fopt)
if xopt_domain_shrink_factor is not None:
spec.xopt_domain_shrink_factor = float(xopt_domain_shrink_factor)
if suite_label is not None:
spec.suite_label = str(suite_label)
return spec
def _list(value):
return list(value) if value is not None else []
def _matrix(value):
return [_list(row) for row in (value or [])]
def _normalize_name(value):
return "".join(ch.lower() for ch in str(value) if ch not in "_- \t\r\n")
def _enum(enum_type, value, aliases=None):
if isinstance(value, enum_type):
return value
if isinstance(value, int):
for item in enum_type.__members__.values():
if item.value == value:
return item
raise ValueError(f"unknown {enum_type.__name__}: {value!r}")
normalized = _normalize_name(value)
aliases = aliases or {}
if normalized in aliases:
return aliases[normalized]
for item in enum_type.__members__.values():
if _normalize_name(item.name) == normalized:
return item
raise ValueError(f"unknown {enum_type.__name__}: {value!r}")
_NO_COORDINATE_TRANSFORM = getattr(CoordinateTransformKind, "None")
_NO_VALUE_TRANSFORM = getattr(ValueTransformKind, "None")
_NO_COMPOSITION = getattr(CompositionKind, "None")
_COORDINATE_ALIASES = {
"": _NO_COORDINATE_TRANSFORM,
"none": _NO_COORDINATE_TRANSFORM,
"identity": _NO_COORDINATE_TRANSFORM,
"rot": CoordinateTransformKind.Rotation,
"aff": CoordinateTransformKind.Affine,
"blockrot": CoordinateTransformKind.BlockRotation,
"blockrotation": CoordinateTransformKind.BlockRotation,
"brot": CoordinateTransformKind.BlockRotation,
}
_VALUE_ALIASES = {
"": _NO_VALUE_TRANSFORM,
"none": _NO_VALUE_TRANSFORM,
"identity": _NO_VALUE_TRANSFORM,
"osc": ValueTransformKind.Oscillatory,
"oscillatory": ValueTransformKind.Oscillatory,
"coszero": ValueTransformKind.CosineZero,
"cosinezero": ValueTransformKind.CosineZero,
}
_COMPOSITION_ALIASES = {
"": _NO_COMPOSITION,
"none": _NO_COMPOSITION,
"identity": _NO_COMPOSITION,
"cpm": CompositionKind.CpmWeightedSum,
"sum": CompositionKind.CpmWeightedSum,
"cpmsum": CompositionKind.CpmWeightedSum,
"cpmwsum": CompositionKind.CpmWeightedSum,
"weightedsum": CompositionKind.CpmWeightedSum,
"cpmpmean": CompositionKind.CpmPowerMean,
"cpmpowermean": CompositionKind.CpmPowerMean,
"powermean": CompositionKind.CpmPowerMean,
"cpmlwell": CompositionKind.CpmLevelWell,
"cpmlevelwell": CompositionKind.CpmLevelWell,
"levelwell": CompositionKind.CpmLevelWell,
"lwell": CompositionKind.CpmLevelWell,
"dpm": CompositionKind.DpmSoftmax,
"softmax": CompositionKind.DpmSoftmax,
"dpmsoftmax": CompositionKind.DpmSoftmax,
"dpmbg": CompositionKind.DpmBgSoftmax,
"bgsoftmax": CompositionKind.DpmBgSoftmax,
"dpmbgsoftmax": CompositionKind.DpmBgSoftmax,
}
[docs]
def basic_function_id(value):
return _enum(BasicFunctionId, value)
[docs]
def composition_kind(value):
return _enum(CompositionKind, value, _COMPOSITION_ALIASES)
[docs]
def domain_spec(data):
if isinstance(data, DomainSpec):
return data
spec = DomainSpec()
if data is None:
return spec
if hasattr(data, "dimension") and hasattr(data, "lower") and hasattr(data, "upper"):
spec.dimension = int(data.dimension)
spec.lower_bound = _list(data.lower)
spec.upper_bound = _list(data.upper)
return spec
spec.dimension = int(data.get("dimension", 0))
spec.lower_bound = _list(data.get("lower_bound", []))
spec.upper_bound = _list(data.get("upper_bound", []))
return spec
[docs]
def component_spec(data):
if isinstance(data, ComponentSpec):
return data
data = data or {}
spec = ComponentSpec()
if data.get("composed_function") is not None:
spec.composed_function = function_spec(data["composed_function"])
else:
if "base_function" not in data:
raise ValueError("basic component requires base_function")
spec.base_function = basic_function_id(data["base_function"])
spec.coordinate_transform = coordinate_transform_spec(data.get("coordinate_transform", {}))
spec.value_transform = value_transform_spec(data.get("value_transform", {}))
spec.seed = int(data.get("seed", 0))
return spec
[docs]
def composition_spec(data):
if isinstance(data, CompositionSpec):
return data
data = data or {}
spec = CompositionSpec()
kind = composition_kind(data.get("kind", _NO_COMPOSITION))
bias_values = _list(data.get("biases", []))
if bias_values and kind not in (CompositionKind.DpmSoftmax, CompositionKind.DpmBgSoftmax):
raise ValueError("composition biases are only valid for DPM compositions")
center_values = _matrix(data.get("centers", []))
if center_values and kind not in (CompositionKind.DpmSoftmax, CompositionKind.DpmBgSoftmax):
raise ValueError("composition centers are only valid for DPM compositions")
spec.kind = kind
spec.parameters = _list(data.get("parameters", []))
spec.biases = bias_values
spec.centers = center_values
return spec
[docs]
def function_spec(data):
if hasattr(data, "spec"):
return function_spec(spec_to_dict(data.spec))
if isinstance(data, FunctionSpec):
return data
if not isinstance(data, Mapping):
return data
spec = FunctionSpec()
spec.dimension = int(data.get("dimension", 0))
spec.domain = domain_spec(data.get("domain", {}))
spec.components = [component_spec(item) for item in data.get("components", [])]
spec.composition = composition_spec(data.get("composition", {}))
spec.assigned_xopt = _list(data.get("assigned_xopt", []))
spec.assigned_fopt = float(data.get("assigned_fopt", 0.0))
spec.scale_factor = data.get("scale_factor", None)
spec.seed = int(data.get("seed", 0))
spec.label = str(data.get("label", ""))
spec.metadata = _list(data.get("metadata", []))
return spec
def _owned_function_spec(data):
"""Return a FunctionSpec instance safe to store in shared_ptr fields."""
return function_spec(spec_to_dict(function_spec(data)))
[docs]
def composition_choice(data):
if isinstance(data, CompositionChoice):
return data
spec = CompositionChoice()
spec.kind = composition_kind(data.get("kind", CompositionKind.CpmWeightedSum))
spec.probability = float(data.get("probability", 1.0))
spec.parameters = _list(data.get("parameters", []))
return spec
[docs]
def suite_spec(data):
if isinstance(data, SuiteSpec):
return data
if not isinstance(data, Mapping):
return data
spec = SuiteSpec()
if "supported_dimensions" in data:
spec.supported_dimensions = str(data["supported_dimensions"])
if "base_functions" in data:
spec.base_functions = [basic_function_id(item) for item in data["base_functions"]]
if "composition_base_functions" in data:
spec.composition_base_functions = [
basic_function_id(item) for item in data["composition_base_functions"]
]
if "coordinate_transforms" in data:
spec.coordinate_transforms = [
coordinate_transform_choice(item) for item in data["coordinate_transforms"]
]
if "value_transforms" in data:
spec.value_transforms = [value_transform_choice(item) for item in data["value_transforms"]]
if "compositions" in data:
spec.compositions = [composition_choice(item) for item in data["compositions"]]
if "min_components" in data:
spec.min_components = int(data["min_components"])
if "max_components" in data:
spec.max_components = int(data["max_components"])
if "max_nested_composition_depth" in data:
spec.max_nested_composition_depth = int(data["max_nested_composition_depth"])
if "nested_probability" in data:
spec.nested_probability = float(data["nested_probability"])
if "requested_number_of_functions" in data:
spec.requested_number_of_functions = int(data["requested_number_of_functions"])
if "max_number_of_functions" in data:
spec.max_number_of_functions = int(data["max_number_of_functions"])
if "master_seed" in data:
spec.master_seed = int(data["master_seed"])
if "lower_bound" in data:
spec.lower_bound = float(data["lower_bound"])
if "upper_bound" in data:
spec.upper_bound = float(data["upper_bound"])
if "assigned_fopt" in data:
spec.assigned_fopt = float(data["assigned_fopt"])
if "xopt_domain_shrink_factor" in data:
spec.xopt_domain_shrink_factor = float(data["xopt_domain_shrink_factor"])
if "suite_label" in data:
spec.suite_label = str(data["suite_label"])
return spec
[docs]
def spec_to_dict(spec):
"""Convert a native C++ spec/choice object to a plain Python dict."""
if isinstance(spec, DomainSpec):
return {
"dimension": spec.dimension,
"lower_bound": _list(spec.lower_bound),
"upper_bound": _list(spec.upper_bound),
}
if isinstance(spec, CoordinateTransformSpec):
return {
"kind": spec.kind.name,
"input_dimension": spec.input_dimension,
"output_dimension": spec.output_dimension,
"assigned_xopt": _list(spec.assigned_xopt),
"selected_indices": _list(spec.selected_indices),
"parameters": _list(spec.parameters),
"matrix": _matrix(spec.matrix),
"seed": spec.seed,
}
if isinstance(spec, ValueTransformSpec):
return {"kind": spec.kind.name, "parameters": _list(spec.parameters)}
if isinstance(spec, ComponentSpec):
result = {
"coordinate_transform": spec_to_dict(spec.coordinate_transform),
"value_transform": spec_to_dict(spec.value_transform),
"seed": spec.seed,
}
if spec.composed_function is not None:
result["composed_function"] = spec_to_dict(spec.composed_function)
else:
if spec.base_function is None:
raise ValueError("basic component requires base_function")
result["base_function"] = spec.base_function.name
return result
if isinstance(spec, CompositionSpec):
result = {
"kind": spec.kind.name,
"parameters": _list(spec.parameters),
}
biases = _list(spec.biases)
if biases:
result["biases"] = biases
centers = _matrix(spec.centers)
if centers:
result["centers"] = centers
return result
if isinstance(spec, FunctionSpec):
return {
"dimension": spec.dimension,
"domain": spec_to_dict(spec.domain),
"components": [spec_to_dict(item) for item in spec.components],
"composition": spec_to_dict(spec.composition),
"assigned_xopt": _list(spec.assigned_xopt),
"assigned_fopt": spec.assigned_fopt,
"scale_factor": spec.scale_factor,
"seed": spec.seed,
"label": spec.label,
"metadata": _list(spec.metadata),
}
if isinstance(spec, (CoordinateTransformChoice, ValueTransformChoice, CompositionChoice)):
return {
"kind": spec.kind.name,
"probability": spec.probability,
"parameters": _list(spec.parameters),
}
if isinstance(spec, SuiteSpec):
return {
"supported_dimensions": spec.supported_dimensions,
"base_functions": [item.name for item in spec.base_functions],
"composition_base_functions": [item.name for item in spec.composition_base_functions],
"coordinate_transforms": [spec_to_dict(item) for item in spec.coordinate_transforms],
"value_transforms": [spec_to_dict(item) for item in spec.value_transforms],
"compositions": [spec_to_dict(item) for item in spec.compositions],
"min_components": spec.min_components,
"max_components": spec.max_components,
"max_nested_composition_depth": spec.max_nested_composition_depth,
"nested_probability": spec.nested_probability,
"requested_number_of_functions": spec.requested_number_of_functions,
"max_number_of_functions": spec.max_number_of_functions,
"master_seed": spec.master_seed,
"lower_bound": spec.lower_bound,
"upper_bound": spec.upper_bound,
"assigned_fopt": spec.assigned_fopt,
"xopt_domain_shrink_factor": spec.xopt_domain_shrink_factor,
"suite_label": spec.suite_label,
}
raise TypeError(f"unsupported spec object: {type(spec)!r}")
__all__ = [
"BasicFunctionId",
"ChoiceSpec",
"ComponentSpec",
"CompositionChoice",
"CompositionKind",
"CompositionSpec",
"CoordinateTransformChoice",
"CoordinateTransformKind",
"CoordinateTransformSpec",
"Domain",
"DomainSpec",
"FunctionSpec",
"SuiteSpec",
"TransformSpec",
"ValueTransformChoice",
"ValueTransformKind",
"ValueTransformSpec",
"basic_function_id",
"component_spec",
"composition_choice",
"composition_kind",
"composition_spec",
"coordinate_transform_choice",
"coordinate_transform_kind",
"coordinate_transform_spec",
"domain_spec",
"function_spec",
"make_choice",
"make_component",
"make_composition",
"make_composition_choice",
"make_coordinate_transform",
"make_coordinate_transform_choice",
"make_domain",
"make_function_spec",
"make_suite_spec",
"make_value_transform",
"make_value_transform_choice",
"spec_to_dict",
"suite_spec",
"value_transform_choice",
"value_transform_kind",
"value_transform_spec",
]