"""Factory helpers that map TE model-type strings to concrete modules."""
from __future__ import annotations
# Import Typing Utilities
from pathlib import Path
from typing import Any
# Import YAML Utilities
import yaml
# Import PyTorch Utilities
import torch.nn as nn
# Import Project Models
from scripts.models.feedforward_network import FeedForwardNetwork
from scripts.models.data_only_residual_capacity_network import (
DataOnlyResidualCapacityNetwork,
)
from scripts.models.harmonic_regression import HarmonicRegression
from scripts.models.harmonic_kinematic_pinn_network import HarmonicKinematicPinnNetwork
from scripts.models.harmonic_residual_offset_network import HarmonicResidualOffsetNetwork
from scripts.models.latent_state_hysteresis_network import LatentStateHysteresisNetwork
from scripts.models.periodic_feature_network import PeriodicFeatureNetwork
from scripts.models.periodic_temporal_sequence_network import PeriodicTemporalSequenceNetwork
from scripts.models.quasi_static_compliance_pinn_network import (
QuasiStaticCompliancePinnNetwork,
)
from scripts.models.residual_harmonic_network import ResidualHarmonicNetwork
from scripts.models.residual_harmonic_temporal_sequence_network import ResidualHarmonicTemporalSequenceNetwork
from scripts.models.sequential_residual_offset_network import SequentialResidualOffsetNetwork
from scripts.models.temporal_sequence_network import RecurrentSequenceNetwork
from scripts.models.temporal_sequence_network import TemporalConvolutionNetwork
from scripts.models.wave3_grouped_harmonic_heads_network import Wave3GroupedHarmonicHeadsNetwork
from scripts.models.wave3_harmonic_prior_residual_network import Wave3HarmonicPriorResidualNetwork
from scripts.models.wave52b_offset_harmonic_guided_network import Wave52BOffsetHarmonicGuidedNetwork
def load_harmonic_analytical_anchor_configuration(
model_configuration: dict[str, Any],
) -> dict[str, Any]:
"""Load one frozen Phase 1 Polynomial-Fourier surface when configured."""
anchor_path_text = model_configuration.get("analytical_anchor_path")
if anchor_path_text in [None, ""]:
return {}
anchor_path = Path(str(anchor_path_text))
assert anchor_path.is_file(), f"Analytical Anchor does not exist | {anchor_path}"
with anchor_path.open("r", encoding="utf-8") as source_file:
anchor_payload = yaml.safe_load(source_file)
assert isinstance(anchor_payload, dict)
model_id = str(
model_configuration.get(
"analytical_anchor_model_id",
"PF_A_LOCAL_QUADRATIC",
)
)
if "surface" in anchor_payload:
assert anchor_payload["model_id"] == model_id
surface_payload = anchor_payload["surface"]
else:
direction_label = str(
model_configuration["analytical_anchor_direction"]
)
surface_payload = anchor_payload["surface_map"][model_id][
direction_label
]
assert list(surface_payload["harmonic_order_list"]) == list(
model_configuration["harmonic_index_list"]
), "Analytical Anchor harmonic orders do not match the PINN configuration"
return {
"analytical_anchor_feature_mean": surface_payload["feature_mean"],
"analytical_anchor_feature_scale": surface_payload["feature_scale"],
"analytical_anchor_coefficient_matrix": (
surface_payload["coefficient_matrix"]
),
}
[docs]
def create_model(model_type: str, model_configuration: dict[str, Any]) -> nn.Module:
"""Instantiate one supported TE model from a configuration dictionary.
Args:
model_type: Canonical model-type string such as `feedforward`,
`harmonic_regression`, `periodic_mlp`,
`residual_harmonic_mlp`, `temporal_convolution`,
`gru_sequence`, `lstm_sequence`, or one of the periodic temporal
sequence and residual harmonic temporal variants.
model_configuration: Model-specific configuration dictionary.
Returns:
nn.Module: Instantiated PyTorch module matching the requested model
type.
Raises:
ValueError: If `model_type` does not match one of the supported model
families.
"""
# Validate Model Type
normalized_model_type = model_type.lower()
# Create Requested Feedforward Model
if normalized_model_type == "feedforward":
return FeedForwardNetwork(
input_size=int(model_configuration["input_size"]),
hidden_size=list(model_configuration["hidden_size"]),
output_size=int(model_configuration["output_size"]),
activation_name=str(model_configuration["activation_name"]),
dropout_probability=float(model_configuration["dropout_probability"]),
use_layer_norm=bool(model_configuration["use_layer_norm"]),
)
# Create Harmonic Regression Baseline
if normalized_model_type == "harmonic_regression":
return HarmonicRegression(
input_size=int(model_configuration["input_size"]),
output_size=int(model_configuration.get("output_size", 1)),
harmonic_order=int(model_configuration["harmonic_order"]),
coefficient_mode=str(model_configuration.get("coefficient_mode", "static")),
harmonic_index_list=model_configuration.get("harmonic_index_list"),
)
# Create Periodic-Feature Feedforward Model
if normalized_model_type == "periodic_mlp":
return PeriodicFeatureNetwork(
input_size=int(model_configuration["input_size"]),
hidden_size=list(model_configuration["hidden_size"]),
output_size=int(model_configuration["output_size"]),
activation_name=str(model_configuration["activation_name"]),
dropout_probability=float(model_configuration["dropout_probability"]),
use_layer_norm=bool(model_configuration["use_layer_norm"]),
harmonic_order=int(model_configuration["harmonic_order"]),
harmonic_index_list=model_configuration.get("harmonic_index_list"),
include_raw_angle_feature=bool(model_configuration.get("include_raw_angle_feature", True)),
)
# Create Residual Harmonic + Feedforward Model
if normalized_model_type == "residual_harmonic_mlp":
return ResidualHarmonicNetwork(
input_size=int(model_configuration["input_size"]),
output_size=int(model_configuration.get("output_size", 1)),
harmonic_order=int(model_configuration["harmonic_order"]),
coefficient_mode=str(model_configuration.get("coefficient_mode", "static")),
harmonic_index_list=model_configuration.get("harmonic_index_list"),
residual_hidden_size=list(model_configuration["residual_hidden_size"]),
residual_activation_name=str(model_configuration.get("residual_activation_name", "GELU")),
residual_dropout_probability=float(model_configuration.get("residual_dropout_probability", 0.10)),
residual_use_layer_norm=bool(model_configuration.get("residual_use_layer_norm", True)),
freeze_structured_branch=bool(model_configuration.get("freeze_structured_branch", False)),
)
# Create Temporal Convolution Sequence Model
if normalized_model_type == "temporal_convolution":
return TemporalConvolutionNetwork(
input_size=int(model_configuration["input_size"]),
channel_size=list(model_configuration["channel_size"]),
output_size=int(model_configuration.get("output_size", 1)),
kernel_size=int(model_configuration.get("kernel_size", 5)),
activation_name=str(model_configuration.get("activation_name", "GELU")),
dropout_probability=float(model_configuration.get("dropout_probability", 0.10)),
readout_position=str(model_configuration.get("readout_position", "center")),
)
# Create Periodic Temporal Convolution Sequence Model
if normalized_model_type == "periodic_temporal_convolution":
return PeriodicTemporalSequenceNetwork(
temporal_model_type="temporal_convolution",
input_size=int(model_configuration["input_size"]),
output_size=int(model_configuration.get("output_size", 1)),
harmonic_order=int(model_configuration["harmonic_order"]),
harmonic_index_list=model_configuration.get("harmonic_index_list"),
include_raw_angle_feature=bool(model_configuration.get("include_raw_angle_feature", True)),
channel_size=list(model_configuration["channel_size"]),
kernel_size=int(model_configuration.get("kernel_size", 5)),
activation_name=str(model_configuration.get("activation_name", "GELU")),
dropout_probability=float(model_configuration.get("dropout_probability", 0.10)),
readout_position=str(model_configuration.get("readout_position", "center")),
)
# Create GRU Sequence Model
if normalized_model_type == "gru_sequence":
return RecurrentSequenceNetwork(
recurrent_type="gru",
input_size=int(model_configuration["input_size"]),
hidden_size=int(model_configuration["hidden_size"]),
output_size=int(model_configuration.get("output_size", 1)),
num_layers=int(model_configuration.get("num_layers", 2)),
dropout_probability=float(model_configuration.get("dropout_probability", 0.10)),
bidirectional=bool(model_configuration.get("bidirectional", False)),
readout_position=str(model_configuration.get("readout_position", "center")),
)
# Create Residual Harmonic GRU Sequence Model
if normalized_model_type == "residual_harmonic_gru_sequence":
return ResidualHarmonicTemporalSequenceNetwork(
temporal_model_type="gru_sequence",
input_size=int(model_configuration["input_size"]),
output_size=int(model_configuration.get("output_size", 1)),
harmonic_order=int(model_configuration["harmonic_order"]),
coefficient_mode=str(model_configuration.get("coefficient_mode", "static")),
harmonic_index_list=model_configuration.get("harmonic_index_list"),
hidden_size=int(model_configuration["hidden_size"]),
num_layers=int(model_configuration.get("num_layers", 2)),
dropout_probability=float(model_configuration.get("dropout_probability", 0.10)),
bidirectional=bool(model_configuration.get("bidirectional", False)),
readout_position=str(model_configuration.get("readout_position", "center")),
freeze_structured_branch=bool(model_configuration.get("freeze_structured_branch", False)),
)
# Create Periodic GRU Sequence Model
if normalized_model_type == "periodic_gru_sequence":
return PeriodicTemporalSequenceNetwork(
temporal_model_type="gru_sequence",
input_size=int(model_configuration["input_size"]),
output_size=int(model_configuration.get("output_size", 1)),
harmonic_order=int(model_configuration["harmonic_order"]),
harmonic_index_list=model_configuration.get("harmonic_index_list"),
include_raw_angle_feature=bool(model_configuration.get("include_raw_angle_feature", True)),
hidden_size=int(model_configuration["hidden_size"]),
num_layers=int(model_configuration.get("num_layers", 2)),
dropout_probability=float(model_configuration.get("dropout_probability", 0.10)),
bidirectional=bool(model_configuration.get("bidirectional", False)),
readout_position=str(model_configuration.get("readout_position", "center")),
)
# Create LSTM Sequence Model
if normalized_model_type == "lstm_sequence":
return RecurrentSequenceNetwork(
recurrent_type="lstm",
input_size=int(model_configuration["input_size"]),
hidden_size=int(model_configuration["hidden_size"]),
output_size=int(model_configuration.get("output_size", 1)),
num_layers=int(model_configuration.get("num_layers", 2)),
dropout_probability=float(model_configuration.get("dropout_probability", 0.10)),
bidirectional=bool(model_configuration.get("bidirectional", False)),
readout_position=str(model_configuration.get("readout_position", "center")),
)
# Create Residual Harmonic LSTM Sequence Model
if normalized_model_type == "residual_harmonic_lstm_sequence":
return ResidualHarmonicTemporalSequenceNetwork(
temporal_model_type="lstm_sequence",
input_size=int(model_configuration["input_size"]),
output_size=int(model_configuration.get("output_size", 1)),
harmonic_order=int(model_configuration["harmonic_order"]),
coefficient_mode=str(model_configuration.get("coefficient_mode", "static")),
harmonic_index_list=model_configuration.get("harmonic_index_list"),
hidden_size=int(model_configuration["hidden_size"]),
num_layers=int(model_configuration.get("num_layers", 2)),
dropout_probability=float(model_configuration.get("dropout_probability", 0.10)),
bidirectional=bool(model_configuration.get("bidirectional", False)),
readout_position=str(model_configuration.get("readout_position", "center")),
freeze_structured_branch=bool(model_configuration.get("freeze_structured_branch", False)),
)
# Create Wave 3.1 Sequential Residual-Offset Probe
if normalized_model_type == "sequential_residual_offset_probe":
return SequentialResidualOffsetNetwork(
input_size=int(model_configuration["input_size"]),
output_size=int(model_configuration.get("output_size", 1)),
base_hidden_size=list(model_configuration.get("base_hidden_size", [96, 64])),
base_activation_name=str(model_configuration.get("base_activation_name", "GELU")),
base_dropout_probability=float(model_configuration.get("base_dropout_probability", 0.05)),
base_use_layer_norm=bool(model_configuration.get("base_use_layer_norm", True)),
offset_hidden_size=int(model_configuration.get("offset_hidden_size", 96)),
offset_num_layers=int(model_configuration.get("offset_num_layers", 2)),
offset_dropout_probability=float(model_configuration.get("offset_dropout_probability", 0.10)),
offset_bidirectional=bool(model_configuration.get("offset_bidirectional", False)),
offset_readout_position=str(model_configuration.get("offset_readout_position", "center")),
offset_scale=float(model_configuration.get("offset_scale", 1.0)),
)
# Create Wave 3.2 Harmonic Residual-Offset Probe
if normalized_model_type in ["harmonic_residual_offset_probe", "curve_aware_harmonic_residual_offset_probe"]:
return HarmonicResidualOffsetNetwork(
input_size=int(model_configuration["input_size"]),
output_size=int(model_configuration.get("output_size", 1)),
harmonic_order=int(model_configuration["harmonic_order"]),
coefficient_mode=str(model_configuration.get("coefficient_mode", "linear_conditioned")),
harmonic_index_list=model_configuration.get("harmonic_index_list"),
offset_hidden_size=int(model_configuration.get("offset_hidden_size", 96)),
offset_num_layers=int(model_configuration.get("offset_num_layers", 2)),
offset_dropout_probability=float(model_configuration.get("offset_dropout_probability", 0.10)),
offset_bidirectional=bool(model_configuration.get("offset_bidirectional", False)),
offset_readout_position=str(model_configuration.get("offset_readout_position", "center")),
offset_scale=float(model_configuration.get("offset_scale", 1.0)),
freeze_structured_branch=bool(model_configuration.get("freeze_structured_branch", False)),
)
# Create Wave 4.4 Latent-State Hysteresis Probe
if normalized_model_type == "latent_state_hysteresis_probe":
return LatentStateHysteresisNetwork(
input_size=int(model_configuration["input_size"]),
output_size=int(model_configuration.get("output_size", 1)),
latent_encoder_type=str(model_configuration.get("latent_encoder_type", "gru")),
latent_hidden_size=int(model_configuration.get("latent_hidden_size", 96)),
latent_num_layers=int(model_configuration.get("latent_num_layers", 2)),
latent_dropout_probability=float(model_configuration.get("latent_dropout_probability", 0.10)),
latent_channel_size=model_configuration.get("latent_channel_size"),
latent_kernel_size=int(model_configuration.get("latent_kernel_size", 5)),
latent_activation_name=str(model_configuration.get("latent_activation_name", "GELU")),
readout_position=str(model_configuration.get("readout_position", "last")),
base_hidden_size=list(model_configuration.get("base_hidden_size", [96, 64])),
head_hidden_size=list(model_configuration.get("head_hidden_size", [96, 64])),
head_activation_name=str(model_configuration.get("head_activation_name", "GELU")),
head_dropout_probability=float(model_configuration.get("head_dropout_probability", 0.05)),
use_layer_norm=bool(model_configuration.get("use_layer_norm", True)),
offset_scale=float(model_configuration.get("offset_scale", 1.0)),
residual_scale=float(model_configuration.get("residual_scale", 1.0)),
)
# Create Embryonic Wave 5.1 Harmonic-Prior Residual Skeleton
if normalized_model_type == "wave3_harmonic_prior_residual":
return Wave3HarmonicPriorResidualNetwork(
input_size=int(model_configuration["input_size"]),
output_size=int(model_configuration.get("output_size", 1)),
harmonic_order=int(model_configuration.get("harmonic_order", 240)),
coefficient_mode=str(model_configuration.get("coefficient_mode", "linear_conditioned")),
harmonic_index_list=model_configuration.get("harmonic_index_list"),
residual_hidden_size=list(model_configuration.get("residual_hidden_size", [96, 64])),
residual_activation_name=str(model_configuration.get("residual_activation_name", "GELU")),
residual_dropout_probability=float(model_configuration.get("residual_dropout_probability", 0.05)),
residual_use_layer_norm=bool(model_configuration.get("residual_use_layer_norm", True)),
residual_scale=float(model_configuration.get("residual_scale", 1.0)),
readout_position=str(model_configuration.get("readout_position", "center")),
freeze_structured_branch=bool(model_configuration.get("freeze_structured_branch", False)),
low_order_harmonic_index_list=model_configuration.get("low_order_harmonic_index_list"),
stable_middle_harmonic_index_list=model_configuration.get("stable_middle_harmonic_index_list"),
high_order_harmonic_index_list=model_configuration.get("high_order_harmonic_index_list"),
)
# Create Wave 5.2 Phase 2 Harmonic-Kinematic PINN
if normalized_model_type == "harmonic_kinematic_pinn":
analytical_anchor_configuration = (
load_harmonic_analytical_anchor_configuration(
model_configuration
)
)
return HarmonicKinematicPinnNetwork(
input_size=int(model_configuration["input_size"]),
output_size=int(model_configuration.get("output_size", 1)),
harmonic_index_list=list(
model_configuration["harmonic_index_list"]
),
condition_hidden_size=list(
model_configuration.get("condition_hidden_size", [96, 64])
),
condition_latent_size=int(
model_configuration.get("condition_latent_size", 48)
),
component_hidden_size=list(
model_configuration.get("component_hidden_size", [32, 32])
),
head_mode=str(
model_configuration.get("head_mode", "implicit_pinn")
),
activation_name=str(
model_configuration.get("activation_name", "Tanh")
),
dropout_probability=float(
model_configuration.get("dropout_probability", 0.0)
),
use_layer_norm=bool(
model_configuration.get("use_layer_norm", False)
),
analytical_anchor_feature_mean=analytical_anchor_configuration.get(
"analytical_anchor_feature_mean",
model_configuration.get("analytical_anchor_feature_mean"),
),
analytical_anchor_feature_scale=analytical_anchor_configuration.get(
"analytical_anchor_feature_scale",
model_configuration.get("analytical_anchor_feature_scale"),
),
analytical_anchor_coefficient_matrix=(
analytical_anchor_configuration.get(
"analytical_anchor_coefficient_matrix",
model_configuration.get(
"analytical_anchor_coefficient_matrix"
),
)
),
)
# Create Wave 5.2R Stage 4 Data-Only Residual Capacity Model
if normalized_model_type == "data_only_residual_capacity":
analytical_anchor_configuration = (
load_harmonic_analytical_anchor_configuration(
model_configuration
)
)
assert analytical_anchor_configuration, (
"Stage 4 requires the qualified PF-A analytical anchor"
)
return DataOnlyResidualCapacityNetwork(
input_size=int(model_configuration["input_size"]),
output_size=int(model_configuration.get("output_size", 1)),
hidden_size=list(model_configuration["hidden_size"]),
harmonic_index_list=list(
model_configuration["harmonic_index_list"]
),
activation_name=str(
model_configuration.get("activation_name", "Tanh")
),
dropout_probability=float(
model_configuration.get("dropout_probability", 0.0)
),
use_layer_norm=bool(
model_configuration.get("use_layer_norm", False)
),
formulation=str(model_configuration["formulation"]),
residual_bound_deg=float(
model_configuration.get("residual_bound_deg", 0.01)
),
residual_basis_order_list=list(
model_configuration.get(
"residual_basis_order_list",
[],
)
),
anchor_mode=str(
model_configuration.get("anchor_mode", "frozen")
),
partial_unfreeze_harmonic_index_list=list(
model_configuration.get(
"partial_unfreeze_harmonic_index_list",
[1, 3],
)
),
zero_initialize_residual=bool(
model_configuration.get(
"zero_initialize_residual",
True,
)
),
include_raw_angle_feature=bool(
model_configuration.get(
"include_raw_angle_feature",
False,
)
),
analytical_anchor_feature_mean=(
analytical_anchor_configuration[
"analytical_anchor_feature_mean"
]
),
analytical_anchor_feature_scale=(
analytical_anchor_configuration[
"analytical_anchor_feature_scale"
]
),
analytical_anchor_coefficient_matrix=(
analytical_anchor_configuration[
"analytical_anchor_coefficient_matrix"
]
),
)
# Create Wave 5.2 Phase 3 Quasi-Static Compliance PINN
if normalized_model_type == "quasi_static_compliance_pinn":
return QuasiStaticCompliancePinnNetwork(
input_size=int(model_configuration["input_size"]),
output_size=int(model_configuration.get("output_size", 1)),
harmonic_index_list=list(
model_configuration["harmonic_index_list"]
),
condition_hidden_size=list(
model_configuration.get("condition_hidden_size", [64, 48])
),
condition_latent_size=int(
model_configuration.get("condition_latent_size", 32)
),
mean_hidden_size=list(
model_configuration.get("mean_hidden_size", [32, 16])
),
formulation=str(
model_configuration.get("formulation", "C1")
),
activation_name=str(
model_configuration.get("activation_name", "Tanh")
),
dropout_probability=float(
model_configuration.get("dropout_probability", 0.0)
),
use_layer_norm=bool(
model_configuration.get("use_layer_norm", False)
),
minimum_stiffness_nm_per_deg=float(
model_configuration.get(
"minimum_stiffness_nm_per_deg",
5000.0,
)
),
maximum_stiffness_nm_per_deg=float(
model_configuration.get(
"maximum_stiffness_nm_per_deg",
100000.0,
)
),
initial_stiffness_nm_per_deg=float(
model_configuration.get(
"initial_stiffness_nm_per_deg",
27250.0,
)
),
initial_forward_intercept_deg=float(
model_configuration.get(
"initial_forward_intercept_deg",
-0.0217,
)
),
initial_backward_intercept_deg=float(
model_configuration.get(
"initial_backward_intercept_deg",
-0.0116,
)
),
reference_temperature_deg_c=float(
model_configuration.get(
"reference_temperature_deg_c",
30.0,
)
),
temperature_scale_deg_c=float(
model_configuration.get(
"temperature_scale_deg_c",
10.0,
)
),
nonlinear_torque_scale_nm=float(
model_configuration.get(
"nonlinear_torque_scale_nm",
400.0,
)
),
maximum_nonlinear_amplitude_deg=float(
model_configuration.get(
"maximum_nonlinear_amplitude_deg",
0.02,
)
),
torque_input_mode=str(
model_configuration.get(
"torque_input_mode",
"nominal_magnitude",
)
),
)
# Create Embryonic Wave 5.1 Grouped Harmonic-Heads Skeleton
if normalized_model_type == "wave3_grouped_harmonic_heads":
return Wave3GroupedHarmonicHeadsNetwork(
input_size=int(model_configuration["input_size"]),
output_size=int(model_configuration.get("output_size", 1)),
harmonic_order=int(model_configuration.get("harmonic_order", 240)),
coefficient_mode=str(model_configuration.get("coefficient_mode", "linear_conditioned")),
low_order_harmonic_index_list=model_configuration.get("low_order_harmonic_index_list"),
stable_middle_harmonic_index_list=model_configuration.get("stable_middle_harmonic_index_list"),
high_order_harmonic_index_list=model_configuration.get("high_order_harmonic_index_list"),
residual_hidden_size=list(model_configuration.get("residual_hidden_size", [96, 64])),
residual_activation_name=str(model_configuration.get("residual_activation_name", "GELU")),
residual_dropout_probability=float(model_configuration.get("residual_dropout_probability", 0.05)),
residual_use_layer_norm=bool(model_configuration.get("residual_use_layer_norm", True)),
low_order_scale=float(model_configuration.get("low_order_scale", 1.0)),
stable_middle_scale=float(model_configuration.get("stable_middle_scale", 1.0)),
high_order_scale=float(model_configuration.get("high_order_scale", 1.0)),
residual_scale=float(model_configuration.get("residual_scale", 1.0)),
readout_position=str(model_configuration.get("readout_position", "center")),
freeze_harmonic_heads=bool(model_configuration.get("freeze_harmonic_heads", False)),
)
# Create Wave 5.2B Offset And Harmonic Guided Model
if normalized_model_type == "wave52b_offset_harmonic_guided":
return Wave52BOffsetHarmonicGuidedNetwork(
input_size=int(model_configuration["input_size"]),
output_size=int(model_configuration.get("output_size", 1)),
base_hidden_size=model_configuration.get("base_hidden_size"),
offset_hidden_size=model_configuration.get("offset_hidden_size"),
activation_name=str(model_configuration.get("activation_name", "GELU")),
dropout_probability=float(model_configuration.get("dropout_probability", 0.05)),
use_layer_norm=bool(model_configuration.get("use_layer_norm", True)),
offset_scale=float(model_configuration.get("offset_scale", 1.0)),
harmonic_scale=float(model_configuration.get("harmonic_scale", 0.0)),
harmonic_order=int(model_configuration.get("harmonic_order", 240)),
coefficient_mode=str(model_configuration.get("coefficient_mode", "linear_conditioned")),
harmonic_index_list=model_configuration.get("harmonic_index_list"),
readout_position=str(model_configuration.get("readout_position", "center")),
freeze_harmonic_branch=bool(model_configuration.get("freeze_harmonic_branch", False)),
)
# Create Periodic LSTM Sequence Model
if normalized_model_type == "periodic_lstm_sequence":
return PeriodicTemporalSequenceNetwork(
temporal_model_type="lstm_sequence",
input_size=int(model_configuration["input_size"]),
output_size=int(model_configuration.get("output_size", 1)),
harmonic_order=int(model_configuration["harmonic_order"]),
harmonic_index_list=model_configuration.get("harmonic_index_list"),
include_raw_angle_feature=bool(model_configuration.get("include_raw_angle_feature", True)),
hidden_size=int(model_configuration["hidden_size"]),
num_layers=int(model_configuration.get("num_layers", 2)),
dropout_probability=float(model_configuration.get("dropout_probability", 0.10)),
bidirectional=bool(model_configuration.get("bidirectional", False)),
readout_position=str(model_configuration.get("readout_position", "center")),
)
raise ValueError(f"Unsupported Model Type | {model_type}")