Skip to content

Microlensing start values invert an underdetermined system: mu_rel/pi_E direction is picked arbitrarily #93

Description

@jdeast

Split out from #92, which made this arbitrary choice deterministic without making it right.

The relations are correct; the inverse problem is not determined

The forward physics in components/mulensing/symbolic_physics.py is right:

relation check
mu_rel_mag**2 = mu_ra_rel**2 + mu_dec_rel**2 Pythagorean
pi_E_N = (pi_rel/theta_E)*(mu_dec_rel/mu_rel_mag) pi_E || mu_rel; North<->dec
pi_E_E = (pi_rel/theta_E)*(mu_ra_rel/mu_rel_mag) East<->RA
pi_rel = KAPPA*m*(pi_E_N**2 + pi_E_E**2) from theta_E = sqrt(kappaMpi_rel), pi_E = pi_rel/theta_E

Forward (physical -> observable) is single-valued and fine. Initialization runs it backwards, and there the system is underdetermined.

What the seed actually contains

For examples/DC2018_128, mmexofast.json carries only:

parameters: t_0, u_0, t_E, s, alpha, rho, q
sigmas:     t_0, u_0, t_E, log_rho, log_s, log_q, alpha

No pi_E, in either fit entry. So with t_E seeded and theta_E from mass/distance, the engine knows only the magnitude mu_rel_mag = theta_E / t_E. Splitting that into (mu_ra_rel, mu_dec_rel) is one equation in two unknowns -- a circle of solutions. The pi_E vector direction is undetermined for the same reason.

What the engine does with that

It assigns the entire magnitude to whichever component it reaches first and leaves the other at (numerically) zero. Observed start values at the pinned GOOD_RAW:

Both satisfy the magnitude equation exactly. They are different physical models.

Why this matters even now that it is deterministic

  1. The direction is observable. With annual parallax modeled, the sky direction of mu_rel affects the light curve -- which is precisely why the two branches gave chi2/N 9.96 vs 268.93 on the same data.
  2. Even the good branch is a poor start. chi2/N ~ 10 at a seed meant to sit near the MMEXOFAST solution suggests the arbitrary direction is leaving real information on the table on every machine, not just the one that exposed the bug.
  3. Exactly zero is a bad place to start. Putting the whole magnitude in one component makes the other exactly 0, so pi_E_N = 0 begins at a cusp of the normalization rather than in the interior.

Options

  1. Seed the direction from the galactic model, which already computes expected proper motions (components/galacticmodel/galacticmodel.py:364-365) -- a physically motivated split instead of "all in RA".
  2. Stop solving for it. Treat the mu_rel position angle as a free parameter with a prior, since the seed genuinely does not determine it.
  3. Have MMEXOFAST fit parallax and seed pi_E_N/pi_E_E for events with parallax signal.

(1) looks cheapest and strictly better than the status quo; (2) is the honest representation of what is known.

Unrelated thing noticed nearby

galacticmodel.py:364 passes pm_ra_cosdec=pm_ra, i.e. it reads star.pm_ra as mu_alpha*cos(delta), while mu_ra_rel = lens_pm_ra - source_pm_ra is convention-agnostic. Self-consistent only if every consumer of star.pm_ra uses the cos(delta)-multiplied convention. Not audited -- flagging, not claiming a bug.

Reproducing

diag_mulens.py (in #92's discussion) prints versions, all resolved initvals/scales, every parameter's physical value at GOOD_RAW, and the mulens residual summary. Run it on two machines and diff.

🤖 Generated with Claude Code

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions