diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 056bb8e6..739c9397 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -12,25 +12,45 @@ if(SKBUILD) # Pybind11 find_package(pybind11 REQUIRED) -# Armadillo -find_package(armadillo REQUIRED) + +# CARMA +ADD_SUBDIRECTORY(../src/libs/carma carma) + +# BLAS and LAPACK. Needed by Armadillo +find_package(BLAS) +find_package(LAPACK) +if(LAPACK_FOUND AND BLAS_FOUND) + set(lapackblas_libraries ${BLAS_LIBRARIES} ${LAPACK_LIBRARIES}) +else() + # IS: This is where they are on my system. This might change from one OS to another + set(lapackblas_libraries "/usr/lib/x86_64-linux-gnu/") +endif() + +# Armadillo +find_package(armadillo) +IF(NOT ARMADILLO_FOUND) + set(ARMADILLO_INCLUDE_DIRS "/usr/lib/") +ENDIF() include_directories(${ARMADILLO_INCLUDE_DIRS}) +# Adam General +pybind11_add_module(_adam_general ../src/python_examples/adamGeneral.cpp) +target_include_directories(_adam_general PRIVATE ../src/python_examples/.) +target_link_libraries(_adam_general PRIVATE carma::carma ${ARMADILLO_LIBRARIES} ${lapackblas_libraries}) +install(TARGETS _adam_general DESTINATION smooth/adam_general) + + +# Old experimental stuff by Leo # code to add the pybind11 cpp module, look at demo project # (https://github.com/ltsaprounis/python-cpp-experiments/tree/main) for details. -pybind11_add_module(_my_linalg ../src/python_examples/my_linalg.cpp) +#pybind11_add_module(_my_linalg ../src/python_examples/my_linalg.cpp) # add CARMA as a subdirectory -add_subdirectory(../src/libs/carma build) -target_link_libraries(_my_linalg - PRIVATE carma::carma - ${ARMADILLO_LIBRARIES} -) +#add_subdirectory(../src/libs/carma build) +#target_link_libraries(_my_linalg +# PRIVATE carma::carma +# ${ARMADILLO_LIBRARIES} +#) -install(TARGETS _my_linalg DESTINATION smooth/my_linalg) +# install(TARGETS _my_linalg DESTINATION smooth/my_linalg) -# Adam General -pybind11_add_module(_adam_general ../src/python_examples/adamGeneral.cpp) -target_link_libraries(_adam_general PRIVATE carma::carma ${ARMADILLO_LIBRARIES}) -target_include_directories(_adam_general PRIVATE ../src/python_examples/.) -install(TARGETS _adam_general DESTINATION smooth/adam_general) diff --git a/python/smooth/adam_general/adam_profile.py b/python/smooth/adam_general/adam_profile.py new file mode 100644 index 00000000..81f37a93 --- /dev/null +++ b/python/smooth/adam_general/adam_profile.py @@ -0,0 +1,63 @@ +import numpy as np + + +def adamProfileCreator( + lagsModelAll, lagsModelMax, obsAll, lags=None, yIndex=None, yClasses=None +): + """ + Creates recent profile and the lookup table for adam. + Parameters: + lagsModelAll (list): All lags used in the model for ETS + ARIMA + xreg. + lagsModelMax (int): The maximum lag used in the model. + obsAll (int): Number of observations to create. + lags (list): The original lags provided by user (optional). + yIndex (list): The indices needed to get the specific dates (optional). + yClasses (list): The class used for the actual data (optional). + Returns: + dict: A dictionary with 'recent' (profilesRecentTable) and 'lookup' (indexLookupTable) as keys. + """ + # Initialize matrices + profilesRecentTable = np.zeros((len(lagsModelAll), lagsModelMax)) + indexLookupTable = np.ones((len(lagsModelAll), obsAll + lagsModelMax)) + profileIndices = ( + np.arange(1, lagsModelMax * len(lagsModelAll) + 1) + .reshape(-1, len(lagsModelAll)) + .T + ) + + # Update matrices based on lagsModelAll + for i, lag in enumerate(lagsModelAll): + # Create the matrix with profiles based on the provided lags. + # For every row, fill the first 'lag' elements from 1 to lag + profilesRecentTable[i, : lag[0]] = np.arange(1, lag[0] + 1) + + # For the i-th row in indexLookupTable, fill with a repeated sequence starting from lagsModelMax to the end of the row. + # The repeated sequence is the i-th row of profileIndices, repeated enough times to cover 'obsAll' observations. + # '- 1' at the end adjusts these values to Python's zero-based indexing. + indexLookupTable[i, lagsModelMax : (lagsModelMax + obsAll)] = ( # noqa + np.tile( + profileIndices[i, : lagsModelAll[i][0]], + int(np.ceil(obsAll / lagsModelAll[i][0])), + )[0:obsAll] + - 1 + ) + + # Extract unique values from from lagsModelMax to lagsModelMax + obsAll of indexLookupTable + unique_values = np.unique( + indexLookupTable[i, lagsModelMax : lagsModelMax + obsAll] # noqa + ) + + # fix the head of teh data before the sample starts + # Repeat the unique values lagsModelMax times and then trim the sequence to only keep the first lagsModelMax elements + indexLookupTable[i, :lagsModelMax] = np.tile(unique_values, lagsModelMax)[ + -lagsModelMax: + ] + + # Convert to int! + indexLookupTable = indexLookupTable.astype(int) + + # Note: I skip andling of special cases (e.g., daylight saving time, leap years) + return { + "recent": np.array(profilesRecentTable, dtype="float64"), + "lookup": np.array(indexLookupTable, dtype="int64"), + } diff --git a/python/smooth/adam_general/sma.py b/python/smooth/adam_general/sma.py index 18854882..c37d0c87 100644 --- a/python/smooth/adam_general/sma.py +++ b/python/smooth/adam_general/sma.py @@ -1,5 +1,6 @@ import numpy as np from smooth.adam_general._adam_general import adam_fitter +from smooth.adam_general.adam_profile import adamProfileCreator def sma(y, order=1, h=10, holdout=False): @@ -24,21 +25,25 @@ def sma(y, order=1, h=10, holdout=False): def creator_sma(order): # lags_model_all = np.ones(shape=(order, 1)) # This needs to be a vector of values - lags_model_all = np.arange(1, order + 1).reshape(order, 1) - lags_model_max = max(lags_model_all) + lags_model_all = np.arange(1, order+1, dtype="int32").reshape(order, 1) + lags_model_max = int(max(lags_model_all)) obs_states = obs_in_sample + lags_model_max - # profiles_recent_table = np.zeros( + profiles_recent_table, index_lookup_table = adamProfileCreator( + lagsModelAll=lags_model_all, + lagsModelMax=lags_model_max, + obsAll=obs_all + ).values() + + # # This needs to be generated by a profileCreator() function + # profiles_recent_table = np.mean(y_in_sample[0 : (order - 1)]) * np.ones( # shape=(order, lags_model_max), dtype=np.float64 # ) - # This needs to be generated by a profileCreator() function - profiles_recent_table = np.mean(y_in_sample[0 : (order - 1)]) * np.ones( - shape=(order, lags_model_max), dtype=np.float64 - ) - - # This as well... - index_lookup_table = np.tile(np.arange(order), (obs_all + lags_model_max, 1)).T + # # This as well... + # index_lookup_table = np.tile( + # np.arange(order), (obs_all + lags_model_max, 1) + # ).T matF = np.ones((order, order)) / order matWt = np.ones((obs_in_sample, order)) @@ -72,64 +77,3 @@ def creator_sma(order): return adam_fitted return creator_sma(order=order) - - -def adamProfileCreator( - lagsModelAll, lagsModelMax, obsAll, lags=None, yIndex=None, yClasses=None -): - """ - Creates recent profile and the lookup table for adam. - - Parameters: - lagsModelAll (list): All lags used in the model for ETS + ARIMA + xreg. - lagsModelMax (int): The maximum lag used in the model. - obsAll (int): Number of observations to create. - lags (list): The original lags provided by user (optional). - yIndex (list): The indices needed to get the specific dates (optional). - yClasses (list): The class used for the actual data (optional). - - Returns: - dict: A dictionary with 'recent' (profilesRecentTable) and 'lookup' (indexLookupTable) as keys. - """ - # Initialize matrices - profilesRecentTable = np.zeros((len(lagsModelAll), lagsModelMax)) - indexLookupTable = np.ones((len(lagsModelAll), obsAll + lagsModelMax)) - profileIndices = ( - np.arange(1, lagsModelMax * len(lagsModelAll) + 1) - .reshape(-1, len(lagsModelAll)) - .T - ) - - # Update matrices based on lagsModelAll - for i, lag in enumerate(lagsModelAll): - # Create the matrix with profiles based on the provided lags. - # For every row, fill the first 'lag' elements from 1 to lag - profilesRecentTable[i, :lag] = np.arange(1, lag + 1) - - # For the i-th row in indexLookupTable, fill with a repeated sequence starting from lagsModelMax to the end of the row. - # The repeated sequence is the i-th row of profileIndices, repeated enough times to cover 'obsAll' observations. - # '- 1' at the end adjusts these values to Python's zero-based indexing. - indexLookupTable[i, lagsModelMax : (lagsModelMax + obsAll)] = ( # noqa - np.tile( - profileIndices[i, : lagsModelAll[i]], - int(np.ceil(obsAll / lagsModelAll[i])), - )[0:obsAll] - - 1 - ) - - # Extract unique values from from lagsModelMax to lagsModelMax + obsAll of indexLookupTable - unique_values = np.unique( - indexLookupTable[i, lagsModelMax : lagsModelMax + obsAll] # noqa - ) - - # fix the head of teh data before the sample starts - # Repeat the unique values lagsModelMax times and then trim the sequence to only keep the first lagsModelMax elements - indexLookupTable[i, :lagsModelMax] = np.tile(unique_values, lagsModelMax)[ - -lagsModelMax: - ] - - # Convert to int! - indexLookupTable = indexLookupTable.astype(int) - - # Note: I skip andling of special cases (e.g., daylight saving time, leap years) - return {"recent": profilesRecentTable, "lookup": indexLookupTable} diff --git a/python/smooth/adam_general/test_script.py b/python/smooth/adam_general/test_script.py new file mode 100644 index 00000000..535516c5 --- /dev/null +++ b/python/smooth/adam_general/test_script.py @@ -0,0 +1,7 @@ +import numpy as np +from smooth.adam_general.sma import sma + +if __name__ == "__main__": + y = np.arange(0, 100) + results = sma(y, order=5) + print(results["yFitted"]) diff --git a/python/test/skeleton.py b/python/test/skeleton.py new file mode 100644 index 00000000..e69de29b