diff --git a/.basedpyright/baseline.json b/.basedpyright/baseline.json index b27e0901..03a0ca99 100644 --- a/.basedpyright/baseline.json +++ b/.basedpyright/baseline.json @@ -31435,6 +31435,2918 @@ "lineCount": 1 } } + ], + "./sumpy/recurrence.py": [ + { + "code": "reportMissingTypeStubs", + "range": { + "startColumn": 7, + "endColumn": 12, + "lineCount": 1 + } + }, + { + "code": "reportMissingTypeStubs", + "range": { + "startColumn": 5, + "endColumn": 10, + "lineCount": 1 + } + }, + { + "code": "reportDeprecated", + "range": { + "startColumn": 21, + "endColumn": 35, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 8, + "endColumn": 15, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 7, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 10, + "endColumn": 20, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 8, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 19, + "endColumn": 28, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 29, + "endColumn": 35, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 33, + "endColumn": 37, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 16, + "endColumn": 23, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 26, + "endColumn": 30, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 33, + "endColumn": 36, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 12, + "endColumn": 23, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 25, + "endColumn": 38, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 7, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 10, + "endColumn": 20, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 8, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 11, + "endColumn": 18, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 18, + "endColumn": 25, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 28, + "endColumn": 32, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 35, + "endColumn": 41, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 16, + "endColumn": 23, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 26, + "endColumn": 30, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 33, + "endColumn": 36, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 23, + "endColumn": 33, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 27, + "endColumn": 35, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 28, + "endColumn": 46, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 47, + "endColumn": 58, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 60, + "endColumn": 73, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 11, + "endColumn": 19, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 29, + "endColumn": 39, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 19, + "endColumn": 32, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 33, + "endColumn": 46, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 48, + "endColumn": 68, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 12, + "endColumn": 31, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 12, + "endColumn": 53, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 12, + "endColumn": 18, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 5, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 8, + "endColumn": 18, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 27, + "endColumn": 28, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 30, + "endColumn": 35, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 15, + "endColumn": 16, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 12, + "endColumn": 28, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 11, + "endColumn": 27, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 15, + "endColumn": 63, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 20, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 23, + "endColumn": 68, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 11, + "endColumn": 22, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 23, + "endColumn": 39, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 19, + "endColumn": 35, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 38, + "endColumn": 48, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 9, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 13, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 5, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 8, + "endColumn": 18, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 13, + "endColumn": 20, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 13, + "endColumn": 43, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 8, + "endColumn": 17, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 8, + "endColumn": 39, + "lineCount": 2 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 18, + "endColumn": 39, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 44, + "endColumn": 45, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 47, + "endColumn": 58, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 19, + "endColumn": 33, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 19, + "endColumn": 44, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 12, + "endColumn": 24, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 25, + "endColumn": 31, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 12, + "endColumn": 24, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 5, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 8, + "endColumn": 18, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 26, + "endColumn": 27, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 30, + "endColumn": 31, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 5, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 8, + "endColumn": 18, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 26, + "endColumn": 27, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 30, + "endColumn": 31, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 42, + "endColumn": 48, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 5, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 8, + "endColumn": 18, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 15, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 8, + "endColumn": 11, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 17, + "endColumn": 30, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 50, + "endColumn": 53, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 11, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 12, + "endColumn": 15, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 16, + "endColumn": 19, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 22, + "endColumn": 30, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 33, + "endColumn": 38, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 59, + "endColumn": 60, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 62, + "endColumn": 67, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 54, + "endColumn": 57, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 8, + "endColumn": 15, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 50, + "endColumn": 53, + "lineCount": 1 + } + } + ], + "./sumpy/test/test_recurrence.py": [ + { + "code": "reportMissingTypeStubs", + "range": { + "startColumn": 7, + "endColumn": 12, + "lineCount": 1 + } + }, + { + "code": "reportMissingTypeStubs", + "range": { + "startColumn": 5, + "endColumn": 10, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 32, + "endColumn": 35, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 32, + "endColumn": 35, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 37, + "endColumn": 42, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 37, + "endColumn": 42, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 44, + "endColumn": 48, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 44, + "endColumn": 48, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 50, + "endColumn": 51, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 50, + "endColumn": 51, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 53, + "endColumn": 59, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 53, + "endColumn": 59, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 59, + "endColumn": 62, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 31, + "endColumn": 35, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 33, + "endColumn": 37, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 5, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 8, + "endColumn": 18, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 19, + "endColumn": 24, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 12, + "endColumn": 19, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 20, + "endColumn": 25, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 27, + "endColumn": 33, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 23, + "endColumn": 27, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 16, + "endColumn": 22, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 23, + "endColumn": 31, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 8, + "endColumn": 26, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 35, + "endColumn": 39, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 35, + "endColumn": 59, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 40, + "endColumn": 46, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 47, + "endColumn": 53, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 30, + "endColumn": 35, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 31, + "endColumn": 32, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 32, + "endColumn": 36, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 32, + "endColumn": 59, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 47, + "endColumn": 53, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 19, + "endColumn": 24, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 15, + "endColumn": 29, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 26, + "endColumn": 56, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 64, + "endColumn": 78, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 28, + "endColumn": 31, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 36, + "endColumn": 43, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 44, + "endColumn": 67, + "lineCount": 2 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 15, + "endColumn": 22, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 23, + "endColumn": 45, + "lineCount": 2 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 15, + "endColumn": 22, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 23, + "endColumn": 66, + "lineCount": 1 + } + } + ], + "./sumpy/test/test_recurrence_qbx.py": [ + { + "code": "reportUnnecessaryTypeIgnoreComment", + "range": { + "startColumn": 75, + "endColumn": 95, + "lineCount": 1 + } + }, + { + "code": "reportMissingTypeStubs", + "range": { + "startColumn": 7, + "endColumn": 12, + "lineCount": 1 + } + }, + { + "code": "reportUnnecessaryTypeIgnoreComment", + "range": { + "startColumn": 42, + "endColumn": 62, + "lineCount": 1 + } + }, + { + "code": "reportUnnecessaryTypeIgnoreComment", + "range": { + "startColumn": 57, + "endColumn": 77, + "lineCount": 1 + } + }, + { + "code": "reportUnnecessaryTypeIgnoreComment", + "range": { + "startColumn": 86, + "endColumn": 106, + "lineCount": 1 + } + }, + { + "code": "reportUnnecessaryTypeIgnoreComment", + "range": { + "startColumn": 67, + "endColumn": 87, + "lineCount": 1 + } + }, + { + "code": "reportMissingTypeStubs", + "range": { + "startColumn": 5, + "endColumn": 10, + "lineCount": 1 + } + }, + { + "code": "reportPrivateLocalImportUsage", + "range": { + "startColumn": 4, + "endColumn": 19, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 20, + "endColumn": 23, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 20, + "endColumn": 23, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 25, + "endColumn": 32, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 25, + "endColumn": 32, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 34, + "endColumn": 41, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 34, + "endColumn": 41, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 43, + "endColumn": 50, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 43, + "endColumn": 50, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 52, + "endColumn": 58, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 52, + "endColumn": 58, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 32, + "endColumn": 41, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 32, + "endColumn": 41, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 43, + "endColumn": 48, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 43, + "endColumn": 48, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 50, + "endColumn": 51, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 24, + "endColumn": 27, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 29, + "endColumn": 34, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 38, + "endColumn": 72, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 55, + "endColumn": 68, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 55, + "endColumn": 71, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 30, + "endColumn": 37, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 30, + "endColumn": 37, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 30, + "endColumn": 37, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 4, + "endColumn": 19, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 20, + "endColumn": 23, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 20, + "endColumn": 23, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 25, + "endColumn": 32, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 37, + "endColumn": 52, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 58, + "endColumn": 59, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 34, + "endColumn": 37, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 7, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 10, + "endColumn": 20, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 12, + "endColumn": 19, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 34, + "endColumn": 37, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 51, + "endColumn": 54, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 13, + "endColumn": 24, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 25, + "endColumn": 28, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 32, + "endColumn": 38, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 27, + "endColumn": 79, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 32, + "endColumn": 54, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 56, + "endColumn": 78, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 23, + "endColumn": 73, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 24, + "endColumn": 46, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 50, + "endColumn": 72, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 11, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 39, + "endColumn": 46, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 36, + "endColumn": 47, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 48, + "endColumn": 51, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 4, + "endColumn": 18, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 19, + "endColumn": 36, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 19, + "endColumn": 36, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 38, + "endColumn": 48, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 38, + "endColumn": 48, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 58, + "endColumn": 75, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 18, + "endColumn": 37, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 23, + "endColumn": 52, + "lineCount": 2 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 24, + "endColumn": 47, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 53, + "endColumn": 76, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 24, + "endColumn": 47, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 19, + "endColumn": 40, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 15, + "endColumn": 22, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 23, + "endColumn": 45, + "lineCount": 2 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 35, + "endColumn": 61, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 43, + "endColumn": 60, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 15, + "endColumn": 22, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 23, + "endColumn": 45, + "lineCount": 2 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 59, + "endColumn": 85, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 67, + "endColumn": 84, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 36, + "endColumn": 43, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 44, + "endColumn": 64, + "lineCount": 2 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 26, + "endColumn": 33, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 45, + "endColumn": 52, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 54, + "endColumn": 61, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 36, + "endColumn": 45, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 47, + "endColumn": 53, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 60, + "endColumn": 67, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 42, + "endColumn": 48, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 50, + "endColumn": 59, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 8, + "endColumn": 18, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 19, + "endColumn": 76, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 18, + "endColumn": 21, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 15, + "endColumn": 22, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 23, + "endColumn": 66, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 26, + "endColumn": 33, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 45, + "endColumn": 52, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 54, + "endColumn": 61, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 63, + "endColumn": 72, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 8, + "endColumn": 14, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 34, + "endColumn": 41, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 43, + "endColumn": 49, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 51, + "endColumn": 60, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 8, + "endColumn": 18, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 19, + "endColumn": 52, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 18, + "endColumn": 21, + "lineCount": 1 + } + } + ], + "./sumpy/recurrence_qbx.py": [ + { + "code": "reportMissingTypeStubs", + "range": { + "startColumn": 7, + "endColumn": 12, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 8, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 10, + "endColumn": 18, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 22, + "endColumn": 26, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 27, + "endColumn": 31, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 33, + "endColumn": 41, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 8, + "endColumn": 9, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 23, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 11, + "endColumn": 30, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 22, + "endColumn": 29, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 22, + "endColumn": 29, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 31, + "endColumn": 38, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 31, + "endColumn": 38, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 40, + "endColumn": 47, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 40, + "endColumn": 47, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 49, + "endColumn": 58, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 49, + "endColumn": 58, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 60, + "endColumn": 66, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 60, + "endColumn": 66, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 68, + "endColumn": 71, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 68, + "endColumn": 71, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 73, + "endColumn": 78, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 73, + "endColumn": 78, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 22, + "endColumn": 26, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 22, + "endColumn": 26, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 28, + "endColumn": 29, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 28, + "endColumn": 29, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 31, + "endColumn": 45, + "lineCount": 1 + } + }, + { + "code": "reportUnusedParameter", + "range": { + "startColumn": 31, + "endColumn": 45, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 51, + "endColumn": 58, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 60, + "endColumn": 67, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 69, + "endColumn": 76, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 31, + "endColumn": 35, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 33, + "endColumn": 37, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 59, + "endColumn": 62, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 10, + "endColumn": 23, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 24, + "endColumn": 34, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 5, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 8, + "endColumn": 18, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 8, + "endColumn": 26, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 27, + "endColumn": 28, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 27, + "endColumn": 28, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 30, + "endColumn": 39, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 30, + "endColumn": 39, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 12, + "endColumn": 27, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 30, + "endColumn": 33, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 23, + "endColumn": 27, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 12, + "endColumn": 27, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 28, + "endColumn": 34, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 35, + "endColumn": 42, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 43, + "endColumn": 48, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 50, + "endColumn": 56, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 58, + "endColumn": 59, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 27, + "endColumn": 31, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 39, + "endColumn": 64, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 65, + "endColumn": 73, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 45, + "endColumn": 46, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 48, + "endColumn": 49, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 15, + "endColumn": 26, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 39, + "endColumn": 43, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 24, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 19, + "endColumn": 22, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 23, + "endColumn": 28, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 66, + "endColumn": 69, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 51, + "endColumn": 54, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 31, + "endColumn": 41, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 8, + "endColumn": 29, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 30, + "endColumn": 31, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 30, + "endColumn": 31, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 33, + "endColumn": 44, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 33, + "endColumn": 44, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 12, + "endColumn": 27, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 30, + "endColumn": 33, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 23, + "endColumn": 27, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 12, + "endColumn": 27, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 28, + "endColumn": 34, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 35, + "endColumn": 42, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 43, + "endColumn": 48, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 50, + "endColumn": 56, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 58, + "endColumn": 59, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 27, + "endColumn": 31, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 39, + "endColumn": 64, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 65, + "endColumn": 73, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 29, + "endColumn": 54, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 55, + "endColumn": 61, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 42, + "endColumn": 43, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 45, + "endColumn": 46, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 15, + "endColumn": 26, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 8, + "endColumn": 27, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 28, + "endColumn": 29, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 28, + "endColumn": 29, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 31, + "endColumn": 42, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 31, + "endColumn": 42, + "lineCount": 1 + } + }, + { + "code": "reportUnknownParameterType", + "range": { + "startColumn": 44, + "endColumn": 55, + "lineCount": 1 + } + }, + { + "code": "reportMissingParameterType", + "range": { + "startColumn": 44, + "endColumn": 55, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 23, + "endColumn": 34, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 12, + "endColumn": 27, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 30, + "endColumn": 33, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 23, + "endColumn": 27, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 12, + "endColumn": 27, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 28, + "endColumn": 34, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 35, + "endColumn": 42, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 43, + "endColumn": 48, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 50, + "endColumn": 56, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 58, + "endColumn": 59, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 27, + "endColumn": 31, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 39, + "endColumn": 64, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 65, + "endColumn": 73, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 40, + "endColumn": 41, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 43, + "endColumn": 44, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 15, + "endColumn": 26, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 19, + "endColumn": 22, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 30, + "endColumn": 75, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 64, + "endColumn": 74, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 16, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 28, + "endColumn": 36, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 48, + "endColumn": 56, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 17, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 29, + "endColumn": 37, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 48, + "endColumn": 56, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 34, + "endColumn": 48, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 34, + "endColumn": 48, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 11, + "endColumn": 56, + "lineCount": 1 + } + } ] } -} \ No newline at end of file +} diff --git a/.gitignore b/.gitignore index ab75b2ee..6f1946c6 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,9 @@ sumpy/_git_rev.py .asv *.vts +*.ipynb +*.pgf +output.png +qbxrecurrence.svg +test/hirish_plotting +.vscode/settings.json diff --git a/doc/expansion.rst b/doc/expansion.rst index 5d72d735..d4352c61 100644 --- a/doc/expansion.rst +++ b/doc/expansion.rst @@ -27,3 +27,13 @@ Estimating Expansion Orders --------------------------- .. automodule:: sumpy.expansion.level_to_order + +Recurrences +----------- + +.. automodule:: sumpy.recurrence + +Recurrence QBX +-------------- + +.. automodule:: sumpy.recurrence_qbx diff --git a/sumpy/recurrence.py b/sumpy/recurrence.py new file mode 100644 index 00000000..0b7a1172 --- /dev/null +++ b/sumpy/recurrence.py @@ -0,0 +1,698 @@ +r""" +With the functionality in this module, we aim to compute a recurrence for +one-dimensional derivatives of functions :math:`f:\mathbb R^n \to \mathbb R` +for functions satisfying two assumptions: + +- :math:`f` satisfies a PDE that is linear and has coefficients polynomial + in the coordinates. +- :math:`f` only depends on the radius :math:`r`, + i.e. :math:`f(\boldsymbol x)=f(|\boldsymbol x|_2)`. + +This process proceeds in multiple steps: + +- Convert from the PDE to an ODE in :math:`r`, using :func:`pde_to_ode_in_r`. +- Convert from an ODE in :math:`r` to one in :math:`x`, + using :func:`ode_in_r_to_x`. +- Sort general-form ODE in :math:`x` into a coefficient array, using + :func:`ode_in_x_to_coeff_array`. +- Finally, get an expression for the recurrence, using + :func:`recurrence_from_coeff_array`. + +The whole process can be automated using :func:`recurrence_from_pde`. + +Once the recurrence is obtained, it is reindexed via +:func:`reindex_recurrence_relation`, so that :math:`s(n)` is expressed in +terms of :math:`s(n-1), s(n-2), \dots` + +Computing derivatives +^^^^^^^^^^^^^^^^^^^^^ + +Given a PDE and its Green's function +:math:`G(\boldsymbol x, \boldsymbol t)` where +:math:`\boldsymbol x = (x_1, x_2, \dots)`, we want to compute the +:math:`n`-th derivative :math:`\partial^n G / \partial x_1^n` with +respect to the first coordinate :math:`x_1` of :math:`\boldsymbol x` +(called ``x0`` in the 0-indexed code variables), with +:math:`\boldsymbol t` fixed. + +There are two regimes, selected based on the relative magnitude of +:math:`|x_1|`: + +- **Large-** :math:`|x_1|` **regime** (:math:`|x_1|/\bar x > 1`): + Use :func:`get_large_x1_recurrence` directly. The recurrence involves + all coordinates :math:`(x_1, x_2, \dots)`. + +- **Small-** :math:`|x_1|` **regime** (:math:`|x_1|/\bar x \le 1`): + Use :func:`get_small_x1_expansion`, which returns a Taylor expansion + in :math:`x_1` whose coefficients are computed via + :func:`get_small_x1_recurrence` (a recurrence evaluated at + :math:`x_1 = 0`). The truncation order of the Taylor expansion is + user-selectable. + +:: + + Want: d^n G / d x_1^n at point (x_1, x_2, ...) + + |x_1| / x_bar > 1? + / \ + Yes No + / \ + +---------------------+ +-------------------------+ + | get_large_x1_ | | get_small_x1_recurrence | + | recurrence | | (recurrence at x_1 = 0 | + | | | for Taylor coefficients)| + | s(n) depends on | +-------------------------+ + | s(n-1), ... and | | + | x_1, x_2, ... | | coefficients + +---------------------+ v + | +-------------------------+ + | | get_small_x1_expansion | + | | (Taylor expansion in | + | | x_1 with user-chosen | + | | truncation order) | + | +-------------------------+ + | | + v v + +------------------------------------------+ + | d^n G / d x_1^n | + +------------------------------------------+ + +Example: large-:math:`|x_1|` recurrence +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We compute + +.. math:: + + s(n) = \frac{\partial^n}{\partial x_1^n} + G(\boldsymbol x, \boldsymbol t)\Big|_{\boldsymbol t=0}, + \qquad n = 0, 1, \dots, p + +for the 2D Laplace Green's function + +.. math:: + + G(\boldsymbol x, \boldsymbol t) + = -\frac{1}{2\pi}\log|\boldsymbol x - \boldsymbol t| + +evaluated at the point :math:`\boldsymbol x = (0.5, 0.3)` with +the target fixed at the origin :math:`\boldsymbol t = 0`. + +.. code-block:: python + + import numpy as np + import sympy as sp + from sumpy.expansion.diff_op import laplacian, make_identity_diff_op + from sumpy.recurrence import get_large_x1_recurrence, _make_sympy_vec + + # 1. Define PDE (2D Laplace) + w = make_identity_diff_op(2) + pde = laplacian(w) + + # 2. Get recurrence + n_initial, order, recurrence = get_large_x1_recurrence(pde) + + # 3. Define G(x, t) and the evaluation point + var = _make_sympy_vec("x", 2) # source coordinates + var_t = _make_sympy_vec("t", 2) # target coordinates + g = (-1/(2*np.pi)) * sp.log(sp.sqrt((var[0]-var_t[0])**2 + + (var[1]-var_t[1])**2)) + + n = sp.symbols("n") + s = sp.Function("s") + # Evaluate at source x = (0.5, 0.3), target t = 0 + x_vals = [(var[0], sp.Rational(1, 2)), (var[1], sp.Rational(3, 10))] + + # 4. Seed initial derivatives by direct differentiation of G(x, 0) + derivs = {} + for i in range(-order, 0): + derivs[i] = 0j + for i in range(n_initial): + d = sp.diff(g, var[0], i) + for j in range(2): + d = d.subs(var_t[j], 0) # fix target at origin + derivs[i] = complex(d.subs(x_vals)) + + # 5. Apply recurrence to get derivatives up to order p + p = 8 + for i in range(n_initial, p + 1): + expr = recurrence.subs(n, i) + for j in range(order, 0, -1): + expr = expr.subs(s(i - j), derivs[i - j]) + derivs[i] = complex(expr.subs(x_vals)) + +Example: small-:math:`|x_1|` expansion +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Continuing from the same setup above, we now compute the same derivatives +via the small-:math:`|x_1|` path. The small-:math:`|x_1|` recurrence +first computes the Taylor coefficients + +.. math:: + + c(n) = \frac{\partial^n}{\partial x_1^n} + G(\boldsymbol x, \boldsymbol t)\Big|_{\boldsymbol t=0,\; x_1=0} + +i.e. the derivatives evaluated at the point :math:`(0, 0.3)`. +The expansion then recovers the derivative at the full point +:math:`\boldsymbol x = (0.5, 0.3)` via a truncated Taylor series +in :math:`x_1`: + +.. math:: + + s(n) = \frac{\partial^n}{\partial x_1^n} + G(\boldsymbol x, \boldsymbol t)\Big|_{\boldsymbol t=0} + \approx \sum_{k=0}^{K} \frac{c(n+k)}{k!}\, x_1^k + +where :math:`K` is the user-chosen truncation order. + +.. code-block:: python + + from sumpy.recurrence import get_small_x1_recurrence, get_small_x1_expansion + + # 1. Get the small-|x_1| recurrence (for Taylor coefficients at x_1=0) + start_order, recur_order, recur = get_small_x1_recurrence(pde) + + # 2. Get the Taylor expansion with chosen truncation order + taylor_order = 8 + expansion, n_coeffs, start_order = get_small_x1_expansion( + pde, taylor_order) + + # 3. Compute Taylor coefficients: derivatives of G(x, 0) at x_1=0 + coeffs = {} + for i in range(-recur_order, 0): + coeffs[i] = 0j + for i in range(start_order): + d = sp.diff(g, var[0], i) + for j in range(2): + d = d.subs(var_t[j], 0) # fix target at origin + coeffs[i] = complex(d.subs(var[0], 0).subs(x_vals)) # then set x_1=0 + for i in range(start_order, p + 1): + expr = recur.subs(n, i) + for j in range(recur_order, 0, -1): + expr = expr.subs(s(i - j), coeffs[i - j]) + coeffs[i] = complex(expr.subs(x_vals)) + + # 4. Evaluate the expansion at x = (0.5, 0.3) + for i in range(start_order, p + 1): + expr = expansion.subs(n, i) + for j in range(n_coeffs, -1, -1): + expr = expr.subs(s(i - j), coeffs[i - j]) + deriv_i = complex(expr.subs(x_vals)) + +.. autofunction:: pde_to_ode_in_r +.. autofunction:: ode_in_r_to_x +.. autofunction:: ode_in_x_to_coeff_array +.. autofunction:: recurrence_from_coeff_array +.. autofunction:: recurrence_from_pde +.. autofunction:: reindex_recurrence_relation +.. autofunction:: get_large_x1_recurrence +.. autofunction:: get_small_x1_recurrence +.. autofunction:: get_small_x1_expansion +""" + +from __future__ import annotations + + +__copyright__ = """ +Copyright (C) 2024 Hirish Chandrasekaran +Copyright (C) 2024 Andreas Kloeckner +""" + +__license__ = """ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" +import math +from typing import TYPE_CHECKING, Any, TypeVar + +import numpy as np +import sympy as sp +from sympy import Expr, Symbol + +from pytools import obj_array + + +if TYPE_CHECKING: + from sumpy.expansion.diff_op import ( + DerivativeIdentifier, + LinearPDESystemOperator, + ) + + +# similar to make_sym_vector in sumpy.symbolic, but returns an object array +# instead of a sympy.Matrix. +def _make_sympy_vec(name: str, n: int) -> np.ndarray: + return obj_array.make_obj_array( # pyright: ignore[reportReturnType] + [sp.Symbol(f"{name}{i}") for i in range(n)]) + + +def pde_to_ode_in_r(pde: LinearPDESystemOperator) -> tuple[ + Expr, np.ndarray, int +]: + r""" + Returns an ODE satisfied by the radial derivatives of a function + :math:`f:\mathbb R^n \to \mathbb R` satisfying + :math:`f(\boldsymbol x)=f(|\boldsymbol x|_2)` and *pde*. + + :arg pde: must satisfy ``pde.eqs == 1`` and have polynomial coefficients. + + :returns: a tuple ``(ode_in_r, var, ode_order)``, where + + - *ode_in_r* with derivatives given as ``sympy.Derivative`` + - *var* is an object array of ``sympy.Symbol``, with successive + variables representing the Cartesian coordinate directions. + - *ode_order* the order of ODE that is returned + """ + if len(pde.eqs) != 1: + raise ValueError("PDE must be scalar") + + dim = pde.dim + ode_order = pde.order + pde_eqn, = pde.eqs + + var = _make_sympy_vec("x", dim) + r = sp.sqrt(sum(var**2)) + eps = sp.symbols("epsilon") + rval = r + eps + f = sp.Function("f") + + def apply_deriv_id(expr: Expr, + deriv_id: DerivativeIdentifier) -> Expr: + for i, nderivs in enumerate(deriv_id.mi): + expr = expr.diff(var[i], nderivs) + return expr + + ode_in_r: Expr = sum( # pyright: ignore[reportAssignmentType] + # pylint: disable-next=not-callable + coeff * apply_deriv_id(f(rval), deriv_id) + for deriv_id, coeff in pde_eqn.items() + ) + + f_r_derivs = _make_sympy_vec("f_r", ode_order+1) + # pylint: disable-next=not-callable + f_derivs = [sp.diff(f(rval), eps, i) for i in range(ode_order+1)] + + # PDE ORDER = ODE ORDER + for i in range(ode_order+1): + ode_in_r = ode_in_r.subs( # pyright: ignore[reportAssignmentType] + f_derivs[i], f_r_derivs[i]) + + return ode_in_r, var, ode_order + + +def _generate_nd_derivative_relations( + var: np.ndarray, ode_order: int +) -> dict[Symbol, Any]: + r""" + Using the chain rule outputs a vector that gives in each component + respectively + :math:`[f(r), f'(r), \dots, f^{(ode_order)}(r)]` as a linear combination of + :math:`[f(x), f'(x), \dots, f^{(ode_order)}(x)]` + + :arg var: array of sympy variables math:`[x_0, x_1, \dots]` + :arg ode_order: the order of the ODE that we will be translating + """ + f_r_derivs = _make_sympy_vec("f_r", ode_order+1) + f_x_derivs = _make_sympy_vec("f_x", ode_order+1) + f = sp.Function("f") + eps = sp.symbols("epsilon") + rval = sp.sqrt(sum(var**2)) + eps + # pylint: disable=not-callable + f_derivs_x = [sp.diff(f(rval), var[0], i) for i in range(ode_order+1)] + f_derivs = [sp.diff(f(rval), eps, i) for i in range(ode_order+1)] + # pylint: disable=not-callable + for i in range(len(f_derivs_x)): + for j in range(len(f_derivs)): + f_derivs_x[i] = f_derivs_x[i].subs(f_derivs[j], f_r_derivs[j]) + system = [f_x_derivs[i] - f_derivs_x[i] for i in range(ode_order+1)] + return sp.solve(system, *f_r_derivs, dict=True)[0] + + +def ode_in_r_to_x(ode_in_r: Expr, var: np.ndarray, + ode_order: int) -> Expr: + r""" + Translates an ode in the variable r into an ode in the variable x + by replacing the terms :math:`f, f_r, f_{rr}, \dots` as a linear + combinations of + :math:`f, f_x, f_{xx}, \dots` using the chain rule. + + :arg ode_in_r: a linear combination of :math:`f, f_r, f_{rr}, \dots` + represented by the sympy variables :math:`f_{r0}, f_{r1}, f_{r2}, \dots` + :arg var: array of sympy variables :math:`[x_0, x_1, \dots]` + :arg ode_order: the order of the input ODE + + :returns: *ode_in_x* a linear combination of :math:`f, f_x, f_{xx}, \dots` + represented by the sympy variables :math:`f_{x0}, f_{x1}, f_{x2}, + \dots` with coefficients as rational functions in + :math:`x_0, x_1, \dots` + """ + subme = _generate_nd_derivative_relations(var, ode_order+1) + ode_in_x = ode_in_r + f_r_derivs = _make_sympy_vec("f_r", ode_order+1) + for i in range(ode_order+1): + ode_in_x = ode_in_x.subs(f_r_derivs[i], subme[f_r_derivs[i]]) + return ode_in_x + + +ODECoefficients = list[list[Expr]] + + +def ode_in_x_to_coeff_array(poly: sp.Poly, ode_order: int, var: + np.ndarray) -> ODECoefficients: + r""" + Organizes the coefficients of an ODE in the :math:`x_0` variable into a + 2D array. + + :arg poly: a sympy polynomial in + :math:`\partial_{x_0}^0 f, \partial_{x_0}^1 f,\cdots` of the form + :math:`(b_{00} x_0^0 + b_{01} x_0^1 + \cdots) \partial_{x_0}^0 f + + (b_{10} x_0^0 + b_{11} x_0^1 +\cdots) \partial_x^1 f` + + :arg var: array of sympy variables :math:`[x_0, x_1, \dots]` + :arg ode_order: the order of the input ODE we return a sequence + + :returns: *coeffs* a sequence of of sequences, with the outer sequence + iterating over derivative orders, and each inner sequence iterating + over powers of :math:`x_0`, so that, in terms of the above form, + coeffs is :math:`[[b_{00}, b_{01}, ...], [b_{10}, b_{11}, ...], ...]` + """ + return [ + # recast ODE coefficient obtained below as polynomial in x0 + sp.Poly( + # get coefficient of deriv_ind'th derivative + poly.coeff_monomial(poly.gens[deriv_ind]), + + var[0]) + # get poly coefficients in /ascending/ order + .all_coeffs()[::-1] + for deriv_ind in range(ode_order+1)] + + +NumberT = TypeVar("NumberT", int, float, complex) + + +def _falling_factorial(arg: NumberT, num_terms: int) -> NumberT: + result = 1 + for i in range(num_terms): + result = result * (arg - i) + return result + + +def _auto_product_rule_single_term(p: int, m: int, var: np.ndarray) -> Expr: + r""" + We assume that we are given the expression :math:`x_0^p f^(m)(x_0)`. We + then output the nth order derivative of the expression where :math:`n` is + a symbolic variable. + We let :math:`s(i)` represent the ith order derivative of f when + we output the final result. + :arg var: array of sympy variables :math:`[x_0, x_1, \dots]` + """ + n = sp.symbols("n") + s = sp.Function("s") + + return sum( # pyright: ignore[reportReturnType] + # pylint: disable=not-callable + _falling_factorial(n, i) + * math.comb(p, i) * s(n-i+m) * var[0]**(p-i) + for i in range(p+1) + ) + + +def recurrence_from_coeff_array( + coeffs: list[list[Any]], var: np.ndarray +) -> Expr: + r""" + A function that takes in as input an organized 2D coefficient array (see + above) and outputs a recurrence relation. + + :arg coeffs: a sequence of of sequences, described in + :func:`ode_in_x_to_coeff_array` + :arg var: array of sympy variables :math:`[x_0, x_1, \dots]` + """ + final_recurrence: Any = 0 + # Outer loop is derivative direction + # Inner is polynomial order of x_0 + for m, _ in enumerate(coeffs): + for p, _ in enumerate(coeffs[m]): + final_recurrence += coeffs[m][p] * _auto_product_rule_single_term( + p, m, var) + return final_recurrence + + +def recurrence_from_pde(pde: LinearPDESystemOperator) -> Expr: + r""" + Takes a PDE and outputs a recurrence relation by composing + :func:`pde_to_ode_in_r`, :func:`ode_in_r_to_x`, + :func:`ode_in_x_to_coeff_array`, and :func:`recurrence_from_coeff_array`. + + :arg pde: a :class:`sumpy.expansion.diff_op.LinearPDESystemOperator` + that must satisfy ``pde.eqs == 1`` and have polynomial coefficients + in the coordinates. + + :returns: a recurrence relation as a sympy expression involving + :math:`s(n), s(n-1), \dots` that evaluates to zero. + """ + ode_in_r, var, ode_order = pde_to_ode_in_r(pde) + ode_in_x = ode_in_r_to_x(ode_in_r, var, ode_order).simplify() + ode_in_x_cleared = (ode_in_x * var[0]**(pde.order*2-1)).simplify() + # ode_in_x_cleared shouldn't have rational function coefficients + assert sp.together(ode_in_x_cleared) == ode_in_x_cleared + f_x_derivs = _make_sympy_vec("f_x", ode_order+1) + poly = sp.Poly(ode_in_x_cleared, *f_x_derivs) + coeffs = ode_in_x_to_coeff_array(poly, ode_order, var) + return recurrence_from_coeff_array(coeffs, var) + + +def reindex_recurrence_relation(r: sp.Basic) -> tuple[int, Expr]: + r""" + Reindexes a recurrence relation so that :math:`s(n)` is expressed in terms + of :math:`s(n-1), s(n-2), \dots`. The input recurrence is an expression + that evaluates to zero, while the output gives :math:`s(n)` directly in + terms of prior values. + + :arg r: a recurrence relation expression in :math:`s(n)` that evaluates + to zero. + + :returns: a tuple ``(order, reindexed_recurrence)``, where + + - *order* is the order of the recurrence (the difference between the + highest and lowest indexed terms). + - *reindexed_recurrence* is a sympy expression giving :math:`s(n)` in + terms of :math:`s(n-1), s(n-2), \dots` + """ + idx_l, terms = _extract_idx_terms_from_recurrence(r) + # Order is the max difference between highest/lowest in idx_l + order = max(idx_l) - min(idx_l) + + # How much do we need to shift the recurrence relation + shift_idx = max(idx_l) + + # Get the respective coefficients in the recurrence relation from r + n = sp.symbols("n") + coeffs = sp.poly(r, list(terms)).coeffs() + + # Re-arrange the recurrence relation so we get s(n) = ____ + # in terms of s(n-1), ... + true_recurrence: Expr = sum( # pyright: ignore[reportAssignmentType] + sp.cancel(-coeffs[i]/coeffs[-1]) * terms[i] + for i in range(0, len(terms)-1)) + true_recurrence1 = true_recurrence.subs(n, n-shift_idx) + + return order, true_recurrence1 + + +def _extract_idx_terms_from_recurrence(r: sp.Basic) -> tuple[np.ndarray, + np.ndarray]: + r""" + Given a recurrence extracts the variables in the recurrence + as well as the indexes, both in sorted order. + + :arg r: recurrence to extract terms from + """ + # We're assuming here that s(...) are the only function calls. + terms = list(r.atoms(sp.Function)) + terms = np.array(terms) + + idx_l = [] + for i in range(len(terms)): + tms = list(terms[i].atoms(sp.Number)) + if len(tms) == 1: + idx_l.append(tms[0]) + else: + idx_l.append(0) + idx_l = np.array(idx_l, dtype="int") + idx_sort = idx_l.argsort() + idx_l = idx_l[idx_sort] + terms = terms[idx_sort] + + return idx_l, terms + + +def _check_neg_ind(r_n: sp.Basic) -> bool: + r""" + Simply checks if a negative index exists in a recurrence relation. + """ + + idx_l, _ = _extract_idx_terms_from_recurrence(r_n) + + return bool(np.any(idx_l < 0)) + + +def _get_initial_order_large_x1(recurrence: Expr) -> int: + r""" + For the large-:math:`|x_1|` recurrence, checks how many initial + conditions are needed by checking for non-negative indexed terms. + """ + n = sp.symbols("n") + + i = 0 + r_c = recurrence.subs(n, i) + while _check_neg_ind(r_c): + i += 1 + r_c = recurrence.subs(n, i) + return i + + +def _get_initial_order_small_x1(recurrence: Expr) -> int: + r""" + For the small-:math:`|x_1|` recurrence, checks how many initial + conditions are needed by checking for non-negative indexed terms. + """ + n = sp.symbols("n") + + i = 0 + r_c = recurrence.subs(n, i) + while (_check_neg_ind(r_c) or r_c == 0) or i % 2 != 0: + i += 1 + r_c = recurrence.subs(n, i) + return i + + +def get_large_x1_recurrence( + pde: LinearPDESystemOperator +) -> tuple[int, int, Expr]: + r""" + Computes the large-:math:`|x_1|` recurrence for evaluating + one-dimensional derivatives of a radially symmetric Green's function + satisfying *pde*. This recurrence is used when :math:`|x_1|` (the + on-axis coordinate) is large relative to the off-axis coordinates. + The recurrence is reindexed so that :math:`s(n)` is given in terms of + :math:`s(n-1), \dots` + + :arg pde: a :class:`sumpy.expansion.diff_op.LinearPDESystemOperator` + that must satisfy ``pde.eqs == 1`` and have polynomial coefficients + in the coordinates. + + :returns: a tuple ``(n_initial, order, recurrence)``, where + + - *n_initial* is the number of initial derivatives that must be + computed directly (i.e. not via the recurrence). + - *order* is the order of the recurrence. + - *recurrence* is the reindexed recurrence giving :math:`s(n)` in + terms of :math:`s(n-1), \dots` + """ + r = recurrence_from_pde(pde) + order, r_p = reindex_recurrence_relation(r) + n_initial = _get_initial_order_large_x1(r_p) + return n_initial, order, r_p + + +# ================ SMALL-|x_1| RECURRENCE AND EXPANSION ================= +def get_small_x1_recurrence( + pde: LinearPDESystemOperator +) -> tuple[int, int, Expr]: + r""" + Computes the small-:math:`|x_1|` recurrence for evaluating + one-dimensional derivatives of a radially symmetric Green's function + satisfying *pde*, evaluated at :math:`x_1 = 0`. This recurrence produces + the Taylor coefficients used by :func:`get_small_x1_expansion`. + The recurrence is reindexed so that :math:`s(n)` is given in terms of + :math:`s(n-1), \dots` + + :arg pde: a :class:`sumpy.expansion.diff_op.LinearPDESystemOperator` + that must satisfy ``pde.eqs == 1`` and have polynomial coefficients + in the coordinates. + + :returns: a tuple ``(start_order, recur_order, recur)``, where + + - *start_order* is the derivative order at which the recurrence + first becomes valid (lower orders must be computed directly). + - *recur_order* is the order of the recurrence *recur*. + - *recur* is the reindexed small-:math:`|x_1|` recurrence giving + :math:`s(n)` in terms of :math:`s(n-1), \dots` + """ + var = _make_sympy_vec("x", 1) + r_exp = recurrence_from_pde(pde).subs(var[0], 0) + recur_order, recur = reindex_recurrence_relation(r_exp) + start_order = _get_initial_order_small_x1(recur) + return start_order, recur_order, recur + + +def get_small_x1_expansion( + pde: LinearPDESystemOperator, taylor_order: int = 4 +) -> tuple[Expr, int, int]: + r""" + Computes the small-:math:`|x_1|` expansion: a truncated Taylor expansion + in :math:`x_1` that expresses the :math:`n`-th derivative in terms of + small-:math:`|x_1|` recurrence values :math:`s(n), s(n-1), \dots` + See :func:`get_small_x1_recurrence`. + + :arg pde: a :class:`sumpy.expansion.diff_op.LinearPDESystemOperator` + that must satisfy ``pde.eqs == 1`` and have polynomial coefficients + in the coordinates. + :arg taylor_order: order of the Taylor expansion in :math:`x_1`. + + :returns: a tuple ``(exp, n_coeffs, start_order)``, where + + - *exp* is the Taylor expansion expression in terms of :math:`s(n), + s(n-1), \dots` and :math:`x_1`. Must not be evaluated for + :math:`n` below *start_order*. + - *n_coeffs* is the number of prior small-:math:`|x_1|` recurrence + values needed. For example, if *n_coeffs* is 3, then + :math:`s(n), s(n-1), s(n-2), s(n-3)` are required. + - *start_order* is the minimum derivative order at which the + expression is valid. + """ + s = sp.Function("s") + n = sp.symbols("n") + deriv_order = n + + start_order, _, t_recurrence = get_small_x1_recurrence(pde) + var = _make_sympy_vec("x", 2) + exp: Any = 0 + for i in range(taylor_order+1): + exp += s(deriv_order+i)/math.factorial(i) * var[0]**i + + # While derivatives w/order larger than the deriv_order exist in our + # taylor expression replace them with smaller order derivatives + + idx_l, _ = _extract_idx_terms_from_recurrence(exp) + max_idx = max(idx_l) + + while max_idx > 0: + for ind in idx_l: + if ind > 0: + exp = exp.subs(s(n+ind), t_recurrence.subs(n, n+ind)) + + idx_l, _ = _extract_idx_terms_from_recurrence(exp) + max_idx = max(idx_l) + + idx_l, _ = _extract_idx_terms_from_recurrence(exp) + + return exp, -min(idx_l), start_order diff --git a/sumpy/recurrence_qbx.py b/sumpy/recurrence_qbx.py new file mode 100644 index 00000000..996b68be --- /dev/null +++ b/sumpy/recurrence_qbx.py @@ -0,0 +1,247 @@ +r""" +Evaluates QBX layer potentials using recurrence-based computation of +Green's function derivatives. Sources are first rotated into a coordinate +system aligned with the expansion center normal, and then the +large-:math:`|x_1|` and small-:math:`|x_1|` recurrences from +:mod:`sumpy.recurrence` are used to build the local expansion. + +.. autofunction:: recurrence_qbx_lp +""" +from __future__ import annotations + + +__copyright__ = """ +Copyright (C) 2024 Hirish Chandrasekaran +Copyright (C) 2024 Andreas Kloeckner +""" + +__license__ = """ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +import math +from typing import TYPE_CHECKING + +import numpy as np +import sympy as sp + +from sumpy.recurrence import ( + _make_sympy_vec, + get_large_x1_recurrence, + get_small_x1_expansion, + get_small_x1_recurrence, +) + + +if TYPE_CHECKING: + from collections.abc import Sequence + + +# ================ Transform/Rotate ================= +def _produce_orthogonal_basis(normals: np.ndarray) -> Sequence[np.ndarray]: + r""" + Produces an orthonormal basis for each center, with the first basis + vector equal to the given normal. The remaining basis vectors are + generated via Gram-Schmidt orthogonalization of random vectors. + + :arg normals: a ``(ndim, ncenters)`` array of unit normal vectors. + + :returns: a list of *ndim* arrays, each of shape ``(ndim, ncenters)``, + forming a per-center orthonormal basis. + """ + ndim, ncenters = normals.shape + orth_coordsys = [normals] + for i in range(1, ndim): + v = np.random.rand(ndim, ncenters) # noqa: NPY002 + v = v/np.linalg.norm(v, 2, axis=0) + for j in range(i): + v = v - np.einsum("dc,dc->c", v, + orth_coordsys[j]) * orth_coordsys[j] + v = v/np.linalg.norm(v, 2, axis=0) + orth_coordsys.append(v) + + return orth_coordsys + + +def _compute_rotated_shifted_coordinates( + sources: np.ndarray, + centers: np.ndarray, + normals: np.ndarray + ) -> np.ndarray: + r""" + Computes source coordinates shifted by the center locations and rotated + into a coordinate system where the first axis is aligned with the + center normal. + + :arg sources: a ``(ndim, nsources)`` array of source locations. + :arg centers: a ``(ndim, ncenters)`` array of expansion center locations. + :arg normals: a ``(ndim, ncenters)`` array of unit normals at each center. + + :returns: a ``(ndim, ncenters, nsources)`` array of rotated, shifted + coordinates. + """ + cts = centers[:, :, None] - sources[:, None] + orth_coordsys = _produce_orthogonal_basis(normals) + cts_rotated_shifted = np.einsum("idc,dcs->ics", orth_coordsys, cts) + + return cts_rotated_shifted + + +# ================ Recurrence LP Eval ================= +def recurrence_qbx_lp(sources, centers, normals, strengths, radius, pde, g_x_y, + ndim, p, off_axis_start=0) -> np.ndarray: + r""" + Computes a single-layer potential using recurrence-based QBX. Sources + are rotated into a per-center coordinate system aligned with the normal, + and derivatives of the Green's function are computed via the + large-:math:`|x_1|` and small-:math:`|x_1|` recurrences. The two + regimes are blended based on the relative magnitude of the coordinates. + + :arg sources: a ``(ndim, nsources)`` array of source locations. + :arg centers: a ``(ndim, ncenters)`` array of expansion center locations. + :arg normals: a ``(ndim, ncenters)`` array of unit normals at each center. + :arg strengths: a ``(nsources,)`` array of quadrature weights multiplied + by the density. + :arg radius: the QBX expansion radius. + :arg pde: a :class:`sumpy.expansion.diff_op.LinearPDESystemOperator` + describing the PDE whose Green's function is used. + :arg g_x_y: a sympy expression for the Green's function in source + variables :math:`(x_0, x_1, \dots)` and target variables + :math:`(t_0, t_1, \dots)`. + :arg ndim: the number of spatial dimensions. + :arg p: the order of the QBX expansion. + + :returns: a ``(ncenters,)`` array of layer potential values at the + target points (located at distance *radius* from each center + along the normal). + """ + + # ------------- 2. Compute rotated/shifted coordinates + cts_r_s = _compute_rotated_shifted_coordinates(sources, centers, normals) + + # ------------- 4. Define input variables for green's function expression + var = _make_sympy_vec("x", ndim) + var_t = _make_sympy_vec("t", ndim) + + # ------------ 5. Compute large-|x_1| recurrence + n_initial, order, recurrence = get_large_x1_recurrence(pde) + + # ------------ 6. Set order p = 5 + n_p = sources.shape[1] + storage = [np.zeros((n_p, n_p))] * order + + s = sp.Function("s") + n = sp.symbols("n") + + def generate_lamb_expr(i, n_initial): + arg_list = [] + for j in range(order, 0, -1): + # pylint: disable-next=not-callable + arg_list.append(s(i-j)) + for j in range(ndim): + arg_list.append(var[j]) + + if i < n_initial: + lamb_expr_symb_deriv = sp.diff(g_x_y, var[0], i) + for j in range(ndim): + lamb_expr_symb_deriv = lamb_expr_symb_deriv.subs(var_t[j], 0) + lamb_expr_symb = lamb_expr_symb_deriv + else: + lamb_expr_symb = recurrence.subs(n, i) + return sp.lambdify(arg_list, lamb_expr_symb) + + coord = [cts_r_s[j] for j in range(ndim)] + interactions_on_axis = coord[0] * 0 + for i in range(p+1): + lamb_expr = generate_lamb_expr(i, n_initial) + a = [*storage, *coord] + s_new = lamb_expr(*a) + interactions_on_axis += s_new * radius**i/math.factorial(i) + + storage.pop(0) + storage.append(s_new) + + # Compute small-|x_1| interactions + start_order, t_recur_order, t_recur = get_small_x1_recurrence(pde) + t_exp, t_exp_order, _ = get_small_x1_expansion(pde, 8) + storage_taylor_order = max(t_recur_order, t_exp_order+1) + + start_order = max(start_order, order) + + storage_taylor = [np.zeros((n_p, n_p))] * storage_taylor_order + + def gen_lamb_expr_t_recur(i, start_order): + arg_list = [] + for j in range(t_recur_order, 0, -1): + # pylint: disable-next=not-callable + arg_list.append(s(i-j)) + for j in range(ndim): + arg_list.append(var[j]) + + if i < start_order: + lamb_expr_symb_deriv = sp.diff(g_x_y, var[0], i) + for j in range(ndim): + lamb_expr_symb_deriv = lamb_expr_symb_deriv.subs(var_t[j], 0) + lamb_expr_symb = lamb_expr_symb_deriv.subs(var[0], 0) + else: + lamb_expr_symb = t_recur.subs(n, i) + + return sp.lambdify(arg_list, lamb_expr_symb) + + def gen_lamb_expr_t_exp(i, t_exp_order, start_order): + arg_list = [] + for j in range(t_exp_order, -1, -1): + # pylint: disable-next=not-callable + arg_list.append(s(i-j)) + for j in range(ndim): + arg_list.append(var[j]) + + if i < start_order: + lamb_expr_symb_deriv = sp.diff(g_x_y, var[0], i) + for j in range(ndim): + lamb_expr_symb_deriv = lamb_expr_symb_deriv.subs(var_t[j], 0) + lamb_expr_symb = lamb_expr_symb_deriv + else: + lamb_expr_symb = t_exp.subs(n, i) + + return sp.lambdify(arg_list, lamb_expr_symb) + + interactions_off_axis: np.ndarray | int = 0 + for i in range(p+1): + lamb_expr_t_recur = gen_lamb_expr_t_recur(i, start_order) + a1 = [*storage_taylor[(-t_recur_order):], *coord] + + storage_taylor.pop(0) + storage_taylor.append(lamb_expr_t_recur(*a1) + np.zeros((n_p, n_p))) + + lamb_expr_t_exp = gen_lamb_expr_t_exp(i, t_exp_order, start_order) + a2 = [*storage_taylor[-(t_exp_order+1):], *coord] + + interactions_off_axis += lamb_expr_t_exp(*a2) * radius**i/math.factorial(i) + + # Blend large-|x_1| and small-|x_1| regimes based on relative coordinates + m = 100 + mask_on_axis = m*np.abs(coord[0]) >= np.abs(coord[1]) + mask_off_axis = m*np.abs(coord[0]) < np.abs(coord[1]) + + interactions_total = np.zeros(coord[0].shape) + interactions_total[mask_on_axis] = interactions_on_axis[mask_on_axis] + interactions_total[mask_off_axis] = interactions_off_axis[mask_off_axis] # pyright: ignore[reportIndexIssue] + + return (interactions_total * strengths[None, :]).sum(axis=1) diff --git a/sumpy/test/test_recurrence.py b/sumpy/test/test_recurrence.py new file mode 100644 index 00000000..1496ac1b --- /dev/null +++ b/sumpy/test/test_recurrence.py @@ -0,0 +1,217 @@ +r""" +Tests for the recurrence computation module :mod:`sumpy.recurrence`. + +Verifies that recurrence relations for Green's function derivatives +produce results matching direct symbolic differentiation. +""" +from __future__ import annotations + + +__copyright__ = """ +Copyright (C) 2024 Hirish Chandrasekaran +Copyright (C) 2024 Andreas Kloeckner +""" + +__license__ = """ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" +import numpy as np +import sympy as sp +from sympy import hankel1 + +from sumpy.expansion.diff_op import ( + laplacian, + make_identity_diff_op, +) +from sumpy.recurrence import ( + _extract_idx_terms_from_recurrence, + _make_sympy_vec, + get_large_x1_recurrence, + get_small_x1_expansion, + get_small_x1_recurrence, + recurrence_from_pde, + reindex_recurrence_relation, +) + + +def _verify_large_x1_recurrence(pde, g_x_y, ndim, p, x_vals): + r""" + Verifies that the large-:math:`|x_1|` recurrence produces derivatives + matching direct symbolic differentiation at a given evaluation point. + + Computes the first *p* + 1 derivatives of *g_x_y* with respect to + the first coordinate at the target origin, both via direct + differentiation and via the recurrence, and compares. + """ + n_initial, order, recurrence = get_large_x1_recurrence(pde) + + var = _make_sympy_vec("x", ndim) + var_t = _make_sympy_vec("t", ndim) + n = sp.symbols("n") + s = sp.Function("s") + + # Compute true derivatives of G w.r.t. x_0 at t=0 + true_derivs = [] + for i in range(p + 1): + d = sp.diff(g_x_y, var[0], i) + for j in range(ndim): + d = d.subs(var_t[j], 0) + true_derivs.append(complex(sp.N(d.subs(x_vals), 30))) + + # Compute via recurrence, seeding with true initial conditions. + # Negative indices are assumed to be zero (matching the zero-initialized + # storage in recurrence_qbx_lp). + recur_vals: dict[int, complex] = {} + for idx in range(-order, 0): + recur_vals[idx] = 0j + + for i in range(n_initial): + recur_vals[i] = true_derivs[i] + + for i in range(n_initial, p + 1): + expr = recurrence.subs(n, i) + for j in range(order, 0, -1): + # pylint: disable-next=not-callable + expr = expr.subs(s(i - j), recur_vals.get(i - j, 0)) + recur_vals[i] = complex(sp.N(expr.subs(x_vals), 30)) + + for i in range(p + 1): + if abs(true_derivs[i]) > 1e-30: + rel_err = abs(recur_vals[i] - true_derivs[i]) / abs(true_derivs[i]) + assert rel_err < 1e-10 + + +def test_recurrence_from_pde_nonzero(): + r""" + Verifies that :func:`recurrence_from_pde` produces a nonzero recurrence + expression for the 2D Laplace equation. + """ + w = make_identity_diff_op(2) + laplace2d = laplacian(w) + r = recurrence_from_pde(laplace2d) + assert r != 0 + + +def test_reindex_recurrence_relation_structure(): + r""" + Verifies that :func:`reindex_recurrence_relation` produces a recurrence + with positive order and all :math:`s()` indices non-positive (i.e., + :math:`s(n)` depends only on :math:`s(n-1), s(n-2), \dots`). + """ + w = make_identity_diff_op(2) + laplace2d = laplacian(w) + r = recurrence_from_pde(laplace2d) + order, reindexed = reindex_recurrence_relation(r) + + assert order > 0 + + idx_l, _ = _extract_idx_terms_from_recurrence(reindexed) + assert all(idx <= 0 for idx in idx_l) + + +def test_large_x1_recurrence_laplace_2d(): + r""" + Verifies the large-:math:`|x_1|` recurrence for the 2D Laplace Green's + function :math:`G = -\frac{1}{2\pi} \log|x - t|` by comparing + recurrence-computed derivatives against direct symbolic differentiation + at a test point. + """ + w = make_identity_diff_op(2) + laplace2d = laplacian(w) + + var = _make_sympy_vec("x", 2) + var_t = _make_sympy_vec("t", 2) + g_x_y = (-1/(2*np.pi)) * sp.log(sp.sqrt((var[0]-var_t[0])**2 + + (var[1]-var_t[1])**2)) + + x_vals = [(var[0], sp.Rational(1, 2)), (var[1], sp.Rational(3, 10))] + _verify_large_x1_recurrence(laplace2d, g_x_y, 2, 8, x_vals) + + +def test_large_x1_recurrence_laplace_3d(): + r""" + Verifies the large-:math:`|x_1|` recurrence for the 3D Laplace Green's + function :math:`G = \frac{1}{4\pi |x - t|}` by comparing + recurrence-computed derivatives against direct symbolic differentiation + at a test point. + """ + w = make_identity_diff_op(3) + laplace3d = laplacian(w) + + var = _make_sympy_vec("x", 3) + var_t = _make_sympy_vec("t", 3) + abs_dist = sp.sqrt((var[0]-var_t[0])**2 + (var[1]-var_t[1])**2 + + (var[2]-var_t[2])**2) + g_x_y = 1/(4*np.pi) * 1/abs_dist + + x_vals = [(var[0], sp.Rational(1, 2)), (var[1], sp.Rational(3, 10)), + (var[2], sp.Rational(1, 5))] + _verify_large_x1_recurrence(laplace3d, g_x_y, 3, 6, x_vals) + + +def test_large_x1_recurrence_helmholtz_2d(): + r""" + Verifies the large-:math:`|x_1|` recurrence for the 2D Helmholtz Green's + function :math:`G = \frac{i}{4} H_0^{(1)}(k|x - t|)` by comparing + recurrence-computed derivatives against direct symbolic differentiation + at a test point. + """ + w = make_identity_diff_op(2) + helmholtz2d = laplacian(w) + w + + var = _make_sympy_vec("x", 2) + var_t = _make_sympy_vec("t", 2) + k = 1 + abs_dist = sp.sqrt((var[0]-var_t[0])**2 + (var[1]-var_t[1])**2) + g_x_y = (1j/4) * hankel1(0, k * abs_dist) + + x_vals = [(var[0], sp.Rational(1, 2)), (var[1], sp.Rational(3, 10))] + _verify_large_x1_recurrence(helmholtz2d, g_x_y, 2, 5, x_vals) + + +def test_small_x1_recurrence_valid_structure(): + r""" + Verifies that the small-:math:`|x_1|` recurrence for 2D Laplace returns + a recurrence with positive order and non-negative start order. + """ + w = make_identity_diff_op(2) + laplace2d = laplacian(w) + + start_order, recur_order, recur = ( + get_small_x1_recurrence(laplace2d) + ) + + assert start_order >= 0 + assert recur_order > 0 + assert recur != 0 + + +def test_small_x1_expansion_valid_structure(): + r""" + Verifies that the small-:math:`|x_1|` expansion for 2D Laplace returns + a nonzero expression with non-negative parameters. + """ + w = make_identity_diff_op(2) + laplace2d = laplacian(w) + + exp, n_coeffs, start_order = get_small_x1_expansion(laplace2d, 4) + + assert exp != 0 + assert n_coeffs >= 0 + assert start_order >= 0 diff --git a/sumpy/test/test_recurrence_qbx.py b/sumpy/test/test_recurrence_qbx.py new file mode 100644 index 00000000..5727b27e --- /dev/null +++ b/sumpy/test/test_recurrence_qbx.py @@ -0,0 +1,273 @@ +r""" +Tests for the recurrence-based QBX layer potential evaluation module +:mod:`sumpy.recurrence_qbx`. + +Compares layer potentials computed via the recurrence method against +sumpy's standard QBX implementation for Laplace and Helmholtz kernels +in 2D and 3D. +""" +from __future__ import annotations + + +__copyright__ = """ +Copyright (C) 2024 Hirish Chandrasekaran +Copyright (C) 2024 Andreas Kloeckner +""" + +__license__ = """ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" +import meshmode.mesh.generation as mgen # type: ignore # pyright: ignore[reportMissingImports] +import numpy as np +import sympy as sp +from meshmode import ( # pyright: ignore[reportMissingImports] + _acf as _acf_meshmode, # type: ignore +) +from meshmode.discretization import ( # pyright: ignore[reportMissingImports] + Discretization, # type: ignore +) +from meshmode.discretization.poly_element import ( # type: ignore # pyright: ignore[reportMissingImports] + default_simplex_group_factory, +) +from pytential import bind, sym # type: ignore # pyright: ignore[reportMissingImports] +from sympy import hankel1 + +from sumpy.array_context import _acf +from sumpy.expansion.diff_op import ( + laplacian, + make_identity_diff_op, +) +from sumpy.expansion.local import LineTaylorLocalExpansion +from sumpy.kernel import HelmholtzKernel, LaplaceKernel +from sumpy.qbx import LayerPotential +from sumpy.recurrence_qbx import ( + _compute_rotated_shifted_coordinates, + _make_sympy_vec, + recurrence_qbx_lp, +) + + +actx_factory = _acf +ExpnClass = LineTaylorLocalExpansion + +actx = actx_factory() +lknl2d = LaplaceKernel(2) +hknl2d = HelmholtzKernel(2) +lknl3d = LaplaceKernel(3) +hknl3d = HelmholtzKernel(3) + + +def _qbx_lp_general(knl, sources, targets, centers, radius, + strengths, order, k=0): + lpot = LayerPotential(actx.context, + expansion=ExpnClass(knl, order), # pyright: ignore[reportCallIssue] + target_kernels=(knl,), + source_kernels=(knl,)) + + expansion_radii = actx.from_numpy(radius * np.ones(sources.shape[1])) + sources = actx.from_numpy(sources) + targets = actx.from_numpy(targets) + centers = actx.from_numpy(centers) + + strengths = (strengths,) + if k == 0: + _evt, (result_qbx,) = lpot( + actx.queue, # pyright: ignore[reportArgumentType] + targets, sources, centers, strengths, + expansion_radii=expansion_radii) + else: + _evt, (result_qbx,) = lpot( + actx.queue, # pyright: ignore[reportArgumentType] + targets, sources, centers, strengths, + expansion_radii=expansion_radii, + k=1) + + result_qbx = actx.to_numpy(result_qbx) + + return result_qbx + + +def _create_ellipse(n_p, mode_nr=10, quad_convg_rate=100, a=2): + t = np.linspace(0, 2 * np.pi, n_p, endpoint=False) + + phi = sp.symbols("phi") + jacob = sp.sqrt(a**2 * sp.sin(phi)**2 + sp.cos(phi)**2) + + jacobs = sp.lambdify(phi, jacob)(t) + + h = ((2*np.pi)/n_p * np.min(jacobs)) + radius = (h/4) * quad_convg_rate + + unit_circle_param = np.exp(1j * t) + unit_circle = np.array([a * unit_circle_param.real, unit_circle_param.imag]) # pyright: ignore[reportAttributeAccessIssue] + + sources = unit_circle + normals = np.array([unit_circle_param.real, a*unit_circle_param.imag]) # pyright: ignore[reportAttributeAccessIssue] + normals = normals / np.linalg.norm(normals, axis=0) + centers = sources - normals * radius + + density = np.cos(mode_nr * t) * sp.lambdify(phi, 1/jacob)(t) + + return sources, centers, normals, density, jacobs, radius + + +def _create_sphere(refinement_rounds, exp_radius): + target_order = 4 + + actx_m = _acf_meshmode() + mesh = mgen.generate_sphere(1.0, target_order, + uniform_refinement_rounds=refinement_rounds) + grp_factory = default_simplex_group_factory(3, target_order) + discr = Discretization(actx_m, mesh, grp_factory) + nodes = actx_m.to_numpy(discr.nodes()) + sources = np.array([nodes[0][0].reshape(-1), + nodes[1][0].reshape(-1), nodes[2][0].reshape(-1)]) + + area_weight_a = bind(discr, sym.QWeight()*sym.area_element(3))(actx_m) # pyright: ignore[reportCallIssue] + area_weight = actx_m.to_numpy(area_weight_a)[0] # pyright: ignore[reportIndexIssue] + area_weight = area_weight.reshape(-1) + + normals_a = bind(discr, sym.normal(3))(actx_m).as_vector(dtype=object) + normals_a = actx_m.to_numpy(normals_a) # pyright: ignore[reportCallIssue, reportArgumentType] + normals = np.array([normals_a[0][0].reshape(-1), normals_a[1][0].reshape(-1), + normals_a[2][0].reshape(-1)]) + + radius = exp_radius + centers = sources - radius * normals + + return sources, centers, normals, area_weight, radius + + +def test_compute_rotated_shifted_coordinates(): + r""" + Verifies that :func:`_compute_rotated_shifted_coordinates` correctly + computes the off-axis distance for a known source-center-normal + configuration. + """ + sources = np.array([[1], [2], [2]]) + centers = np.array([[0], [0], [0]]) + normals = np.array([[1], [0], [0]]) + cts = _compute_rotated_shifted_coordinates(sources, centers, normals) + assert np.sqrt(cts[1]**2 + cts[2]**2) - np.sqrt(8) <= 1e-12 + + +def test_recurrence_laplace_3d_sphere(): + r""" + Compares the recurrence-based QBX evaluation of the 3D Laplace + single-layer potential on a sphere against sumpy's standard QBX. + """ + radius = 0.0001 + sources, centers, normals, area_weight, radius = _create_sphere(1, radius) + + out = _qbx_lp_general(lknl3d, sources, sources, centers, radius, + area_weight, 4) + + w = make_identity_diff_op(3) + laplace3d = laplacian(w) + var = _make_sympy_vec("x", 3) + var_t = _make_sympy_vec("t", 3) + abs_dist = sp.sqrt((var[0]-var_t[0])**2 + (var[1]-var_t[1])**2 + + (var[2]-var_t[2])**2) + g_x_y = 1/(4*np.pi) * 1/abs_dist + + exp_res = recurrence_qbx_lp(sources, centers, normals, area_weight, + radius, laplace3d, g_x_y, 3, 4) + + assert (np.max(exp_res-out)/np.max(abs(exp_res))) <= 1e-12 + + +def test_recurrence_helmholtz_3d_sphere(): + r""" + Compares the recurrence-based QBX evaluation of the 3D Helmholtz + single-layer potential on a sphere against sumpy's standard QBX. + """ + radius = 0.0001 + sources, centers, normals, area_weight, radius = _create_sphere(2, radius) + + out = _qbx_lp_general(hknl3d, sources, sources, centers, radius, + np.ones(area_weight.shape), 1, 1) # pyright: ignore[reportCallIssue, reportArgumentType] + + w = make_identity_diff_op(3) + helmholtz3d = laplacian(w) + w + var = _make_sympy_vec("x", 3) + var_t = _make_sympy_vec("t", 3) + abs_dist = sp.sqrt((var[0]-var_t[0])**2 + (var[1]-var_t[1])**2 + + (var[2]-var_t[2])**2) + g_x_y = (1/(4*np.pi)) * sp.exp(1j * abs_dist) / abs_dist + + exp_res = recurrence_qbx_lp(sources, centers, normals, np.ones(area_weight.shape), # pyright: ignore[reportCallIssue, reportArgumentType] + radius, helmholtz3d, g_x_y, 3, 1) + + assert np.max(abs(out - exp_res)) <= 1e-8 + + +def test_recurrence_laplace_2d_ellipse(): + r""" + Compares the recurrence-based QBX evaluation of the 2D Laplace + single-layer potential on an ellipse against sumpy's standard QBX, + verifying convergence across multiple panel counts. + """ + w = make_identity_diff_op(2) + laplace2d = laplacian(w) + + var = _make_sympy_vec("x", 2) + var_t = _make_sympy_vec("t", 2) + g_x_y = (-1/(2*np.pi)) * sp.log(sp.sqrt((var[0]-var_t[0])**2 + + (var[1]-var_t[1])**2)) + + p = 4 + err = [] + for n_p in range(200, 1001, 200): + sources, centers, normals, density, jacobs, radius = _create_ellipse(n_p) + strengths = jacobs * density * (2*np.pi/n_p) + exp_res = recurrence_qbx_lp(sources, centers, normals, + strengths, radius, laplace2d, + g_x_y, 2, p) + qbx_res = _qbx_lp_general(lknl2d, sources, sources, centers, + radius, strengths, p) + err.append(np.max(np.abs(exp_res - qbx_res))/np.max(np.abs(qbx_res))) + assert np.max(err) <= 1e-13 + + +def test_recurrence_helmholtz_2d_ellipse(): + r""" + Compares the recurrence-based QBX evaluation of the 2D Helmholtz + single-layer potential on an ellipse against sumpy's standard QBX, + verifying convergence across multiple panel counts. + """ + w = make_identity_diff_op(2) + helmholtz2d = laplacian(w) + w + + var = _make_sympy_vec("x", 2) + var_t = _make_sympy_vec("t", 2) + k = 1 + abs_dist = sp.sqrt((var[0]-var_t[0])**2 + (var[1]-var_t[1])**2) + g_x_y = (1j/4) * hankel1(0, k * abs_dist) + + p = 5 + err = [] + for n_p in range(200, 1001, 200): + sources, centers, normals, density, jacobs, radius = _create_ellipse(n_p) + strengths = jacobs * density * (2*np.pi/n_p) + exp_res = recurrence_qbx_lp(sources, centers, normals, strengths, + radius, helmholtz2d, g_x_y, 2, p) + qbx_res = _qbx_lp_general(hknl2d, sources, sources, + centers, radius, strengths, p, 1) + err.append(np.max(np.abs(exp_res - qbx_res))) + assert np.max(err) <= 1e-13