Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-18 08:21:41

0001 import argparse
0002 import contextlib
0003 import sys
0004 
0005 import sympy as sym
0006 from sympy import MatrixSymbol
0007 
0008 from codegen.sympy_common import (
0009     NamedExpr,
0010     name_expr,
0011     find_by_name,
0012     cxx_printer,
0013     my_expression_print,
0014 )
0015 
0016 step_path_derivatives = (
0017     MatrixSymbol("step_path_derivatives", 8, 1).as_explicit().as_mutable()
0018 )
0019 step_path_derivatives[7, 0] = 0  # qop
0020 
0021 surface_path_derivatives = (
0022     MatrixSymbol("surface_path_derivatives", 1, 8).as_explicit().as_mutable()
0023 )
0024 surface_path_derivatives[0, 3] = 0
0025 surface_path_derivatives[0, 7] = 0
0026 
0027 # M is the bound-to-free jacobian transported to the current point.  loc0 and
0028 # loc1 stay position only, phi and theta pick up a direction part (position too
0029 # on a line surface), q/p picks up all three, and time stays exactly e_time.
0030 J_bf = MatrixSymbol("M", 8, 6).as_explicit().as_mutable()
0031 tmp = sym.zeros(8, 6)
0032 tmp[0:3, 0:2] = J_bf[0:3, 0:2]
0033 tmp[0:3, 2:5] = J_bf[0:3, 2:5]
0034 tmp[3, 4] = J_bf[3, 4]
0035 tmp[4:7, 2:5] = J_bf[4:7, 2:5]
0036 tmp[7, 4] = J_bf[7, 4]
0037 tmp[3, 5] = 1
0038 J_bf = tmp
0039 
0040 J_fb = MatrixSymbol("J_fb", 6, 8).as_explicit().as_mutable()
0041 tmp = sym.zeros(6, 8)
0042 tmp[0:2, 0:3] = J_fb[0:2, 0:3]
0043 tmp[2:4, 4:7] = J_fb[2:4, 4:7]
0044 tmp[5, 3] = 1
0045 tmp[4, 7] = 1
0046 J_fb = tmp
0047 
0048 
0049 def full_transport_jacobian_generic() -> list[NamedExpr]:
0050     J_full = name_expr(
0051         "J_full",
0052         J_fb * (sym.eye(8) + step_path_derivatives * surface_path_derivatives) * J_bf,
0053     )
0054 
0055     return [J_full]
0056 
0057 
0058 def full_transport_jacobian_curvilinear(direction: MatrixSymbol) -> list[NamedExpr]:
0059     surface_path_derivatives = (
0060         MatrixSymbol("surface_path_derivatives", 1, 8).as_explicit().as_mutable()
0061     )
0062     surface_path_derivatives[0, 0:3] = -direction.as_explicit().transpose()
0063     surface_path_derivatives[0, 3:8] = sym.zeros(1, 5)
0064 
0065     J_full = name_expr(
0066         "J_full",
0067         J_fb * (sym.eye(8) + step_path_derivatives * surface_path_derivatives) * J_bf,
0068     )
0069 
0070     return [J_full]
0071 
0072 
0073 def my_full_transport_jacobian_generic_function_print(name_exprs, run_cse=True):
0074     printer = cxx_printer
0075     outputs = [find_by_name(name_exprs, name)[0] for name in ["J_full"]]
0076 
0077     lines = []
0078 
0079     head = (
0080         "template <typename T> void boundToBoundTransportJacobianImpl("
0081         "std::span<const T, 48> J_fb, std::span<const T, 48> M,"
0082         " std::span<const T, 8> step_path_derivatives,"
0083         " std::span<const T, 8> surface_path_derivatives,"
0084         " std::span<T, 36> J_full) {"
0085     )
0086     lines.append(head)
0087 
0088     code = my_expression_print(
0089         printer,
0090         name_exprs,
0091         outputs,
0092         run_cse=run_cse,
0093     )
0094     lines.extend([f"  {l}" for l in code.split("\n")])
0095 
0096     lines.append("}")
0097 
0098     return "\n".join(lines)
0099 
0100 
0101 def my_full_transport_jacobian_curvilinear_function_print(name_exprs, run_cse=True):
0102     printer = cxx_printer
0103     outputs = [find_by_name(name_exprs, name)[0] for name in ["J_full"]]
0104 
0105     lines = []
0106 
0107     head = (
0108         "template <typename T> void boundToCurvilinearTransportJacobianImpl("
0109         "std::span<const T, 48> J_fb, std::span<const T, 48> M,"
0110         " std::span<const T, 8> step_path_derivatives,"
0111         " std::span<const T, 3> dir, std::span<T, 36> J_full) {"
0112     )
0113     lines.append(head)
0114 
0115     code = my_expression_print(
0116         printer,
0117         name_exprs,
0118         outputs,
0119         run_cse=run_cse,
0120     )
0121     lines.extend([f"  {l}" for l in code.split("\n")])
0122 
0123     lines.append("}")
0124 
0125     return "\n".join(lines)
0126 
0127 
0128 def check_curvilinear_is_generic_specialised() -> None:
0129     """Assert the curvilinear jacobian is the generic one at a curvilinear surface.
0130 
0131     They are printed as two functions, so nothing else keeps them in step. A
0132     curvilinear surface has path derivatives -direction over the position part
0133     and zero elsewhere.
0134     """
0135     direction = MatrixSymbol("dir", 3, 1)
0136     generic = full_transport_jacobian_generic()[0].expr
0137     curvilinear = full_transport_jacobian_curvilinear(direction)[0].expr
0138 
0139     spd = MatrixSymbol("surface_path_derivatives", 1, 8).as_explicit()
0140     at_curvilinear = {spd[0, i]: -direction.as_explicit()[i, 0] for i in range(3)}
0141     at_curvilinear.update({spd[0, i]: 0 for i in range(3, 8)})
0142 
0143     diff = sym.expand(generic.subs(at_curvilinear) - curvilinear)
0144     if any(e != 0 for e in diff):
0145         bad = [
0146             (i, j)
0147             for i in range(diff.rows)
0148             for j in range(diff.cols)
0149             if diff[i, j] != 0
0150         ]
0151         raise AssertionError(
0152             f"curvilinear jacobian is not the generic one specialised, at {bad}"
0153         )
0154 
0155 
0156 def check_covariance_transport_sparsity() -> None:
0157     """Assert the shape the covariance transport masks this jacobian down to.
0158 
0159     Live entries outside it would be dropped there silently, so check here.
0160 
0161     Raises AssertionError if the jacobian reaches outside that shape.
0162     """
0163     J = sym.expand(full_transport_jacobian_generic()[0].expr)
0164     qop, time = 4, 5
0165     # q/p depends on nothing but itself, its own diagonal free; nothing depends
0166     # on time.
0167     leaks = [(qop, j) for j in range(6) if j != qop and J[qop, j] != 0]
0168     leaks += [(i, time) for i in range(6) if J[i, time] != (1 if i == time else 0)]
0169     if leaks:
0170         raise AssertionError(
0171             "bound-to-bound jacobian is not of the shape the covariance "
0172             f"transport assumes; live entries outside it: {leaks}"
0173         )
0174 
0175 
0176 HEADER = """// This file is part of the ACTS project.
0177 //
0178 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0179 //
0180 // This Source Code Form is subject to the terms of the Mozilla Public
0181 // License, v. 2.0. If a copy of the MPL was not distributed with this
0182 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0183 
0184 // Note: This file is generated by generate_sympy_jac.py
0185 //       Do not modify it manually.
0186 
0187 #pragma once
0188 
0189 #include <cmath>
0190 #include <span>
0191 """
0192 
0193 
0194 def main(argv: list[str]) -> None:
0195     """Generate the transport jacobians.
0196 
0197     @param argv is the command line, argv[0] being the program name
0198     """
0199     parser = argparse.ArgumentParser(description=__doc__)
0200     parser.add_argument(
0201         "output",
0202         nargs="?",
0203         help="file to write the generated jacobians to; stdout if omitted",
0204     )
0205     parser.add_argument(
0206         "--no-check",
0207         action="store_true",
0208         help="skip the symbolic assertions",
0209     )
0210     args = parser.parse_args(argv[1:])
0211 
0212     if not args.no_check:
0213         # If one of these fires the generated code would be wrong, so they
0214         # guard the generator rather than a test that might not be run.
0215         check_curvilinear_is_generic_specialised()
0216         check_covariance_transport_sparsity()
0217 
0218     with (
0219         open(args.output, "w") if args.output else contextlib.nullcontext(sys.stdout)
0220     ) as out:
0221         out.write(HEADER)
0222         out.write(
0223             my_full_transport_jacobian_generic_function_print(
0224                 full_transport_jacobian_generic(), run_cse=True
0225             )
0226         )
0227         out.write("\n")
0228         out.write(
0229             my_full_transport_jacobian_curvilinear_function_print(
0230                 full_transport_jacobian_curvilinear(MatrixSymbol("dir", 3, 1)),
0231                 run_cse=True,
0232             )
0233         )
0234         out.write("\n")
0235 
0236 
0237 if __name__ == "__main__":
0238     main(sys.argv)