.. _pfs-api-ref: ================= PFS API Reference ================= Complete API reference for NeurEco Parametric Frequency Sweep (PFS) models. .. contents:: On this page :local: :depth: 2 Import ====== .. code-block:: python from NeurEco import NeurEcoFrequential as freq PFS === .. code-block:: python class freq.PFS Parametric Frequency Sweep model for complex-valued frequency response prediction. **File extension:** ``.efnn`` Constructor ----------- .. code-block:: python model = freq.PFS(dll_path=None) **Parameters:** - ``dll_path`` *(str, optional)* — Path to NeurEco binaries Methods ------- build ^^^^^ .. code-block:: python status = model.build(config) Train the PFS model. **Parameters:** - ``config`` *(PFSBuildConfig)* — Build configuration **Returns:** ``int`` — Status code (0 = success) evaluate ^^^^^^^^ .. code-block:: python outputs = model.evaluate(inputs, vec=None) Run inference on input data. **Parameters:** - ``inputs`` *(ndarray or list)* — Input data, shape ``(n_samples, n_inputs)``. **First column must be frequency.** - ``vec`` *(ndarray, optional)* — Custom weights **Returns:** ``ndarray`` — Complex-valued predictions, dtype ``complex128`` .. warning:: The **first column** of inputs must **always** be the frequency, even when evaluating at a single frequency point. compute_error ^^^^^^^^^^^^^ .. code-block:: python error = model.compute_error(predictions, targets) Compute L2 relative error for complex-valued data. **Parameters:** - ``predictions`` *(ndarray)* — Model predictions (complex) - ``targets`` *(ndarray)* — Ground truth (complex) **Returns:** ``float`` — L2 relative error load / save ^^^^^^^^^^^ .. code-block:: python model.load(path) model.save(path) Load or save model. **Parameters:** - ``path`` *(str)* — File path **Returns:** ``int`` — Status code get_input_count / get_output_count ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python n_inputs = model.get_input_count() n_outputs = model.get_output_count() Get number of input and output features. **Returns:** ``int`` get_denominator_size ^^^^^^^^^^^^^^^^^^^^ .. code-block:: python denom_size = model.get_denominator_size() Get the denominator size of the rational approximation. **Returns:** ``int`` The denominator size indicates model complexity. Larger values can capture more complex frequency behavior (more poles/zeros) but increase computation time. perform_input_sweep ^^^^^^^^^^^^^^^^^^^ .. code-block:: python model.perform_input_sweep( input_feature, input_id, input_interval, output_id, n_points=100, show=True, save_path=None ) Visualize how one input affects the output. **Parameters:** - ``input_feature`` *(ndarray)* — Reference input sample (1D) - ``input_id`` *(int)* — Index of input to sweep - ``input_interval`` *(list)* — ``[min, max]`` range - ``output_id`` *(int)* — Output to plot - ``n_points`` *(int)* — Number of sweep points - ``show`` *(bool)* — Display plot - ``save_path`` *(str)* — Path to save figure export_fmu ^^^^^^^^^^ .. code-block:: python status = model.export_fmu(path) Export to FMU format. **Parameters:** - ``path`` *(str)* — Output .fmu file path **Returns:** ``int`` — Status code .. note:: Requires NeurEco Embed PFS license. get_number_of_networks_from_checkpoint ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python n = model.get_number_of_networks_from_checkpoint(path) Get number of models saved in a checkpoint. **Parameters:** - ``path`` *(str)* — Checkpoint path **Returns:** ``int`` — Number of checkpointed models load_model_from_checkpoint ^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python model.load_model_from_checkpoint(path, model_index) Load a specific model from checkpoint. **Parameters:** - ``path`` *(str)* — Checkpoint path - ``model_index`` *(int)* — Model index (0-based) The build log and the GUI number checkpoints from 1: "Checkpoint N" there is ``model_index`` N - 1 here. get_weights / set_weights ^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python weights = model.get_weights() model.set_weights(weights) Get or set model weights. delete ^^^^^^ .. code-block:: python model.delete() Free native memory. **Always call when done.** PFSBuildConfig ============== .. code-block:: python config = freq.PFSBuildConfig( # Required input_data, output_data, # Optional validation_input_data=None, validation_output_data=None, test_input_data=None, test_output_data=None, valid_percentage=33.33, write_model_to="model.efnn", checkpoint_address="ckpt.checkpoint", reduced_space_dimension=5, min_enrichment_steps=10, max_enrichment_steps=200, unsuccessful_enrichment_steps=4, enrichment_rate=0.2, ) **Parameters:** .. list-table:: :header-rows: 1 :widths: 32 15 53 * - Parameter - Default - Description * - ``input_data`` - (required) - Training inputs, shape ``(n_samples, n_inputs)``. First column = frequency. * - ``output_data`` - (required) - Training outputs, shape ``(n_samples, n_outputs)``, dtype ``complex128`` * - ``validation_input_data`` - ``None`` - Validation inputs * - ``validation_output_data`` - ``None`` - Validation outputs * - ``test_input_data`` - ``None`` - Test inputs * - ``test_output_data`` - ``None`` - Test outputs * - ``valid_percentage`` - ``33.33`` - Validation split percentage (1-50) * - ``write_model_to`` - ``"model.efnn"`` - Path to save trained model * - ``checkpoint_address`` - ``"ckpt.checkpoint"`` - Path for checkpoints * - ``reduced_space_dimension`` - ``5`` - Dimension of compressed output space * - ``min_enrichment_steps`` - ``10`` - Minimum enrichment iterations * - ``max_enrichment_steps`` - ``200`` - Maximum enrichment iterations * - ``unsuccessful_enrichment_steps`` - ``4`` - Failures before early stopping * - ``enrichment_rate`` - ``0.2`` - Parameter addition rate (0 < rate <= 1) Data Format =========== Input Data ---------- - Shape: ``(n_samples, n_inputs)`` where ``n_inputs >= 2`` - Type: ``float64`` - **First column must be frequency** .. code-block:: python # Example: frequency + 3 design parameters inputs = np.zeros((n_samples, 4), dtype=np.float64) inputs[:, 0] = frequencies # Column 0: frequency (REQUIRED) inputs[:, 1] = param1_values # Column 1: parameter 1 inputs[:, 2] = param2_values # Column 2: parameter 2 inputs[:, 3] = param3_values # Column 3: parameter 3 Output Data ----------- - Shape: ``(n_samples, n_outputs)`` - Type: ``complex128`` .. code-block:: python outputs = np.zeros((n_samples, 2), dtype=np.complex128) outputs[:, 0] = complex_response_1 outputs[:, 1] = complex_response_2 Normalization ------------- PFS performs automatic normalization: **Inputs** (per feature): .. math:: x_{norm} = \frac{x - \min(x)}{\max(x) - \min(x)} **Outputs** (global): .. math:: y_{norm} = \frac{y}{\max(|y|)} Example Usage ============= Basic Usage ----------- .. code-block:: python from NeurEco import NeurEcoFrequential as freq import numpy as np # Load data x_train = np.load('inputs_train.npy') # (n_samples, n_inputs), float64 y_train = np.load('targets_train.npy') # (n_samples, n_outputs), complex128 # Build model = freq.PFS() config = freq.PFSBuildConfig( input_data=x_train, output_data=y_train, write_model_to="./pfs_model.efnn" ) model.build(config) # Evaluate predictions = model.evaluate(x_test) error = model.compute_error(predictions, y_test) print(f"L2 error: {100*error:.2f}%") # Model info print(f"Denominator size: {model.get_denominator_size()}") model.delete() Exploring Checkpoints --------------------- .. code-block:: python model = freq.PFS() # Get checkpoint info n_models = model.get_number_of_networks_from_checkpoint("./pfs_checkpoint") # Find best accuracy/complexity trade-off for i in range(n_models): model.load_model_from_checkpoint("./pfs_checkpoint", i) outputs = model.evaluate(x_test) error = model.compute_error(outputs, y_test) denom = model.get_denominator_size() print(f"Model {i}: error={100*error:.2f}%, denom_size={denom}") Visualizing Frequency Response ------------------------------ .. code-block:: python import matplotlib.pyplot as plt # Evaluate over frequency range frequencies = np.linspace(1, 500, 200) param_value = 0.05 # Remember: first column is always frequency inputs = np.column_stack([frequencies, np.full(200, param_value)]) outputs = model.evaluate(inputs) # Plot magnitude (dB) magnitude_db = 20 * np.log10(np.abs(outputs[:, 0])) plt.plot(frequencies, magnitude_db) plt.xlabel("Frequency (Hz)") plt.ylabel("Magnitude (dB)") plt.grid(True) plt.show() # Plot phase phase_deg = np.angle(outputs[:, 0], deg=True) plt.plot(frequencies, phase_deg) plt.xlabel("Frequency (Hz)") plt.ylabel("Phase (degrees)") plt.grid(True) plt.show() Single-Point Evaluation ----------------------- Even when evaluating at a single point, the frequency column must be present: .. code-block:: python # Single frequency evaluation - frequency column required! single_input = np.array([[100.0, 0.5, 1.2]]) # [freq, param1, param2] single_output = model.evaluate(single_input) print(f"Response at 100 Hz: {single_output[0, 0]}") print(f"Magnitude: {np.abs(single_output[0, 0]):.4f}") print(f"Phase: {np.angle(single_output[0, 0], deg=True):.2f} degrees")