astropy / astropy/astropy

Tied Parameters Break in Compound Models Due to Name Mangling

Open
#18,259 3 comments 0 reactions 0 assignees View on GitHub
Bug modeling
Dominant language
Python
Stars
5.3k
Forks
2.2k
Avg merge
1d 18h
Merged PRs (30d)
74

Description

### Description

When using astropy.modeling, it’s possible to tie parameters within a custom model using lambda functions, e.g. param2.tied = lambda m: 0.5 * m.param1. This works as expected when the model is standalone.

However, if the same model is added to another (e.g., forming a compound model with model1 + model2), the parameter names are internally renamed (e.g., param1 → model_0_param1), and the original tie function no longer works, because it refers to attributes (m.param1) that no longer exist on the compound model.

### Expected behavior

In complex models with many parameters it would be great to be able to add and remove components without every time adjusting the links.
Is there a way to easily link (tie) parameters keeping the possibility to change the compound model later? Is the lambda approach the only one possible?

### How to Reproduce

```python
import numpy as np
from astropy.modeling import Fittable1DModel, Parameter, models, fitting

# --- Custom model with two tied amplitudes ---
class TwoGaussians(Fittable1DModel):
amp1 = Parameter(default=1.0)
amp2 = Parameter(default=0.5)
mean1 = Parameter(default=0.0)
mean2 = Parameter(default=2.0)
stddev1 = Parameter(default=1.0)
stddev2 = Parameter(default=1.0)

def evaluate(self, x, amp1, amp2, mean1, mean2, stddev1, stddev2):
g1 = amp1 * np.exp(-0.5 * ((x - mean1) / stddev1)**2)
g2 = amp2 * np.exp(-0.5 * ((x - mean2) / stddev2)**2)
return g1 + g2

# Create data
x = np.linspace(-5, 5, 100)
true_model = TwoGaussians(amp1=2.0, amp2=1.0, mean1=0.0, mean2=2.0, stddev1=0.5, stddev2=0.5)
y = true_model(x) + 0.1 * np.random.normal(size=len(x))

# --- First: tied parameters, works! ---
model = TwoGaussians()
model.amp2.tied = lambda m: 0.5 * m.amp1 # Tie works in standalone model

fitter = fitting.LevMarLSQFitter()
fit1 = fitter(model, x, y)

print("Standalone model fit:")
print(f"amp1 = {fit1.amp1.value:.3f}, amp2 = {fit1.amp2.value:.3f} (should be half of amp1)")

# --- Now: Add a constant model, tie breaks ---
compound = model + models.Const1D(amplitude=0.0)

# # Need to reset the tied parameter in the compound model to make it work!
# compound.amp2_0.tied = lambda m: 0.5 * m.amp1_0

try:
fit2 = fitter(compound, x, y)
print("\nCompound model fit:")
print(f"amp1 = {fit2.amp1_0.value:.3f}, amp2 = {fit2.amp2_0.value:.3f}")
except Exception as e:
print("\nCompound model fit failed with error:")
raise e
```
```
Standalone model fit:
amp1 = 1.986, amp2 = 0.993 (should be half of amp1)

Compound model fit failed with error:
Attribute "amp1" not found
```

### Versions

_No response_

Contributor guide

Open the contributing guide

Research direction

Start by running the reproduction in the issue and inspect astropy.modeling's compound-model parameter renaming and tied-parameter handling. Done means a tie defined on a standalone model remains usable after that model is composed, with regression coverage for the shown failure and the existing fitting behavior preserved.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.