Source code for eat.factor
from __future__ import division
from __future__ import unicode_literals
from __future__ import print_function
from __future__ import absolute_import
from future import standard_library
standard_library.install_aliases()
import math
import numpy as np
from scipy.optimize import least_squares
[docs]def factor(bb, initial_guess=None, weight=1.0):
"""
Factor out site-based delay and rate from baseline-based slopes
The linear drift (slopes) of phase with frequency and time are
usually interpreted as delays and rates, and are removed from the
VLBI data in fringe fitting. Let n be the number of feeds. There
are n (n-1) such slopes in total. Using all of them, such as
currently done in HOPS, breaks the closure relationships in
general.
Global fringe solution takes into account the assumption that
delays and rates are site-based. The main advantage is that it
preserves the closure relationships. Even better, "one person's
noise is another person's signal", the remaining slopes after
global fringe fit actually contain information about the source
images. Being able to factor out these site-based "noise" from
the baseline-based "data", therefore, is essential for developing
new high-order image reconstruction methods.
We implement here a very general approach to factor out site-based
information from baseline-based information. Let obs[] be the
observational data and sol[] be the solution array, the simplest
error function is
chi[ref, rem] = obs[ref, rem] - (sol[ref] - sol[rem])
so that the minimization is performed over
chi^2 = sum_baselines chi[ref, rem]^2 / sigma[ref, rem]^2
However, it is clear that sol[] is not uniquely determined because
chi[ref, rem] is invariant to a global constant offset to sol[].
The simplest fix is to add the regularizer
w mean(sol)^2
Args:
bb: A numpy structured array or pandas dataframe of
baseline-based input data
initial_guess: Initial conditions of the minimizer
weight: Weight of the regularizer
Returns:
A dictionary of site-based data being factored out
"""
feeds = set(bb['ref']) | set(bb['rem'])
fmap = {f: i for i, f in enumerate(feeds)}
ref = np.array([fmap[f] for f in bb['ref']])
rem = np.array([fmap[f] for f in bb['rem']])
obs = np.array( bb['val'] )
try:
err = np.array(bb['err'])
except:
err = 1.0
def regchi(sol):
# closure (as in functional languages) on ref, rem, obs, and err
return np.append((obs - (sol[ref] - sol[rem])) / err,
math.sqrt(weight) * np.mean(sol))
if initial_guess is None:
initial_guess = np.zeros(len(feeds))
elif len(initial_guess) != len(feeds):
raise IndexError("Lengths of initial_guess ({}) and feeds ({}) "
"do not match".format(len(initial_guess),
len(feeds)))
sol = least_squares(regchi, initial_guess)
if sol.success:
return {f: sol.x[i] for i, f in enumerate(feeds)}
else:
return None