Skip to content

Shape#431

Merged
quaquel merged 14 commits intomasterfrom
shape
Nov 4, 2025
Merged

Shape#431
quaquel merged 14 commits intomasterfrom
shape

Conversation

@quaquel
Copy link
Copy Markdown
Owner

@quaquel quaquel commented Oct 30, 2025

This PR adds a new shape keyword argument to Parameter, closing #303.

Before this PR, each uncertainty/lever needs to match with a keyword argument. However, if you have many identical parameters such as in case of the intertemporal lake problem, this is annoying to specify both at the model level and at the workbench level. This PR adds a shape argument to Parameter to resolve this issue. This PR also ensures that this shape parameter is properly handled in exploration, optimization, and when storing results.

Old API

def my_func(l_0=0.1, l_1=0.1, l_2=0.1):
    ....

Model("my_model", function=my_func)

model.levers = [RealParameter(f"l_{i}", 0, 1) for i in range(3)]

New API

def my_func(decisions:np.array[float]):
    ....

Model("my_model", function=my_func)

model.levers = [RealParameter(decisions 0, 1, shape=(3,)),]

@coveralls
Copy link
Copy Markdown

coveralls commented Oct 30, 2025

Coverage Status

coverage: 92.654% (+0.3%) from 92.394%
when pulling 7f03f46 on shape
into ba7fbbf on master.

@EwoutH
Copy link
Copy Markdown
Collaborator

EwoutH commented Oct 30, 2025

API looks quite clean!

Can you add a prefix or f-string or something for the name? T-strings might even be useful here.

Old way also still works like, if you need more flexibility on parameters, right?

@quaquel
Copy link
Copy Markdown
Owner Author

quaquel commented Oct 30, 2025

T-strings might be useful in the future, but they are Python 3.14. I need to work with 3.12 for HPC reasons.

yes old way will still work.

@EwoutH
Copy link
Copy Markdown
Collaborator

EwoutH commented Oct 30, 2025

I'm a bit confused looking at the implementation. The description shows:

def my_func(decisions:np.array[float]):
    ....

model.levers = [RealParameter(decisions, shape=(3,)),]

Several questions:

  1. What is decisions here? In RealParameter(decisions, shape=(3,)), is decisions supposed to be:
    • The string "decisions" (the parameter name)?
    • The numpy array type annotation from the function signature?
    • Something else?
  2. Where is shape implemented? I don't see shape being added to any Parameter.__init__() method in the diff. Is this still TODO?
  3. Where are the bounds? The old API has RealParameter(f"l_{i}", 0, 1) with bounds. The new API example doesn't show bounds, are they still required?

@quaquel
Copy link
Copy Markdown
Owner Author

quaquel commented Oct 30, 2025

Its wip, so I was doing stuff locally. The latest commit should provide some additional clarity, but there is still more work to be done.

1: The new API contained a mistake. I had forgotten the bounds.
2. "decisions" is indeed the name of the keyword argument. As allways with the workbench, parameter names must match keyword arguments on a function (if your model is a python function).
3. Shape is now added to all parameter classes. I have updated the samplers to reflect this change. Next, we need to turn the samples back into their original shape. After that, there is still the storage in the callback and, of course, the optimization side of things.

@EwoutH
Copy link
Copy Markdown
Collaborator

EwoutH commented Oct 31, 2025

Thanks for explaining.

Might be useful to mark the PR as "Draft", to reflect its state.

@quaquel quaquel merged commit e8f4a28 into master Nov 4, 2025
24 checks passed
@quaquel quaquel deleted the shape branch November 4, 2025 13:43
@quaquel quaquel added the feature label Nov 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants