From a547424cdf8ab7d3fccca6c70ff47ede12cfac2e Mon Sep 17 00:00:00 2001 From: Jacob Bryan Date: Mon, 18 Jul 2022 12:51:07 -0600 Subject: [PATCH 1/7] Moves to ARIMA class in statsmodels --- ravenframework/SupervisedLearning/ARMA.py | 30 ++++++++++++----------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/ravenframework/SupervisedLearning/ARMA.py b/ravenframework/SupervisedLearning/ARMA.py index 5ff7b6124e..d207d5236a 100644 --- a/ravenframework/SupervisedLearning/ARMA.py +++ b/ravenframework/SupervisedLearning/ARMA.py @@ -916,14 +916,14 @@ def _generateARMASignal(self, model, numSamples=None,randEngine=None): if randEngine is None: randEngine=self.randomEng import statsmodels.tsa - hist = statsmodels.tsa.arima_process.arma_generate_sample(ar = np.append(1., -model.arparams), - ma = np.append(1., model.maparams), - nsample = numSamples, - distrvs = functools.partial(randomUtils.randomNormal,engine=randEngine), + hist = statsmodels.tsa.arima_process.arma_generate_sample(ar=model.polynomial_ar, + ma=model.polynomial_ma, + nsample=numSamples, + distrvs=functools.partial(randomUtils.randomNormal,engine=randEngine), # functool.partial provide the random number generator as a function # with normal distribution and take engine as the positional arguments keywords. - scale = np.sqrt(model.sigma2), - burnin = 2*max(self.P,self.Q)) # @alfoa, 2020 + scale=np.sqrt(model.params[model.param_names.index('sigma2')]), + burnin=2*max(self.P,self.Q)) # @alfoa, 2020 return hist def _generateFourierSignal(self, pivots, periods): @@ -1094,7 +1094,7 @@ def _trainARMA(self, data, masks=None): if masks is not None: data = data[masks] import statsmodels.api - results = statsmodels.tsa.arima_model.ARMA(data, order = (self.P, self.Q)).fit(disp = False) + results = statsmodels.tsa.arima.model.ARIMA(data, order=(self.P, 0, self.Q), trend='c').fit() return results def _trainCDF(self, data, binOps=None): @@ -1382,7 +1382,7 @@ def writeXML(self, writeTo, targets=None, skip=None): root.append(targetNode) armaNode = xmlUtils.newNode('ARMA_params') targetNode.append(armaNode) - armaNode.append(xmlUtils.newNode('std', text=np.sqrt(arma.sigma2))) + armaNode.append(xmlUtils.newNode('std', text=np.sqrt(arma.params[arma.param_names.index('sigma2')]))) # TODO covariances, P and Q, etc for target,peakInfo in self.peaks.items(): targetNode = root.find(target) @@ -1564,13 +1564,13 @@ def getFundamentalFeatures(self, requestedFeatures, featureTemplate=None): for target, arma in self.armaResult.items(): # sigma feature = featureTemplate.format(target=target, metric='arma', id='std') - features[feature] = np.sqrt(arma.sigma2) + features[feature] = np.sqrt(arma.params[arma.param_names.index('sigma2')]) # autoregression - for p, val in enumerate(arma.arparams): + for p, val in enumerate(-arma.polynomial_ar[1:]): feature = featureTemplate.format(target=target, metric='arma', id='AR_{}'.format(p)) features[feature] = val # moving average - for q, val in enumerate(arma.maparams): + for q, val in enumerate(arma.polynomial_ma[1:]): feature = featureTemplate.format(target=target, metric='arma', id='MA_{}'.format(q)) features[feature] = val for target, cdfParam in self.cdfParams.items(): @@ -2163,6 +2163,7 @@ def adjustLocalRomSegment(self, settings, picker): Call this before training the subspace segment ROMs Note this is called on the LOCAL subsegment ROMs, NOT on the GLOBAL templateROM from the ROMcollection! @ In, settings, object, arbitrary information about ROM clustering settings from getGlobalRomSegmentSettings + @ In, picker, slice, slice object for selecting the desired segment @ Out, None """ if self.zeroFilterTarget: @@ -2545,6 +2546,7 @@ def __init__(self, arparams, maparams, sigma): @ In, sigma, float, standard deviation of ARMA residual noise @ Out, None """ - self.arparams = np.atleast_1d(arparams) - self.maparams = np.atleast_1d(maparams) - self.sigma2 = sigma**2 + self.polynomial_ar = np.append(1, -np.atleast_1d(arparams)) + self.polynomial_ma = np.append(1, np.atleast_1d(maparams)) + self.param_names = ['sigma2'] + self.params = [sigma ** 2] From 4d72d070f713cc42c1d20300d97273edb472257a Mon Sep 17 00:00:00 2001 From: Jacob Bryan Date: Mon, 18 Jul 2022 12:52:36 -0600 Subject: [PATCH 2/7] regold for new ARIMA class used in ARMA ROM --- .../ARMA/gold/ARMAreseed/rommeta.xml | 2 +- .../TimeSeries/ARMA/gold/Basic/romMeta.xml | 2 +- .../TimeSeries/ARMA/gold/Peaks/romMeta.xml | 35 ++++++++++--------- .../unit_tests/SupervisedLearning/testARMA.py | 4 +-- 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/tests/framework/ROM/TimeSeries/ARMA/gold/ARMAreseed/rommeta.xml b/tests/framework/ROM/TimeSeries/ARMA/gold/ARMAreseed/rommeta.xml index 3d33c5a67c..cb0b26a462 100644 --- a/tests/framework/ROM/TimeSeries/ARMA/gold/ARMAreseed/rommeta.xml +++ b/tests/framework/ROM/TimeSeries/ARMA/gold/ARMAreseed/rommeta.xml @@ -28,7 +28,7 @@ - 0.258086103993 + 0.258080826596 diff --git a/tests/framework/ROM/TimeSeries/ARMA/gold/Basic/romMeta.xml b/tests/framework/ROM/TimeSeries/ARMA/gold/Basic/romMeta.xml index ffa5aecbff..57bfa41617 100644 --- a/tests/framework/ROM/TimeSeries/ARMA/gold/Basic/romMeta.xml +++ b/tests/framework/ROM/TimeSeries/ARMA/gold/Basic/romMeta.xml @@ -41,7 +41,7 @@ - 0.258086103993 + 0.258080826596 diff --git a/tests/framework/ROM/TimeSeries/ARMA/gold/Peaks/romMeta.xml b/tests/framework/ROM/TimeSeries/ARMA/gold/Peaks/romMeta.xml index 7a279f6e10..69dbf89a30 100644 --- a/tests/framework/ROM/TimeSeries/ARMA/gold/Peaks/romMeta.xml +++ b/tests/framework/ROM/TimeSeries/ARMA/gold/Peaks/romMeta.xml @@ -7,6 +7,7 @@ segment_number + romMeta seg_Time_end,seg_Time_start,seg_index_end,seg_index_start RAVEN_sample_ID @@ -24,7 +25,7 @@ - 1.39905186671 + 1.39906272172 @@ -41,7 +42,7 @@ - 1.40116930574 + 1.40117407499 @@ -58,7 +59,7 @@ - 1.44350608151 + 1.44351127777 @@ -75,7 +76,7 @@ - 1.40741081917 + 1.4074139545 @@ -92,7 +93,7 @@ - 1.40295178957 + 1.402957792 @@ -109,7 +110,7 @@ - 1.37645138378 + 1.37645633643 @@ -126,7 +127,7 @@ - 1.4551579758 + 1.45516109199 @@ -143,7 +144,7 @@ - 1.40077421912 + 1.40077918937 @@ -160,7 +161,7 @@ - 1.39729542433 + 1.3972982007 @@ -177,7 +178,7 @@ - 1.39409898092 + 1.3941043431 @@ -194,7 +195,7 @@ - 1.40451155501 + 1.40451697137 @@ -211,7 +212,7 @@ - 1.41542838444 + 1.41542097263 @@ -228,7 +229,7 @@ - 1.39571008147 + 1.39571506343 @@ -245,7 +246,7 @@ - 1.42764320201 + 1.42763966138 @@ -262,7 +263,7 @@ - 1.39176698371 + 1.3917729782 @@ -279,7 +280,7 @@ - 1.38512558105 + 1.38513462051 @@ -296,7 +297,7 @@ - 1.38110648914 + 1.38110661531 diff --git a/tests/framework/unit_tests/SupervisedLearning/testARMA.py b/tests/framework/unit_tests/SupervisedLearning/testARMA.py index feaf1385a0..bb8b3b83fb 100644 --- a/tests/framework/unit_tests/SupervisedLearning/testARMA.py +++ b/tests/framework/unit_tests/SupervisedLearning/testARMA.py @@ -467,8 +467,8 @@ def plotPDF(edges, bins, ax, label, color, s='.', alpha=1.0): arma.setEngine(eng, seed=901017) signal7 = arma._generateARMASignal(testVal) -sig7 = [0.39975177, -0.14531468, 0.13138866, -0.56565224, 0.06020252, - 0.60752306, -0.29076173, -1.1758456, 0.41108591, -0.05735384] +sig7=[0.39974990, -0.14531400, 0.1313880, -0.565649598, 0.06020223, + 0.60752023, -0.29076037, -1.1758401, 0.41108399, -0.05735358] for n in range(10): checkFloat('signal 7, evaluation ind{}'.format(n), signal7[n], sig7[n], tol=1e-7) From 968008dc10c4c96e61c01c726e285bb06f89f05a Mon Sep 17 00:00:00 2001 From: Jacob Bryan Date: Mon, 18 Jul 2022 14:27:05 -0600 Subject: [PATCH 3/7] formatting and comments --- ravenframework/SupervisedLearning/ARMA.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ravenframework/SupervisedLearning/ARMA.py b/ravenframework/SupervisedLearning/ARMA.py index d207d5236a..26108ccbac 100644 --- a/ravenframework/SupervisedLearning/ARMA.py +++ b/ravenframework/SupervisedLearning/ARMA.py @@ -917,13 +917,13 @@ def _generateARMASignal(self, model, numSamples=None,randEngine=None): randEngine=self.randomEng import statsmodels.tsa hist = statsmodels.tsa.arima_process.arma_generate_sample(ar=model.polynomial_ar, - ma=model.polynomial_ma, - nsample=numSamples, - distrvs=functools.partial(randomUtils.randomNormal,engine=randEngine), - # functool.partial provide the random number generator as a function - # with normal distribution and take engine as the positional arguments keywords. - scale=np.sqrt(model.params[model.param_names.index('sigma2')]), - burnin=2*max(self.P,self.Q)) # @alfoa, 2020 + ma=model.polynomial_ma, + nsample=numSamples, + distrvs=functools.partial(randomUtils.randomNormal,engine=randEngine), + # functool.partial provide the random number generator as a function + # with normal distribution and take engine as the positional arguments keywords. + scale=np.sqrt(model.params[model.param_names.index('sigma2')]), + burnin=2*max(self.P,self.Q)) # @alfoa, 2020 return hist def _generateFourierSignal(self, pivots, periods): @@ -1566,11 +1566,11 @@ def getFundamentalFeatures(self, requestedFeatures, featureTemplate=None): feature = featureTemplate.format(target=target, metric='arma', id='std') features[feature] = np.sqrt(arma.params[arma.param_names.index('sigma2')]) # autoregression - for p, val in enumerate(-arma.polynomial_ar[1:]): + for p, val in enumerate(-arma.polynomial_ar[1:]): # The AR coefficients are stored in polynomial form here (flipped sign and with a term in the zero position of the array for lag=0) feature = featureTemplate.format(target=target, metric='arma', id='AR_{}'.format(p)) features[feature] = val # moving average - for q, val in enumerate(arma.polynomial_ma[1:]): + for q, val in enumerate(arma.polynomial_ma[1:]): # keep only the terms for lag>0 feature = featureTemplate.format(target=target, metric='arma', id='MA_{}'.format(q)) features[feature] = val for target, cdfParam in self.cdfParams.items(): From 5879bb4ba7515c0b9e5443f9770e3b6110808cad Mon Sep 17 00:00:00 2001 From: jbryan314 Date: Mon, 25 Jul 2022 16:20:09 -0600 Subject: [PATCH 4/7] handles storage of ARIMAResults --- ravenframework/SupervisedLearning/ARMA.py | 27 ++++++++++++++--------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/ravenframework/SupervisedLearning/ARMA.py b/ravenframework/SupervisedLearning/ARMA.py index 26108ccbac..718baeb36e 100644 --- a/ravenframework/SupervisedLearning/ARMA.py +++ b/ravenframework/SupervisedLearning/ARMA.py @@ -915,14 +915,14 @@ def _generateARMASignal(self, model, numSamples=None,randEngine=None): numSamples = len(self.pivotParameterValues) if randEngine is None: randEngine=self.randomEng - import statsmodels.tsa + import statsmodels.api hist = statsmodels.tsa.arima_process.arma_generate_sample(ar=model.polynomial_ar, ma=model.polynomial_ma, nsample=numSamples, distrvs=functools.partial(randomUtils.randomNormal,engine=randEngine), # functool.partial provide the random number generator as a function # with normal distribution and take engine as the positional arguments keywords. - scale=np.sqrt(model.params[model.param_names.index('sigma2')]), + scale=model.sigma, burnin=2*max(self.P,self.Q)) # @alfoa, 2020 return hist @@ -1095,6 +1095,12 @@ def _trainARMA(self, data, masks=None): data = data[masks] import statsmodels.api results = statsmodels.tsa.arima.model.ARIMA(data, order=(self.P, 0, self.Q), trend='c').fit() + # The ARIMAResults object here can cause problems with ray when running in parallel. Dropping it + # in exchange for the armaResultsProxy class avoids the issue while preserving what we really + # care out from the ARIMAResults object. + results = armaResultsProxy(results.polynomial_ar, + results.polynomial_ma, + np.sqrt(results.params[results.param_names.index('sigma2')])) return results def _trainCDF(self, data, binOps=None): @@ -1382,7 +1388,7 @@ def writeXML(self, writeTo, targets=None, skip=None): root.append(targetNode) armaNode = xmlUtils.newNode('ARMA_params') targetNode.append(armaNode) - armaNode.append(xmlUtils.newNode('std', text=np.sqrt(arma.params[arma.param_names.index('sigma2')]))) + armaNode.append(xmlUtils.newNode('std', text=arma.sigma)) # TODO covariances, P and Q, etc for target,peakInfo in self.peaks.items(): targetNode = root.find(target) @@ -1564,7 +1570,7 @@ def getFundamentalFeatures(self, requestedFeatures, featureTemplate=None): for target, arma in self.armaResult.items(): # sigma feature = featureTemplate.format(target=target, metric='arma', id='std') - features[feature] = np.sqrt(arma.params[arma.param_names.index('sigma2')]) + features[feature] = arma.sigma # autoregression for p, val in enumerate(-arma.polynomial_ar[1:]): # The AR coefficients are stored in polynomial form here (flipped sign and with a term in the zero position of the array for lag=0) feature = featureTemplate.format(target=target, metric='arma', id='AR_{}'.format(p)) @@ -2538,15 +2544,14 @@ class armaResultsProxy: Class that can be used to artifically construct ARMA information from pre-determined values """ - def __init__(self, arparams, maparams, sigma): + def __init__(self, polynomial_ar, polynomial_ma, sigma): """ Constructor. - @ In, arparams, np.array(float), autoregressive coefficients - @ In, maparams, np.array(float), moving average coefficients + @ In, polynomial_ar, np.array(float), autoregressive coefficients + @ In, polynomial_ma, np.array(float), moving average coefficients @ In, sigma, float, standard deviation of ARMA residual noise @ Out, None """ - self.polynomial_ar = np.append(1, -np.atleast_1d(arparams)) - self.polynomial_ma = np.append(1, np.atleast_1d(maparams)) - self.param_names = ['sigma2'] - self.params = [sigma ** 2] + self.polynomial_ar = polynomial_ar + self.polynomial_ma = polynomial_ma + self.sigma = sigma From 1b155b5c6f33b6eb7093d71634d5628ac1632307 Mon Sep 17 00:00:00 2001 From: jbryan314 Date: Wed, 27 Jul 2022 11:02:15 -0600 Subject: [PATCH 5/7] corrects variable names --- ravenframework/SupervisedLearning/ARMA.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ravenframework/SupervisedLearning/ARMA.py b/ravenframework/SupervisedLearning/ARMA.py index 718baeb36e..2b3c6463a8 100644 --- a/ravenframework/SupervisedLearning/ARMA.py +++ b/ravenframework/SupervisedLearning/ARMA.py @@ -916,8 +916,8 @@ def _generateARMASignal(self, model, numSamples=None,randEngine=None): if randEngine is None: randEngine=self.randomEng import statsmodels.api - hist = statsmodels.tsa.arima_process.arma_generate_sample(ar=model.polynomial_ar, - ma=model.polynomial_ma, + hist = statsmodels.tsa.arima_process.arma_generate_sample(ar=model.polyar, + ma=model.polyma, nsample=numSamples, distrvs=functools.partial(randomUtils.randomNormal,engine=randEngine), # functool.partial provide the random number generator as a function @@ -1572,11 +1572,11 @@ def getFundamentalFeatures(self, requestedFeatures, featureTemplate=None): feature = featureTemplate.format(target=target, metric='arma', id='std') features[feature] = arma.sigma # autoregression - for p, val in enumerate(-arma.polynomial_ar[1:]): # The AR coefficients are stored in polynomial form here (flipped sign and with a term in the zero position of the array for lag=0) + for p, val in enumerate(-arma.polyar[1:]): # The AR coefficients are stored in polynomial form here (flipped sign and with a term in the zero position of the array for lag=0) feature = featureTemplate.format(target=target, metric='arma', id='AR_{}'.format(p)) features[feature] = val # moving average - for q, val in enumerate(arma.polynomial_ma[1:]): # keep only the terms for lag>0 + for q, val in enumerate(arma.polyma[1:]): # keep only the terms for lag>0 feature = featureTemplate.format(target=target, metric='arma', id='MA_{}'.format(q)) features[feature] = val for target, cdfParam in self.cdfParams.items(): @@ -2544,14 +2544,14 @@ class armaResultsProxy: Class that can be used to artifically construct ARMA information from pre-determined values """ - def __init__(self, polynomial_ar, polynomial_ma, sigma): + def __init__(self, polyar, polyma, sigma): """ Constructor. - @ In, polynomial_ar, np.array(float), autoregressive coefficients - @ In, polynomial_ma, np.array(float), moving average coefficients + @ In, polyar, np.array(float), autoregressive coefficients + @ In, polyma, np.array(float), moving average coefficients @ In, sigma, float, standard deviation of ARMA residual noise @ Out, None """ - self.polynomial_ar = polynomial_ar - self.polynomial_ma = polynomial_ma + self.polyar = polyar + self.polyma = polyma self.sigma = sigma From 9b2450dda63c796aea4ff5b234c3b73caf27f91b Mon Sep 17 00:00:00 2001 From: Jacob Bryan Date: Wed, 27 Jul 2022 15:06:07 -0600 Subject: [PATCH 6/7] fixes setting ARMA parameters for interpolated ROMs --- ravenframework/SupervisedLearning/ARMA.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ravenframework/SupervisedLearning/ARMA.py b/ravenframework/SupervisedLearning/ARMA.py index 2b3c6463a8..0a9e8c544d 100644 --- a/ravenframework/SupervisedLearning/ARMA.py +++ b/ravenframework/SupervisedLearning/ARMA.py @@ -1870,15 +1870,15 @@ def _setArmaResults(self, paramDict): if 'AR' in info: AR_keys, AR_vals = zip(*list(info['AR'].items())) AR_keys, AR_vals = zip(*sorted(zip(AR_keys, AR_vals), key=lambda x:x[0])) - AR_vals = np.asarray(AR_vals) + AR_vals = np.concatenate(([1], -np.asarray(AR_vals))) # convert the AR params into polynomial form else: - AR_vals = np.array([]) + AR_vals = np.array([1]) # must include a 1 for the zero lag term if 'MA' in info: MA_keys, MA_vals = zip(*list(info['MA'].items())) MA_keys, MA_vals = zip(*sorted(zip(MA_keys, MA_vals), key=lambda x:x[0])) - MA_vals = np.asarray(MA_vals) + MA_vals = np.concatenate(([1], np.asarray(MA_vals))) # converts the MA params into polynomial form else: - MA_vals = np.array([]) + MA_vals = np.array([1]) if 'bin' in info: bin_keys, bin_vals = zip(*list(info['bin'].items())) bin_keys, bin_vals = zip(*sorted(zip(bin_keys, bin_vals), key=lambda x:x[0])) From 3d9d72aa6ebe276155f182cf08d257efe0e7bc2b Mon Sep 17 00:00:00 2001 From: Jacob Bryan Date: Mon, 1 Aug 2022 11:37:41 -0600 Subject: [PATCH 7/7] loosens Basic test tolerance --- tests/framework/ROM/TimeSeries/ARMA/tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/framework/ROM/TimeSeries/ARMA/tests b/tests/framework/ROM/TimeSeries/ARMA/tests index b7c08e9aa8..80dbb6404a 100644 --- a/tests/framework/ROM/TimeSeries/ARMA/tests +++ b/tests/framework/ROM/TimeSeries/ARMA/tests @@ -15,7 +15,7 @@ input = 'basic.xml' output = 'Basic/romMeta.csv' UnorderedXml = 'Basic/romMeta.xml' - rel_err = 1e-6 + rel_err = 7e-6 [../] [./ARMAparallel]