Skip to content

Commit 4fd0c17

Browse files
committed
Merge branch 'release/0.7'
2 parents 8c04845 + a87fdbb commit 4fd0c17

19 files changed

+38280
-35111
lines changed

bfast/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
from .models import BFASTMonitor
22

33

4-
__version__ = '0.6'
4+
__version__ = '0.7'

bfast/base.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
from abc import ABC
88
from abc import abstractmethod
99

10-
class BFASTMonitorBase(ABC):
1110

11+
class BFASTMonitorBase(ABC):
1212
def __init__(
1313
self,
1414
start_monitor,
@@ -20,7 +20,6 @@ def __init__(
2020
period=10,
2121
verbose=0,
2222
):
23-
2423
self.start_monitor = start_monitor
2524
self.freq = freq
2625
self.k = k
@@ -32,5 +31,4 @@ def __init__(
3231

3332
@abstractmethod
3433
def fit(self, y):
35-
3634
raise Exception("Function 'fit' not implemented!")

bfast/models.py

+17-14
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from bfast.monitor import BFASTMonitorPython
22
from bfast.monitor import BFASTMonitorOpenCL
33

4-
class BFASTMonitor(object):
4+
5+
class BFASTMonitor():
56
"""
67
BFASTMonitor implements the BFASTMonitor approach and
78
provides two backends/implementations:
@@ -85,7 +86,6 @@ class BFASTMonitor(object):
8586
different phases.
8687
8788
"""
88-
8989
def __init__(
9090
self,
9191
start_monitor,
@@ -102,7 +102,6 @@ def __init__(
102102
detailed_results=False,
103103
find_magnitudes=True,
104104
):
105-
106105
self.start_monitor = start_monitor
107106
self.freq = freq
108107
self.k = k
@@ -139,7 +138,6 @@ def fit(self, data, dates, n_chunks=None, nan_value=0):
139138
-------
140139
self : The BFASTMonitor object.
141140
"""
142-
143141
self._model = None
144142

145143
if self.backend == 'python':
@@ -184,11 +182,9 @@ def fit(self, data, dates, n_chunks=None, nan_value=0):
184182
)
185183

186184
elif self.backend == "C":
187-
188185
raise Exception("Multi-core C implementation will be added soon!")
189186

190187
else:
191-
192188
raise Exception("Unknown backend '{}".format(self.backend))
193189

194190
# fit BFASTMonitor models
@@ -208,7 +204,6 @@ def get_params(self):
208204
dict: Mapping of string to any
209205
parameter names mapped to their values.
210206
"""
211-
212207
params = {
213208
"start_monitor": self.start_monitor,
214209
"freq": self.freq,
@@ -236,7 +231,6 @@ def set_params(self, **params):
236231
Dictionary containing the
237232
parameters to be used.
238233
"""
239-
240234
for parameter, value in params.items():
241235
self.setattr(parameter, value)
242236

@@ -250,7 +244,6 @@ def timers(self):
250244
dict : An dictionary containing the runtimes
251245
for the different phases.
252246
"""
253-
254247
if self._is_fitted():
255248
return self._model.get_timers()
256249

@@ -269,7 +262,6 @@ def breaks(self):
269262
entries correspond to the first break that was
270263
detected in the monitor period (i.e., its index).
271264
"""
272-
273265
if self._is_fitted():
274266
return self._model.breaks
275267

@@ -286,7 +278,6 @@ def means(self):
286278
mean for a pixel corresponds to an increase of the
287279
vegetation in case indices such as NDMI are considered)
288280
"""
289-
290281
if self._is_fitted():
291282
return self._model.means
292283

@@ -302,16 +293,28 @@ def magnitudes(self):
302293
values median of the difference between the data
303294
and the model prediction in the monitoring period
304295
"""
305-
306296
if self._is_fitted():
307297
return self._model.magnitudes
308298

309299
raise Exception("Model not yet fitted!")
310300

311-
312301
def _is_fitted(self):
313-
314302
if hasattr(self, '_model_fitted'):
315303
return self._model_fitted
316304

317305
return False
306+
307+
@property
308+
def valids(self):
309+
""" Returns the number of valid values for each pixel
310+
311+
Returns
312+
-------
313+
array-like : An array containing the number
314+
of valid values for each pixel in the
315+
aray data
316+
"""
317+
if self._is_fitted():
318+
return self._model.valids
319+
320+
raise Exception("Model not yet fitted!")

bfast/monitor/__init__.py

-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
from .opencl import BFASTMonitorOpenCL
22
from .python import BFASTMonitorPython
3-

bfast/monitor/opencl/base.py

+44-41
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,17 @@
1616
from bfast.base import BFASTMonitorBase
1717
from bfast.monitor.utils import compute_end_history, compute_lam, map_indices
1818

19+
# generated by Futhark
1920
from .bfastfinal import bfastfinal
2021

21-
#################################################
22-
# ## Remember to compile with, e.g.,
23-
# ## $ gpu-pyopencl --library bfastfuth.fut
24-
#################################################
22+
############################################################
23+
# ## Remember to run:
24+
# ## cd futhark
25+
# ## make
26+
# ## cp bfastfinal.py ../
27+
############################################################
2528

2629
class BFASTMonitorOpenCL(BFASTMonitorBase):
27-
2830
""" Parallel BFASTMonitor implementation optimized for GPUs. The
2931
interface follows the one of the corresponding R package,
3032
see: https://cran.r-project.org/web/packages/bfast
@@ -67,7 +69,6 @@ class BFASTMonitorOpenCL(BFASTMonitorBase):
6769
-----
6870
6971
"""
70-
7172
def __init__(self,
7273
start_monitor,
7374
freq=365,
@@ -82,20 +83,19 @@ def __init__(self,
8283
platform_id=0,
8384
device_id=0
8485
):
85-
8686
k_valid = list(range(3, 11))
8787

8888
if k not in k_valid:
8989
raise Exception("Current implementation can only handle the following values for k: {}".format(k_valid))
9090

91-
super(BFASTMonitorOpenCL, self).__init__(start_monitor,
92-
freq,
93-
k=k,
94-
hfrac=hfrac,
95-
trend=trend,
96-
level=level,
97-
period=period,
98-
verbose=verbose)
91+
super().__init__(start_monitor,
92+
freq,
93+
k=k,
94+
hfrac=hfrac,
95+
trend=trend,
96+
level=level,
97+
period=period,
98+
verbose=verbose)
9999

100100
self.detailed_results = detailed_results
101101
self.find_magnitudes = find_magnitudes
@@ -112,9 +112,7 @@ def __init__(self,
112112
sizes=self._get_futhark_params())
113113

114114
def _init_device(self, platform_id, device_id):
115-
""" Initializes the device.
116-
"""
117-
115+
""" Initializes the device."""
118116
try:
119117
platforms = pyopencl.get_platforms()
120118
devices = platforms[platform_id].get_devices()
@@ -126,9 +124,7 @@ def _init_device(self, platform_id, device_id):
126124
self.queue = pyopencl.CommandQueue(self.ctx)
127125

128126
def _print_device_info(self):
129-
""" Prints information about the current device.
130-
"""
131-
127+
""" Prints information about the current device. """
132128
if self.verbose > 0:
133129
print("=================================================================================")
134130
print("Device id: " + str(self.device_id))
@@ -227,6 +223,7 @@ def fit(self, data, dates, n_chunks=None, nan_value=0):
227223

228224
self.breaks = results['breaks']
229225
self.means = results['means']
226+
self.valids = results['valids']
230227

231228
if self.find_magnitudes or self.detailed_results:
232229
self.magnitudes = results['magnitudes']
@@ -242,16 +239,13 @@ def get_timers(self):
242239
dict : An array containing the runtimes
243240
for the different phases.
244241
"""
245-
246242
return self._timers
247243

248244
def _fit_chunks(self, data, dates, n_chunks=10, nan_value=0):
249245
data_chunks = numpy.array_split(data, n_chunks, axis=1)
250-
251246
results = []
252247

253248
for chunk_idx in range(n_chunks):
254-
255249
start_chunk = time.time()
256250
if self.verbose > 0:
257251
print("Processing chunk index {}/{}".format(chunk_idx + 1, n_chunks))
@@ -317,7 +311,7 @@ def _fit_single_preprocess(self, data, dates, nan_value):
317311
print("--- runtime for data transfer (host->device):\t{}".format(end - start))
318312

319313
start = time.time()
320-
data_cl = self.futobj.remove_nans(nan_value, data_cl)
314+
data_cl = self.futobj.convertToFloat(nan_value, data_cl)
321315
y_cl = self.futobj.reshapeTransp(data_cl)
322316
end = time.time()
323317
if self.verbose > 0:
@@ -353,21 +347,24 @@ def _fit_single_kernel(self, y_cl, mapped_indices_cl):
353347
self.lam,
354348
mapped_indices_cl, y_cl)
355349
elif self.find_magnitudes:
356-
breaks, means, magnitudes = self.futobj.mainMagnitude(trend,
357-
self.k,
358-
self.n,
359-
self.freq,
360-
self.hfrac,
361-
self.lam,
362-
mapped_indices_cl, y_cl)
350+
Ns, \
351+
breaks, \
352+
means, \
353+
magnitudes = self.futobj.mainMagnitude(trend,
354+
self.k,
355+
self.n,
356+
self.freq,
357+
self.hfrac,
358+
self.lam,
359+
mapped_indices_cl, y_cl)
363360
else:
364-
breaks, means = self.futobj.main(trend,
365-
self.k,
366-
self.n,
367-
self.freq,
368-
self.hfrac,
369-
self.lam,
370-
mapped_indices_cl, y_cl)
361+
Ns, breaks, means = self.futobj.main(trend,
362+
self.k,
363+
self.n,
364+
self.freq,
365+
self.hfrac,
366+
self.lam,
367+
mapped_indices_cl, y_cl)
371368

372369
end = time.time()
373370

@@ -382,6 +379,7 @@ def _fit_single_kernel(self, y_cl, mapped_indices_cl):
382379
results['y_pred'] = y_pred
383380
results['breaks'] = breaks
384381
results['means'] = means
382+
results['valids'] = Ns
385383

386384
if self.find_magnitudes or self.detailed_results:
387385
results['magnitudes'] = magnitudes
@@ -399,6 +397,7 @@ def _fit_single_postprocessing(self, results, oshape):
399397

400398
results['breaks'] = results['breaks'].get().reshape(oshape[1:])
401399
results['means'] = results['means'].get().reshape(oshape[1:])
400+
results['valids'] = results['valids'].get().T.reshape(oshape[1:])
402401

403402
if self.find_magnitudes or self.detailed_results:
404403
results['magnitudes'] = results['magnitudes'].get().reshape(oshape[1:])
@@ -431,14 +430,18 @@ def __append_results(self, res, results):
431430
else:
432431
results['means'] = res['means']
433432

434-
if self.find_magnitudes or self.detailed:
433+
if 'valids' in results.keys():
434+
results['valids'] = numpy.concatenate([results['valids'], res['valids']], axis=0)
435+
else:
436+
results['valids'] = res['valids']
437+
438+
if self.find_magnitudes or self.detailed_results:
435439
if 'magnitudes' in results.keys():
436440
results['magnitudes'] = numpy.concatenate([results['magnitudes'], res['magnitudes']], axis=0)
437441
else:
438442
results['magnitudes'] = res['magnitudes']
439443

440444
if self.detailed_results:
441-
442445
if not 'bounds' in results.keys():
443446
results['bounds'] = res['bounds']
444447

0 commit comments

Comments
 (0)