Beispiel #1
0
    def __init__(self, label, data_arr, ndir, nmod, times, frequencies,
                 chunk_label, options, cykernel):
        """
        Initialises a gain machine which supports solution intervals.
        
        Args:
            label (str):
                Label identifying the Jones term.
            data_arr (np.ndarray): 
                Shape (n_mod, n_tim, n_fre, n_ant, n_ant, n_cor, n_cor) array containing observed 
                visibilities. 
            ndir (int):
                Number of directions.
            nmod (nmod):
                Number of models.
            times (np.ndarray):
                Times for the data being processed.
            freqs (np.ndarray):
                Frequencies for the data being processsed.
            options (dict): 
                Dictionary of options. 
        """

        MasterMachine.__init__(self, label, data_arr, ndir, nmod, times,
                               frequencies, chunk_label, options)

        self.cykernel = cykernel

        self.t_int = options["time-int"] or self.n_tim
        self.f_int = options["freq-int"] or self.n_fre
        self.eps = 1e-6

        # Initialise attributes used for computing values over intervals.
        # n_tim and n_fre are the time and frequency dimensions of the data arrays.
        # n_timint and n_freint are the time and frequency dimensions of the gains.

        self.t_bins = range(0, self.n_tim, self.t_int)
        self.f_bins = range(0, self.n_fre, self.f_int)

        self.n_timint = len(self.t_bins)
        self.n_freint = len(self.f_bins)
        self.n_tf_ints = self.n_timint * self.n_freint

        # number of valid solutions
        self.n_valid_sols = self.n_dir * self.n_tf_ints

        # split grids into intervals, and find the centre of gravity of each
        timebins = np.split(times, self.t_bins[1:])
        freqbins = np.split(frequencies, self.f_bins[1:])
        timegrid = np.array([float(x.mean()) for x in timebins])
        freqgrid = np.array([float(x.mean()) for x in freqbins])

        # interval_grid determines the per-interval grid poins
        self.interval_grid = dict(time=timegrid, freq=freqgrid)
        # data_grid determines the full resolution grid
        self.data_grid = dict(time=times, freq=frequencies)

        # compute index from each data point to interval number
        t_ind = np.arange(self.n_tim) // self.t_int
        f_ind = np.arange(self.n_fre) // self.f_int

        self.t_mapping, self.f_mapping = np.meshgrid(t_ind,
                                                     f_ind,
                                                     indexing="ij")

        # Initialise attributes used in convergence testing. n_cnvgd is the number
        # of solutions which have converged.

        self._has_stalled = False
        self.n_cnvgd = 0
        self._frac_cnvgd = 0
        self.iters = 0
        self.min_quorum = options["conv-quorum"]
        self.update_type = options["update-type"]
        self.ref_ant = options["ref-ant"]
        self.fix_directions = options["fix-dirs"] or []
        if type(self.fix_directions) is int:
            self.fix_directions = [self.fix_directions]
        # True if gains are loaded from a DB
        self._gains_loaded = False

        # Construct flag array and populate flagging attributes.
        self.max_gain_error = options["max-prior-error"]
        self.max_post_error = options["max-post-error"]

        self.clip_lower = options["clip-low"]
        self.clip_upper = options["clip-high"]
        self.clip_after = options["clip-after"]

        self.init_gains()
        self.old_gains = self.gains.copy()

        # Gain error estimates. Populated by subclasses, if available
        # Should be array of same shape as the gains
        self.prior_gain_error = None
        self.posterior_gain_error = None

        # buffers for arrays used in internal updates
        self._jh = self._jhr = self._jhj = self._gh = self._r = self._ginv = self._ghinv = None
        self._update = None

        # flag: have gains been updated
        self._gh_update = self._ghinv_update = True
