sacc 1.0.2__tar.gz → 2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sacc-1.0.2 → sacc-2.0}/.github/workflows/desc-ci.yml +14 -4
- {sacc-1.0.2/sacc.egg-info → sacc-2.0}/PKG-INFO +6 -4
- {sacc-1.0.2 → sacc-2.0}/README.md +2 -0
- sacc-2.0/codecov.yml +36 -0
- sacc-2.0/examples/demo.py +50 -0
- {sacc-1.0.2 → sacc-2.0}/pyproject.toml +3 -5
- {sacc-1.0.2 → sacc-2.0}/sacc/__init__.py +4 -1
- {sacc-1.0.2 → sacc-2.0}/sacc/covariance.py +202 -96
- {sacc-1.0.2 → sacc-2.0}/sacc/data_types.py +64 -9
- sacc-2.0/sacc/io.py +414 -0
- {sacc-1.0.2 → sacc-2.0}/sacc/sacc.py +372 -164
- sacc-2.0/sacc/tracer_uncertainty/__init__.py +1 -0
- sacc-2.0/sacc/tracer_uncertainty/base.py +34 -0
- sacc-2.0/sacc/tracer_uncertainty/nz.py +287 -0
- sacc-2.0/sacc/tracers/__init__.py +6 -0
- sacc-2.0/sacc/tracers/base.py +127 -0
- sacc-2.0/sacc/tracers/clusters.py +332 -0
- sacc-2.0/sacc/tracers/maps.py +206 -0
- sacc-2.0/sacc/tracers/misc.py +87 -0
- sacc-2.0/sacc/tracers/nz.py +285 -0
- sacc-2.0/sacc/tracers/survey.py +75 -0
- {sacc-1.0.2 → sacc-2.0}/sacc/utils.py +46 -2
- {sacc-1.0.2 → sacc-2.0}/sacc/windows.py +116 -102
- {sacc-1.0.2 → sacc-2.0/sacc.egg-info}/PKG-INFO +6 -4
- {sacc-1.0.2 → sacc-2.0}/sacc.egg-info/SOURCES.txt +16 -2
- {sacc-1.0.2 → sacc-2.0}/sacc.egg-info/requires.txt +3 -3
- sacc-2.0/test/legacy_files/old_qp_sacc.fits +0 -0
- {sacc-1.0.2 → sacc-2.0}/test/test_cluster_data_tracers.py +17 -15
- sacc-2.0/test/test_covariance.py +141 -0
- {sacc-1.0.2 → sacc-2.0}/test/test_sacc.py +431 -148
- sacc-1.0.2/sacc/tracers.py +0 -1217
- {sacc-1.0.2 → sacc-2.0}/.git_archival.txt +0 -0
- {sacc-1.0.2 → sacc-2.0}/.gitattributes +0 -0
- {sacc-1.0.2 → sacc-2.0}/.github/workflows/publish.yml +0 -0
- {sacc-1.0.2 → sacc-2.0}/.gitignore +0 -0
- {sacc-1.0.2 → sacc-2.0}/.readthedocs.yml +0 -0
- {sacc-1.0.2 → sacc-2.0}/LICENSE +0 -0
- {sacc-1.0.2 → sacc-2.0}/MANIFEST.in +0 -0
- {sacc-1.0.2 → sacc-2.0}/doc/Makefile +0 -0
- {sacc-1.0.2 → sacc-2.0}/doc/format.md +0 -0
- {sacc-1.0.2 → sacc-2.0}/doc/requirements.txt +0 -0
- {sacc-1.0.2 → sacc-2.0}/doc/source/Makefile +0 -0
- {sacc-1.0.2 → sacc-2.0}/doc/source/api.rst +0 -0
- {sacc-1.0.2 → sacc-2.0}/doc/source/conf.py +0 -0
- {sacc-1.0.2 → sacc-2.0}/doc/source/covariance.rst +0 -0
- {sacc-1.0.2 → sacc-2.0}/doc/source/data_types.rst +0 -0
- {sacc-1.0.2 → sacc-2.0}/doc/source/index.rst +0 -0
- {sacc-1.0.2 → sacc-2.0}/doc/source/intro.rst +0 -0
- {sacc-1.0.2 → sacc-2.0}/doc/source/sacc.rst +0 -0
- {sacc-1.0.2 → sacc-2.0}/doc/source/tracers.rst +0 -0
- {sacc-1.0.2 → sacc-2.0}/doc/source/utils.rst +0 -0
- {sacc-1.0.2 → sacc-2.0}/doc/source/windows.rst +0 -0
- {sacc-1.0.2 → sacc-2.0}/examples/.gitignore +0 -0
- {sacc-1.0.2 → sacc-2.0}/examples/CMB_LSS_read.ipynb +0 -0
- {sacc-1.0.2 → sacc-2.0}/examples/CMB_LSS_write.ipynb +0 -0
- {sacc-1.0.2 → sacc-2.0}/examples/Convert_DES_Sacc.ipynb +0 -0
- {sacc-1.0.2 → sacc-2.0}/examples/Convert_KIDS_Sacc.ipynb +0 -0
- {sacc-1.0.2 → sacc-2.0}/examples/Create_Sacc.ipynb +0 -0
- {sacc-1.0.2 → sacc-2.0}/examples/README.md +0 -0
- {sacc-1.0.2 → sacc-2.0}/examples/SACC_for_clusters.ipynb +0 -0
- {sacc-1.0.2 → sacc-2.0}/examples/SACC_read.ipynb +0 -0
- {sacc-1.0.2 → sacc-2.0}/examples/SACC_write.ipynb +0 -0
- {sacc-1.0.2 → sacc-2.0}/examples/demo_sacc_for_clusters_N+M.ipynb +0 -0
- {sacc-1.0.2 → sacc-2.0}/examples/example-txpipe-sacc1.sacc +0 -0
- {sacc-1.0.2 → sacc-2.0}/requirements.txt +0 -0
- {sacc-1.0.2 → sacc-2.0}/sacc.egg-info/dependency_links.txt +0 -0
- {sacc-1.0.2 → sacc-2.0}/sacc.egg-info/top_level.txt +0 -0
- {sacc-1.0.2 → sacc-2.0}/setup.cfg +0 -0
- {sacc-1.0.2 → sacc-2.0}/test/__init__.py +0 -0
- {sacc-1.0.2 → sacc-2.0}/test/data/.gitignore +0 -0
- {sacc-1.0.2 → sacc-2.0}/test/make_test_data.py +0 -0
@@ -12,11 +12,11 @@ jobs:
|
|
12
12
|
runs-on: ubuntu-latest
|
13
13
|
strategy:
|
14
14
|
matrix:
|
15
|
-
python-version: [3.
|
15
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
16
16
|
|
17
17
|
steps:
|
18
18
|
- name: Checkout repository
|
19
|
-
uses: actions/checkout@
|
19
|
+
uses: actions/checkout@v4
|
20
20
|
|
21
21
|
- name: Setup python
|
22
22
|
uses: actions/setup-python@v4
|
@@ -26,15 +26,25 @@ jobs:
|
|
26
26
|
- name: Dependencies
|
27
27
|
run: |
|
28
28
|
python -m pip install --upgrade numpy pytest pytest-cov coverage pip
|
29
|
+
# Temporary until fixes applied in another PR
|
29
30
|
python -m pip install '.[all]'
|
30
31
|
|
31
32
|
- name: Run tests and measure coverage
|
32
33
|
run: |
|
33
|
-
pytest --cov sacc --cov-report xml:coverage.xml
|
34
|
+
pytest --cov sacc --cov-report xml:coverage.xml --cov-branch ./test
|
34
35
|
|
35
|
-
- name: Report coverage
|
36
|
+
- name: Report coverage in GitHub PR
|
36
37
|
uses: orgoro/coverage@v3.2
|
37
38
|
if: matrix.python-version == '3.11'
|
38
39
|
with:
|
39
40
|
coverageFile: ./coverage.xml
|
40
41
|
token: ${{ secrets.GITHUB_TOKEN }}
|
42
|
+
- name: Upload coverage reports to Codecov
|
43
|
+
if: matrix.python-version == '3.11'
|
44
|
+
uses: codecov/codecov-action@v5
|
45
|
+
with:
|
46
|
+
fail_ci_if_error: true
|
47
|
+
verbose: true
|
48
|
+
use_oidc: false
|
49
|
+
env:
|
50
|
+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: sacc
|
3
|
-
Version:
|
3
|
+
Version: 2.0
|
4
4
|
Summary: SACC - the LSST/DESC summary statistic data format library
|
5
5
|
Author-email: LSST DESC <joezuntz@googlemail.com>
|
6
6
|
License: BSD-3-Clause
|
@@ -10,17 +10,19 @@ Classifier: Operating System :: OS Independent
|
|
10
10
|
Description-Content-Type: text/markdown
|
11
11
|
License-File: LICENSE
|
12
12
|
Requires-Dist: scipy
|
13
|
-
Requires-Dist: numpy>=
|
13
|
+
Requires-Dist: numpy>=2
|
14
14
|
Requires-Dist: astropy
|
15
15
|
Provides-Extra: all
|
16
|
-
Requires-Dist: qp-prob[all]; extra == "all"
|
16
|
+
Requires-Dist: qp-prob[all]>=1; extra == "all"
|
17
17
|
Requires-Dist: numpydoc; extra == "all"
|
18
18
|
Provides-Extra: doc
|
19
19
|
Requires-Dist: numpydoc; extra == "doc"
|
20
20
|
Provides-Extra: qp
|
21
|
-
Requires-Dist: qp-prob[all]; extra == "qp"
|
21
|
+
Requires-Dist: qp-prob[all]>=1; extra == "qp"
|
22
22
|
Dynamic: license-file
|
23
23
|
|
24
|
+
[](https://codecov.io/github/LSSTDESC/sacc)
|
25
|
+
|
24
26
|
Sacc
|
25
27
|
====
|
26
28
|
|
sacc-2.0/codecov.yml
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# See https://docs.codecov.com/docs/codecovyml-reference for config reference
|
2
|
+
# To validate: `curl -X POST --data-binary @codecov.yml https://codecov.io/validate`
|
3
|
+
|
4
|
+
comment:
|
5
|
+
layout: "diff, flags, files"
|
6
|
+
behavior: default
|
7
|
+
require_changes: false
|
8
|
+
require_base: false
|
9
|
+
require_head: true
|
10
|
+
hide_project_coverage: false
|
11
|
+
|
12
|
+
coverage:
|
13
|
+
precision: 1
|
14
|
+
round: down
|
15
|
+
# red below 70, yellow between 70 and 95, green above 95.
|
16
|
+
range: 70..95
|
17
|
+
|
18
|
+
status:
|
19
|
+
patch:
|
20
|
+
default:
|
21
|
+
target: 100%
|
22
|
+
threshold: 0%
|
23
|
+
if_ci_failed: error
|
24
|
+
only_pulls: false
|
25
|
+
|
26
|
+
project:
|
27
|
+
default:
|
28
|
+
target: auto
|
29
|
+
threshold: 0%
|
30
|
+
if_ci_failed: error
|
31
|
+
only_pulls: false
|
32
|
+
removed_code_behavior: fully_covered_patch
|
33
|
+
|
34
|
+
github_checks:
|
35
|
+
annotations: true
|
36
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
|
2
|
+
import numpy as np
|
3
|
+
from sacc import Sacc, BandpowerWindow
|
4
|
+
|
5
|
+
# Create Sacc object
|
6
|
+
s = Sacc()
|
7
|
+
|
8
|
+
# Add metadata
|
9
|
+
s.metadata['creator'] = 'Demo'
|
10
|
+
s.metadata['purpose'] = 'Full feature test'
|
11
|
+
|
12
|
+
# Add tracers
|
13
|
+
z = np.linspace(0, 1, 101)
|
14
|
+
nz = np.exp(-((z-0.5)/0.1)**2)
|
15
|
+
s.add_tracer('NZ', 'source_0', z, nz)
|
16
|
+
s.add_tracer('NZ', 'source_1', z, nz)
|
17
|
+
|
18
|
+
nu = np.linspace(30, 60, 5)
|
19
|
+
bandpass = np.ones(5)
|
20
|
+
|
21
|
+
ell = np.linspace(10, 1000, 100)
|
22
|
+
beam = np.exp(-0.01 * ell)
|
23
|
+
s.add_tracer('NuMap', 'cmb', 2, nu, bandpass, ell, beam)
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
# Add window (correct shape)
|
34
|
+
ells_large = np.arange(100)
|
35
|
+
window_matrix = np.eye(100) # shape (100, 100)
|
36
|
+
win1 = BandpowerWindow(ells_large, window_matrix)
|
37
|
+
|
38
|
+
# Add data points, referencing the window object directly
|
39
|
+
for i in range(100):
|
40
|
+
s.add_data_point('cl_ee', ('source_0', 'source_1'), 0.1*i, ell=ell[i], window=win1)
|
41
|
+
s.add_data_point('cl_00', ('source_0', 'cmb'), 0.2*i, ell=ell[i], window=win1)
|
42
|
+
|
43
|
+
# Add covariance
|
44
|
+
n = len(s.data)
|
45
|
+
cov = np.eye(n)
|
46
|
+
s.add_covariance(cov)
|
47
|
+
|
48
|
+
# Write to files
|
49
|
+
s.save_fits('demo.fits', overwrite=True)
|
50
|
+
s.save_hdf5('demo.hdf5', overwrite=True)
|
@@ -17,7 +17,7 @@ classifiers = [
|
|
17
17
|
|
18
18
|
dependencies = [
|
19
19
|
"scipy",
|
20
|
-
"numpy>=
|
20
|
+
"numpy>=2",
|
21
21
|
"astropy"
|
22
22
|
]
|
23
23
|
|
@@ -37,9 +37,9 @@ Homepage = "https://github.com/LSSTDESC/sacc"
|
|
37
37
|
|
38
38
|
|
39
39
|
[project.optional-dependencies]
|
40
|
-
all = ["qp-prob[all]", "numpydoc"]
|
40
|
+
all = ["qp-prob[all]>=1", "numpydoc"]
|
41
41
|
doc = ["numpydoc"]
|
42
|
-
qp = ["qp-prob[all]"]
|
42
|
+
qp = ["qp-prob[all]>=1"]
|
43
43
|
|
44
44
|
[tool.setuptools.packages.find]
|
45
45
|
where = ["."]
|
@@ -48,5 +48,3 @@ include = ["sacc"]
|
|
48
48
|
[tool.setuptools.dynamic]
|
49
49
|
version = {attr = "sacc.__version__"}
|
50
50
|
|
51
|
-
[tool.coverage.run]
|
52
|
-
relative_paths = true
|
@@ -3,4 +3,7 @@ from .windows import Window, BandpowerWindow, TopHatWindow, LogTopHatWindow # n
|
|
3
3
|
from .data_types import standard_types, parse_data_type_name, build_data_type_name # noqa
|
4
4
|
from .tracers import BaseTracer # noqa
|
5
5
|
from .covariance import BaseCovariance # noqa
|
6
|
-
|
6
|
+
from .tracer_uncertainty import NZLinearUncertainty, NZShiftUncertainty, NZShiftStretchUncertainty # noqa
|
7
|
+
from .io import BaseIO # noqa
|
8
|
+
from . import io # noqa
|
9
|
+
__version__ = '2.0' #noqa
|
@@ -1,12 +1,12 @@
|
|
1
|
-
from astropy.io import fits
|
2
1
|
from astropy.table import Table
|
3
2
|
import scipy.linalg
|
4
3
|
import numpy as np
|
4
|
+
import warnings
|
5
5
|
|
6
6
|
from .utils import invert_spd_matrix
|
7
|
+
from .io import BaseIO, ONE_OBJECT_PER_TABLE, ONE_OBJECT_MULTIPLE_TABLES
|
7
8
|
|
8
|
-
|
9
|
-
class BaseCovariance:
|
9
|
+
class BaseCovariance(BaseIO):
|
10
10
|
"""
|
11
11
|
The abstract base class for covariances in different forms.
|
12
12
|
These are not currently designed to be modified after creation.
|
@@ -26,7 +26,7 @@ class BaseCovariance:
|
|
26
26
|
cov_type: string
|
27
27
|
The type of the covariance (class variable)
|
28
28
|
"""
|
29
|
-
|
29
|
+
_sub_classes = {}
|
30
30
|
|
31
31
|
def __init__(self):
|
32
32
|
"""Abstract superclass constructor.
|
@@ -37,13 +37,34 @@ class BaseCovariance:
|
|
37
37
|
self._dense = None
|
38
38
|
self._dense_inverse = None
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
def
|
45
|
-
|
46
|
-
|
40
|
+
# At the moment we only allow one covariance object per table,
|
41
|
+
# so this is only used for consistency when saving objects.
|
42
|
+
self.name = "cov"
|
43
|
+
|
44
|
+
def __eq__(self, other):
|
45
|
+
"""
|
46
|
+
Test for equality
|
47
|
+
|
48
|
+
Parameters
|
49
|
+
----------
|
50
|
+
other: object
|
51
|
+
The other object to test for equality
|
52
|
+
|
53
|
+
Returns
|
54
|
+
-------
|
55
|
+
equal: bool
|
56
|
+
True if the objects are equal
|
57
|
+
"""
|
58
|
+
if not isinstance(other, self.__class__):
|
59
|
+
return False
|
60
|
+
# We do not test the inverse; we rely on the fact that
|
61
|
+
# if the dense matrices are equal, then the inverses will be equal.
|
62
|
+
# We are also relying on each subclass to have an instance variable
|
63
|
+
# 'size'.
|
64
|
+
return self.name == other.name and \
|
65
|
+
self.size == other.size and \
|
66
|
+
((self._dense is None and other._dense is None) or \
|
67
|
+
np.allclose(self._dense, other._dense))
|
47
68
|
|
48
69
|
@classmethod
|
49
70
|
def from_hdu(cls, hdu):
|
@@ -64,8 +85,11 @@ class BaseCovariance:
|
|
64
85
|
instance: BaseCovariance
|
65
86
|
A covariance instance
|
66
87
|
"""
|
88
|
+
warnings.warn("You are using an older SACC legacy SACC file format with old covariance data."
|
89
|
+
" Consider updating it by loading it and saving it again with the latest SACC version.",
|
90
|
+
DeprecationWarning)
|
67
91
|
subclass_name = hdu.header['saccclss']
|
68
|
-
subclass = cls.
|
92
|
+
subclass = cls._sub_classes[subclass_name]
|
69
93
|
return subclass.from_hdu(hdu)
|
70
94
|
|
71
95
|
@classmethod
|
@@ -80,32 +104,29 @@ class BaseCovariance:
|
|
80
104
|
Parameters
|
81
105
|
----------
|
82
106
|
cov: list[array] or array
|
83
|
-
If a list,
|
84
|
-
|
85
|
-
|
107
|
+
If a list, it should be a list of array-like objects each of which
|
108
|
+
can be coerced into a 2d array. A BlockDiagonalCovariance will be
|
109
|
+
returned.
|
86
110
|
|
87
|
-
|
88
|
-
|
111
|
+
If an array, it should be either 1D or 2d and square. Either a
|
112
|
+
DiagonalCovariance or a FullCovariance will be returned.
|
89
113
|
"""
|
90
114
|
if isinstance(cov, list):
|
91
|
-
s = 0
|
92
115
|
for block in cov:
|
93
116
|
block = np.atleast_2d(block)
|
94
117
|
if (block.ndim != 2) or (block.shape[0] != block.shape[1]):
|
95
118
|
raise ValueError("Covariance block has wrong size "
|
96
119
|
f"or shape {block.shape}")
|
97
|
-
s += block.shape[0]
|
98
120
|
return BlockDiagonalCovariance(cov)
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
return FullCovariance(cov)
|
121
|
+
cov = np.array(cov).squeeze()
|
122
|
+
if cov.ndim == 0:
|
123
|
+
return DiagonalCovariance(np.atleast_1d(cov))
|
124
|
+
if cov.ndim == 1:
|
125
|
+
return DiagonalCovariance(cov)
|
126
|
+
if (cov.ndim != 2) or (cov.shape[0] != cov.shape[1]):
|
127
|
+
raise ValueError("Covariance is not a 2D square matrix "
|
128
|
+
f"- shape: {cov.shape}")
|
129
|
+
return FullCovariance(cov)
|
109
130
|
|
110
131
|
@property
|
111
132
|
def dense(self):
|
@@ -140,7 +161,7 @@ class BaseCovariance:
|
|
140
161
|
return self._dense_inverse
|
141
162
|
|
142
163
|
|
143
|
-
class FullCovariance(BaseCovariance,
|
164
|
+
class FullCovariance(BaseCovariance, type_name='full'):
|
144
165
|
"""
|
145
166
|
A covariance subclass representing a full matrix with correlations
|
146
167
|
anywhere. Represented as an n x n matrix.
|
@@ -153,15 +174,40 @@ class FullCovariance(BaseCovariance, cov_type='full'):
|
|
153
174
|
covmat: 2D array
|
154
175
|
The matrix itself, of shape (size x size)
|
155
176
|
"""
|
177
|
+
|
178
|
+
storage_type = ONE_OBJECT_PER_TABLE
|
179
|
+
|
156
180
|
def __init__(self, covmat):
|
157
181
|
self.covmat = np.atleast_2d(covmat)
|
158
182
|
self.size = self.covmat.shape[0]
|
159
183
|
super().__init__()
|
160
184
|
|
161
|
-
def
|
185
|
+
def __eq__(self, other):
|
186
|
+
return super().__eq__(other) and \
|
187
|
+
np.allclose(self.covmat, other.covmat)
|
188
|
+
|
189
|
+
@classmethod
|
190
|
+
def from_hdu(cls, hdu):
|
191
|
+
"""
|
192
|
+
|
193
|
+
Load a covariance object from the data in the HDU
|
194
|
+
LEGACY METHOD: new sacc files will use from_table
|
195
|
+
|
196
|
+
Parameters
|
197
|
+
----------
|
198
|
+
hdu: astropy.fits.ImageHDU instance
|
199
|
+
|
200
|
+
Returns
|
201
|
+
-------
|
202
|
+
cov: FullCovariance
|
203
|
+
Loaded covariance object
|
204
|
+
"""
|
205
|
+
C = hdu.data
|
206
|
+
return cls(C)
|
207
|
+
|
208
|
+
def to_table(self):
|
162
209
|
"""
|
163
|
-
Make an astropy
|
164
|
-
This is represented as an image.
|
210
|
+
Make an astropy table object with this covariance in it.
|
165
211
|
|
166
212
|
Parameters
|
167
213
|
----------
|
@@ -169,32 +215,33 @@ class FullCovariance(BaseCovariance, cov_type='full'):
|
|
169
215
|
|
170
216
|
Returns
|
171
217
|
-------
|
172
|
-
|
173
|
-
|
218
|
+
table: astropy.table.Table instance
|
219
|
+
Table that can be used to reconstruct the object.
|
174
220
|
"""
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
return hdu
|
221
|
+
col_names = [f'col_{i}' for i in range(self.size)]
|
222
|
+
cols = [self.covmat[i] for i in range(self.size)]
|
223
|
+
table = Table(data=cols, names=col_names)
|
224
|
+
table.meta['SIZE'] = self.size
|
225
|
+
return table
|
181
226
|
|
182
227
|
@classmethod
|
183
|
-
def
|
228
|
+
def from_table(cls, table):
|
184
229
|
"""
|
185
|
-
Load a covariance object from the data in the
|
230
|
+
Load a covariance object from the data in the table
|
186
231
|
|
187
232
|
Parameters
|
188
233
|
----------
|
189
|
-
|
234
|
+
table: astropy.table.Table instance
|
190
235
|
|
191
236
|
Returns
|
192
237
|
-------
|
193
238
|
cov: FullCovariance
|
194
239
|
Loaded covariance object
|
195
240
|
"""
|
196
|
-
|
197
|
-
|
241
|
+
size = table.meta['SIZE']
|
242
|
+
covmat = np.array([table[f'col_{i}'] for i in range(size)])
|
243
|
+
return cls(covmat)
|
244
|
+
|
198
245
|
|
199
246
|
def keeping_indices(self, indices):
|
200
247
|
"""
|
@@ -240,7 +287,7 @@ class FullCovariance(BaseCovariance, cov_type='full'):
|
|
240
287
|
return self.covmat.copy()
|
241
288
|
|
242
289
|
|
243
|
-
class BlockDiagonalCovariance(BaseCovariance,
|
290
|
+
class BlockDiagonalCovariance(BaseCovariance, type_name='block'):
|
244
291
|
"""A covariance subclass representing block diagonal covariances
|
245
292
|
|
246
293
|
Block diagonal covariances have sub-blocks that are full dense matrices,
|
@@ -258,6 +305,9 @@ class BlockDiagonalCovariance(BaseCovariance, cov_type='block'):
|
|
258
305
|
size: int
|
259
306
|
overall total size of the matrix
|
260
307
|
"""
|
308
|
+
|
309
|
+
storage_type = ONE_OBJECT_MULTIPLE_TABLES
|
310
|
+
|
261
311
|
def __init__(self, blocks):
|
262
312
|
"""Create a BlockDiagonalCovariance object from a list of blocks
|
263
313
|
|
@@ -271,34 +321,17 @@ class BlockDiagonalCovariance(BaseCovariance, cov_type='block'):
|
|
271
321
|
self.size = sum(self.block_sizes)
|
272
322
|
super().__init__()
|
273
323
|
|
274
|
-
def
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
Parameters
|
281
|
-
----------
|
282
|
-
None
|
283
|
-
|
284
|
-
Returns
|
285
|
-
-------
|
286
|
-
hdu: astropy.fits.ImageHDU object
|
287
|
-
HDU containing data and metadata
|
288
|
-
"""
|
289
|
-
hdu = fits.ImageHDU(np.concatenate([b.flatten() for b in self.blocks]))
|
290
|
-
hdu.name = 'covariance'
|
291
|
-
hdu.header['sacctype'] = 'cov'
|
292
|
-
hdu.header['saccclss'] = self.cov_type
|
293
|
-
hdu.header['size'] = self.size
|
294
|
-
hdu.header['blocks'] = len(self.blocks)
|
295
|
-
for i, s in enumerate(self.block_sizes):
|
296
|
-
hdu.header[f'size_{i}'] = s
|
297
|
-
return hdu
|
324
|
+
def __eq__(self, other):
|
325
|
+
return super().__eq__(other) and \
|
326
|
+
self.block_sizes == other.block_sizes and \
|
327
|
+
all(np.allclose(b1, b2)
|
328
|
+
for b1, b2
|
329
|
+
in zip(self.blocks, other.blocks))
|
298
330
|
|
299
331
|
@classmethod
|
300
332
|
def from_hdu(cls, hdu):
|
301
333
|
"""Read a covariance object from a loaded FITS HDU.
|
334
|
+
LEGACY METHOD: new sacc files will use from_tables
|
302
335
|
|
303
336
|
Parameters
|
304
337
|
----------
|
@@ -320,6 +353,59 @@ class BlockDiagonalCovariance(BaseCovariance, cov_type='block'):
|
|
320
353
|
blocks.append(B)
|
321
354
|
return cls(blocks)
|
322
355
|
|
356
|
+
@classmethod
|
357
|
+
def from_tables(cls, tables):
|
358
|
+
"""
|
359
|
+
Load a covariance object from the data in the tables
|
360
|
+
|
361
|
+
Parameters
|
362
|
+
----------
|
363
|
+
tables: list
|
364
|
+
list of astropy.table.Table instances in block order
|
365
|
+
|
366
|
+
Returns
|
367
|
+
-------
|
368
|
+
cov: BlockDiagonalCovariance
|
369
|
+
Loaded covariance object
|
370
|
+
"""
|
371
|
+
|
372
|
+
blocks = []
|
373
|
+
# Get the block count from the first table
|
374
|
+
nblock = list(tables.values())[0].meta['SACCBCNT']
|
375
|
+
for i in range(nblock):
|
376
|
+
table = tables[f'block_{i}']
|
377
|
+
block_size = table.meta['SACCBSZE']
|
378
|
+
cols = [table[f'block_col_{i}'] for i in range(block_size)]
|
379
|
+
blocks.append(np.array(cols))
|
380
|
+
return cls(blocks)
|
381
|
+
|
382
|
+
def to_tables(self):
|
383
|
+
"""
|
384
|
+
Make an astropy table object with this covariance in it.
|
385
|
+
|
386
|
+
Parameters
|
387
|
+
----------
|
388
|
+
None
|
389
|
+
|
390
|
+
Returns
|
391
|
+
-------
|
392
|
+
table: astropy.table.Table instance
|
393
|
+
Table that can be used to reconstruct the object.
|
394
|
+
"""
|
395
|
+
tables = {}
|
396
|
+
nblock = len(self.blocks)
|
397
|
+
for j, block in enumerate(self.blocks):
|
398
|
+
b = len(block)
|
399
|
+
col_names = [f'block_col_{i}' for i in range(b)]
|
400
|
+
cols = [block[i] for i in range(b)]
|
401
|
+
table = Table(data=cols, names=col_names)
|
402
|
+
table.meta['SIZE'] = self.size
|
403
|
+
table.meta['SACCBIDX'] = j
|
404
|
+
table.meta['SACCBCNT'] = nblock
|
405
|
+
table.meta['SACCBSZE'] = b
|
406
|
+
tables[f'block_{j}'] = table
|
407
|
+
return tables
|
408
|
+
|
323
409
|
def get_block(self, indices):
|
324
410
|
"""Read a (not necessarily contiguous) sublock of the matrix
|
325
411
|
|
@@ -378,7 +464,7 @@ class BlockDiagonalCovariance(BaseCovariance, cov_type='block'):
|
|
378
464
|
blocks = [self.blocks[i][m][:, m] for i, m in
|
379
465
|
enumerate(block_masks)]
|
380
466
|
return self.__class__(blocks)
|
381
|
-
|
467
|
+
if (np.diff(indices) > 0).all():
|
382
468
|
s = 0
|
383
469
|
sub_blocks = []
|
384
470
|
for block, sz in zip(self.blocks, self.block_sizes):
|
@@ -387,10 +473,9 @@ class BlockDiagonalCovariance(BaseCovariance, cov_type='block'):
|
|
387
473
|
sub_blocks.append(block[m][:, m])
|
388
474
|
s += sz
|
389
475
|
return self.__class__(sub_blocks)
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
return FullCovariance(C)
|
476
|
+
C = scipy.linalg.block_diag(*self.blocks)
|
477
|
+
C = C[indices][:, indices]
|
478
|
+
return FullCovariance(C)
|
394
479
|
|
395
480
|
def _get_dense_inverse(self):
|
396
481
|
# Invert all the blocks individually and then
|
@@ -405,7 +490,7 @@ class BlockDiagonalCovariance(BaseCovariance, cov_type='block'):
|
|
405
490
|
return scipy.linalg.block_diag(*self.blocks)
|
406
491
|
|
407
492
|
|
408
|
-
class DiagonalCovariance(BaseCovariance,
|
493
|
+
class DiagonalCovariance(BaseCovariance, type_name='diagonal'):
|
409
494
|
"""A covariance subclass representing covariances that are
|
410
495
|
purely diagonal.
|
411
496
|
|
@@ -417,6 +502,9 @@ class DiagonalCovariance(BaseCovariance, cov_type='diagonal'):
|
|
417
502
|
diag: array
|
418
503
|
The diagonal terms in the covariance (i.e. the variances)
|
419
504
|
"""
|
505
|
+
|
506
|
+
storage_type = ONE_OBJECT_PER_TABLE
|
507
|
+
|
420
508
|
def __init__(self, variances):
|
421
509
|
"""
|
422
510
|
Create a DiagonalCovariance object from the variances
|
@@ -431,26 +519,10 @@ class DiagonalCovariance(BaseCovariance, cov_type='diagonal'):
|
|
431
519
|
self.size = len(self.diag)
|
432
520
|
super().__init__()
|
433
521
|
|
434
|
-
def
|
435
|
-
|
436
|
-
|
437
|
-
In this can a binary table HDU is created.
|
522
|
+
def __eq__(self, other):
|
523
|
+
return super().__eq__(other) and \
|
524
|
+
np.allclose(self.diag, other.diag)
|
438
525
|
|
439
|
-
Parameters
|
440
|
-
----------
|
441
|
-
None
|
442
|
-
|
443
|
-
Returns
|
444
|
-
-------
|
445
|
-
hdu: astropy.fits.BinTableHDU instance
|
446
|
-
HDU that can be used to reconstruct the object.
|
447
|
-
"""
|
448
|
-
table = Table(names=['variance'], data=[self.diag])
|
449
|
-
hdu = fits.table_to_hdu(table)
|
450
|
-
hdu.name = 'covariance'
|
451
|
-
hdu.header['sacctype'] = 'cov'
|
452
|
-
hdu.header['saccclss'] = self.cov_type
|
453
|
-
return hdu
|
454
526
|
|
455
527
|
def keeping_indices(self, indices):
|
456
528
|
"""
|
@@ -472,6 +544,40 @@ class DiagonalCovariance(BaseCovariance, cov_type='diagonal'):
|
|
472
544
|
D = self.diag[indices]
|
473
545
|
return self.__class__(D)
|
474
546
|
|
547
|
+
@classmethod
|
548
|
+
def from_table(cls, table):
|
549
|
+
"""
|
550
|
+
Load a covariance object from the data in the table
|
551
|
+
|
552
|
+
Parameters
|
553
|
+
----------
|
554
|
+
table: astropy.table.Table instance
|
555
|
+
|
556
|
+
Returns
|
557
|
+
-------
|
558
|
+
cov: DiagonalCovariance
|
559
|
+
Loaded covariance object
|
560
|
+
"""
|
561
|
+
D = table['variance']
|
562
|
+
return cls(D)
|
563
|
+
|
564
|
+
def to_table(self):
|
565
|
+
"""
|
566
|
+
Make an astropy table object with this covariance in it.
|
567
|
+
|
568
|
+
Parameters
|
569
|
+
----------
|
570
|
+
None
|
571
|
+
|
572
|
+
Returns
|
573
|
+
-------
|
574
|
+
table: astropy.table.Table instance
|
575
|
+
Table that can be used to reconstruct the object.
|
576
|
+
"""
|
577
|
+
table = Table(data=[self.diag], names=['variance'])
|
578
|
+
table.meta['SIZE'] = self.size
|
579
|
+
return table
|
580
|
+
|
475
581
|
@classmethod
|
476
582
|
def from_hdu(cls, hdu):
|
477
583
|
"""
|