'Compute a lifetimes matrix from our AssetFinder, then drop columns that
didn\'t exist at all during the query dates.
Parameters
start_date : pd.Timestamp
Base start date for the matrix.
end_date : pd.Timestamp
End date for the matrix.
extra_rows : int
Number of extra rows to compute before `start_date`.
Extra rows are needed by terms like moving averages that require a
trailing window of data.
Returns
lifetimes : pd.DataFrame
Frame of dtype `bool` containing dates from `extra_rows` days
before `start_date`, continuing through to `end_date`.  The
returned frame contains as columns all assets in our AssetFinder
that existed for at least one day between `start_date` and
`end_date`.'

def _compute_root_mask(self, start_date, end_date, extra_rows):

    calendar = self._calendar
    finder = self._finder
    (start_idx, end_idx) = self._calendar.slice_locs(start_date, end_date)
    if (start_idx < extra_rows):
        raise NoFurtherDataError.from_lookback_window(initial_message='Insufficient    data    to    compute    Pipeline:', first_date=calendar[0], lookback_start=start_date, lookback_length=extra_rows)
    lifetimes = finder.lifetimes(calendar[(start_idx - extra_rows):end_idx], include_start_date=False)
    assert (lifetimes.index[extra_rows] == start_date)
    assert (lifetimes.index[(-1)] == end_date)
    if (not lifetimes.columns.unique):
        columns = lifetimes.columns
        duplicated = columns[columns.duplicated()].unique()
        raise AssertionError(('Duplicated    sids:    %d' % duplicated))
    existed = lifetimes.any()
    ret = lifetimes.loc[:, existed]
    shape = ret.shape
    assert ((shape[0] * shape[1]) != 0), 'root    mask    cannot    be    empty'
    return ret
