Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALPulsar 7.1.1.1-5e288d3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
mols_for_gte.py
Go to the documentation of this file.
1# Copyright (C) 2019--2023 Benjamin Grace
2#
3# This program is free software; you can redistribute it and/or modify it
4# under the terms of the GNU General Public License as published by the
5# Free Software Foundation; either version 2 of the License, or (at your
6# option) any later version.
7#
8# This program is distributed in the hope that it will be useful, but
9# WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
11# Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along
14# with this program; if not, write to the Free Software Foundation, Inc.,
15# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16
17## \file
18## \ingroup lalpulsar_python_piecewise_model
19"""
20Method Of Least Squares (MOLS) for matching a piecewise model to the general
21torque equation (GTE).
22"""
23
24import logging
25
26import numpy as np
27from scipy import integrate
28
29from . import basis_functions as bf
30from . import gte_and_other_methods as gom
31from . import errors
32from . import sampling_methods as sm
33
34
35# Our b vector for MOLS
36def bvec(points, f0, ngte, kgte):
37 return [gom.gte(t - bf.knotslist[0], f0, ngte, kgte) for t in points]
38
39
40# Constructs an individual row of the a matrix for the sample point 'point' and the coefficients of our basis functions
41# 'coeffs'
42def rowofa(point, coeffs, s):
43 j = sm.thisint(point)
44 ints = len(bf.knotslist) - 1
45 firstzeros = [0] * j * s
46 lastzeros = [0] * (ints - 1 - j) * s
47
48 coeffsb = []
49 coeffsc = []
50
51 for thiss in range(s):
52 coeffb = bf.basisfunctionvalue(point, j, 0, thiss, coeffs)
53 coeffc = bf.basisfunctionvalue(point, j, 1, thiss, coeffs)
54
55 coeffsb.append(coeffb)
56 coeffsc.append(coeffc)
57
58 return firstzeros + coeffsb + coeffsc + lastzeros
59
60
61# Builds our a matrix
62def a(points, coeffs, s):
63 amat = []
64
65 for point in points:
66 amat.append(rowofa(point, coeffs, s))
67
68 return amat
69
70
71# Returns a diagonal matrix containing the diagonal elements of mat to power -1/2
72def pmat(mat):
73 return np.diag(bf.listpseudoinv(np.diagonal(mat), 1 / 2))
74
75
76# Returns mat multiplied by its transpose
77def ata(mat):
78 return np.matmul(np.transpose(mat), mat)
79
80
81# Returns the solutions for our LSTSQ problem
82def sols(coeffs, ppint, s, f0, ngte, kgte, conditioning=True):
83 while True:
84 try:
85 points = sm.samplepoints(ppint, f0, ngte, kgte)
86 break
88 logging.debug("Reattempting MOLS fitting with greater sampling")
89 ppint *= 2
90
91 b = bvec(points, f0, ngte, kgte)
92 amat = a(points, coeffs, s)
93
94 atamat = ata(amat)
95
96 if conditioning:
97 pm = pmat(atamat)
98 else:
99 pm = np.identity(len(atamat))
100
101 lhs = np.matmul(pm, np.matmul(atamat, pm))
102 rhs = np.matmul(np.matmul(pm, np.transpose(amat)), b)
103
104 logging.debug(
105 "Condition number for matrix A is: " + "{:.2E}".format(np.linalg.cond(amat))
106 )
107 logging.debug(
108 "Condition number for matrix A' is: " + "{:.2E}".format(np.linalg.cond(lhs))
109 )
110
111 try:
112 params = np.matmul(pm, np.linalg.solve(lhs, rhs))
113 except np.linalg.LinAlgError as error:
114 logging.debug(error)
115 logging.debug(
116 "Error in calculating MOLS parameters, using Python LSTSQ method instead"
117 )
118 params = np.matmul(pm, np.linalg.lstsq(lhs, rhs, rcond=-1)[0])
119
120 return params
121
122
123# Partitions the 1D list params into a 3D list for our parameters. Extract parameter by [ int ][ B or C ][ k ]
124def solsbyint(params, s):
125 ints = int(len(params) / s) - 1
126
127 partedsols = np.zeros((ints, 2, s))
128
129 for i in range(ints):
130 solsb = np.array(params[i * s : (i + 1) * s])
131 solsc = np.array(params[(i + 1) * s : (i + 2) * s])
132
133 partedsols[i][0] = solsb
134 partedsols[i][1] = solsc
135
136 return partedsols
137
138
140 knotnuma, knotnumb, coeffs, ppint, s, f0, ngte, kgte, conditioning=True
141):
142
143 while True:
144 try:
145 points = sm.samplepointswithinknots(
146 knotnuma, knotnumb, ppint, f0, ngte, kgte
147 )
148 break
150 logging.debug("Reattempting MOLS fitting with greater sampling")
151 ppint *= 2
152
153 b = bvec(points, f0, ngte, kgte)
154 amat = a(points, coeffs, s)
155
156 for i in range(knotnuma * s):
157 [row.pop(0) for row in amat]
158
159 atamat = ata(amat)
160
161 if conditioning:
162 pm = pmat(atamat)
163 else:
164 pm = np.identity(len(atamat))
165
166 lhs = np.matmul(pm, np.matmul(atamat, pm))
167 rhs = np.matmul(np.matmul(pm, np.transpose(amat)), b)
168
169 logging.debug(
170 "Condition number for matrix A is: " + "{:.2E}".format(np.linalg.cond(amat))
171 )
172 logging.debug(
173 "Condition number for matrix A' is: " + "{:.2E}".format(np.linalg.cond(lhs))
174 )
175
176 try:
177 params = np.matmul(pm, np.linalg.solve(lhs, rhs))
178 except np.linalg.LinAlgError as error:
179 logging.debug(error)
180 logging.debug(
181 "Error in calculating MOLS parameters, using Python LSTSQ method instead"
182 )
183 params = np.matmul(pm, np.linalg.lstsq(lhs, rhs)[0])
184
185 zerobuff = [0] * (s * knotnuma)
186 paramszerobuff = zerobuff + list(params)
187
188 return paramszerobuff
189
190
191points = []
192
193# Calculates model value at given point for basis function coeffs 'coeffs' and parameter values 'params'
194def modelvalueatpoint(point, coeffs, params, ignoreintcheck=False, singleseg=False):
195
196 s = len(params[0][0])
197
198 if ignoreintcheck:
199 try:
200 j = sm.thisint(point)
202
203 t = point - bf.knotslist[0]
204
205 if t < 0:
206 f0 = params[0][0][0]
207 f1 = params[0][0][1]
208 f2 = 0
209
210 if s > 2:
211 f2 = params[0][0][2]
212
213 return f0 + f1 * t + 1 / 2 * f2 * t**2
214 elif t > bf.knotslist[-1] - bf.knotslist[0]:
215 f0 = params[-1][1][0]
216 f1 = params[-1][1][1]
217 f2 = 0
218
219 if s > 2:
220 f2 = params[-1][1][2]
221
222 return f0 + f1 * t + 1 / 2 * f2 * t**2
223
224 return 0
225 else:
226 j = sm.thisint(point)
227
228 # if singleseg:
229 # j = 0
230
231 points.append(point)
232
233 s = np.shape(params)[2]
234
235 val = 0
236
237 for thiss in range(s):
238 basisfuncs = bf.basisfunctionvalue(point, j, 0, thiss, coeffs)
239 basisfunce = bf.basisfunctionvalue(point, j, 1, thiss, coeffs)
240
241 if singleseg:
242 val += params[0][0][thiss] * basisfuncs
243 val += params[0][1][thiss] * basisfunce
244 else:
245 val += params[j][0][thiss] * basisfuncs
246 val += params[j][1][thiss] * basisfunce
247
248 return val
249
250
251def phase(point, coeffs, params, ignoreintcheck=False):
252
253 model = lambda t: modelvalueatpoint(
254 t, coeffs, params, ignoreintcheck=ignoreintcheck
255 )
256 # logging.debug("Integral bounds: " + str([bf.knotslist[0], point]))
257 phasemodel = 2 * np.pi * integrate.quad(model, bf.knotslist[0], point, epsabs=0)[0]
258
259 return phasemodel
260
261
262# Returns the value of the error of our model at a given point
263def errorvalueatpoint(point, coeffs, params, f0, ngte, kgte):
264 error = np.abs(
265 modelvalueatpoint(point, coeffs, params)
266 - gom.gte(point - bf.knotslist[0], f0, ngte, kgte)
267 )
268
269 return error
def solsbetweenknots(knotnuma, knotnumb, coeffs, ppint, s, f0, ngte, kgte, conditioning=True)
def phase(point, coeffs, params, ignoreintcheck=False)
def modelvalueatpoint(point, coeffs, params, ignoreintcheck=False, singleseg=False)
def errorvalueatpoint(point, coeffs, params, f0, ngte, kgte)
def bvec(points, f0, ngte, kgte)
Definition: mols_for_gte.py:36
def sols(coeffs, ppint, s, f0, ngte, kgte, conditioning=True)
Definition: mols_for_gte.py:82