pysiglib 2.2.0__tar.gz → 2.3.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.
- {pysiglib-2.2.0 → pysiglib-2.3.0}/CMakeLists.txt +55 -3
- {pysiglib-2.2.0 → pysiglib-2.3.0}/PKG-INFO +1 -1
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pyproject.toml +1 -1
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pysiglib/__init__.py +2 -2
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pysiglib/param_checks.py +14 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pysiglib/sig_kernel.py +58 -70
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pysiglib/sig_kernel_backprop.py +3 -16
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pysiglib/sig_metrics.py +16 -16
- pysiglib-2.3.0/pysiglib/static_kernels.py +418 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pysiglib/torch_api/__init__.py +1 -1
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pysiglib/torch_api/torch_api.py +30 -21
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/cp_bch.h +8 -8
- pysiglib-2.3.0/siglib/cpsig/cp_sig_kernel.h +432 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/cp_tensor_poly.h +2 -2
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/cp_vector_funcs.h +156 -8
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/macros.h +4 -0
- pysiglib-2.3.0/siglib/cpsig/multithreading.h +157 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/cu_sig_kernel.cu +224 -249
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/cu_signature.cu +30 -9
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/cu_tensor_poly.cu +2 -2
- pysiglib-2.3.0/tests/test_static_kernels.py +615 -0
- pysiglib-2.2.0/pysiglib/static_kernels.py +0 -206
- pysiglib-2.2.0/siglib/cpsig/cp_sig_kernel.h +0 -739
- pysiglib-2.2.0/siglib/cpsig/multithreading.h +0 -196
- {pysiglib-2.2.0 → pysiglib-2.3.0}/.gitattributes +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/.gitignore +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/CITATION.cff +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/LICENSE +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/README.md +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/avx_info/avx_info.cpp +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/examples/README.md +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/examples/cpu_vs_cuda/log_sig_times_cpu_vs_cuda_plot.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/examples/cpu_vs_cuda/sig_backprop_times_cpu_vs_cuda_plot.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/examples/cpu_vs_cuda/sig_coef_times_cpu_vs_cuda_plot.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/examples/cpu_vs_cuda/sig_combine_times_cpu_vs_cuda_plot.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/examples/cpu_vs_cuda/sig_kernel_backprop_times_cpu_vs_cuda_plot.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/examples/cpu_vs_cuda/sig_kernel_times_cpu_vs_cuda_plot.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/examples/cpu_vs_cuda/sig_times_cpu_vs_cuda_plot.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/examples/log_sig_times_plot.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/examples/plotting_params.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/examples/sig_backprop_times.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/examples/sig_backprop_times_plot.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/examples/sig_combine_example.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/examples/sig_kernel_backprop_times.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/examples/sig_kernel_backprop_times_plot.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/examples/sig_kernel_example.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/examples/sig_kernel_times.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/examples/sig_kernel_times_plot.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/examples/sig_times.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/examples/sig_times_plot.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/examples/signature_example.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/examples/small_example.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/examples/timing_utils.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pysiglib/README.md +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pysiglib/data_handlers.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pysiglib/dtypes.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pysiglib/error_codes.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pysiglib/load_siglib.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pysiglib/log_sig.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pysiglib/log_sig_backprop.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pysiglib/log_sig_combine.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pysiglib/sig.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pysiglib/sig_backprop.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pysiglib/sig_coef.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pysiglib/sig_coef_backprop.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pysiglib/sig_length.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pysiglib/transform_path.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pysiglib/transform_path_backprop.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/pysiglib/words.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/requirements.txt +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/.gitignore +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/README.md +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/CMakeLists.txt +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/cp_bch.cpp +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/cp_bch_data.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/cp_log_signature.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/cp_log_signatures.cpp +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/cp_path.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/cp_path_transforms.cpp +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/cp_path_transforms.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/cp_sig_coef.cpp +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/cp_sig_coef.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/cp_sig_kernel.cpp +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/cp_signature.cpp +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/cp_signature.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/cp_tensor_poly.cpp +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/cppch.cpp +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/cppch.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/cpsig.cpp +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/cpsig.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/log_sig_cache.cpp +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/log_sig_cache.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/sparse.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/words.cpp +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig/words.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cpsig_unit_testing/cp_unit_tests.cpp +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/CMakeLists.txt +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/cu_atomic.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/cu_log_sig_backprop.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/cu_log_sig_cache.cu +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/cu_log_sig_cache.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/cu_log_sig_combine.cu +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/cu_log_sig_combine.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/cu_log_signature.cu +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/cu_log_signature.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/cu_macros.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/cu_path_transforms.cu +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/cu_path_transforms.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/cu_sig_coef.cu +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/cu_sig_coef.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/cu_sig_kernel.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/cu_signature.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/cu_tensor_poly.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/cuda_constants.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/cupch.cpp +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/cupch.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig/cusig.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/cusig_unit_testing/cu_unit_tests.cpp +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/shared/bch_data.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/siglib.sln +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/test_app/dll_funcs.cpp +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/test_app/dll_funcs.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/test_app/test_app.cpp +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/test_app/tests.cpp +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/test_app/tests.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/test_app/utils.cpp +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/siglib/test_app/utils.h +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/conftest.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_apis_match.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_errors.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_log_sig.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_log_sig_backprop.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_log_sig_combine.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_log_sig_combine_backprop.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_log_sig_combine_cuda.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_sig_backprop.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_sig_coef.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_sig_coef_backprop.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_sig_combine.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_sig_combine_backprop.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_sig_kernel.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_sig_kernel_backprop.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_sig_kernel_backprop_grid.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_sig_kernel_gram.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_sig_kernel_gram_backprop.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_sig_kernel_gram_backprop_grid.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_sig_length.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_sig_metrics.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_sig_metrics_backprop.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_signature.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_transform_path.py +0 -0
- {pysiglib-2.2.0 → pysiglib-2.3.0}/tests/test_words.py +0 -0
|
@@ -31,14 +31,32 @@ endif()
|
|
|
31
31
|
# CUDA detection
|
|
32
32
|
# ---------------------------------------------------------------------------
|
|
33
33
|
set(_cuda_enabled FALSE)
|
|
34
|
+
set(_cuda_version "")
|
|
34
35
|
if(PYSIGLIB_CUDA)
|
|
35
36
|
check_language(CUDA)
|
|
36
37
|
if(CMAKE_CUDA_COMPILER)
|
|
37
38
|
enable_language(CUDA)
|
|
38
39
|
find_package(CUDAToolkit REQUIRED)
|
|
39
40
|
set(_cuda_enabled TRUE)
|
|
41
|
+
set(_cuda_version "${CUDAToolkit_VERSION}")
|
|
40
42
|
else()
|
|
41
43
|
message(STATUS "CUDA compiler not found — building without CUDA")
|
|
44
|
+
message(STATUS " CUDA_PATH env var = '$ENV{CUDA_PATH}'")
|
|
45
|
+
find_program(_nvcc_probe nvcc
|
|
46
|
+
HINTS "$ENV{CUDA_PATH}/bin" "$ENV{CUDA_PATH}/Library/bin")
|
|
47
|
+
if(_nvcc_probe)
|
|
48
|
+
message(STATUS " nvcc found at: ${_nvcc_probe}")
|
|
49
|
+
message(STATUS " nvcc was found but failed to compile a test program.")
|
|
50
|
+
message(STATUS " Common causes:")
|
|
51
|
+
message(STATUS " - cl.exe (MSVC) is not on PATH — install Visual Studio Build Tools")
|
|
52
|
+
message(STATUS " - CUDA Visual Studio integration files are missing — install the")
|
|
53
|
+
message(STATUS " CUDA Toolkit from https://developer.nvidia.com/cuda-downloads")
|
|
54
|
+
message(STATUS " (the conda cuda-toolkit package does not include VS integration)")
|
|
55
|
+
else()
|
|
56
|
+
message(STATUS " nvcc was not found on PATH or in CUDA_PATH")
|
|
57
|
+
message(STATUS " Install the CUDA Toolkit from https://developer.nvidia.com/cuda-downloads")
|
|
58
|
+
endif()
|
|
59
|
+
message(STATUS " CMAKE_GENERATOR = ${CMAKE_GENERATOR}")
|
|
42
60
|
endif()
|
|
43
61
|
endif()
|
|
44
62
|
|
|
@@ -77,8 +95,39 @@ if(PYSIGLIB_AVX)
|
|
|
77
95
|
endif()
|
|
78
96
|
endif()
|
|
79
97
|
|
|
80
|
-
message(STATUS "
|
|
81
|
-
message(STATUS "pysiglib
|
|
98
|
+
message(STATUS "")
|
|
99
|
+
message(STATUS " ---- pysiglib build summary ----")
|
|
100
|
+
message(STATUS " System : ${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_PROCESSOR}")
|
|
101
|
+
message(STATUS " Generator : ${CMAKE_GENERATOR}")
|
|
102
|
+
message(STATUS " CXX compiler : ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}")
|
|
103
|
+
if(CMAKE_CONFIGURATION_TYPES)
|
|
104
|
+
message(STATUS " Build configs : ${CMAKE_CONFIGURATION_TYPES}")
|
|
105
|
+
else()
|
|
106
|
+
message(STATUS " Build type : ${CMAKE_BUILD_TYPE}")
|
|
107
|
+
endif()
|
|
108
|
+
message(STATUS " CUDA enabled : ${_cuda_enabled}")
|
|
109
|
+
if(_cuda_enabled)
|
|
110
|
+
message(STATUS " CUDA compiler : ${CMAKE_CUDA_COMPILER}")
|
|
111
|
+
message(STATUS " CUDA version : ${_cuda_version}")
|
|
112
|
+
message(STATUS " CUDA arch : ${PYSIGLIB_CUDA_ARCH}")
|
|
113
|
+
endif()
|
|
114
|
+
message(STATUS " AVX enabled : ${_enable_vec}")
|
|
115
|
+
if(_enable_vec AND NOT APPLE)
|
|
116
|
+
if(_has_avx2)
|
|
117
|
+
set(_avx2_str "yes")
|
|
118
|
+
else()
|
|
119
|
+
set(_avx2_str "no")
|
|
120
|
+
endif()
|
|
121
|
+
if(_has_avx512f)
|
|
122
|
+
set(_avx512_str "yes")
|
|
123
|
+
else()
|
|
124
|
+
set(_avx512_str "no")
|
|
125
|
+
endif()
|
|
126
|
+
message(STATUS " AVX2 : ${_avx2_str}")
|
|
127
|
+
message(STATUS " AVX-512F : ${_avx512_str}")
|
|
128
|
+
endif()
|
|
129
|
+
message(STATUS " --------------------------------")
|
|
130
|
+
message(STATUS "")
|
|
82
131
|
|
|
83
132
|
# ---------------------------------------------------------------------------
|
|
84
133
|
# Generate _config.py (consumed by pysiglib.load_siglib at runtime)
|
|
@@ -99,7 +148,10 @@ file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/_config.py"
|
|
|
99
148
|
# It contains information about how the package was built.\n\
|
|
100
149
|
SYSTEM = '${CMAKE_SYSTEM_NAME}'\n\
|
|
101
150
|
BUILT_WITH_CUDA = ${_cuda_py}\n\
|
|
102
|
-
BUILT_WITH_AVX = ${_vec_py}\n
|
|
151
|
+
BUILT_WITH_AVX = ${_vec_py}\n\
|
|
152
|
+
CUDA_VERSION = '${_cuda_version}'\n\
|
|
153
|
+
CXX_COMPILER = '${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}'\n\
|
|
154
|
+
CMAKE_GENERATOR = '${CMAKE_GENERATOR}'\n")
|
|
103
155
|
|
|
104
156
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_config.py" DESTINATION pysiglib)
|
|
105
157
|
|
|
@@ -28,10 +28,10 @@ from .sig_kernel_backprop import sig_kernel_backprop, sig_kernel_gram_backprop
|
|
|
28
28
|
from .sig_metrics import sig_score, expected_sig_score, sig_mmd
|
|
29
29
|
from .transform_path import transform_path
|
|
30
30
|
from .transform_path_backprop import transform_path_backprop
|
|
31
|
-
from .static_kernels import Context, StaticKernel, LinearKernel, ScaledLinearKernel, RBFKernel
|
|
31
|
+
from .static_kernels import Context, StaticKernel, LinearKernel, ScaledLinearKernel, RBFKernel, PolynomialKernel, Matern12Kernel, Matern32Kernel, Matern52Kernel, RationalQuadraticKernel
|
|
32
32
|
|
|
33
33
|
signature = sig
|
|
34
34
|
|
|
35
35
|
import pysiglib.torch_api
|
|
36
36
|
|
|
37
|
-
__version__ = "2.
|
|
37
|
+
__version__ = "2.3.0"
|
|
@@ -87,6 +87,20 @@ def ensure_own_contiguous_storage(arr):
|
|
|
87
87
|
|
|
88
88
|
raise TypeError("Unexpected error in ensure_own_contiguous_storage: arr must be of type torch.Tensor or numpy.ndarray")
|
|
89
89
|
|
|
90
|
+
def parse_dyadic_order(dyadic_order):
|
|
91
|
+
if isinstance(dyadic_order, tuple) and len(dyadic_order) == 2:
|
|
92
|
+
do1, do2 = dyadic_order
|
|
93
|
+
elif isinstance(dyadic_order, int):
|
|
94
|
+
do1 = do2 = dyadic_order
|
|
95
|
+
else:
|
|
96
|
+
raise TypeError("dyadic_order must be an integer or a tuple of length 2")
|
|
97
|
+
if do1 < 0 or do2 < 0:
|
|
98
|
+
raise ValueError("dyadic_order must be a non-negative integer or tuple of non-negative integers")
|
|
99
|
+
return do1, do2
|
|
100
|
+
|
|
101
|
+
def dyadic_grid_length(path_length, dyadic_order):
|
|
102
|
+
return ((path_length - 1) << dyadic_order) + 1
|
|
103
|
+
|
|
90
104
|
def check_log_sig_method(method):
|
|
91
105
|
if method < 0 or method > 3:
|
|
92
106
|
raise ValueError("method must be one of 0, 1, 2 or 3. Got " + str(method) + " instead.")
|
|
@@ -20,7 +20,7 @@ import numpy as np
|
|
|
20
20
|
import torch
|
|
21
21
|
|
|
22
22
|
from .transform_path import transform_path
|
|
23
|
-
from .param_checks import check_type
|
|
23
|
+
from .param_checks import check_type, parse_dyadic_order, dyadic_grid_length
|
|
24
24
|
from .error_codes import err_msg
|
|
25
25
|
from .dtypes import CPSIG_BATCH_SIG_KERNEL, DTYPES, CUSIG_BATCH_SIG_KERNEL_CUDA
|
|
26
26
|
from .data_handlers import MultiplePathInputHandler, ScalarOutputHandler, GridOutputHandler
|
|
@@ -67,7 +67,8 @@ def sig_kernel(
|
|
|
67
67
|
lead_lag : bool = False,
|
|
68
68
|
end_time : float = 1.,
|
|
69
69
|
n_jobs : int = 1,
|
|
70
|
-
return_grid: bool = False
|
|
70
|
+
return_grid: bool = False,
|
|
71
|
+
normalize : bool = False
|
|
71
72
|
) -> Union[np.ndarray, torch.tensor]:
|
|
72
73
|
"""
|
|
73
74
|
Computes a single signature kernel or a batch of signature kernels.
|
|
@@ -121,6 +122,9 @@ def sig_kernel(
|
|
|
121
122
|
:type n_jobs: int
|
|
122
123
|
:param return_grid: If ``True``, returns the entire PDE grid.
|
|
123
124
|
:type return_grid: bool
|
|
125
|
+
:param normalize: If ``True``, normalizes the signature kernel so that :math:`k(x, x) = 1`
|
|
126
|
+
by dividing by :math:`\\sqrt{k(x, x) \\cdot k(y, y)}`. Cannot be used with ``return_grid=True``.
|
|
127
|
+
:type normalize: bool
|
|
124
128
|
:return: Single signature kernel or batch of signature kernels
|
|
125
129
|
:rtype: numpy.ndarray | torch.tensor
|
|
126
130
|
|
|
@@ -175,18 +179,10 @@ def sig_kernel(
|
|
|
175
179
|
check_type(n_jobs, "n_jobs", int)
|
|
176
180
|
if n_jobs == 0:
|
|
177
181
|
raise ValueError("n_jobs cannot be 0")
|
|
182
|
+
if normalize and return_grid:
|
|
183
|
+
raise ValueError("normalize=True cannot be used with return_grid=True")
|
|
178
184
|
|
|
179
|
-
|
|
180
|
-
dyadic_order_1 = dyadic_order[0]
|
|
181
|
-
dyadic_order_2 = dyadic_order[1]
|
|
182
|
-
elif isinstance(dyadic_order, int):
|
|
183
|
-
dyadic_order_1 = dyadic_order
|
|
184
|
-
dyadic_order_2 = dyadic_order
|
|
185
|
-
else:
|
|
186
|
-
raise TypeError("dyadic_order must be an integer or a tuple of length 2")
|
|
187
|
-
|
|
188
|
-
if dyadic_order_1 < 0 or dyadic_order_2 < 0:
|
|
189
|
-
raise ValueError("dyadic_order must be a non-negative integer or tuple of non-negative integers")
|
|
185
|
+
dyadic_order_1, dyadic_order_2 = parse_dyadic_order(dyadic_order)
|
|
190
186
|
|
|
191
187
|
if time_aug or lead_lag:
|
|
192
188
|
path1 = transform_path(path1, time_aug, lead_lag, end_time, n_jobs)
|
|
@@ -198,8 +194,8 @@ def sig_kernel(
|
|
|
198
194
|
if not return_grid:
|
|
199
195
|
result = ScalarOutputHandler(data)
|
|
200
196
|
else:
|
|
201
|
-
dyadic_len_1 = (
|
|
202
|
-
dyadic_len_2 = (
|
|
197
|
+
dyadic_len_1 = dyadic_grid_length(data.length[0], dyadic_order_1)
|
|
198
|
+
dyadic_len_2 = dyadic_grid_length(data.length[1], dyadic_order_2)
|
|
203
199
|
result = GridOutputHandler(dyadic_len_1, dyadic_len_2, data)
|
|
204
200
|
|
|
205
201
|
torch_path1 = torch.as_tensor(data.path[0]) # Avoids data copy
|
|
@@ -238,6 +234,15 @@ def sig_kernel(
|
|
|
238
234
|
stacklevel=2
|
|
239
235
|
)
|
|
240
236
|
|
|
237
|
+
if normalize:
|
|
238
|
+
# Paths are already transformed at this point, so pass time_aug=False, lead_lag=False
|
|
239
|
+
k1 = sig_kernel(path1, path1, dyadic_order, static_kernel, n_jobs=n_jobs)
|
|
240
|
+
k2 = sig_kernel(path2, path2, dyadic_order, static_kernel, n_jobs=n_jobs)
|
|
241
|
+
if isinstance(result.data, np.ndarray):
|
|
242
|
+
result.data = result.data / np.sqrt(np.maximum(k1 * k2, 1e-30))
|
|
243
|
+
else:
|
|
244
|
+
result.data = result.data / torch.sqrt(torch.clamp(k1 * k2, min=1e-30))
|
|
245
|
+
|
|
241
246
|
return result.data
|
|
242
247
|
|
|
243
248
|
|
|
@@ -251,7 +256,8 @@ def sig_kernel_gram(
|
|
|
251
256
|
end_time : float = 1.,
|
|
252
257
|
n_jobs : int = 1,
|
|
253
258
|
max_batch : int = -1,
|
|
254
|
-
return_grid : bool = False
|
|
259
|
+
return_grid : bool = False,
|
|
260
|
+
normalize : bool = False
|
|
255
261
|
) -> Union[np.ndarray, torch.tensor]:
|
|
256
262
|
"""
|
|
257
263
|
Given batches of paths :math:`\\{x_i\\}_{i=1}^B` and :math:`\\{y_i\\}_{i=1}^B`, computes the gram matrix of signature kernels
|
|
@@ -314,6 +320,9 @@ def sig_kernel_gram(
|
|
|
314
320
|
:type max_batch: int
|
|
315
321
|
:param return_grid: If ``True``, returns the entire PDE grid.
|
|
316
322
|
:type return_grid: bool
|
|
323
|
+
:param normalize: If ``True``, normalizes the gram matrix so that :math:`K(x, x) = 1` by
|
|
324
|
+
dividing each entry by :math:`\\sqrt{K(x_i, x_i) \\cdot K(y_j, y_j)}`. Cannot be used with ``return_grid=True``.
|
|
325
|
+
:type normalize: bool
|
|
317
326
|
:return: Gram matrix of signature kernels
|
|
318
327
|
:rtype: numpy.ndarray | torch.tensor
|
|
319
328
|
|
|
@@ -368,6 +377,8 @@ def sig_kernel_gram(
|
|
|
368
377
|
check_type(max_batch, "max_batch", int)
|
|
369
378
|
if max_batch == 0 or max_batch < -1:
|
|
370
379
|
raise ValueError("max_batch must be a positive integer or -1")
|
|
380
|
+
if normalize and return_grid:
|
|
381
|
+
raise ValueError("normalize=True cannot be used with return_grid=True")
|
|
371
382
|
|
|
372
383
|
data = MultiplePathInputHandler([path1, path2], time_aug, lead_lag, end_time, ["path1", "path2"], False)
|
|
373
384
|
|
|
@@ -388,44 +399,41 @@ def sig_kernel_gram(
|
|
|
388
399
|
# Now run computation in batches
|
|
389
400
|
####################################
|
|
390
401
|
|
|
402
|
+
do1, do2 = parse_dyadic_order(dyadic_order)
|
|
403
|
+
|
|
404
|
+
if return_grid:
|
|
405
|
+
gl1 = dyadic_grid_length(data.length[0], do1)
|
|
406
|
+
gl2 = dyadic_grid_length(data.length[1], do2)
|
|
407
|
+
|
|
391
408
|
if symmetric:
|
|
392
409
|
# Symmetric case: only compute upper triangle pairs, mirror to lower.
|
|
393
|
-
# This guarantees exact symmetry and halves the number of kernel evaluations.
|
|
394
410
|
# Cannot mirror grids when dyadic orders differ (gl1 != gl2), so fall through.
|
|
395
|
-
if return_grid:
|
|
396
|
-
|
|
397
|
-
do1, do2 = dyadic_order
|
|
398
|
-
else:
|
|
399
|
-
do1 = do2 = dyadic_order
|
|
400
|
-
gl1 = ((data.length[0] - 1) << do1) + 1
|
|
401
|
-
gl2 = ((data.length[1] - 1) << do2) + 1
|
|
402
|
-
if gl1 != gl2:
|
|
403
|
-
symmetric = False
|
|
411
|
+
if return_grid and gl1 != gl2:
|
|
412
|
+
symmetric = False
|
|
404
413
|
|
|
405
414
|
if symmetric:
|
|
406
415
|
idx_i, idx_j = torch.triu_indices(batch1, batch1, device=path1.device)
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
else:
|
|
413
|
-
res = torch.empty(batch1, batch1, dtype=path1.dtype, device=path1.device)
|
|
414
|
-
|
|
415
|
-
for start in range(0, n_pairs, chunk_size):
|
|
416
|
-
end = min(start + chunk_size, n_pairs)
|
|
417
|
-
ci = idx_i[start:end]
|
|
418
|
-
cj = idx_j[start:end]
|
|
416
|
+
src1, src2 = path1, path1
|
|
417
|
+
else:
|
|
418
|
+
idx_i = torch.arange(batch1, device=path1.device).repeat_interleave(batch2)
|
|
419
|
+
idx_j = torch.arange(batch2, device=path2.device).repeat(batch1)
|
|
420
|
+
src1, src2 = path1, path2
|
|
419
421
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
+
if return_grid:
|
|
423
|
+
res = torch.empty(batch1, batch2, gl1, gl2, dtype=path1.dtype, device=path1.device)
|
|
424
|
+
else:
|
|
425
|
+
res = torch.empty(batch1, batch2, dtype=path1.dtype, device=path1.device)
|
|
422
426
|
|
|
423
|
-
|
|
427
|
+
chunk_size = max_batch * max_batch
|
|
428
|
+
for start in range(0, idx_i.shape[0], chunk_size):
|
|
429
|
+
end = min(start + chunk_size, idx_i.shape[0])
|
|
430
|
+
ci = idx_i[start:end]
|
|
431
|
+
cj = idx_j[start:end]
|
|
424
432
|
|
|
425
|
-
|
|
426
|
-
|
|
433
|
+
k = sig_kernel(src1[ci], src2[cj], dyadic_order, static_kernel, time_aug, lead_lag, end_time, n_jobs, return_grid)
|
|
434
|
+
res[ci, cj] = k
|
|
427
435
|
|
|
428
|
-
|
|
436
|
+
if symmetric:
|
|
429
437
|
off = ci != cj
|
|
430
438
|
if off.any():
|
|
431
439
|
k_mirror = k[off]
|
|
@@ -433,31 +441,11 @@ def sig_kernel_gram(
|
|
|
433
441
|
k_mirror = k_mirror.transpose(-2, -1)
|
|
434
442
|
res[cj[off], ci[off]] = k_mirror
|
|
435
443
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
idx_j = torch.arange(batch2, device=path2.device).repeat(batch1)
|
|
441
|
-
n_pairs = idx_i.shape[0]
|
|
442
|
-
chunk_size = max_batch * max_batch
|
|
443
|
-
|
|
444
|
-
if return_grid:
|
|
445
|
-
if isinstance(dyadic_order, tuple) and len(dyadic_order) == 2:
|
|
446
|
-
do1, do2 = dyadic_order
|
|
447
|
-
else:
|
|
448
|
-
do1 = do2 = dyadic_order
|
|
449
|
-
gl1 = ((data.length[0] - 1) << do1) + 1
|
|
450
|
-
gl2 = ((data.length[1] - 1) << do2) + 1
|
|
451
|
-
res = torch.empty(batch1, batch2, gl1, gl2, dtype=path1.dtype, device=path1.device)
|
|
452
|
-
else:
|
|
453
|
-
res = torch.empty(batch1, batch2, dtype=path1.dtype, device=path1.device)
|
|
454
|
-
|
|
455
|
-
for start in range(0, n_pairs, chunk_size):
|
|
456
|
-
end = min(start + chunk_size, n_pairs)
|
|
457
|
-
ci = idx_i[start:end]
|
|
458
|
-
cj = idx_j[start:end]
|
|
459
|
-
|
|
460
|
-
k = sig_kernel(path1[ci], path2[cj], dyadic_order, static_kernel, time_aug, lead_lag, end_time, n_jobs, return_grid)
|
|
461
|
-
res[ci, cj] = k
|
|
444
|
+
if normalize:
|
|
445
|
+
d1 = sig_kernel(path1, path1, dyadic_order, static_kernel, time_aug, lead_lag, end_time, n_jobs)
|
|
446
|
+
d2 = sig_kernel(path2, path2, dyadic_order, static_kernel, time_aug, lead_lag, end_time, n_jobs) if not symmetric else d1
|
|
447
|
+
res = res / torch.sqrt(torch.clamp(d1.unsqueeze(1) * d2.unsqueeze(0), min=1e-30))
|
|
462
448
|
|
|
449
|
+
if data.type_ == "numpy":
|
|
450
|
+
return res.numpy()
|
|
463
451
|
return res
|
|
@@ -22,7 +22,7 @@ import torch
|
|
|
22
22
|
from .transform_path import transform_path
|
|
23
23
|
from .transform_path_backprop import transform_path_backprop
|
|
24
24
|
from .sig_kernel import sig_kernel
|
|
25
|
-
from .param_checks import check_type
|
|
25
|
+
from .param_checks import check_type, parse_dyadic_order
|
|
26
26
|
from .error_codes import err_msg
|
|
27
27
|
from .dtypes import CPSIG_BATCH_SIG_KERNEL_BACKPROP, DTYPES, CUSIG_BATCH_SIG_KERNEL_BACKPROP_CUDA
|
|
28
28
|
from .data_handlers import MultiplePathInputHandler, ScalarInputHandler, GridOutputHandler, PathInputHandler
|
|
@@ -203,17 +203,7 @@ def sig_kernel_backprop(
|
|
|
203
203
|
if not (left_deriv or right_deriv):
|
|
204
204
|
return None, None
|
|
205
205
|
|
|
206
|
-
|
|
207
|
-
dyadic_order_1 = dyadic_order[0]
|
|
208
|
-
dyadic_order_2 = dyadic_order[1]
|
|
209
|
-
elif isinstance(dyadic_order, int):
|
|
210
|
-
dyadic_order_1 = dyadic_order
|
|
211
|
-
dyadic_order_2 = dyadic_order
|
|
212
|
-
else:
|
|
213
|
-
raise TypeError("dyadic_order must be an integer or a tuple of length 2")
|
|
214
|
-
|
|
215
|
-
if dyadic_order_1 < 0 or dyadic_order_2 < 0:
|
|
216
|
-
raise ValueError("dyadic_order must be a non-negative integer or tuple of non-negative integers")
|
|
206
|
+
dyadic_order_1, dyadic_order_2 = parse_dyadic_order(dyadic_order)
|
|
217
207
|
|
|
218
208
|
if time_aug or lead_lag:
|
|
219
209
|
path1 = transform_path(path1, time_aug, lead_lag, end_time, n_jobs)
|
|
@@ -444,10 +434,7 @@ def sig_kernel_gram_backprop(
|
|
|
444
434
|
chunk_size = max_batch * max_batch
|
|
445
435
|
|
|
446
436
|
# Check if k_grid can be transposed for symmetric off-diagonal pairs
|
|
447
|
-
|
|
448
|
-
do1, do2 = dyadic_order
|
|
449
|
-
else:
|
|
450
|
-
do1 = do2 = dyadic_order
|
|
437
|
+
do1, do2 = parse_dyadic_order(dyadic_order)
|
|
451
438
|
can_transpose_k = (do1 == do2)
|
|
452
439
|
|
|
453
440
|
for start in range(0, n_pairs, chunk_size):
|
|
@@ -3,8 +3,6 @@ import numpy as np
|
|
|
3
3
|
import torch
|
|
4
4
|
|
|
5
5
|
from .param_checks import check_type_multiple
|
|
6
|
-
from .data_handlers import MultiplePathInputHandler
|
|
7
|
-
|
|
8
6
|
from .static_kernels import StaticKernel
|
|
9
7
|
from .sig_kernel import sig_kernel_gram
|
|
10
8
|
|
|
@@ -112,25 +110,25 @@ def sig_score(
|
|
|
112
110
|
check_type_multiple(sample, "sample", (np.ndarray, torch.Tensor))
|
|
113
111
|
check_type_multiple(y, "y", (np.ndarray, torch.Tensor))
|
|
114
112
|
|
|
115
|
-
|
|
113
|
+
is_numpy = isinstance(sample, np.ndarray)
|
|
116
114
|
sample = torch.as_tensor(sample)
|
|
117
115
|
y = torch.as_tensor(y)
|
|
118
116
|
if len(y.shape) == 2:
|
|
119
117
|
y = y.unsqueeze(0).contiguous().clone()
|
|
120
118
|
|
|
121
|
-
data = MultiplePathInputHandler([sample, y], time_aug, lead_lag, end_time, ["sample_paths", "y"], False)
|
|
122
|
-
|
|
123
119
|
B = sample.shape[0]
|
|
120
|
+
if B < 2:
|
|
121
|
+
raise ValueError("sig_score requires at least 2 sample paths (got {}).".format(B))
|
|
124
122
|
|
|
125
123
|
xx = sig_kernel_gram(sample, sample, dyadic_order, static_kernel, time_aug, lead_lag, end_time, n_jobs, max_batch, False)
|
|
126
124
|
xy = sig_kernel_gram(sample, y, dyadic_order, static_kernel, time_aug, lead_lag, end_time, n_jobs, max_batch, False)
|
|
127
125
|
|
|
128
|
-
xx_sum = (torch.sum(xx) - torch.
|
|
129
|
-
xy_sum = torch.sum(xy, dim
|
|
126
|
+
xx_sum = (torch.sum(xx) - torch.trace(xx)) / (B * (B - 1.))
|
|
127
|
+
xy_sum = torch.sum(xy, dim=0) * (2. / B)
|
|
130
128
|
|
|
131
129
|
res = lam * xx_sum - xy_sum
|
|
132
130
|
|
|
133
|
-
if
|
|
131
|
+
if is_numpy:
|
|
134
132
|
return res.numpy()
|
|
135
133
|
return res
|
|
136
134
|
|
|
@@ -341,25 +339,27 @@ def sig_mmd(
|
|
|
341
339
|
print(mmd)
|
|
342
340
|
|
|
343
341
|
"""
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
sample1 = torch.as_tensor(data.path[0])
|
|
348
|
-
sample2 = torch.as_tensor(data.path[1])
|
|
342
|
+
is_numpy = isinstance(sample1, np.ndarray)
|
|
343
|
+
sample1 = torch.as_tensor(sample1)
|
|
344
|
+
sample2 = torch.as_tensor(sample2)
|
|
349
345
|
|
|
350
346
|
m = sample1.shape[0]
|
|
351
347
|
n = sample2.shape[0]
|
|
348
|
+
if m < 2:
|
|
349
|
+
raise ValueError("sig_mmd requires at least 2 paths in sample1 (got {}).".format(m))
|
|
350
|
+
if n < 2:
|
|
351
|
+
raise ValueError("sig_mmd requires at least 2 paths in sample2 (got {}).".format(n))
|
|
352
352
|
|
|
353
353
|
xx = sig_kernel_gram(sample1, sample1, dyadic_order, static_kernel, time_aug, lead_lag, end_time, n_jobs, max_batch, False)
|
|
354
354
|
xy = sig_kernel_gram(sample1, sample2, dyadic_order, static_kernel, time_aug, lead_lag, end_time, n_jobs, max_batch, False)
|
|
355
355
|
yy = sig_kernel_gram(sample2, sample2, dyadic_order, static_kernel, time_aug, lead_lag, end_time, n_jobs, max_batch, False)
|
|
356
356
|
|
|
357
|
-
xx_sum = (torch.sum(xx) - torch.
|
|
357
|
+
xx_sum = (torch.sum(xx) - torch.trace(xx)) / (m * (m - 1))
|
|
358
358
|
xy_sum = 2. * torch.mean(xy)
|
|
359
|
-
yy_sum = (torch.sum(yy) - torch.
|
|
359
|
+
yy_sum = (torch.sum(yy) - torch.trace(yy)) / (n * (n - 1))
|
|
360
360
|
|
|
361
361
|
res = xx_sum - xy_sum + yy_sum
|
|
362
362
|
|
|
363
|
-
if
|
|
363
|
+
if is_numpy:
|
|
364
364
|
return res.numpy()
|
|
365
365
|
return res
|