Beispiel #2
0
    def __init__(self, label, data_arr, ndir, nmod, times, frequencies,
                 chunk_label, jones_options):
        """
        Initialises a chain of complex 2x2 gain machines.
        
        Args:
            label (str):
                Label identifying the Jones term.
            data_arr (np.ndarray): 
                Shape (n_mod, n_tim, n_fre, n_ant, n_ant, n_cor, n_cor) array containing observed 
                visibilities. 
            ndir (int):
                Number of directions.
            nmod (nmod):
                Number of models.
            times (np.ndarray):
                Times for the data being processed.
            frequencies (np.ndarray):
                Frequencies for the data being processsed.
            jones_options (dict): 
                Dictionary of options pertaining to the chain. 
        """
        from cubical.main import UserInputError
        # This instantiates the number of complex 2x2 elements in our chain. Each element is a
        # gain machine in its own right - the purpose of this machine is to manage these machines
        # and do the relevant fiddling between parameter updates. When combining DD terms with
        # DI terms, we need to be initialise the DI terms using only one direction - we do this with
        # slicing rather than summation as it is slightly faster.
        self.jones_terms = []
        self.num_left_di_terms = 0  # how many DI terms are there at the left of the chain
        seen_dd_term = False

        for iterm, term_opts in enumerate(jones_options['chain']):
            jones_class = machine_types.get_machine_class(term_opts['type'])
            if jones_class is None:
                raise UserInputError("unknown Jones class '{}'".format(
                    term_opts['type']))
            if not issubclass(jones_class, Complex2x2Gains) and not issubclass(
                    jones_class, ComplexW2x2Gains) and term_opts['solvable']:
                raise UserInputError(
                    "only complex-2x2 or robust-2x2 terms can be made solvable in a Jones chain"
                )
            term = jones_class(term_opts["label"], data_arr, ndir, nmod, times,
                               frequencies, chunk_label, term_opts)
            self.jones_terms.append(term)
            if term.dd_term:
                seen_dd_term = True
            elif not seen_dd_term:
                self.num_left_di_terms = iterm

        MasterMachine.__init__(self, label, data_arr, ndir, nmod, times,
                               frequencies, chunk_label, jones_options)

        self.chain = cubical.kernels.import_kernel("chain")
        # kernel used for compute_residuals and such
        self.kernel = Complex2x2Gains.get_full_kernel(
            jones_options, diag_gains=self.is_diagonal)

        self.n_dir, self.n_mod = ndir, nmod
        _, self.n_tim, self.n_fre, self.n_ant, self.n_ant, self.n_cor, self.n_cor = data_arr.shape

        self.n_terms = len(self.jones_terms)
        # make list of number of iterations per solvable term
        # If not specified, just use the maxiter setting of each term
        # note that this list is updated as we converge, so make a copy
        term_iters = jones_options['sol']['term-iters']
        if not term_iters:
            self.term_iters = [
                term.maxiter for term in self.jones_terms if term.solvable
            ]
        elif type(term_iters) is int:
            self.term_iters = [term_iters]
        elif isinstance(term_iters, (list, tuple)):
            self.term_iters = list(term_iters)
        else:
            raise UserInputError(
                "invalid term-iters={} setting".format(term_iters))

        self.solvable = bool(self.term_iters) and any(
            [term.solvable for term in self.jones_terms])

        # setup first solvable term in chain
        self.active_index = None

        # this list accumulates the per-term convergence status strings
        self._convergence_states = []
        # True when the last active term has had its convergence status queried
        self._convergence_states_finalized = False

        self.cached_model_arr = self._r = self._m = None
    def __init__(self, label, data_arr, ndir, nmod, times, frequencies, chunk_label, options):
        """
        Initialises a gain machine which supports solution intervals.
        
        Args:
            label (str):
                Label identifying the Jones term.
            data_arr (np.ndarray): 
                Shape (n_mod, n_tim, n_fre, n_ant, n_ant, n_cor, n_cor) array containing observed 
                visibilities. 
            ndir (int):
                Number of directions.
            nmod (nmod):
                Number of models.
            times (np.ndarray):
                Times for the data being processed.
            freqs (np.ndarray):
                Frequencies for the data being processsed.
            options (dict): 
                Dictionary of options.
            diag_gains (bool):
                If True, gains are diagonal-only. Else gains are full 2x2.
        """

        MasterMachine.__init__(self, label, data_arr, ndir, nmod, times, frequencies,
                               chunk_label, options)

        # select which kernels to use for computing full data
        self.kernel = self.get_full_kernel(options, self.is_diagonal)

        # kernel used in solver is diag-diag in diag mode, else uses full kernel version
        if options.get('diag-data') or options.get('diag-only'):
            self.kernel_solve = cubical.kernels.import_kernel('diagdiag_complex')
        else:
            self.kernel_solve = self.kernel

        log(2).print("{} kernels are {} {}".format(label, self.kernel, self.kernel_solve))

        self.t_int = options["time-int"] or self.n_tim
        self.f_int = options["freq-int"] or self.n_fre
        self.eps = 1e-6

        # Initialise attributes used for computing values over intervals.
        # n_tim and n_fre are the time and frequency dimensions of the data arrays.
        # n_timint and n_freint are the time and frequency dimensions of the gains.

        self.t_bins = list(range(0, self.n_tim, self.t_int))
        self.f_bins = list(range(0, self.n_fre, self.f_int))

        self.n_timint = len(self.t_bins)
        self.n_freint = len(self.f_bins)
        self.n_tf_ints = self.n_timint * self.n_freint

        # number of valid solutions
        self.n_valid_sols = self.n_dir * self.n_tf_ints

        # split grids into intervals, and find the centre of gravity of each
        timebins = np.split(times, self.t_bins[1:])
        freqbins = np.split(frequencies, self.f_bins[1:])
        timegrid = np.array([float(x.mean()) for x in timebins])
        freqgrid = np.array([float(x.mean()) for x in freqbins])

        # interval_grid determines the per-interval grid poins
        self.interval_grid = dict(time=timegrid, freq=freqgrid)
        # data_grid determines the full resolution grid
        self.data_grid = dict(time=times, freq=frequencies)

        # compute index from each data point to interval number
        t_ind = np.arange(self.n_tim)//self.t_int
        f_ind = np.arange(self.n_fre)//self.f_int

        self.t_mapping, self.f_mapping = np.meshgrid(t_ind, f_ind, indexing="ij")

        # Initialise attributes used in convergence testing. n_cnvgd is the number
        # of solutions which have converged.

        self._has_stalled = False
        self.n_cnvgd = 0
        self._frac_cnvgd = 0
        self.iters = 0
        self.min_quorum = options["conv-quorum"]
        self.update_type = options["update-type"]
        self.ref_ant = options["ref-ant"]
        self.fix_directions = options["fix-dirs"] if options["fix-dirs"] is not None and \
                options["fix-dirs"] != "" else []

        if type(self.fix_directions) is int:
            self.fix_directions = [self.fix_directions]
        if type(self.fix_directions) is str and re.match(r"^\W*\d{1,}(\W*,\W*\d{1,})*\W*$", self.fix_directions):
            self.fix_directions = map(int, map(str.strip, ",".split(self.fix_directions)))

        if not (type(self.fix_directions) is list and
                all(map(lambda x: type(x) is int, self.fix_directions))):
            raise ArgumentError("Fix directions must be number or list of numbers")

        # True if gains are loaded from a DB
        self._gains_loaded = False

        # Construct flag array and populate flagging attributes.
        self.max_gain_error = options["max-prior-error"]
        self.max_post_error = options["max-post-error"]
        self.low_snr_warn = options["low-snr-warn"]
        self.high_gain_var_warn = options["high-gain-var-warn"]
        self.clip_lower = options["clip-low"]
        self.clip_upper = options["clip-high"]
        self.clip_after = options["clip-after"]

        self.init_gains()
        self.old_gains = self.gains.copy()

        # Gain error estimates. Populated by subclasses, if available
        # Should be array of same shape as the gains
        self.prior_gain_error = None
        self.posterior_gain_error = None

        # buffers for arrays used in internal updates
        self._jh = self._jhr = self._jhj = self._gh = self._r = self._ginv = self._ghinv = None
        self._update = None

        # flag: have gains been updated
        self._gh_update = self._ghinv_update = True