release: v7.17.0 — neural network metamodel expansion and a Spring Boot generator - #608
Merged
Merged
Conversation
Fixed line 112 in setup_nn_components.py.j2 where conv layer was using input variable instead of padded output variable. Changed from module_details[2] to module_details[1] for the second line in ZeroPadding block.
…erator - Add setup_standalone_activation() to handle activations from functional API (F.relu) that appear after tensorops as GeneralLayer instances - Add add_separate_activation_if_needed() for layers that don't support activation parameter (BatchNorm, LayerNorm, Dropout, Embedding, Pooling, Flatten) - Implement shared activation instances: one definition per activation type, reused across all calls (e.g., 6 relu calls use 1 shared activation_relu layer) - Use DEF: prefix for shared activation definitions (printed in __init__ only) - Use CALL: prefix for activation call references (printed in forward method) - Update template to handle _activ suffix entries with DEF:/CALL: prefixes - Update get_input_var() to support _activ entries in name_module_input lookups - Update get_layer_syntax() to handle standalone activation case (cls_name == GeneralLayer) - Skip adding _layer entry when layer_synt is None (standalone activations)
TensorFlow generator (utils_tf.py):
- Add add_permute method to create tf.transpose operations for CNN layers
- Read permute_in/permute_out flags in setup_conv and setup_pooling
PyTorch generator (utils_pytorch.py):
- Fix BatchNorm syntax typo: .BatchNorm(){dim}d -> .BatchNorm{dim}d
Shared code (utils_nn.py):
- Fix channel_last condition to handle all three cases: None (TF always adds permutes), True (PyTorch adds permutes), False (PyTorch skips permutes)
- Add hasattr check before calling add_permute since only TF generator has this method, PyTorch generator does not
Metamodel changes (besser/BUML/metamodel/nn/neural_network.py): - Add reduce_dim parameter to TensorOp for dimension-reducing operations - Add actual_vars parameter to TensorOp to distinguish between RNN output and hidden state components - Add new tensor operation types: mean, squeeze, unsqueeze, and binary operations (add, subtract, multiply, divide) - Add both as valid RNN return_type to return both output sequences and hidden states Core generator utilities (besser/generators/nn/utils_nn.py): - Extend get_layers_output_for_tensorops() to accept actual_vars parameter - Use actual_vars to select correct component when RNN has return_type=both (either hidden variable or last timestep of output sequence) - Store hidden variable as 5th element in modules_details for return_type=both - Pass actual_vars through from TensorOp to parameter extraction functions PyTorch generator (besser/generators/nn/pytorch/): - Add template handling for return_type=both to unpack both output and hidden states from RNN calls - Handle bidirectional case by concatenating forward and backward hidden states - Add code generation for mean, squeeze, unsqueeze, and binary operations TensorFlow generator (besser/generators/nn/tf/): - Add return_sequences=True, return_state=True flags for return_type=both - Add template handling to unpack both components from RNN/LSTM calls - Handle bidirectional unpacking with separate forward and backward states - Add code generation for mean, squeeze, unsqueeze, and binary operations These changes enable migration of complex RNN patterns where both output sequences and hidden states are used simultaneously in the forward pass.
The generator now accepts numeric values (int, float) directly in layers_of_tensors instead of only layer names, supporting operations like multiply by constant.
When tensor operations reference other tensor operations (e.g., nested binary operations like x + y + z), the generator now assigns unique variable names to intermediate results. This prevents variable name collisions where multiple operands would incorrectly resolve to the same variable. The fix uses a two-pass approach: first identify which tensorops are referenced by others, then assign unique names (_op_N) to those that need them.
Standalone activations inside Sequential blocks now use direct layer definitions instead of the shared activation pattern. Added is_subnn parameter to track when layers are being processed within a Sequential and adjusted the syntax generation accordingly.
Update get_layers_output_for_tensorops to check for _activ suffix when looking up layer outputs in tensor operations like concatenate. This enables proper handling of standalone GeneralLayer activation instances that are stored with _activ suffix instead of _layer suffix in modules_details. Fixes KeyError when concat operations reference activation layers created from nested functional calls.
Add 'subscript' as valid tns_type in TensorOp metamodel to support general slicing and subscripting operations beyond RNN specific slicing. Add subscript_indices attribute to store slice pattern as string representation. Update TensorOp initialization to set subscript_indices before tns_type to allow validation. Add validation requiring subscript_indices and non-empty layers_of_tensors for subscript type. Update get_tensorop_params in utils_nn.py to handle subscript operations by extracting input variable from layers_of_tensors. Update TensorFlow and PyTorch generators to render subscript TensorOps as inline subscript syntax using subscript_indices pattern. Enables representation of subscripting patterns like tensor[:, :, -1] or embeddings[:, 0] in neural network models while maintaining correct variable tracking through TensorOp instances.
… output - Add INPUT marker handling in get_input_var to return network input variable - Extend get_tensorop_params to check name_module_input for TensorOps and use 'inp' variable when INPUT marker is set - Update subscript handling to check name_module_input before layers_of_tensors - Store TensorOp object in modules_details (3 elements instead of 2) to enable template access to name_module_input attribute - Add INPUT preservation detection in print_forward templates (TF and PyTorch) to generate 'inp = x' when modules reference network input - Fix activation function output in TF template by adding whitespace control to properly render DEF: activations in __init__ - Add guard to only access module_details[3] for _layer and _activ modules, not _nn modules which use dict structure
Add 'max' to valid TensorOp types in neural_network.py metamodel. Add tf.reduce_max syntax generation in utils_tf.py for max TensorOp type, matching mean operation pattern with axis parameter from reduce_dim. Add source layer resolution in get_tensorop_params for mean and max operations. When layers_of_tensors is set, resolve the source layer name to get the correct output variable, checking both _layer and _op module name patterns. This ensures mean and max operate on the specified input tensor rather than the previous output in sequence
Add check for Sequential sub-networks in get_layers_output_for_tensorops. Sub-networks are stored with format name_N_nn (with counter), not just name_nn. Use pattern matching to find the correct key.
- Convert LayerNorm dimension size to axis indices using range(-num_axes, 0) - Handle 'INPUT' source layer in mean/max TensorOp to use 'inp' variable
Add shape_dim type for extracting tensor dimensions (e.g., b = tf.shape(x)[0]). Fix variable naming to prevent shape variables from being overwritten by subsequent operations. - Add 'shape_dim' to valid TensorOp types in metamodel - Generate tf.shape()[index] syntax for shape_dim ops - Track reshape source variables via layers_of_tensors - Fix get_out_var_input_reused to handle non-standard variable names robustly - Prevent reusing shape variable names (b, t) for subsequent x_N operations
Add check to assign _op_ prefix to all binary operations, preventing them from overwriting layer output variables.
Add dimension parameter to DropoutLayer for 1D/2D/3D spatial variants.
When dimension is None, generates regular Dropout. When set, generates
SpatialDropout (TF) or Dropout{1,2,3}d (PyTorch).
- Add dimension parameter to DropoutLayer.__init__
- Update TF generator to use SpatialDropout{1,2,3}D
- Update PyTorch generator to use Dropout{1,2,3}d
- Fix transpose(dim0, dim1) conversion to TensorFlow full permutation (e.g., transpose(1, 2) → perm=[0, 2, 1]) - Add transpose source variable resolution via layers_of_tensors in utils_nn.py - Assumes 3D tensors for 2-arg transpose (most common case for RNN/CNN-1D)
- Add explanation to tns_type validation error message that transpose_dim is capped at 2 by design; for arbitrary N-dimensional tns_type='permute' should be used instead - Document in TensorOp docstring the distinction between transpose (exactly 2 dims, common 3D case) and permute (full arbitrary permutation over any number of dimensions)
… object support - Clarify in TensorOp class docstring and layers_of_tensors getter/setter that elements must be layer name strings or scalar values (float or int) - Explicitly note that passing Layer objects directly is no longer supported; the layer name string should be used
… validation before generation - Add resolve_var_chain() to NN to propagate input_var and return_vars into the first and last modules - Remove mutation from _validate_input_output_var_chain() so validate() is now side-effect free - Call resolve_var_chain() then validate() in NNCodeGenerator.__init__ before building modules_details, so generation always starts from a clean and valid model - Remove left-over validate() from tensorop class - Rename skip_validation flag to allow_unresolved_shapes to better reflect its purpose in TF to PyTorch migration
Remove tests/generators/nn_migration/ from version control; the fixtures have no test module reading them and are out of date with the current generators.
Make input_var take priority over layers_of_tensors when resolving the input variable for a TensorOp, consistently across all operation types in both generators.
…date docstrings - Add TensorOp Parameters section covering all operation types, their required and optional parameters, and the shared attributes with input resolution order - Add Layer Parameters section covering BESSER-specific attributes shared across all layers and RNN-specific attributes - Update the Numerical bounds validation bullet to reflect the actual checks in place - Expand the subscript_indices docstring with the full dict structure and an example - Remove the misleading Required note from split_sizes docstring
…e, subscript, and identity tensorops from the editor
…tion Skip input_var/output_var mismatch checks when module values are None, preventing false validation errors
…always produce all return vars
…hape_dim block Factor the repeated base-layer fields (is_layer_call, input_var, output_var) emission into a single _append_base_layer_fields() helper called once per layer branch. Remove dead unreachable elif block for shape_dim TensorOp (already handled earlier in the reduce operations branch).
…ation When previous module is multi-output (like RNN family returning (output, hidden_state)), the downstream layer must specify which output to consume. The validation was checking only input_var but name_module_input is also valid. Also fix three safety test exception types to match implementation (TypeError not ValueError). Also update cnn_rnn.json and lstm.json fixtures to satisfy validation requirements.
# Conflicts: # besser/utilities/web_modeling_editor/backend/backend.py
The new layer and tensor-op parameters were added with a bare `*` in every
constructor, which silently turned pre-existing parameters keyword-only:
TensorOp("t", "reshape", None, None, [1, 2]) worked on 7.16.1 and raised
TypeError here. The marker now sits after the last parameter that already
existed on development, so only genuinely new parameters are keyword-only
and no model written against an earlier release breaks.
Three defects in validate() are fixed alongside it. A mis-indented block
nested the pad, dropout, split, permute and variable-name checks under
`if tns.interpolate_mode is not None:`; since the setter accepts None, that
path silently skipped roughly 130 lines of validation. The subscript_indices
branch appended "must be dict" and then subscripted the element anyway,
raising KeyError out of a method contracted to collect errors. And eval()
was used to parse user-authored dimension strings, with an except clause
that missed NameError.
Also drops the duplicated CNN.permute_in/permute_out properties that shadowed
the base Layer ones, exports ALLOWED_TENSOR_OP_TYPES so the converter stops
carrying its own copy of the list, and fixes two docstrings that promised
validation the setters do not perform.
Whether an attribute had been set explicitly was recorded in a sidecar that only the JSON processor populated, so models built in Python or imported from BUML code lost most of their configuration on the way back out. A Conv2D constructed with bias, permute_in, permute_out, is_layer_call, dilation, groups and input_reused came back as Conv2D(name, kernel_dim, out_channels, stride_dim) -- seven of nine values gone. The check now falls back to comparing the value against the default declared by the constructor, so both exporters agree regardless of how the model was built. Four fields were lost in narrower ways. TensorOp.shape_dim was parsed and then never passed to the constructor or emitted by either exporter. repeat_dim went through parse_list_of_ints even though the metamodel declares list[int | str], so any entry naming a layer discarded the whole list. subscript_indices was emitted as a Python repr, which is not valid JSON and so failed to parse on import. The code builder omitted dilation and groups for convolutions and permute_in/permute_out for embedding, dropout and batch-norm layers. Malformed subscript_indices and pad_amount payloads were also being swallowed into None; they now raise a ValueError naming the element and field, matching how the neighbouring fields already report bad input. The AST-safety rejections in the BUML reader raise ValueError rather than TypeError so they surface as HTTP 400 instead of a generic 500. Adds 15 regression tests, including the Conv2D case above.
The v7.17.0 notes file was present but missing from the releases toctree, so Sphinx treated it as orphaned and it did not appear in the built docs. It is now wired in at the top of the list, and setup.cfg is bumped to match. Deleting the generated tests/BUML/metamodel/nn/output tree also broke the build: docs/source/generators/pytorch.rst and tensorflow.rst literalinclude the tutorial_example output, and Sphinx failed to find it. The two files that the docs reference are restored -- regenerating them reproduces what the branch had committed before the directory was removed, so the deletion was housekeeping that overlooked the references. The neural network page described the new layer parameters as "standard parameters expected from the equivalent layer in a deep learning framework" without naming any of them; bias, dilation, groups, padding_idx, eps, momentum, affine, track_running_stats and dimension are now documented with their types, defaults and effects. Two docstrings in neural_network.py used inline literals spanning a line break, which docutils cannot parse; they are now a literal block and a single-line literal respectively. The notes cover both features in this release and credit the contributors.
… that compiles The generator could not be instantiated by the web editor at all: SpringBackendGenerator required spring_boot_version, java_version and app_name positionally before output_dir, while the router calls every generator as generator_class(model, output_dir=...). Those are now keyword arguments with defaults, matching the rest of besser/generators. The generated project also did not compile. Four copies of the B-UML to Java type map had drifted apart: TimeType mapped to LocalTime in the entity and controller generators but to LocalDateTime in the repository and service generators, so an entity field and the methods taking it disagreed on type. The map now lives once in java_types.py, and the findAllBy signature builder is shared so the two sides cannot drift again. Association ownership compared a Multiplicity object against the integer 1, which is always unequal, so every association resolved to the same end as owner and mappedBy was placed arbitrarily. Ownership is now decided from multiplicity.max against UNLIMITED_MAX_MULTIPLICITY, which also fixes self-associations emitting one end twice instead of both. B-UML names reached the filesystem unsanitized. NamedElement.name rejects spaces and hyphens but permits "..", "/" and "\", so a class named ../../evil was a valid model that wrote outside output_dir. Class, field, package and file names now go through Java identifier sanitization, and the new _generate_spring router branch resolves the project directory with the existing _safe_path helper rather than a raw os.path.join. The Maven wrapper shipped as three .j2 templates containing no Jinja tags at all, rendered into .mvn/ where Maven does not look for them. They are static files under resources/ now, copied to the project root with the executable bit and fixed line endings, and declared as package data so they ship in the wheel. Also: the generator is registered in SUPPORTED_GENERATORS and dispatched with its configuration rather than falling through to the generic path that ignored it; unmapped types fall back to Object instead of raising KeyError; a class with no identifier reports which class rather than a bare StopIteration; the controller uses the resolved identifier accessor instead of a hardcoded setId; enumeration literals and associations are sorted so output is deterministic; and example.py, a 206-line scratch script no other generator ships, is removed. Adds tests/generators/spring with 24 tests covering structure, content, determinism, name sanitization and config pass-through. Verified end to end by running mvnw compile on a generated project.
The traversal test asserted evil.java while the generator correctly writes Evil.java, since a class name is capitalized on the way to a Java identifier. Windows matched it case-insensitively so the assertion passed locally and failed on the Linux CI runners.
feat: enhance neural network support with extended metamodel, generators and web editor
…erator Feature/spring boot project generator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Minor release: a substantially expanded neural network metamodel, and a new Spring Boot generator. The neural network work was contributed by @DaoudiNadia (#584); the Spring Boot generator by @miloss01 (#607), which started life as his master's thesis. Both were reviewed and completed by the BESSER team.
dilation/groupson convolutions,padding_idxon embeddings,eps/momentum/affine/track_running_statson normalization,dimensionon dropout,biasacross conv/linear/recurrent.NN.input_var/NN.return_varsmake non-sequential architectures — branches, skip connections, encoder→decoder hidden state — expressible in the model rather than patched into the output..httpfiles.Test plan
development: 1605 passed, 9 skipped, 3 xfailedv7.17.0andgenerators/springpages render_generate_standarddoes — 13 files,mvnwat project root,pom.xmlpresent./mvnw -B -q compileexits 0develop: 365 tests pass,npm ciclean,build:webappgreen, i18n check passesv7.17.0, publish release, deploy backend + frontend