Skip to content

Integrate structured parameters into simulation#211

Open
TimothyWillard wants to merge 4 commits intomainfrom
integrate-params-into-sim
Open

Integrate structured parameters into simulation#211
TimothyWillard wants to merge 4 commits intomainfrom
integrate-params-into-sim

Conversation

@TimothyWillard
Copy link
Copy Markdown
Collaborator

It pushes ParameterValue and model-state handling through the
simulator and engine interfaces so simulation, external providers, and
the docs example all execute against the same structured parameter
contract.

  • Updated EngineABC and the engine protocol to run against structured
    initial_state, params, and optional model_state.
  • Removed the engine protocol from flepimop2.typing and keep the
    engine specific contract alongside engine.abc and allow it to depend
    on ParameterValue.
  • Adapted wrapper engine fixtures and tests to assemble numeric state
    from ModelStateSpecification and ParameterValue inputs.
  • Wired the simulator and CLI flow through resolved structured inputs
    instead of hard-coded scalar state assembly.
  • Added external-provider integration coverage for axis-aware state and
    a linear parameter source.

Copy link
Copy Markdown
Contributor

@jc-macdonald jc-macdonald left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — one bug to fix in the docs engine, and a minor note on dead code in _sample_parameter.

@@ -47,6 +56,9 @@ def runner(
msg = f"times[0] must be >= 0; got times[0]={times[0]}"
raise ValueError(msg)

y0 = np.stack([
initial_state[name].value for name in model_state.parameter_names
]).astype(np.float64)
args = tuple(val for val in params.values()) if params is not None else None
result = solve_ivp(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

params values are now ParameterValue objects, which have no arithmetic dunders. When solve_ivp forwards these as positional args to the stepper, beta * y_s * y_i raises TypeError: unsupported operand type(s) for *: 'ParameterValue' and 'float'. Needs unwrapping:

args = tuple(val.item() for val in params.values()) if params is not None else None

Comment thread src/flepimop2/simulator.py Outdated
msg = "parameter_configs must be set before resolving parameters."
raise ValueError(msg)
if name not in self.parameter_configs:
if request.optional:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this branch raises KeyError for a missing optional parameter, but resolve_inputs() already guards missing optionals with continue before calling _sample_parameter — so the request.optional branch here is dead code. Not blocking, just a heads-up.

Added the `axis` module for realizing and manipulating axes from
configuration. Includes:

- `ResolvedShape` as a container for metadata about the shape of an
  object based on its axes.
- `Axis` as a data class for a realized axis that objects which need
  axes can use to do their calculations.
- `AxisCollection` as a mapping of `Axis` objects that simplify access
  when multiple axes are needed.
- Dropped deprecation warning from using the `axes` attribute of
  `ConfigurationModel`.

Follow up to #147, #164.
Introduced `ParameterValue` as the return type for `ParameterABC.sample`
that contains the underlying value as a numpy array as well as metadata,
which at this point is the `ResolvedShape`. This container type is
extensible for further metadata in the future such as type info and
could easily be converted to a generic to generalize the underlying
value, e.g. to use jax arrays or similar.

Also added `ModelStateSpecification`, `ParameterRequest` as a way for
systems to specify their underlying state and shape as well as request
parameters that match that specification. However, this is not connected
to systems yet.

Currently the shapes are not utilized in simulation, but existing
engines/systems should work as is due to the simulate command reducing
parameters to scalars before calling `Simulator.run`
Added `SystemABC.requested_parameters()/model_state()` to allow systems
to provide information about their underlying state specification,
including shape, as well as request parameters that comply with said
requirements. Also integrated this work into the existing wrapper system
to demonstrate the functionality.

- Added `requested_parameters()/model_state()` methods to `SystemABC`. 
- Extended `wrap()`/`_AdapterSystem` to accept explicit parameter
  request and model state callbacks.
- Updated `_checked_partial()` so already materialized `ParameterValue`
  instances are preserved when binding static parameters.
- Simplified the documentation quick start wrapper config to the minimal
  valid example.
It pushes `ParameterValue` and model-state handling through the
simulator and engine interfaces so simulation, external providers, and
the docs example all execute against the same structured parameter
contract.

- Updated `EngineABC` and the engine protocol to run against structured
  `initial_state`, `params`, and optional `model_state`.
- Removed the engine protocol from `flepimop2.typing` and keep the
  engine specific contract alongside `engine.abc` and allow it to depend
  on `ParameterValue`.
- Adapted wrapper engine fixtures and tests to assemble numeric state
  from `ModelStateSpecification` and `ParameterValue` inputs.
- Wired the simulator and CLI flow through resolved structured inputs
  instead of hard-coded scalar state assembly.
- Added external-provider integration coverage for axis-aware state and
  a linear parameter source.
@TimothyWillard TimothyWillard force-pushed the integrate-params-into-sim branch from 87fa852 to 818ff28 Compare April 28, 2026 18:32
@TimothyWillard TimothyWillard marked this pull request as ready for review April 28, 2026 18:36
Comment on lines +33 to +38
def _scenario_value(
value: object,
template: ParameterValue | None = None,
) -> ParameterValue:
shape = template.shape if template is not None else ResolvedShape()
return ParameterValue(np.asarray(value, dtype=np.float64), shape)
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is temporary, I think ideally scenarios would also emit ParameterValue by default so scenarios could override parameters and the rest of flepimop2 could just consume them as the same thing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants