From 5a8a7974571b52bd85399d6f6afdc22ad15040bd Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Thu, 19 Jun 2025 02:16:10 -0400 Subject: [PATCH 01/77] Fix some typos --- src/derived_variables/horizons.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/derived_variables/horizons.jl b/src/derived_variables/horizons.jl index bc34154d..80c031bb 100644 --- a/src/derived_variables/horizons.jl +++ b/src/derived_variables/horizons.jl @@ -155,7 +155,7 @@ The "quadrupolar polarisability" of object 1 used by [Bohé et al. Note that Bohé et al. refer to the closely related (and co-authored) [Marsat (2014)](https://arxiv.org/abs/1411.4118), who notes above Eq. (4.7) that this is denoted ``C_{\mathrm{ES}^2}`` in [Levi and Steinhoff (2014)](https://arxiv.org/abs/1410.2601), who -in turn notes that "we can set ... the Wilson coefficients ``C_{\mathrm{ES}^2} = +in turn note that "we can set ... the Wilson coefficients ``C_{\mathrm{ES}^2} = C_{\mathrm{BS}^3} = 1`` for the black hole case." However, a very similar constant ``\kappa_A`` is used in Eq. (2.1) of [Buonanno et al. @@ -212,8 +212,8 @@ end The "quadrupolar polarisability" of object 1 used by [Marsat (2014)](https://arxiv.org/abs/1411.4118), who notes above Eq. (4.11) that this is denoted -``C_{\mathrm{BS}^2}`` in [Levi and Steinhoff (2014)](https://arxiv.org/abs/1410.2601), who -in turn notes that "we can set ... the Wilson coefficients ``C_{\mathrm{ES}^2} = +``C_{\mathrm{BS}^3}`` in [Levi and Steinhoff (2014)](https://arxiv.org/abs/1410.2601), who +in turn note that "we can set ... the Wilson coefficients ``C_{\mathrm{ES}^2} = C_{\mathrm{BS}^3} = 1`` for the black hole case." See also [`κ₁`](@ref). From 6215ca1f1d2a164cc18ce84fa0be87832aca1d41 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Sun, 22 Jun 2025 13:18:22 -0400 Subject: [PATCH 02/77] Start restructuring code --- docs/Project.toml | 2 + docs/make.jl | 2 + docs/src/internals/code_diagram.md | 92 + sources/PNpedia/.gitignore | 1 + sources/PNpedia/Project.toml | 3 + sources/PNpedia/README.md | 13 + sources/PNpedia/characters.html | 20 + sources/PNpedia/test.jl | 99 + sources/parse-literature/.python-version | 1 + sources/parse-literature/README.md | 0 sources/parse-literature/main.py | 6 + sources/parse-literature/pyproject.toml | 10 + sources/parse-literature/uv.lock | 1815 +++++++++++++++++ src2/PostNewtonian.jl | 1 + src2/core/PNExpansion.jl | 1 + src2/core/PNExpression.jl | 1 + src2/core/PNReference.jl | 38 + src2/core/PNTerm.jl | 1 + src2/interface/orbital_evolution.jl | 1 + src2/interface/pn.jl | 1 + src2/interface/waveform.jl | 1 + .../PetersMathews1963/PetersMathews1963.jl | 3 + src2/literature/common_variables.jl | 10 + src2/literature/ref1/ref1.jl | 10 + src2/pn_expressions/angular_momentum.jl | 1 + src2/pn_expressions/dynamics.jl | 1 + src2/pn_expressions/energy.jl | 1 + src2/pn_expressions/flux.jl | 1 + src2/pn_expressions/precession.jl | 1 + src2/pn_expressions/waveforms.jl | 1 + src2/pn_systems/BBH.jl | 61 + src2/pn_systems/PNSystem.jl | 164 ++ 32 files changed, 2363 insertions(+) create mode 100644 docs/src/internals/code_diagram.md create mode 100644 sources/PNpedia/.gitignore create mode 100644 sources/PNpedia/Project.toml create mode 100644 sources/PNpedia/README.md create mode 100644 sources/PNpedia/characters.html create mode 100644 sources/PNpedia/test.jl create mode 100644 sources/parse-literature/.python-version create mode 100644 sources/parse-literature/README.md create mode 100644 sources/parse-literature/main.py create mode 100644 sources/parse-literature/pyproject.toml create mode 100644 sources/parse-literature/uv.lock create mode 100644 src2/PostNewtonian.jl create mode 100644 src2/core/PNExpansion.jl create mode 100644 src2/core/PNExpression.jl create mode 100644 src2/core/PNReference.jl create mode 100644 src2/core/PNTerm.jl create mode 100644 src2/interface/orbital_evolution.jl create mode 100644 src2/interface/pn.jl create mode 100644 src2/interface/waveform.jl create mode 100644 src2/literature/PetersMathews1963/PetersMathews1963.jl create mode 100644 src2/literature/common_variables.jl create mode 100644 src2/literature/ref1/ref1.jl create mode 100644 src2/pn_expressions/angular_momentum.jl create mode 100644 src2/pn_expressions/dynamics.jl create mode 100644 src2/pn_expressions/energy.jl create mode 100644 src2/pn_expressions/flux.jl create mode 100644 src2/pn_expressions/precession.jl create mode 100644 src2/pn_expressions/waveforms.jl create mode 100644 src2/pn_systems/BBH.jl create mode 100644 src2/pn_systems/PNSystem.jl diff --git a/docs/Project.toml b/docs/Project.toml index 8a25f2f5..d7fec299 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,5 +1,7 @@ [deps] Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +DocumenterCitations = "daee34ce-89f3-4625-b898-19384cb65244" +DocumenterMermaid = "a078cd44-4d9c-4618-b545-3ab9d77f9177" LiveServer = "16fef848-5104-11e9-1b77-fb7a48bbb589" PlotlyJS = "f0f68f2c-4968-5e81-91da-67840de0976a" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" diff --git a/docs/make.jl b/docs/make.jl index bbabee6b..bf2accb3 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,6 +1,7 @@ using PostNewtonian using Symbolics # To document the extension using Documenter +using DocumenterMermaid DocMeta.setdocmeta!( PostNewtonian, :DocTestSetup, :(using PostNewtonian); recursive=true, warn=false @@ -33,6 +34,7 @@ makedocs(; "GWFrames" => "interface/gwframes.md", ], "Internals" => [ + "Code diagram" => "internals/code_diagram.md", "Code structure" => "internals/code_structure.md", "internals/pn_systems.md", "internals/fundamental_variables.md", diff --git a/docs/src/internals/code_diagram.md b/docs/src/internals/code_diagram.md new file mode 100644 index 00000000..dc4e92fb --- /dev/null +++ b/docs/src/internals/code_diagram.md @@ -0,0 +1,92 @@ +# Diagram + +```mermaid +flowchart TB + %% define each layer as its own box + subgraph Core["core
Building blocks of the code"] + a["XYZ"] + b["CYS"] + end + subgraph Systems["pn_systems
Types encoding various binaries"] + end + subgraph Literature["literature
Modules for each reference containing pieces of PN expressions"] + end + subgraph Expressions["pn_expressions
Functions for computing physical quantities"] + end + subgraph Interface["interface
High-level functions for users to call"] + end + + %% draw the arrows in build‐order + Core --> Systems + Systems --> Literature + Literature --> Expressions + Expressions --> Interface +``` + +- PostNewtonian + - interface: high-level functions for users to call + - orbital_evolution + - waveform + - pn + - pn_expressions: functions for computing physical quantities + - flux + - energy + - angular_momentum + - precession + - dynamics + - waveforms + - literature: modules for each reference containing pieces of PN expressions + - common variables + - Ref1 + - import fundamental variables + - import common variables + - define variables not in common variables + - write individual expressions as separate functions with `@pn_expression` + - pn_systems: types encoding various binaries + - PNSystem + - AbstractBBHSystem + - QuasicircularBBH + - QuasisphericalBBH + - EccentricNonspinningBBH + - BBH + - AbstractBHNSSystem + - BHNS + - AbstractNSNSSystem + - NSNS + - core: the building blocks of the code + - PNExpression + - PNReference + - PNExpansion + - PNTerm + +- `PNSystem` + - Define abstract subtypes: + - `AbstractBBHSystem` + - `QuasicircularBBH` + - `QuasisphericalBBH` + - `EccentricNonspinningBBH` + +- `PNExpression` +- `PNExpansion` +- `PNTerm` + +- literature + - common variables + - each reference + - directory and module named by bibtex key + - modules are imported directly under top-level `PostNewtonian` + - explicitly import any fundamental variables used in the expressions + - import variables that are defined the same as the common variables + - define variables that are not defined in the common variables + - write individuals + +- PN expressions + - Assemble functions for high-level quantities like flux, etc. + - Inside each function, simply call the expressions from the + literature, prepending with module name. + +`@pn_expression` will + +1. look for symbols in the expression, and for any that matches a function + name in the current module, add a `let` binding for that symbol to equal + the function called on the `pnsystem` argument. diff --git a/sources/PNpedia/.gitignore b/sources/PNpedia/.gitignore new file mode 100644 index 00000000..d239ddcb --- /dev/null +++ b/sources/PNpedia/.gitignore @@ -0,0 +1 @@ +PNpedia diff --git a/sources/PNpedia/Project.toml b/sources/PNpedia/Project.toml new file mode 100644 index 00000000..4cffcc42 --- /dev/null +++ b/sources/PNpedia/Project.toml @@ -0,0 +1,3 @@ +[deps] +PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d" +SymPyPythonCall = "bc8888f7-b21e-4b7c-a06a-5d9c9496438c" diff --git a/sources/PNpedia/README.md b/sources/PNpedia/README.md new file mode 100644 index 00000000..e03ab610 --- /dev/null +++ b/sources/PNpedia/README.md @@ -0,0 +1,13 @@ +# PNpedia + +This directory contains code to translate results from David +Trestini's [PNpedia](https://github.com/davidtrestini/PNpedia) — which +are given as `.txt` files containing Mathematica output — into Julia +code. + +Though it is `.gitignore`d, to run the code you should first clone +PNpedia into this directory, so that the files are available + +```bash +git clone https://github.com/davidtrestini/PNpedia.git +``` diff --git a/sources/PNpedia/characters.html b/sources/PNpedia/characters.html new file mode 100644 index 00000000..80ccc0b6 --- /dev/null +++ b/sources/PNpedia/characters.html @@ -0,0 +1,20 @@ +

The Wolfram Language not only has systemwide support for arbitrary Unicode characters, but also includes nearly a thousand carefully designed characters for mathematical notation and technical presentationall fully integrated into the Wolfram Language's input, output, and graphics.

nnn or \[Name] named character (e.g. α, a, [Alpha])

Insert Special Character palette of all named special characters

Wolfram Language Syntax »

  ▪  °  ▪  π  ▪  ×  ▪    ▪    ▪    ▪    ▪    ▪    ▪    ▪    ▪    ▪    ▪    ▪  ...

\[Degree]  ▪  \[ImaginaryI]  ▪  \[Times]  ▪  \[Element]  ▪  \[Rule]  ▪  ...

Greek Letters »

α  ▪  β  ▪  θ  ▪  ϑ  ▪  ϕ  ▪  ω  ▪  Δ  ▪  Ψ  ▪  Ω  ▪  Ϡ  ▪  ...

\[Alpha]  ▪  \[CurlyTheta]  ▪  \[CapitalDelta]  ▪  \[Sampi]  ▪  ...

Notational Alphabets »

  ▪    ▪    ▪    ▪    ▪    ▪  a  ▪  A  ▪    ▪  ...

\[ScriptA]  ▪  \[ScriptCapitalA]  ▪  \[GothicA]  ▪  \[FormalA]  ▪  \[Aleph]  ▪  ...

Mathematical Notation »

  ▪    ▪    ▪    ▪    ▪    ▪    ▪    ▪    ▪    ▪    ▪    ▪    ▪  ...

\[Integral]  ▪  \[Sum]  ▪  \[Del]  ▪  \[ForAll]  ▪  \[CirclePlus]  ▪  ...

Arrows & Arrow-Like Forms »

  ▪    ▪    ▪    ▪    ▪    ▪    ▪    ▪    ▪    ▪    ▪  ...

\[RightArrow]  ▪  \[LongRightArrow]  ▪  \[DoubleRightArrow]  ▪  \[LeftRightArrow]  ▪  ...

Textual Forms »

  ▪    ▪    ▪    ▪    ▪    ▪    ▪    ▪    ▪  ¿  ▪  ©  ▪  ...

\[Dash]  ▪  \[LongDash]  ▪  \[Bullet]  ▪  \[Copyright]  ▪  ...

Currency, Units, and Special Notations »

  ▪  £  ▪  µ  ▪  Å  ▪    ▪    ▪    ▪    ▪    ▪    ▪  \[WhiteKnight]  ▪  ...

\[Euro]  ▪  \[Sterling]  ▪  \[Micro]  ▪  \[Mars]  ▪  \[Natural]  ▪  ...

User Interfaces & Documentation »

  ▪  »  ▪    ▪    ▪    ▪    ▪    ▪  ...

Shapes, Icons, etc. »

  ▪    ▪    ▪    ▪    ▪    ▪    ▪    ▪  ...

\[FilledSquare]  ▪  \[EmptySquare]  ▪  \[HappySmiley]  ▪  ...

Non-Printing Characters »

\[ThinSpace]  ▪  \[NonBreakingSpace]  ▪  \[InvisibleComma]  ▪  \[AlignmentMarker]  ▪  ...

International Characters »

é  ▪  ö  ▪  à  ▪  á  ▪  â  ▪  ã  ▪  ä  ▪  å  ▪  ç  ▪  č  ▪  ø  ▪  æ  ▪  ß  ▪  ...

\[EAcute]  ▪  \[ODoubleDot]  ▪  \[CHacek]  ▪  ...

Formal Symbols »

a  ▪  A  ▪  ω  ▪  ...

\[FormalA]  ▪  \[FormalCapitalA]  ▪  \[FormalOmega]  ▪  ...

FullForm show characters in their full named form

\.nn, \:nnnn, \|nnnnnn enter forms with hex values corresponding to Unicode code points

\alpha, Α, etc. enter TeX, HTML, SGML, etc. forms

ToCharacterCode  ▪  FromCharacterCode  ▪  CharacterRange

StringToByteArray  ▪  ByteArrayToString

Alphabet

+
+

+ Alphabet[] +

+

gives a list of the lowercase letters a through z in the English alphabet.

+
+
+

+ Alphabet[type] +

+

gives the alphabet for the language or class type.

+
+
+

+ Alphabet[type,prop] +

+

gives the alphabet defined by prop for the language or class type .

+
+
alphabets for different scripts

diff --git a/sources/PNpedia/test.jl b/sources/PNpedia/test.jl new file mode 100644 index 00000000..f03085f9 --- /dev/null +++ b/sources/PNpedia/test.jl @@ -0,0 +1,99 @@ +""" + +We need to parse Mathematica expressions in Julia, using SymPy's parsing capabilities. +Unfortunately, at the moment, there is a bug in SymPy + + https://github.com/sympy/sympy/issues/27868 + +that prevents it from parsing, e.g., `Sqrt[2]*σ`, which does occur in these expressions. +There is a PR at + + https://github.com/sympy/sympy/pull/27876 + +that claims to fix this, but it has not been merged yet. + +Also, David Trestini has evidently used `FullForm` to output the Mathematica to TXT files, +which translates the Greek letters to their full names — e.g., `ν` becomes `\[Nu]` — which +sympy cannot parse. Specifically, `FullForm` encodes its output as "PrintableASCII". + +There are some packages that may be useful for translating quite generally (in descending +order of how up-to-date I would guess they are): + + * `pygments-mathematica` + * `mathics-scanner` + * `FoxySheep2` + +I'm wondering if it's worthwhile to to write a translator by getting all possible identifier +letters in Python, using + + import sys + [c for c in map(chr, range(sys.maxunicode+1)) if ("_"+c).isidentifier()] + +Then, we can take that into Mathematica, run each through `FullForm`, and create a JSON file +or something that we can read in with Python to replace the identifiers. There are 139,463 +such identifiers — some of which may not output at all with `FullForm`, and some of which +(not many) will be output exactly as input, so don't need to be included. + +Note that Python's spec for identifiers lives at + + https://docs.python.org/3/reference/lexical_analysis.html#identifiers + +A few useful pieces of information I glean from that: + +> Within the ASCII range (U+0001..U+007F), the valid characters for identifiers include the +> uppercase and lowercase letters A through Z, the underscore _ and, except for the first +> character, the digits 0 through 9. + +> All identifiers are converted into the normal form NFKC while parsing; comparison of +> identifiers is based on NFKC. + +I suppose it's possible that some of these things that map to the same character in NFKC +could come up, and be encoded differently in Mathematica. In that case, the output of the +"translate-to-python" function would be able to output different characters that map to the +same character in NFKC, but that's just a problem with how python works, so I don't know +that we can do anything about it. Maybe warn about it? + + +One problem is unicode's combining characters, some of which + +""" + + + + +using Pkg +Pkg.activate(@__DIR__) + +import SymPyPythonCall +import PythonCall + +greek_replacements = ( + raw"\[Nu]" => "ν", + raw"\[Delta]" => "δ", + raw"\[Tau]" => "τ", + raw"\[Chi]" => "χ", + raw"\[Sigma]" => "σ", + raw"\[Kappa]" => "κ", + raw"\[Lambda]" => "λ", +) + + + +const parse_mathematica = PythonCall.pyimport("sympy.parsing.mathematica" => "parse_mathematica") + +s = replace( + raw"((1594323*I)/4480)*Sqrt[Pi/4199]*x^(9/2)*\[Nu]*(\[Delta] - 6*\[Delta]*\[Nu] + 10*\[Delta]*\[Nu]^2 - 4*\[Delta]*\[Nu]^3)", + greek_replacements... +) + +ex = parse_mathematica(s)#, Dict("EllipticE[x]"=>"elliptic_e(x)")) + + + +const elliptic_e = PythonCall.pyimport("sympy.functions.special.elliptic_integrals" => "elliptic_e") + +s = raw"(Sqrt[Pi])/(2*EllipticE[m])" +s = raw"((1594323*I)/4480)*Sqrt[Pi/4199]*x^(9/2)*\[Nu]*(\[Delta] - 6*\[Delta]*\[Nu] + 10*\[Delta]*\[Nu]^2 - 4*\[Delta]*\[Nu]^3)" +ex = parse_mathematica(s)#, Dict("EllipticE[x]"=>"elliptic_e(x)")) + +ex.args[2].args[0].args diff --git a/sources/parse-literature/.python-version b/sources/parse-literature/.python-version new file mode 100644 index 00000000..e4fba218 --- /dev/null +++ b/sources/parse-literature/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/sources/parse-literature/README.md b/sources/parse-literature/README.md new file mode 100644 index 00000000..e69de29b diff --git a/sources/parse-literature/main.py b/sources/parse-literature/main.py new file mode 100644 index 00000000..839cdad8 --- /dev/null +++ b/sources/parse-literature/main.py @@ -0,0 +1,6 @@ +def main(): + print("Hello from parse-literature!") + + +if __name__ == "__main__": + main() diff --git a/sources/parse-literature/pyproject.toml b/sources/parse-literature/pyproject.toml new file mode 100644 index 00000000..5beb3793 --- /dev/null +++ b/sources/parse-literature/pyproject.toml @@ -0,0 +1,10 @@ +[project] +name = "parse-literature" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [ + "ipython>=9.3.0", + "mathics-omnibus>=8.0.0", +] diff --git a/sources/parse-literature/uv.lock b/sources/parse-literature/uv.lock new file mode 100644 index 00000000..503957c3 --- /dev/null +++ b/sources/parse-literature/uv.lock @@ -0,0 +1,1815 @@ +version = 1 +revision = 2 +requires-python = ">=3.12" + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "asgiref" +version = "3.8.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/29/38/b3395cc9ad1b56d2ddac9970bc8f4141312dbaec28bc7c218b0dfafd0f42/asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590", size = 35186, upload-time = "2024-03-22T14:39:36.863Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/e3/893e8757be2612e6c266d9bb58ad2e3651524b5b40cf56761e985a28b13e/asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47", size = 23828, upload-time = "2024-03-22T14:39:34.521Z" }, +] + +[[package]] +name = "asttokens" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978, upload-time = "2024-11-30T04:30:14.439Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918, upload-time = "2024-11-30T04:30:10.946Z" }, +] + +[[package]] +name = "blis" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/96/f3/7c5a47a0d5ec0362bab29fd4f497b4b1975473bf30b7a02bc9c0b0e84f7a/blis-1.3.0.tar.gz", hash = "sha256:1695a87e3fc4c20d9b9140f5238cac0514c411b750e8cdcec5d8320c71f62e99", size = 2510328, upload-time = "2025-04-03T15:09:47.767Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/3f/67140d6588e600577f92d2c938e9492a8cd0706bab770978ee84ecb86e70/blis-1.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ef188f1f914d52acbbd75993ba25554e381ec9099758b340cd0da41af94ae8ae", size = 6988854, upload-time = "2025-04-03T15:09:13.203Z" }, + { url = "https://files.pythonhosted.org/packages/d1/05/30587d1b168fa27d1bf6869a1be4bcb3f10493f836381a033aa9c7a10ab8/blis-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:626f84522faa51d5a52f9820551a84a5e02490bf6d1abdfc8d27934a0ff939de", size = 1282465, upload-time = "2025-04-03T15:09:15.081Z" }, + { url = "https://files.pythonhosted.org/packages/35/13/60d2dd0443a7a56a0a160d873444e4b9189bb2939d93457864432ee18c90/blis-1.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f56e0454ce44bc08797383ce427ee5e2b044aab1eafb450eab82e86f8bfac853", size = 3061088, upload-time = "2025-04-03T15:09:16.535Z" }, + { url = "https://files.pythonhosted.org/packages/2f/30/4909baf57c3cd48414c284e4fced42157c4768f83bf6c95b0bb446192b45/blis-1.3.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9bb5770efe233374d73a567af5cdef24f48bead83d118bdb9bd5c2187b0f010", size = 3259127, upload-time = "2025-04-03T15:09:18.528Z" }, + { url = "https://files.pythonhosted.org/packages/bb/bf/625121119107d3beafe96eb776b00a472f0210c07d07b1ed160ab7db292a/blis-1.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d52ce33a1895d82f2f39f7689d5e70b06ebba6bc6f610046ecd81db88d650aac", size = 11619003, upload-time = "2025-04-03T15:09:20.139Z" }, + { url = "https://files.pythonhosted.org/packages/81/92/0bad7a4c29c7a1ab10db27b04babec7ca4a3f504543ef2d1f985fb84c41a/blis-1.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6c78e8dd420e0e695df0ceecf950f3cf823e0a1b8c2871a7e35117c744d45861", size = 3062135, upload-time = "2025-04-03T15:09:22.142Z" }, + { url = "https://files.pythonhosted.org/packages/35/b5/ea9b4f6b75c9dce24ce0d6fa15d5eaab54b115a57967d504e460db901c59/blis-1.3.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7a060700ee98ea44a1b9833b16d3dd1375aaa9d3230222bfc5f13c4664e5710e", size = 4298755, upload-time = "2025-04-03T15:09:24.064Z" }, + { url = "https://files.pythonhosted.org/packages/e5/c5/9b7383752cdc4ca92359c161b1086bd158b4f3cda5813a390ff9c8c1b892/blis-1.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:250f0b0aeca0fdde7117751a54ae6d6b6818a446a619f3c0c63f3deb77f700a8", size = 14785385, upload-time = "2025-04-03T15:09:25.74Z" }, + { url = "https://files.pythonhosted.org/packages/0c/92/6bb1940a491ce9d3ec52372bc35988bec779b16ace7e87287d981df31eeb/blis-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:2e6f468467a18a7c2ac2e411643f5cfa45a435701e2c04ad4aa46bb02fc3aa5c", size = 6260208, upload-time = "2025-04-03T15:09:28.207Z" }, + { url = "https://files.pythonhosted.org/packages/91/ec/2b1e366e7b4e3cdb052a4eeba33cc6a3e25fe20566f3062dbe59a8dd7f78/blis-1.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4d6a91c8726d0bc3345a8e0c8b7b8e800bee0b9acc4c2a0dbeb782b8b651f824", size = 6985730, upload-time = "2025-04-03T15:09:29.884Z" }, + { url = "https://files.pythonhosted.org/packages/f1/8b/a3374a970e1ae6138b2ec6bffeb1018068c5f0dbf2b12dd8ab16a47ae4a0/blis-1.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e3c20bc3d7143383195cc472373fb301d3bafbacd8ab8f3bffc27c68bef45d81", size = 1280751, upload-time = "2025-04-03T15:09:32.007Z" }, + { url = "https://files.pythonhosted.org/packages/53/97/83cc91c451709c85650714df3464024bf37ef791be1e0fae0d2a0f945da6/blis-1.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:778c4b84c6eccab223d8afe20727820f6c7dd7a010c3bfb262104cc83b0a8e4c", size = 3047726, upload-time = "2025-04-03T15:09:33.521Z" }, + { url = "https://files.pythonhosted.org/packages/ae/21/fbf9b45d6af91c5ce32df4007886c0332b977558cba34b0bc00b98ebc188/blis-1.3.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:69584589977366366cd99cc7cb23a76a814df8bcae8b777fde4a94e8684c1fb8", size = 3249935, upload-time = "2025-04-03T15:09:36.264Z" }, + { url = "https://files.pythonhosted.org/packages/ee/b1/5716b8cd784c0a0d08f9b3773c8eb4c37f5f9ed3a9f6ef961373e123b1cf/blis-1.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b2adc4549e610b59e8db5a57ab7206e4ac1502ac5b261ed0e6de42d3fb311d5", size = 11614296, upload-time = "2025-04-03T15:09:38.342Z" }, + { url = "https://files.pythonhosted.org/packages/36/0f/e2ed2642cf41dcae3431cfbcd94543646adba46eaa2736ac27647216e4f7/blis-1.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9aaa84df638e0bb7909a35e3c220168df2b90f267967b3004a88f57b49fbe4ec", size = 3063082, upload-time = "2025-04-03T15:09:40.329Z" }, + { url = "https://files.pythonhosted.org/packages/cb/f0/627a36b99a9cd9af73be7bb451d6884d5b4aece297eb29b9fc13e70c1f2b/blis-1.3.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0da7b54331bed31aa55839da2d0e5451447e1f5e8a9367cce7ff1fb27498a22a", size = 4290919, upload-time = "2025-04-03T15:09:41.845Z" }, + { url = "https://files.pythonhosted.org/packages/5b/f9/a415707185a82082b96ab857e5c3b7a59b0ad73ed04ace1cbb64835c3432/blis-1.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:682175bf2d047129b3715e3f1305c6b23a45e2ce24c4b1d0fa2eb03eb877edd4", size = 14795975, upload-time = "2025-04-03T15:09:43.611Z" }, + { url = "https://files.pythonhosted.org/packages/16/f1/8cc8118946dbb9cbd74f406d30d31ee8d2f723f6fb4c8245e2bc67175fd4/blis-1.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:91de2baf03da3a173cf62771f1d6b9236a27a8cbd0e0033be198f06ef6224986", size = 6258624, upload-time = "2025-04-03T15:09:46.056Z" }, +] + +[[package]] +name = "catalogue" +version = "2.0.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/38/b4/244d58127e1cdf04cf2dc7d9566f0d24ef01d5ce21811bab088ecc62b5ea/catalogue-2.0.10.tar.gz", hash = "sha256:4f56daa940913d3f09d589c191c74e5a6d51762b3a9e37dd53b7437afd6cda15", size = 19561, upload-time = "2023-09-25T06:29:24.962Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/96/d32b941a501ab566a16358d68b6eb4e4acc373fab3c3c4d7d9e649f7b4bb/catalogue-2.0.10-py3-none-any.whl", hash = "sha256:58c2de0020aa90f4a2da7dfad161bf7b3b054c86a5f09fcedc0b2b740c109a9f", size = 17325, upload-time = "2023-09-25T06:29:23.337Z" }, +] + +[[package]] +name = "certifi" +version = "2025.6.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/73/f7/f14b46d4bcd21092d7d3ccef689615220d8a08fb25e564b65d20738e672e/certifi-2025.6.15.tar.gz", hash = "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b", size = 158753, upload-time = "2025-06-15T02:45:51.329Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/ae/320161bd181fc06471eed047ecce67b693fd7515b16d495d8932db763426/certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057", size = 157650, upload-time = "2025-06-15T02:45:49.977Z" }, +] + +[[package]] +name = "chardet" +version = "5.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/f7b6ab21ec75897ed80c17d79b15951a719226b9fababf1e40ea74d69079/chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7", size = 2069618, upload-time = "2023-08-01T19:23:02.662Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/6f/f5fbc992a329ee4e0f288c1fe0e2ad9485ed064cac731ed2fe47dcc38cbf/chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970", size = 199385, upload-time = "2023-08-01T19:23:00.661Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, + { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, + { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, + { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, + { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, + { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, + { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, + { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, + { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, + { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, + { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, + { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, + { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, + { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, + { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, + { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, + { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, + { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, + { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, + { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, + { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, + { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, + { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, + { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, +] + +[[package]] +name = "click" +version = "8.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, +] + +[[package]] +name = "cloudpathlib" +version = "0.21.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/15/ae3256348834b92b9594d73eb7230538bae2bf726c2d721b920a668017c5/cloudpathlib-0.21.1.tar.gz", hash = "sha256:f26a855abf34d98f267aafd15efdb2db3c9665913dbabe5fad079df92837a431", size = 45295, upload-time = "2025-05-15T02:32:05.42Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/e7/6fea57b887f8e367c1e4a496ba03bfaf57824b766f777723ce1faf28834b/cloudpathlib-0.21.1-py3-none-any.whl", hash = "sha256:bfe580ad72ec030472ec233cd7380701b2d3227da7b2898387bd170aa70c803c", size = 52776, upload-time = "2025-05-15T02:32:03.99Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "columnize" +version = "0.3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/ab/47c68ccca6052e18ccce562e6af92404b08cb2715edd9e9da31f4118cbcd/columnize-0.3.11.tar.gz", hash = "sha256:a631b863b310a6c1457629b7bf32a3777ea5a407f8985311ce8c24c31d1d8bb2", size = 18776, upload-time = "2022-03-12T01:17:02.747Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/da/c1f84838ecb3e84c3c85c691f51f1e2dfcf7be745761d9f6462c909175ad/columnize-0.3.11-py3-none-any.whl", hash = "sha256:3c474ca632e474df8f012843fb74a4c0bea4e8876d49a17f952f8e82f14cbc3d", size = 7750, upload-time = "2022-03-12T01:15:38.843Z" }, + { url = "https://files.pythonhosted.org/packages/dd/c3/9902c94a4835f64d7151c6c5c40687de1438f37118d9270d15b5a2212ab1/columnize-0.3.11-py36-none-any.whl", hash = "sha256:10dc33cbe4ef96b777b407a96078cea2b195acfaf86592357e318180c81a83d9", size = 7728, upload-time = "2022-03-12T01:15:37.727Z" }, +] + +[[package]] +name = "confection" +version = "0.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "srsly" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/51/d3/57c6631159a1b48d273b40865c315cf51f89df7a9d1101094ef12e3a37c2/confection-0.1.5.tar.gz", hash = "sha256:8e72dd3ca6bd4f48913cd220f10b8275978e740411654b6e8ca6d7008c590f0e", size = 38924, upload-time = "2024-05-31T16:17:01.559Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/00/3106b1854b45bd0474ced037dfe6b73b90fe68a68968cef47c23de3d43d2/confection-0.1.5-py3-none-any.whl", hash = "sha256:e29d3c3f8eac06b3f77eb9dfb4bf2fc6bcc9622a98ca00a698e3d019c6430b14", size = 35451, upload-time = "2024-05-31T16:16:59.075Z" }, +] + +[[package]] +name = "contourpy" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/f7/44785876384eff370c251d58fd65f6ad7f39adce4a093c934d4a67a7c6b6/contourpy-1.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4caf2bcd2969402bf77edc4cb6034c7dd7c0803213b3523f111eb7460a51b8d2", size = 271580, upload-time = "2025-04-15T17:37:03.105Z" }, + { url = "https://files.pythonhosted.org/packages/93/3b/0004767622a9826ea3d95f0e9d98cd8729015768075d61f9fea8eeca42a8/contourpy-1.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:82199cb78276249796419fe36b7386bd8d2cc3f28b3bc19fe2454fe2e26c4c15", size = 255530, upload-time = "2025-04-15T17:37:07.026Z" }, + { url = "https://files.pythonhosted.org/packages/e7/bb/7bd49e1f4fa805772d9fd130e0d375554ebc771ed7172f48dfcd4ca61549/contourpy-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106fab697af11456fcba3e352ad50effe493a90f893fca6c2ca5c033820cea92", size = 307688, upload-time = "2025-04-15T17:37:11.481Z" }, + { url = "https://files.pythonhosted.org/packages/fc/97/e1d5dbbfa170725ef78357a9a0edc996b09ae4af170927ba8ce977e60a5f/contourpy-1.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d14f12932a8d620e307f715857107b1d1845cc44fdb5da2bc8e850f5ceba9f87", size = 347331, upload-time = "2025-04-15T17:37:18.212Z" }, + { url = "https://files.pythonhosted.org/packages/6f/66/e69e6e904f5ecf6901be3dd16e7e54d41b6ec6ae3405a535286d4418ffb4/contourpy-1.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:532fd26e715560721bb0d5fc7610fce279b3699b018600ab999d1be895b09415", size = 318963, upload-time = "2025-04-15T17:37:22.76Z" }, + { url = "https://files.pythonhosted.org/packages/a8/32/b8a1c8965e4f72482ff2d1ac2cd670ce0b542f203c8e1d34e7c3e6925da7/contourpy-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b383144cf2d2c29f01a1e8170f50dacf0eac02d64139dcd709a8ac4eb3cfe", size = 323681, upload-time = "2025-04-15T17:37:33.001Z" }, + { url = "https://files.pythonhosted.org/packages/30/c6/12a7e6811d08757c7162a541ca4c5c6a34c0f4e98ef2b338791093518e40/contourpy-1.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c49f73e61f1f774650a55d221803b101d966ca0c5a2d6d5e4320ec3997489441", size = 1308674, upload-time = "2025-04-15T17:37:48.64Z" }, + { url = "https://files.pythonhosted.org/packages/2a/8a/bebe5a3f68b484d3a2b8ffaf84704b3e343ef1addea528132ef148e22b3b/contourpy-1.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d80b2c0300583228ac98d0a927a1ba6a2ba6b8a742463c564f1d419ee5b211e", size = 1380480, upload-time = "2025-04-15T17:38:06.7Z" }, + { url = "https://files.pythonhosted.org/packages/34/db/fcd325f19b5978fb509a7d55e06d99f5f856294c1991097534360b307cf1/contourpy-1.3.2-cp312-cp312-win32.whl", hash = "sha256:90df94c89a91b7362e1142cbee7568f86514412ab8a2c0d0fca72d7e91b62912", size = 178489, upload-time = "2025-04-15T17:38:10.338Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/fadd0b92ffa7b5eb5949bf340a63a4a496a6930a6c37a7ba0f12acb076d6/contourpy-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c942a01d9163e2e5cfb05cb66110121b8d07ad438a17f9e766317bcb62abf73", size = 223042, upload-time = "2025-04-15T17:38:14.239Z" }, + { url = "https://files.pythonhosted.org/packages/2e/61/5673f7e364b31e4e7ef6f61a4b5121c5f170f941895912f773d95270f3a2/contourpy-1.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:de39db2604ae755316cb5967728f4bea92685884b1e767b7c24e983ef5f771cb", size = 271630, upload-time = "2025-04-15T17:38:19.142Z" }, + { url = "https://files.pythonhosted.org/packages/ff/66/a40badddd1223822c95798c55292844b7e871e50f6bfd9f158cb25e0bd39/contourpy-1.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3f9e896f447c5c8618f1edb2bafa9a4030f22a575ec418ad70611450720b5b08", size = 255670, upload-time = "2025-04-15T17:38:23.688Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c7/cf9fdee8200805c9bc3b148f49cb9482a4e3ea2719e772602a425c9b09f8/contourpy-1.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71e2bd4a1c4188f5c2b8d274da78faab884b59df20df63c34f74aa1813c4427c", size = 306694, upload-time = "2025-04-15T17:38:28.238Z" }, + { url = "https://files.pythonhosted.org/packages/dd/e7/ccb9bec80e1ba121efbffad7f38021021cda5be87532ec16fd96533bb2e0/contourpy-1.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de425af81b6cea33101ae95ece1f696af39446db9682a0b56daaa48cfc29f38f", size = 345986, upload-time = "2025-04-15T17:38:33.502Z" }, + { url = "https://files.pythonhosted.org/packages/dc/49/ca13bb2da90391fa4219fdb23b078d6065ada886658ac7818e5441448b78/contourpy-1.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:977e98a0e0480d3fe292246417239d2d45435904afd6d7332d8455981c408b85", size = 318060, upload-time = "2025-04-15T17:38:38.672Z" }, + { url = "https://files.pythonhosted.org/packages/c8/65/5245ce8c548a8422236c13ffcdcdada6a2a812c361e9e0c70548bb40b661/contourpy-1.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:434f0adf84911c924519d2b08fc10491dd282b20bdd3fa8f60fd816ea0b48841", size = 322747, upload-time = "2025-04-15T17:38:43.712Z" }, + { url = "https://files.pythonhosted.org/packages/72/30/669b8eb48e0a01c660ead3752a25b44fdb2e5ebc13a55782f639170772f9/contourpy-1.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c66c4906cdbc50e9cba65978823e6e00b45682eb09adbb78c9775b74eb222422", size = 1308895, upload-time = "2025-04-15T17:39:00.224Z" }, + { url = "https://files.pythonhosted.org/packages/05/5a/b569f4250decee6e8d54498be7bdf29021a4c256e77fe8138c8319ef8eb3/contourpy-1.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8b7fc0cd78ba2f4695fd0a6ad81a19e7e3ab825c31b577f384aa9d7817dc3bef", size = 1379098, upload-time = "2025-04-15T17:43:29.649Z" }, + { url = "https://files.pythonhosted.org/packages/19/ba/b227c3886d120e60e41b28740ac3617b2f2b971b9f601c835661194579f1/contourpy-1.3.2-cp313-cp313-win32.whl", hash = "sha256:15ce6ab60957ca74cff444fe66d9045c1fd3e92c8936894ebd1f3eef2fff075f", size = 178535, upload-time = "2025-04-15T17:44:44.532Z" }, + { url = "https://files.pythonhosted.org/packages/12/6e/2fed56cd47ca739b43e892707ae9a13790a486a3173be063681ca67d2262/contourpy-1.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e1578f7eafce927b168752ed7e22646dad6cd9bca673c60bff55889fa236ebf9", size = 223096, upload-time = "2025-04-15T17:44:48.194Z" }, + { url = "https://files.pythonhosted.org/packages/54/4c/e76fe2a03014a7c767d79ea35c86a747e9325537a8b7627e0e5b3ba266b4/contourpy-1.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0475b1f6604896bc7c53bb070e355e9321e1bc0d381735421a2d2068ec56531f", size = 285090, upload-time = "2025-04-15T17:43:34.084Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e2/5aba47debd55d668e00baf9651b721e7733975dc9fc27264a62b0dd26eb8/contourpy-1.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c85bb486e9be652314bb5b9e2e3b0d1b2e643d5eec4992c0fbe8ac71775da739", size = 268643, upload-time = "2025-04-15T17:43:38.626Z" }, + { url = "https://files.pythonhosted.org/packages/a1/37/cd45f1f051fe6230f751cc5cdd2728bb3a203f5619510ef11e732109593c/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:745b57db7758f3ffc05a10254edd3182a2a83402a89c00957a8e8a22f5582823", size = 310443, upload-time = "2025-04-15T17:43:44.522Z" }, + { url = "https://files.pythonhosted.org/packages/8b/a2/36ea6140c306c9ff6dd38e3bcec80b3b018474ef4d17eb68ceecd26675f4/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:970e9173dbd7eba9b4e01aab19215a48ee5dd3f43cef736eebde064a171f89a5", size = 349865, upload-time = "2025-04-15T17:43:49.545Z" }, + { url = "https://files.pythonhosted.org/packages/95/b7/2fc76bc539693180488f7b6cc518da7acbbb9e3b931fd9280504128bf956/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6c4639a9c22230276b7bffb6a850dfc8258a2521305e1faefe804d006b2e532", size = 321162, upload-time = "2025-04-15T17:43:54.203Z" }, + { url = "https://files.pythonhosted.org/packages/f4/10/76d4f778458b0aa83f96e59d65ece72a060bacb20cfbee46cf6cd5ceba41/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc829960f34ba36aad4302e78eabf3ef16a3a100863f0d4eeddf30e8a485a03b", size = 327355, upload-time = "2025-04-15T17:44:01.025Z" }, + { url = "https://files.pythonhosted.org/packages/43/a3/10cf483ea683f9f8ab096c24bad3cce20e0d1dd9a4baa0e2093c1c962d9d/contourpy-1.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d32530b534e986374fc19eaa77fcb87e8a99e5431499949b828312bdcd20ac52", size = 1307935, upload-time = "2025-04-15T17:44:17.322Z" }, + { url = "https://files.pythonhosted.org/packages/78/73/69dd9a024444489e22d86108e7b913f3528f56cfc312b5c5727a44188471/contourpy-1.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e298e7e70cf4eb179cc1077be1c725b5fd131ebc81181bf0c03525c8abc297fd", size = 1372168, upload-time = "2025-04-15T17:44:33.43Z" }, + { url = "https://files.pythonhosted.org/packages/0f/1b/96d586ccf1b1a9d2004dd519b25fbf104a11589abfd05484ff12199cca21/contourpy-1.3.2-cp313-cp313t-win32.whl", hash = "sha256:d0e589ae0d55204991450bb5c23f571c64fe43adaa53f93fc902a84c96f52fe1", size = 189550, upload-time = "2025-04-15T17:44:37.092Z" }, + { url = "https://files.pythonhosted.org/packages/b0/e6/6000d0094e8a5e32ad62591c8609e269febb6e4db83a1c75ff8868b42731/contourpy-1.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:78e9253c3de756b3f6a5174d024c4835acd59eb3f8e2ca13e775dbffe1558f69", size = 238214, upload-time = "2025-04-15T17:44:40.827Z" }, +] + +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, +] + +[[package]] +name = "cymem" +version = "2.0.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/4a/1acd761fb6ac4c560e823ce40536a62f886f2d59b2763b5c3fc7e9d92101/cymem-2.0.11.tar.gz", hash = "sha256:efe49a349d4a518be6b6c6b255d4a80f740a341544bde1a807707c058b88d0bd", size = 10346, upload-time = "2025-01-16T21:50:41.045Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/67/0d74f7e9d79f934368a78fb1d1466b94bebdbff14f8ae94dd3e4ea8738bb/cymem-2.0.11-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a0fbe19ce653cd688842d81e5819dc63f911a26e192ef30b0b89f0ab2b192ff2", size = 42621, upload-time = "2025-01-16T21:49:46.585Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d6/f7a19c63b48efc3f00a3ee8d69070ac90202e1e378f6cf81b8671f0cf762/cymem-2.0.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de72101dc0e6326f6a2f73e05a438d1f3c6110d41044236d0fbe62925091267d", size = 42249, upload-time = "2025-01-16T21:49:48.973Z" }, + { url = "https://files.pythonhosted.org/packages/d7/60/cdc434239813eef547fb99b6d0bafe31178501702df9b77c4108c9a216f6/cymem-2.0.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee4395917f6588b8ac1699499128842768b391fe8896e8626950b4da5f9a406", size = 224758, upload-time = "2025-01-16T21:49:51.382Z" }, + { url = "https://files.pythonhosted.org/packages/1d/68/8fa6efae17cd3b2ba9a2f83b824867c5b65b06f7aec3f8a0d0cabdeffb9b/cymem-2.0.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b02f2b17d760dc3fe5812737b1ce4f684641cdd751d67761d333a3b5ea97b83", size = 227995, upload-time = "2025-01-16T21:49:54.538Z" }, + { url = "https://files.pythonhosted.org/packages/e4/f3/ceda70bf6447880140602285b7c6fa171cb7c78b623d35345cc32505cd06/cymem-2.0.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:04ee6b4041ddec24512d6e969ed6445e57917f01e73b9dabbe17b7e6b27fef05", size = 215325, upload-time = "2025-01-16T21:49:57.229Z" }, + { url = "https://files.pythonhosted.org/packages/d3/47/6915eaa521e1ce7a0ba480eecb6870cb4f681bcd64ced88c2f0ed7a744b4/cymem-2.0.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e1048dae7e627ee25f22c87bb670b13e06bc0aecc114b89b959a798d487d1bf4", size = 216447, upload-time = "2025-01-16T21:50:00.432Z" }, + { url = "https://files.pythonhosted.org/packages/7b/be/8e02bdd31e557f642741a06c8e886782ef78f0b00daffd681922dc9bbc88/cymem-2.0.11-cp312-cp312-win_amd64.whl", hash = "sha256:0c269c7a867d74adeb9db65fa1d226342aacf44d64b7931282f0b0eb22eb6275", size = 39283, upload-time = "2025-01-16T21:50:03.384Z" }, + { url = "https://files.pythonhosted.org/packages/bd/90/b064e2677e27a35cf3605146abc3285d4f599cc1b6c18fc445ae876dd1e3/cymem-2.0.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4a311c82f743275c84f708df89ac5bf60ddefe4713d532000c887931e22941f", size = 42389, upload-time = "2025-01-16T21:50:05.925Z" }, + { url = "https://files.pythonhosted.org/packages/fd/60/7aa0561a6c1f0d42643b02c4fdeb2a16181b0ff4e85d73d2d80c6689e92a/cymem-2.0.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:02ed92bead896cca36abad00502b14fa651bdf5d8319461126a2d5ac8c9674c5", size = 41948, upload-time = "2025-01-16T21:50:08.375Z" }, + { url = "https://files.pythonhosted.org/packages/5f/4e/88a29cc5575374982e527b4ebcab3781bdc826ce693c6418a0f836544246/cymem-2.0.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44ddd3588379f8f376116384af99e3fb5f90091d90f520c341942618bf22f05e", size = 219382, upload-time = "2025-01-16T21:50:13.089Z" }, + { url = "https://files.pythonhosted.org/packages/9b/3a/8f96e167e93b7f7ec105ed7b25c77bbf215d15bcbf4a24082cdc12234cd6/cymem-2.0.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87ec985623624bbd298762d8163fc194a096cb13282731a017e09ff8a60bb8b1", size = 222974, upload-time = "2025-01-16T21:50:17.969Z" }, + { url = "https://files.pythonhosted.org/packages/6a/fc/ce016bb0c66a4776345fac7508fddec3b739b9dd4363094ac89cce048832/cymem-2.0.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3385a47285435848e0ed66cfd29b35f3ed8703218e2b17bd7a0c053822f26bf", size = 213426, upload-time = "2025-01-16T21:50:19.349Z" }, + { url = "https://files.pythonhosted.org/packages/5c/c8/accf7cc768f751447a5050b14a195af46798bc22767ac25f49b02861b1eb/cymem-2.0.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5461e65340d6572eb64deadce79242a446a1d39cb7bf70fe7b7e007eb0d799b0", size = 219195, upload-time = "2025-01-16T21:50:21.407Z" }, + { url = "https://files.pythonhosted.org/packages/74/65/c162fbac63e867a055240b6600b92ef96c0eb7a1895312ac53c4be93d056/cymem-2.0.11-cp313-cp313-win_amd64.whl", hash = "sha256:25da111adf425c29af0cfd9fecfec1c71c8d82e2244a85166830a0817a66ada7", size = 39090, upload-time = "2025-01-16T21:50:24.239Z" }, +] + +[[package]] +name = "decorator" +version = "5.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, +] + +[[package]] +name = "django" +version = "5.2.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asgiref" }, + { name = "sqlparse" }, + { name = "tzdata", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c6/af/77b403926025dc6f7fd7b31256394d643469418965eb528eab45d0505358/django-5.2.3.tar.gz", hash = "sha256:335213277666ab2c5cac44a792a6d2f3d58eb79a80c14b6b160cd4afc3b75684", size = 10850303, upload-time = "2025-06-10T10:14:05.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/11/7aff961db37e1ea501a2bb663d27a8ce97f3683b9e5b83d3bfead8b86fa4/django-5.2.3-py3-none-any.whl", hash = "sha256:c517a6334e0fd940066aa9467b29401b93c37cec2e61365d663b80922542069d", size = 8301935, upload-time = "2025-06-10T10:13:58.993Z" }, +] + +[[package]] +name = "executing" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/50/a9d80c47ff289c611ff12e63f7c5d13942c65d68125160cefd768c73e6e4/executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755", size = 978693, upload-time = "2025-01-22T15:41:29.403Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa", size = 26702, upload-time = "2025-01-22T15:41:25.929Z" }, +] + +[[package]] +name = "flexcache" +version = "0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/b0/8a21e330561c65653d010ef112bf38f60890051d244ede197ddaa08e50c1/flexcache-0.3.tar.gz", hash = "sha256:18743bd5a0621bfe2cf8d519e4c3bfdf57a269c15d1ced3fb4b64e0ff4600656", size = 15816, upload-time = "2024-03-09T03:21:07.555Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/cd/c883e1a7c447479d6e13985565080e3fea88ab5a107c21684c813dba1875/flexcache-0.3-py3-none-any.whl", hash = "sha256:d43c9fea82336af6e0115e308d9d33a185390b8346a017564611f1466dcd2e32", size = 13263, upload-time = "2024-03-09T03:21:05.635Z" }, +] + +[[package]] +name = "flexparser" +version = "0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/82/99/b4de7e39e8eaf8207ba1a8fa2241dd98b2ba72ae6e16960d8351736d8702/flexparser-0.4.tar.gz", hash = "sha256:266d98905595be2ccc5da964fe0a2c3526fbbffdc45b65b3146d75db992ef6b2", size = 31799, upload-time = "2024-11-07T02:00:56.249Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/5e/3be305568fe5f34448807976dc82fc151d76c3e0e03958f34770286278c1/flexparser-0.4-py3-none-any.whl", hash = "sha256:3738b456192dcb3e15620f324c447721023c0293f6af9955b481e91d00179846", size = 27625, upload-time = "2024-11-07T02:00:54.523Z" }, +] + +[[package]] +name = "fonttools" +version = "4.58.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/5a/1124b2c8cb3a8015faf552e92714040bcdbc145dfa29928891b02d147a18/fonttools-4.58.4.tar.gz", hash = "sha256:928a8009b9884ed3aae17724b960987575155ca23c6f0b8146e400cc9e0d44ba", size = 3525026, upload-time = "2025-06-13T17:25:15.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/3c/1d1792bfe91ef46f22a3d23b4deb514c325e73c17d4f196b385b5e2faf1c/fonttools-4.58.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:462211c0f37a278494e74267a994f6be9a2023d0557aaa9ecbcbfce0f403b5a6", size = 2754082, upload-time = "2025-06-13T17:24:24.862Z" }, + { url = "https://files.pythonhosted.org/packages/2a/1f/2b261689c901a1c3bc57a6690b0b9fc21a9a93a8b0c83aae911d3149f34e/fonttools-4.58.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0c7a12fb6f769165547f00fcaa8d0df9517603ae7e04b625e5acb8639809b82d", size = 2321677, upload-time = "2025-06-13T17:24:26.815Z" }, + { url = "https://files.pythonhosted.org/packages/fe/6b/4607add1755a1e6581ae1fc0c9a640648e0d9cdd6591cc2d581c2e07b8c3/fonttools-4.58.4-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2d42c63020a922154add0a326388a60a55504629edc3274bc273cd3806b4659f", size = 4896354, upload-time = "2025-06-13T17:24:28.428Z" }, + { url = "https://files.pythonhosted.org/packages/cd/95/34b4f483643d0cb11a1f830b72c03fdd18dbd3792d77a2eb2e130a96fada/fonttools-4.58.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f2b4e6fd45edc6805f5f2c355590b092ffc7e10a945bd6a569fc66c1d2ae7aa", size = 4941633, upload-time = "2025-06-13T17:24:30.568Z" }, + { url = "https://files.pythonhosted.org/packages/81/ac/9bafbdb7694059c960de523e643fa5a61dd2f698f3f72c0ca18ae99257c7/fonttools-4.58.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f155b927f6efb1213a79334e4cb9904d1e18973376ffc17a0d7cd43d31981f1e", size = 4886170, upload-time = "2025-06-13T17:24:32.724Z" }, + { url = "https://files.pythonhosted.org/packages/ae/44/a3a3b70d5709405f7525bb7cb497b4e46151e0c02e3c8a0e40e5e9fe030b/fonttools-4.58.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e38f687d5de97c7fb7da3e58169fb5ba349e464e141f83c3c2e2beb91d317816", size = 5037851, upload-time = "2025-06-13T17:24:35.034Z" }, + { url = "https://files.pythonhosted.org/packages/21/cb/e8923d197c78969454eb876a4a55a07b59c9c4c46598f02b02411dc3b45c/fonttools-4.58.4-cp312-cp312-win32.whl", hash = "sha256:636c073b4da9db053aa683db99580cac0f7c213a953b678f69acbca3443c12cc", size = 2187428, upload-time = "2025-06-13T17:24:36.996Z" }, + { url = "https://files.pythonhosted.org/packages/46/e6/fe50183b1a0e1018e7487ee740fa8bb127b9f5075a41e20d017201e8ab14/fonttools-4.58.4-cp312-cp312-win_amd64.whl", hash = "sha256:82e8470535743409b30913ba2822e20077acf9ea70acec40b10fcf5671dceb58", size = 2236649, upload-time = "2025-06-13T17:24:38.985Z" }, + { url = "https://files.pythonhosted.org/packages/d4/4f/c05cab5fc1a4293e6bc535c6cb272607155a0517700f5418a4165b7f9ec8/fonttools-4.58.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5f4a64846495c543796fa59b90b7a7a9dff6839bd852741ab35a71994d685c6d", size = 2745197, upload-time = "2025-06-13T17:24:40.645Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d3/49211b1f96ae49308f4f78ca7664742377a6867f00f704cdb31b57e4b432/fonttools-4.58.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e80661793a5d4d7ad132a2aa1eae2e160fbdbb50831a0edf37c7c63b2ed36574", size = 2317272, upload-time = "2025-06-13T17:24:43.428Z" }, + { url = "https://files.pythonhosted.org/packages/b2/11/c9972e46a6abd752a40a46960e431c795ad1f306775fc1f9e8c3081a1274/fonttools-4.58.4-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fe5807fc64e4ba5130f1974c045a6e8d795f3b7fb6debfa511d1773290dbb76b", size = 4877184, upload-time = "2025-06-13T17:24:45.527Z" }, + { url = "https://files.pythonhosted.org/packages/ea/24/5017c01c9ef8df572cc9eaf9f12be83ad8ed722ff6dc67991d3d752956e4/fonttools-4.58.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b610b9bef841cb8f4b50472494158b1e347d15cad56eac414c722eda695a6cfd", size = 4939445, upload-time = "2025-06-13T17:24:47.647Z" }, + { url = "https://files.pythonhosted.org/packages/79/b0/538cc4d0284b5a8826b4abed93a69db52e358525d4b55c47c8cef3669767/fonttools-4.58.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2daa7f0e213c38f05f054eb5e1730bd0424aebddbeac094489ea1585807dd187", size = 4878800, upload-time = "2025-06-13T17:24:49.766Z" }, + { url = "https://files.pythonhosted.org/packages/5a/9b/a891446b7a8250e65bffceb248508587958a94db467ffd33972723ab86c9/fonttools-4.58.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:66cccb6c0b944496b7f26450e9a66e997739c513ffaac728d24930df2fd9d35b", size = 5021259, upload-time = "2025-06-13T17:24:51.754Z" }, + { url = "https://files.pythonhosted.org/packages/17/b2/c4d2872cff3ace3ddd1388bf15b76a1d8d5313f0a61f234e9aed287e674d/fonttools-4.58.4-cp313-cp313-win32.whl", hash = "sha256:94d2aebb5ca59a5107825520fde596e344652c1f18170ef01dacbe48fa60c889", size = 2185824, upload-time = "2025-06-13T17:24:54.324Z" }, + { url = "https://files.pythonhosted.org/packages/98/57/cddf8bcc911d4f47dfca1956c1e3aeeb9f7c9b8e88b2a312fe8c22714e0b/fonttools-4.58.4-cp313-cp313-win_amd64.whl", hash = "sha256:b554bd6e80bba582fd326ddab296e563c20c64dca816d5e30489760e0c41529f", size = 2236382, upload-time = "2025-06-13T17:24:56.291Z" }, + { url = "https://files.pythonhosted.org/packages/0b/2f/c536b5b9bb3c071e91d536a4d11f969e911dbb6b227939f4c5b0bca090df/fonttools-4.58.4-py3-none-any.whl", hash = "sha256:a10ce13a13f26cbb9f37512a4346bb437ad7e002ff6fa966a7ce7ff5ac3528bd", size = 1114660, upload-time = "2025-06-13T17:25:13.321Z" }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, +] + +[[package]] +name = "ipython" +version = "9.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "decorator" }, + { name = "ipython-pygments-lexers" }, + { name = "jedi" }, + { name = "matplotlib-inline" }, + { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit" }, + { name = "pygments" }, + { name = "stack-data" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dc/09/4c7e06b96fbd203e06567b60fb41b06db606b6a82db6db7b2c85bb72a15c/ipython-9.3.0.tar.gz", hash = "sha256:79eb896f9f23f50ad16c3bc205f686f6e030ad246cc309c6279a242b14afe9d8", size = 4426460, upload-time = "2025-05-31T16:34:55.678Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/99/9ed3d52d00f1846679e3aa12e2326ac7044b5e7f90dc822b60115fa533ca/ipython-9.3.0-py3-none-any.whl", hash = "sha256:1a0b6dd9221a1f5dddf725b57ac0cb6fddc7b5f470576231ae9162b9b3455a04", size = 605320, upload-time = "2025-05-31T16:34:52.154Z" }, +] + +[[package]] +name = "ipython-pygments-lexers" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" }, +] + +[[package]] +name = "jedi" +version = "0.19.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parso" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "joblib" +version = "1.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/fe/0f5a938c54105553436dbff7a61dc4fed4b1b2c98852f8833beaf4d5968f/joblib-1.5.1.tar.gz", hash = "sha256:f4f86e351f39fe3d0d32a9f2c3d8af1ee4cec285aafcb27003dda5205576b444", size = 330475, upload-time = "2025-05-23T12:04:37.097Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/4f/1195bbac8e0c2acc5f740661631d8d750dc38d4a32b23ee5df3cde6f4e0d/joblib-1.5.1-py3-none-any.whl", hash = "sha256:4719a31f054c7d766948dcd83e9613686b27114f190f717cec7eaa2084f8a74a", size = 307746, upload-time = "2025-05-23T12:04:35.124Z" }, +] + +[[package]] +name = "kiwisolver" +version = "1.4.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/59/7c91426a8ac292e1cdd53a63b6d9439abd573c875c3f92c146767dd33faf/kiwisolver-1.4.8.tar.gz", hash = "sha256:23d5f023bdc8c7e54eb65f03ca5d5bb25b601eac4d7f1a042888a1f45237987e", size = 97538, upload-time = "2024-12-24T18:30:51.519Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/aa/cea685c4ab647f349c3bc92d2daf7ae34c8e8cf405a6dcd3a497f58a2ac3/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6af5e8815fd02997cb6ad9bbed0ee1e60014438ee1a5c2444c96f87b8843502", size = 124152, upload-time = "2024-12-24T18:29:16.85Z" }, + { url = "https://files.pythonhosted.org/packages/c5/0b/8db6d2e2452d60d5ebc4ce4b204feeb16176a851fd42462f66ade6808084/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bade438f86e21d91e0cf5dd7c0ed00cda0f77c8c1616bd83f9fc157fa6760d31", size = 66555, upload-time = "2024-12-24T18:29:19.146Z" }, + { url = "https://files.pythonhosted.org/packages/60/26/d6a0db6785dd35d3ba5bf2b2df0aedc5af089962c6eb2cbf67a15b81369e/kiwisolver-1.4.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b83dc6769ddbc57613280118fb4ce3cd08899cc3369f7d0e0fab518a7cf37fdb", size = 65067, upload-time = "2024-12-24T18:29:20.096Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ed/1d97f7e3561e09757a196231edccc1bcf59d55ddccefa2afc9c615abd8e0/kiwisolver-1.4.8-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111793b232842991be367ed828076b03d96202c19221b5ebab421ce8bcad016f", size = 1378443, upload-time = "2024-12-24T18:29:22.843Z" }, + { url = "https://files.pythonhosted.org/packages/29/61/39d30b99954e6b46f760e6289c12fede2ab96a254c443639052d1b573fbc/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:257af1622860e51b1a9d0ce387bf5c2c4f36a90594cb9514f55b074bcc787cfc", size = 1472728, upload-time = "2024-12-24T18:29:24.463Z" }, + { url = "https://files.pythonhosted.org/packages/0c/3e/804163b932f7603ef256e4a715e5843a9600802bb23a68b4e08c8c0ff61d/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b5637c3f316cab1ec1c9a12b8c5f4750a4c4b71af9157645bf32830e39c03a", size = 1478388, upload-time = "2024-12-24T18:29:25.776Z" }, + { url = "https://files.pythonhosted.org/packages/8a/9e/60eaa75169a154700be74f875a4d9961b11ba048bef315fbe89cb6999056/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:782bb86f245ec18009890e7cb8d13a5ef54dcf2ebe18ed65f795e635a96a1c6a", size = 1413849, upload-time = "2024-12-24T18:29:27.202Z" }, + { url = "https://files.pythonhosted.org/packages/bc/b3/9458adb9472e61a998c8c4d95cfdfec91c73c53a375b30b1428310f923e4/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc978a80a0db3a66d25767b03688f1147a69e6237175c0f4ffffaaedf744055a", size = 1475533, upload-time = "2024-12-24T18:29:28.638Z" }, + { url = "https://files.pythonhosted.org/packages/e4/7a/0a42d9571e35798de80aef4bb43a9b672aa7f8e58643d7bd1950398ffb0a/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:36dbbfd34838500a31f52c9786990d00150860e46cd5041386f217101350f0d3", size = 2268898, upload-time = "2024-12-24T18:29:30.368Z" }, + { url = "https://files.pythonhosted.org/packages/d9/07/1255dc8d80271400126ed8db35a1795b1a2c098ac3a72645075d06fe5c5d/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:eaa973f1e05131de5ff3569bbba7f5fd07ea0595d3870ed4a526d486fe57fa1b", size = 2425605, upload-time = "2024-12-24T18:29:33.151Z" }, + { url = "https://files.pythonhosted.org/packages/84/df/5a3b4cf13780ef6f6942df67b138b03b7e79e9f1f08f57c49957d5867f6e/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a66f60f8d0c87ab7f59b6fb80e642ebb29fec354a4dfad687ca4092ae69d04f4", size = 2375801, upload-time = "2024-12-24T18:29:34.584Z" }, + { url = "https://files.pythonhosted.org/packages/8f/10/2348d068e8b0f635c8c86892788dac7a6b5c0cb12356620ab575775aad89/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858416b7fb777a53f0c59ca08190ce24e9abbd3cffa18886a5781b8e3e26f65d", size = 2520077, upload-time = "2024-12-24T18:29:36.138Z" }, + { url = "https://files.pythonhosted.org/packages/32/d8/014b89fee5d4dce157d814303b0fce4d31385a2af4c41fed194b173b81ac/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:085940635c62697391baafaaeabdf3dd7a6c3643577dde337f4d66eba021b2b8", size = 2338410, upload-time = "2024-12-24T18:29:39.991Z" }, + { url = "https://files.pythonhosted.org/packages/bd/72/dfff0cc97f2a0776e1c9eb5bef1ddfd45f46246c6533b0191887a427bca5/kiwisolver-1.4.8-cp312-cp312-win_amd64.whl", hash = "sha256:01c3d31902c7db5fb6182832713d3b4122ad9317c2c5877d0539227d96bb2e50", size = 71853, upload-time = "2024-12-24T18:29:42.006Z" }, + { url = "https://files.pythonhosted.org/packages/dc/85/220d13d914485c0948a00f0b9eb419efaf6da81b7d72e88ce2391f7aed8d/kiwisolver-1.4.8-cp312-cp312-win_arm64.whl", hash = "sha256:a3c44cb68861de93f0c4a8175fbaa691f0aa22550c331fefef02b618a9dcb476", size = 65424, upload-time = "2024-12-24T18:29:44.38Z" }, + { url = "https://files.pythonhosted.org/packages/79/b3/e62464a652f4f8cd9006e13d07abad844a47df1e6537f73ddfbf1bc997ec/kiwisolver-1.4.8-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1c8ceb754339793c24aee1c9fb2485b5b1f5bb1c2c214ff13368431e51fc9a09", size = 124156, upload-time = "2024-12-24T18:29:45.368Z" }, + { url = "https://files.pythonhosted.org/packages/8d/2d/f13d06998b546a2ad4f48607a146e045bbe48030774de29f90bdc573df15/kiwisolver-1.4.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a62808ac74b5e55a04a408cda6156f986cefbcf0ada13572696b507cc92fa1", size = 66555, upload-time = "2024-12-24T18:29:46.37Z" }, + { url = "https://files.pythonhosted.org/packages/59/e3/b8bd14b0a54998a9fd1e8da591c60998dc003618cb19a3f94cb233ec1511/kiwisolver-1.4.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68269e60ee4929893aad82666821aaacbd455284124817af45c11e50a4b42e3c", size = 65071, upload-time = "2024-12-24T18:29:47.333Z" }, + { url = "https://files.pythonhosted.org/packages/f0/1c/6c86f6d85ffe4d0ce04228d976f00674f1df5dc893bf2dd4f1928748f187/kiwisolver-1.4.8-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34d142fba9c464bc3bbfeff15c96eab0e7310343d6aefb62a79d51421fcc5f1b", size = 1378053, upload-time = "2024-12-24T18:29:49.636Z" }, + { url = "https://files.pythonhosted.org/packages/4e/b9/1c6e9f6dcb103ac5cf87cb695845f5fa71379021500153566d8a8a9fc291/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc373e0eef45b59197de815b1b28ef89ae3955e7722cc9710fb91cd77b7f47", size = 1472278, upload-time = "2024-12-24T18:29:51.164Z" }, + { url = "https://files.pythonhosted.org/packages/ee/81/aca1eb176de671f8bda479b11acdc42c132b61a2ac861c883907dde6debb/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77e6f57a20b9bd4e1e2cedda4d0b986ebd0216236f0106e55c28aea3d3d69b16", size = 1478139, upload-time = "2024-12-24T18:29:52.594Z" }, + { url = "https://files.pythonhosted.org/packages/49/f4/e081522473671c97b2687d380e9e4c26f748a86363ce5af48b4a28e48d06/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08e77738ed7538f036cd1170cbed942ef749137b1311fa2bbe2a7fda2f6bf3cc", size = 1413517, upload-time = "2024-12-24T18:29:53.941Z" }, + { url = "https://files.pythonhosted.org/packages/8f/e9/6a7d025d8da8c4931522922cd706105aa32b3291d1add8c5427cdcd66e63/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5ce1e481a74b44dd5e92ff03ea0cb371ae7a0268318e202be06c8f04f4f1246", size = 1474952, upload-time = "2024-12-24T18:29:56.523Z" }, + { url = "https://files.pythonhosted.org/packages/82/13/13fa685ae167bee5d94b415991c4fc7bb0a1b6ebea6e753a87044b209678/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fc2ace710ba7c1dfd1a3b42530b62b9ceed115f19a1656adefce7b1782a37794", size = 2269132, upload-time = "2024-12-24T18:29:57.989Z" }, + { url = "https://files.pythonhosted.org/packages/ef/92/bb7c9395489b99a6cb41d502d3686bac692586db2045adc19e45ee64ed23/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3452046c37c7692bd52b0e752b87954ef86ee2224e624ef7ce6cb21e8c41cc1b", size = 2425997, upload-time = "2024-12-24T18:29:59.393Z" }, + { url = "https://files.pythonhosted.org/packages/ed/12/87f0e9271e2b63d35d0d8524954145837dd1a6c15b62a2d8c1ebe0f182b4/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7e9a60b50fe8b2ec6f448fe8d81b07e40141bfced7f896309df271a0b92f80f3", size = 2376060, upload-time = "2024-12-24T18:30:01.338Z" }, + { url = "https://files.pythonhosted.org/packages/02/6e/c8af39288edbce8bf0fa35dee427b082758a4b71e9c91ef18fa667782138/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:918139571133f366e8362fa4a297aeba86c7816b7ecf0bc79168080e2bd79957", size = 2520471, upload-time = "2024-12-24T18:30:04.574Z" }, + { url = "https://files.pythonhosted.org/packages/13/78/df381bc7b26e535c91469f77f16adcd073beb3e2dd25042efd064af82323/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e063ef9f89885a1d68dd8b2e18f5ead48653176d10a0e324e3b0030e3a69adeb", size = 2338793, upload-time = "2024-12-24T18:30:06.25Z" }, + { url = "https://files.pythonhosted.org/packages/d0/dc/c1abe38c37c071d0fc71c9a474fd0b9ede05d42f5a458d584619cfd2371a/kiwisolver-1.4.8-cp313-cp313-win_amd64.whl", hash = "sha256:a17b7c4f5b2c51bb68ed379defd608a03954a1845dfed7cc0117f1cc8a9b7fd2", size = 71855, upload-time = "2024-12-24T18:30:07.535Z" }, + { url = "https://files.pythonhosted.org/packages/a0/b6/21529d595b126ac298fdd90b705d87d4c5693de60023e0efcb4f387ed99e/kiwisolver-1.4.8-cp313-cp313-win_arm64.whl", hash = "sha256:3cd3bc628b25f74aedc6d374d5babf0166a92ff1317f46267f12d2ed54bc1d30", size = 65430, upload-time = "2024-12-24T18:30:08.504Z" }, + { url = "https://files.pythonhosted.org/packages/34/bd/b89380b7298e3af9b39f49334e3e2a4af0e04819789f04b43d560516c0c8/kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:370fd2df41660ed4e26b8c9d6bbcad668fbe2560462cba151a721d49e5b6628c", size = 126294, upload-time = "2024-12-24T18:30:09.508Z" }, + { url = "https://files.pythonhosted.org/packages/83/41/5857dc72e5e4148eaac5aa76e0703e594e4465f8ab7ec0fc60e3a9bb8fea/kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:84a2f830d42707de1d191b9490ac186bf7997a9495d4e9072210a1296345f7dc", size = 67736, upload-time = "2024-12-24T18:30:11.039Z" }, + { url = "https://files.pythonhosted.org/packages/e1/d1/be059b8db56ac270489fb0b3297fd1e53d195ba76e9bbb30e5401fa6b759/kiwisolver-1.4.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7a3ad337add5148cf51ce0b55642dc551c0b9d6248458a757f98796ca7348712", size = 66194, upload-time = "2024-12-24T18:30:14.886Z" }, + { url = "https://files.pythonhosted.org/packages/e1/83/4b73975f149819eb7dcf9299ed467eba068ecb16439a98990dcb12e63fdd/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7506488470f41169b86d8c9aeff587293f530a23a23a49d6bc64dab66bedc71e", size = 1465942, upload-time = "2024-12-24T18:30:18.927Z" }, + { url = "https://files.pythonhosted.org/packages/c7/2c/30a5cdde5102958e602c07466bce058b9d7cb48734aa7a4327261ac8e002/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f0121b07b356a22fb0414cec4666bbe36fd6d0d759db3d37228f496ed67c880", size = 1595341, upload-time = "2024-12-24T18:30:22.102Z" }, + { url = "https://files.pythonhosted.org/packages/ff/9b/1e71db1c000385aa069704f5990574b8244cce854ecd83119c19e83c9586/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d6d6bd87df62c27d4185de7c511c6248040afae67028a8a22012b010bc7ad062", size = 1598455, upload-time = "2024-12-24T18:30:24.947Z" }, + { url = "https://files.pythonhosted.org/packages/85/92/c8fec52ddf06231b31cbb779af77e99b8253cd96bd135250b9498144c78b/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:291331973c64bb9cce50bbe871fb2e675c4331dab4f31abe89f175ad7679a4d7", size = 1522138, upload-time = "2024-12-24T18:30:26.286Z" }, + { url = "https://files.pythonhosted.org/packages/0b/51/9eb7e2cd07a15d8bdd976f6190c0164f92ce1904e5c0c79198c4972926b7/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:893f5525bb92d3d735878ec00f781b2de998333659507d29ea4466208df37bed", size = 1582857, upload-time = "2024-12-24T18:30:28.86Z" }, + { url = "https://files.pythonhosted.org/packages/0f/95/c5a00387a5405e68ba32cc64af65ce881a39b98d73cc394b24143bebc5b8/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b47a465040146981dc9db8647981b8cb96366fbc8d452b031e4f8fdffec3f26d", size = 2293129, upload-time = "2024-12-24T18:30:30.34Z" }, + { url = "https://files.pythonhosted.org/packages/44/83/eeb7af7d706b8347548313fa3a3a15931f404533cc54fe01f39e830dd231/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:99cea8b9dd34ff80c521aef46a1dddb0dcc0283cf18bde6d756f1e6f31772165", size = 2421538, upload-time = "2024-12-24T18:30:33.334Z" }, + { url = "https://files.pythonhosted.org/packages/05/f9/27e94c1b3eb29e6933b6986ffc5fa1177d2cd1f0c8efc5f02c91c9ac61de/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:151dffc4865e5fe6dafce5480fab84f950d14566c480c08a53c663a0020504b6", size = 2390661, upload-time = "2024-12-24T18:30:34.939Z" }, + { url = "https://files.pythonhosted.org/packages/d9/d4/3c9735faa36ac591a4afcc2980d2691000506050b7a7e80bcfe44048daa7/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:577facaa411c10421314598b50413aa1ebcf5126f704f1e5d72d7e4e9f020d90", size = 2546710, upload-time = "2024-12-24T18:30:37.281Z" }, + { url = "https://files.pythonhosted.org/packages/4c/fa/be89a49c640930180657482a74970cdcf6f7072c8d2471e1babe17a222dc/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:be4816dc51c8a471749d664161b434912eee82f2ea66bd7628bd14583a833e85", size = 2349213, upload-time = "2024-12-24T18:30:40.019Z" }, +] + +[[package]] +name = "langcodes" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "language-data" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3a/7a/5a97e327063409a5caa21541e6d08ae4a0f2da328447e9f2c7b39e179226/langcodes-3.5.0.tar.gz", hash = "sha256:1eef8168d07e51e131a2497ffecad4b663f6208e7c3ae3b8dc15c51734a6f801", size = 191030, upload-time = "2024-11-19T10:23:45.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/6b/068c2ea7a712bf805c62445bd9e9c06d7340358ef2824150eceac027444b/langcodes-3.5.0-py3-none-any.whl", hash = "sha256:853c69d1a35e0e13da2f427bb68fb2fa4a8f4fb899e0c62ad8df8d073dcfed33", size = 182974, upload-time = "2024-11-19T10:23:42.824Z" }, +] + +[[package]] +name = "langid" +version = "1.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ea/4c/0fb7d900d3b0b9c8703be316fbddffecdab23c64e1b46c7a83561d78bd43/langid-1.1.6.tar.gz", hash = "sha256:044bcae1912dab85c33d8e98f2811b8f4ff1213e5e9a9e9510137b84da2cb293", size = 1925978, upload-time = "2016-04-05T22:34:15.786Z" } + +[[package]] +name = "language-data" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "marisa-trie" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/ce/3f144716a9f2cbf42aa86ebc8b085a184be25c80aa453eea17c294d239c1/language_data-1.3.0.tar.gz", hash = "sha256:7600ef8aa39555145d06c89f0c324bf7dab834ea0b0a439d8243762e3ebad7ec", size = 5129310, upload-time = "2024-11-19T10:21:37.912Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/e9/5a5ffd9b286db82be70d677d0a91e4d58f7912bb8dd026ddeeb4abe70679/language_data-1.3.0-py3-none-any.whl", hash = "sha256:e2ee943551b5ae5f89cd0e801d1fc3835bb0ef5b7e9c3a4e8e17b2b214548fbf", size = 5385760, upload-time = "2024-11-19T10:21:36.005Z" }, +] + +[[package]] +name = "llvmlite" +version = "0.44.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/89/6a/95a3d3610d5c75293d5dbbb2a76480d5d4eeba641557b69fe90af6c5b84e/llvmlite-0.44.0.tar.gz", hash = "sha256:07667d66a5d150abed9157ab6c0b9393c9356f229784a4385c02f99e94fc94d4", size = 171880, upload-time = "2025-01-20T11:14:41.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/86/e3c3195b92e6e492458f16d233e58a1a812aa2bfbef9bdd0fbafcec85c60/llvmlite-0.44.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:1d671a56acf725bf1b531d5ef76b86660a5ab8ef19bb6a46064a705c6ca80aad", size = 28132297, upload-time = "2025-01-20T11:13:32.57Z" }, + { url = "https://files.pythonhosted.org/packages/d6/53/373b6b8be67b9221d12b24125fd0ec56b1078b660eeae266ec388a6ac9a0/llvmlite-0.44.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f79a728e0435493611c9f405168682bb75ffd1fbe6fc360733b850c80a026db", size = 26201105, upload-time = "2025-01-20T11:13:38.744Z" }, + { url = "https://files.pythonhosted.org/packages/cb/da/8341fd3056419441286c8e26bf436923021005ece0bff5f41906476ae514/llvmlite-0.44.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0143a5ef336da14deaa8ec26c5449ad5b6a2b564df82fcef4be040b9cacfea9", size = 42361901, upload-time = "2025-01-20T11:13:46.711Z" }, + { url = "https://files.pythonhosted.org/packages/53/ad/d79349dc07b8a395a99153d7ce8b01d6fcdc9f8231355a5df55ded649b61/llvmlite-0.44.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d752f89e31b66db6f8da06df8b39f9b91e78c5feea1bf9e8c1fba1d1c24c065d", size = 41184247, upload-time = "2025-01-20T11:13:56.159Z" }, + { url = "https://files.pythonhosted.org/packages/e2/3b/a9a17366af80127bd09decbe2a54d8974b6d8b274b39bf47fbaedeec6307/llvmlite-0.44.0-cp312-cp312-win_amd64.whl", hash = "sha256:eae7e2d4ca8f88f89d315b48c6b741dcb925d6a1042da694aa16ab3dd4cbd3a1", size = 30332380, upload-time = "2025-01-20T11:14:02.442Z" }, + { url = "https://files.pythonhosted.org/packages/89/24/4c0ca705a717514c2092b18476e7a12c74d34d875e05e4d742618ebbf449/llvmlite-0.44.0-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:319bddd44e5f71ae2689859b7203080716448a3cd1128fb144fe5c055219d516", size = 28132306, upload-time = "2025-01-20T11:14:09.035Z" }, + { url = "https://files.pythonhosted.org/packages/01/cf/1dd5a60ba6aee7122ab9243fd614abcf22f36b0437cbbe1ccf1e3391461c/llvmlite-0.44.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c58867118bad04a0bb22a2e0068c693719658105e40009ffe95c7000fcde88e", size = 26201090, upload-time = "2025-01-20T11:14:15.401Z" }, + { url = "https://files.pythonhosted.org/packages/d2/1b/656f5a357de7135a3777bd735cc7c9b8f23b4d37465505bd0eaf4be9befe/llvmlite-0.44.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46224058b13c96af1365290bdfebe9a6264ae62fb79b2b55693deed11657a8bf", size = 42361904, upload-time = "2025-01-20T11:14:22.949Z" }, + { url = "https://files.pythonhosted.org/packages/d8/e1/12c5f20cb9168fb3464a34310411d5ad86e4163c8ff2d14a2b57e5cc6bac/llvmlite-0.44.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0097052c32bf721a4efc03bd109d335dfa57d9bffb3d4c24cc680711b8b4fc", size = 41184245, upload-time = "2025-01-20T11:14:31.731Z" }, + { url = "https://files.pythonhosted.org/packages/d0/81/e66fc86539293282fd9cb7c9417438e897f369e79ffb62e1ae5e5154d4dd/llvmlite-0.44.0-cp313-cp313-win_amd64.whl", hash = "sha256:2fb7c4f2fb86cbae6dca3db9ab203eeea0e22d73b99bc2341cdf9de93612e930", size = 30331193, upload-time = "2025-01-20T11:14:38.578Z" }, +] + +[[package]] +name = "marisa-trie" +version = "1.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/31/15/9d9743897e4450b2de199ee673b50cb018980c4ced477d41cf91304a85e3/marisa_trie-1.2.1.tar.gz", hash = "sha256:3a27c408e2aefc03e0f1d25b2ff2afb85aac3568f6fa2ae2a53b57a2e87ce29d", size = 416124, upload-time = "2024-10-12T11:30:15.989Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/bf/8bd4ac8436b33fd46c9e1ffe3c2a131cd9744cc1649dbbe13308f744ef2b/marisa_trie-1.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:638506eacf20ca503fff72221a7e66a6eadbf28d6a4a6f949fcf5b1701bb05ec", size = 360041, upload-time = "2024-10-12T11:28:59.436Z" }, + { url = "https://files.pythonhosted.org/packages/ab/dd/4d3151e302e66ae387885f6ec265bd189e096b0c43c1379bfd9a3b9d2543/marisa_trie-1.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de1665eaafefa48a308e4753786519888021740501a15461c77bdfd57638e6b4", size = 190520, upload-time = "2024-10-12T11:29:01.07Z" }, + { url = "https://files.pythonhosted.org/packages/00/28/ae5991c74fb90b173167a366a634c83445f948ad044d37287b478d6b457e/marisa_trie-1.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f713af9b8aa66a34cd3a78c7d150a560a75734713abe818a69021fd269e927fa", size = 174175, upload-time = "2024-10-12T11:29:02.516Z" }, + { url = "https://files.pythonhosted.org/packages/5a/6a/fbfa89a8680eaabc6847a6c421e65427c43182db0c4bdb60e1516c81c822/marisa_trie-1.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2a7d00f53f4945320b551bccb826b3fb26948bde1a10d50bb9802fabb611b10", size = 1354995, upload-time = "2024-10-12T11:29:04.294Z" }, + { url = "https://files.pythonhosted.org/packages/9e/4c/2ba0b385e5f64ca4ddb0c10ec52ddf881bc4521f135948786fc339d1d6c8/marisa_trie-1.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98042040d1d6085792e8d0f74004fc0f5f9ca6091c298f593dd81a22a4643854", size = 1390989, upload-time = "2024-10-12T11:29:05.576Z" }, + { url = "https://files.pythonhosted.org/packages/6b/22/0791ed3045c91d0938345a86be472fc7c188b894f16c5dfad2ef31e7f882/marisa_trie-1.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6532615111eec2c79e711965ece0bc95adac1ff547a7fff5ffca525463116deb", size = 1328810, upload-time = "2024-10-12T11:29:07.522Z" }, + { url = "https://files.pythonhosted.org/packages/9d/7d/3f566e563abae6efce7fc311c63282a447c611739b3cd66c0e36077c86f8/marisa_trie-1.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:20948e40ab2038e62b7000ca6b4a913bc16c91a2c2e6da501bd1f917eeb28d51", size = 2230222, upload-time = "2024-10-12T11:29:09.374Z" }, + { url = "https://files.pythonhosted.org/packages/a5/0b/38fbb4611b5d1030242ddc2aa62e524438c8076e26f87395dbbf222dc62d/marisa_trie-1.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:66b23e5b35dd547f85bf98db7c749bc0ffc57916ade2534a6bbc32db9a4abc44", size = 2383620, upload-time = "2024-10-12T11:29:10.904Z" }, + { url = "https://files.pythonhosted.org/packages/ae/17/4553c63de29904d5d2521a24cad817bc7883cfa90506ab702ec4dae59a7b/marisa_trie-1.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6704adf0247d2dda42e876b793be40775dff46624309ad99bc7537098bee106d", size = 2329202, upload-time = "2024-10-12T11:29:12.266Z" }, + { url = "https://files.pythonhosted.org/packages/45/08/6307a630e63cd763fe77ac56516faa67fa9cd342060691e40fabc84be6b0/marisa_trie-1.2.1-cp312-cp312-win32.whl", hash = "sha256:3ad356442c2fea4c2a6f514738ddf213d23930f942299a2b2c05df464a00848a", size = 129652, upload-time = "2024-10-12T11:29:13.454Z" }, + { url = "https://files.pythonhosted.org/packages/a1/fe/67c357bfd92710d95a16b86e1453c663d565415d7f7838781c79ff7e1a7e/marisa_trie-1.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:f2806f75817392cedcacb24ac5d80b0350dde8d3861d67d045c1d9b109764114", size = 150845, upload-time = "2024-10-12T11:29:15.092Z" }, + { url = "https://files.pythonhosted.org/packages/2a/a4/a110cd9952f0e72da7bafea1f0084b18b9e03952110d9083bfda52279f5c/marisa_trie-1.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:b5ea16e69bfda0ac028c921b58de1a4aaf83d43934892977368579cd3c0a2554", size = 354439, upload-time = "2024-10-12T11:29:16.831Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a5/a6099eb1c3fd8d7e93408c45501e1d08536ac57dfef02ec331f78e1ace18/marisa_trie-1.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9f627f4e41be710b6cb6ed54b0128b229ac9d50e2054d9cde3af0fef277c23cf", size = 188187, upload-time = "2024-10-12T11:29:18.558Z" }, + { url = "https://files.pythonhosted.org/packages/7c/cc/f637127e2beffa920d21f7fc45b4029575bcd1b28a90c0d90cb2b08c2205/marisa_trie-1.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5e649f3dc8ab5476732094f2828cc90cac3be7c79bc0c8318b6fda0c1d248db4", size = 171484, upload-time = "2024-10-12T11:29:19.596Z" }, + { url = "https://files.pythonhosted.org/packages/6d/0f/29f2ad7260b956570f69f25a542efa51ba76eb76ecd53c63ee9d21987c3d/marisa_trie-1.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46e528ee71808c961baf8c3ce1c46a8337ec7a96cc55389d11baafe5b632f8e9", size = 1319770, upload-time = "2024-10-12T11:29:20.661Z" }, + { url = "https://files.pythonhosted.org/packages/f2/12/0b69ed61fba59551a5f3d569af367afae614db7214ce1da12946ba9a433a/marisa_trie-1.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36aa4401a1180615f74d575571a6550081d84fc6461e9aefc0bb7b2427af098e", size = 1356488, upload-time = "2024-10-12T11:29:21.95Z" }, + { url = "https://files.pythonhosted.org/packages/33/23/483b110db7ffe8729d6ebea2bf74258aef51f10fef5775f99e4bac7aef69/marisa_trie-1.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce59bcd2cda9bb52b0e90cc7f36413cd86c3d0ce7224143447424aafb9f4aa48", size = 1302334, upload-time = "2024-10-12T11:29:24.217Z" }, + { url = "https://files.pythonhosted.org/packages/1c/6f/46c2be99ce925985127fdf78900f1673bce8cb72debfebee6dccd11032c6/marisa_trie-1.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f4cd800704a5fc57e53c39c3a6b0c9b1519ebdbcb644ede3ee67a06eb542697d", size = 2202624, upload-time = "2024-10-12T11:29:25.499Z" }, + { url = "https://files.pythonhosted.org/packages/fd/b6/ef642327dbd4ec35be55d5682520b8f70fca98a54024f441ef2732f6b305/marisa_trie-1.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2428b495003c189695fb91ceeb499f9fcced3a2dce853e17fa475519433c67ff", size = 2364206, upload-time = "2024-10-12T11:29:26.771Z" }, + { url = "https://files.pythonhosted.org/packages/69/04/ef8197a79d0ab5043b781cc9b457bd11b81d4204fe78adf7625a67f48c21/marisa_trie-1.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:735c363d9aaac82eaf516a28f7c6b95084c2e176d8231c87328dc80e112a9afa", size = 2304801, upload-time = "2024-10-12T11:29:28.095Z" }, + { url = "https://files.pythonhosted.org/packages/03/72/f87564d653daf31d8f33d9bf0121e99ccc21f18f5c485fb404ba06abc10e/marisa_trie-1.2.1-cp313-cp313-win32.whl", hash = "sha256:eba6ca45500ca1a042466a0684aacc9838e7f20fe2605521ee19f2853062798f", size = 128799, upload-time = "2024-10-12T11:29:30.28Z" }, + { url = "https://files.pythonhosted.org/packages/27/40/5f9eb8b73030cc4b0d6817176e66079a62a2ddd9d5530da54f8011473428/marisa_trie-1.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:aa7cd17e1c690ce96c538b2f4aae003d9a498e65067dd433c52dd069009951d4", size = 149035, upload-time = "2024-10-12T11:29:31.332Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, +] + +[[package]] +name = "mathics-django" +version = "8.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, + { name = "mathics-scanner" }, + { name = "mathics3" }, + { name = "matplotlib" }, + { name = "networkx" }, + { name = "pygments" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bb/be/5674ea373ae53eda45505fb4a272c6b462a2a8a9e4f2bb06d1576f27fcb9/Mathics_Django-8.0.1.tar.gz", hash = "sha256:f896fef54e0aa19fe2eb13f650540c49481679843101f912f97b30c3c5a99bb3", size = 24237855, upload-time = "2025-02-08T15:02:08.74Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/c4/b4a9d20509d2a0c453386b915fb52cc2a5c413ed02635841c6bca11eeb6f/Mathics_Django-8.0.1-py3-none-any.whl", hash = "sha256:f92e439536e4d4bffc4182c1dec454ddabc5e94738edb527fbca303e401221d1", size = 25459733, upload-time = "2025-02-08T15:02:02.636Z" }, +] + +[[package]] +name = "mathics-omnibus" +version = "8.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mathics-django" }, + { name = "mathics3" }, + { name = "mathics3-trepan" }, + { name = "mathicsscript" }, + { name = "pymathics-graph" }, + { name = "pymathics-natlang" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/47/29/d5343adeb65461829d2d4aa0f29cccefb32d450dfde38653525da857e926/mathics_omnibus-8.0.0.tar.gz", hash = "sha256:f3dc17d483c1d31a4d2957accc5e696de5df23e3528fed05fdb5751983a442cd", size = 21374, upload-time = "2025-01-28T22:43:09.537Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/a8/e639113c20a87c173c902c939c7a09123196a08a0dcb4bcafecb306b983d/Mathics_omnibus-8.0.0-py2.py3-none-any.whl", hash = "sha256:1290edf153ee823b078517f9e4638b91d9f615ee13e26058bbb9c1fcf569f6e5", size = 25454, upload-time = "2025-01-28T22:42:31.295Z" }, +] + +[[package]] +name = "mathics-pygments" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mathics-scanner" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/64/11/d0c99863e949090a0f167213e3b0e9d8f487f09c215fb9b36a3b06507ebd/mathics_pygments-1.0.4.tar.gz", hash = "sha256:694a2fdaf61eb60068f3dc83385a5eedb2ce9c0444b73603e8d20387586c4fc5", size = 57747, upload-time = "2025-01-28T22:26:21.021Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/57/2423061094e1cffa72c36fb6de951f50a54f47f4023fc368b44897bd53e1/mathics_pygments-1.0.4-py2.py3-none-any.whl", hash = "sha256:bb6af332d387f1983092ebdb9f26d203f2ddc2944a5f6e8ab84ca3e5bfee3ce8", size = 46699, upload-time = "2025-01-28T22:26:19.144Z" }, +] + +[[package]] +name = "mathics-scanner" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "chardet" }, + { name = "click" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b4/a9/8410a1d37a360fcd27735b2bdc1ee20dd29744c21365ad71fe92cf6dddff/mathics_scanner-1.4.1.tar.gz", hash = "sha256:a98a5ee11815bf94edf8a6f665e788915c09b9cd4fd96ed4fc26583973dc6fb1", size = 182389, upload-time = "2025-01-26T17:39:37.681Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/f9/4e20ccd1a51d6078c640f9a420b5e42415f3c1f7f36c68941215a0066ef4/Mathics_Scanner-1.4.1-py3-none-any.whl", hash = "sha256:5955cf75e3e6b71e30107f5d45d046e9cc45926ac96f0a26049be590abac830a", size = 153640, upload-time = "2025-01-26T17:39:36.419Z" }, +] + +[[package]] +name = "mathics3" +version = "8.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mathics-scanner" }, + { name = "mpmath" }, + { name = "numpy" }, + { name = "palettable" }, + { name = "pillow" }, + { name = "pint" }, + { name = "pympler" }, + { name = "python-dateutil" }, + { name = "requests" }, + { name = "scipy" }, + { name = "setuptools" }, + { name = "stopit", marker = "sys_platform != 'emscripten'" }, + { name = "sympy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/a2/8a0e9e6868fd75c1e738e4eb96ed3934b55417a9d82eedc6813c6d06b72c/Mathics3-8.0.1.tar.gz", hash = "sha256:4ce42e4bbb0aec61ab03ccd537f478dc2eabb953ab69073c07515a6e610c22fb", size = 19546895, upload-time = "2025-02-08T14:23:20.995Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/4b/464aa013d4ea19c7cf69fa0d2a8db7c9a273ec2f469bd736bac8ae63f670/Mathics3-8.0.1-py3-none-any.whl", hash = "sha256:9fdb89215ecf149011118a02120de358f4aa90fa9708a95c14055bfebaf9022a", size = 18340878, upload-time = "2025-02-08T14:23:15.899Z" }, +] + +[[package]] +name = "mathics3-trepan" +version = "1.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mathics-pygments" }, + { name = "mathics3" }, + { name = "trepan3k" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/23/dd2e144583d1d71d15ba850acfd3ab4cfa41ffd9b7261116bd8e571b64b8/Mathics3_trepan-1.0.2.tar.gz", hash = "sha256:2c39d3df8347bf3c8a175e08dd8a744723b4986cb1d3fed2fa35436d72e394f6", size = 78013, upload-time = "2025-01-28T12:29:14.539Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/be/eb40a8ea016368e894937b02bf897e4fb0480ff726d4876353ec1bcc3448/Mathics3_trepan-1.0.2-py3-none-any.whl", hash = "sha256:024fb61c1607bb1b9125109c79fa06c16a9f286907dd768a32444f0e8d8d2c74", size = 118383, upload-time = "2025-01-28T12:29:11.563Z" }, +] + +[[package]] +name = "mathicsscript" +version = "8.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "colorama" }, + { name = "columnize" }, + { name = "mathics-pygments" }, + { name = "mathics-scanner" }, + { name = "mathics3" }, + { name = "networkx" }, + { name = "prompt-toolkit" }, + { name = "pygments" }, + { name = "term-background" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/87/f6ab24f239821804b9135c4d0da33c7b23a5098bc65ecaa96c48903ca87b/mathicsscript-8.0.0.tar.gz", hash = "sha256:84a9dc55580d07a9616d7549745583086a5de75ee25cb5f68ae5aab44e1a518b", size = 66662, upload-time = "2025-01-26T21:34:13.315Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/dc/818f348aec295ac4cd2c7d3ab7c32b3b011eb84d4ccce60093971594d85f/mathicsscript-8.0.0-py3-none-any.whl", hash = "sha256:44d280d52edb50759de7d06f44eae7d3b5c0646c08e87b4aa505589d6cabd271", size = 68306, upload-time = "2025-01-26T21:34:12.051Z" }, +] + +[[package]] +name = "matplotlib" +version = "3.10.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "contourpy" }, + { name = "cycler" }, + { name = "fonttools" }, + { name = "kiwisolver" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "pyparsing" }, + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/91/d49359a21893183ed2a5b6c76bec40e0b1dcbf8ca148f864d134897cfc75/matplotlib-3.10.3.tar.gz", hash = "sha256:2f82d2c5bb7ae93aaaa4cd42aca65d76ce6376f83304fa3a630b569aca274df0", size = 34799811, upload-time = "2025-05-08T19:10:54.39Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/43/6b80eb47d1071f234ef0c96ca370c2ca621f91c12045f1401b5c9b28a639/matplotlib-3.10.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0ab1affc11d1f495ab9e6362b8174a25afc19c081ba5b0775ef00533a4236eea", size = 8179689, upload-time = "2025-05-08T19:10:07.602Z" }, + { url = "https://files.pythonhosted.org/packages/0f/70/d61a591958325c357204870b5e7b164f93f2a8cca1dc6ce940f563909a13/matplotlib-3.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2a818d8bdcafa7ed2eed74487fdb071c09c1ae24152d403952adad11fa3c65b4", size = 8050466, upload-time = "2025-05-08T19:10:09.383Z" }, + { url = "https://files.pythonhosted.org/packages/e7/75/70c9d2306203148cc7902a961240c5927dd8728afedf35e6a77e105a2985/matplotlib-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:748ebc3470c253e770b17d8b0557f0aa85cf8c63fd52f1a61af5b27ec0b7ffee", size = 8456252, upload-time = "2025-05-08T19:10:11.958Z" }, + { url = "https://files.pythonhosted.org/packages/c4/91/ba0ae1ff4b3f30972ad01cd4a8029e70a0ec3b8ea5be04764b128b66f763/matplotlib-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed70453fd99733293ace1aec568255bc51c6361cb0da94fa5ebf0649fdb2150a", size = 8601321, upload-time = "2025-05-08T19:10:14.47Z" }, + { url = "https://files.pythonhosted.org/packages/d2/88/d636041eb54a84b889e11872d91f7cbf036b3b0e194a70fa064eb8b04f7a/matplotlib-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dbed9917b44070e55640bd13419de83b4c918e52d97561544814ba463811cbc7", size = 9406972, upload-time = "2025-05-08T19:10:16.569Z" }, + { url = "https://files.pythonhosted.org/packages/b1/79/0d1c165eac44405a86478082e225fce87874f7198300bbebc55faaf6d28d/matplotlib-3.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:cf37d8c6ef1a48829443e8ba5227b44236d7fcaf7647caa3178a4ff9f7a5be05", size = 8067954, upload-time = "2025-05-08T19:10:18.663Z" }, + { url = "https://files.pythonhosted.org/packages/3b/c1/23cfb566a74c696a3b338d8955c549900d18fe2b898b6e94d682ca21e7c2/matplotlib-3.10.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9f2efccc8dcf2b86fc4ee849eea5dcaecedd0773b30f47980dc0cbeabf26ec84", size = 8180318, upload-time = "2025-05-08T19:10:20.426Z" }, + { url = "https://files.pythonhosted.org/packages/6c/0c/02f1c3b66b30da9ee343c343acbb6251bef5b01d34fad732446eaadcd108/matplotlib-3.10.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3ddbba06a6c126e3301c3d272a99dcbe7f6c24c14024e80307ff03791a5f294e", size = 8051132, upload-time = "2025-05-08T19:10:22.569Z" }, + { url = "https://files.pythonhosted.org/packages/b4/ab/8db1a5ac9b3a7352fb914133001dae889f9fcecb3146541be46bed41339c/matplotlib-3.10.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:748302b33ae9326995b238f606e9ed840bf5886ebafcb233775d946aa8107a15", size = 8457633, upload-time = "2025-05-08T19:10:24.749Z" }, + { url = "https://files.pythonhosted.org/packages/f5/64/41c4367bcaecbc03ef0d2a3ecee58a7065d0a36ae1aa817fe573a2da66d4/matplotlib-3.10.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a80fcccbef63302c0efd78042ea3c2436104c5b1a4d3ae20f864593696364ac7", size = 8601031, upload-time = "2025-05-08T19:10:27.03Z" }, + { url = "https://files.pythonhosted.org/packages/12/6f/6cc79e9e5ab89d13ed64da28898e40fe5b105a9ab9c98f83abd24e46d7d7/matplotlib-3.10.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:55e46cbfe1f8586adb34f7587c3e4f7dedc59d5226719faf6cb54fc24f2fd52d", size = 9406988, upload-time = "2025-05-08T19:10:29.056Z" }, + { url = "https://files.pythonhosted.org/packages/b1/0f/eed564407bd4d935ffabf561ed31099ed609e19287409a27b6d336848653/matplotlib-3.10.3-cp313-cp313-win_amd64.whl", hash = "sha256:151d89cb8d33cb23345cd12490c76fd5d18a56581a16d950b48c6ff19bb2ab93", size = 8068034, upload-time = "2025-05-08T19:10:31.221Z" }, + { url = "https://files.pythonhosted.org/packages/3e/e5/2f14791ff69b12b09e9975e1d116d9578ac684460860ce542c2588cb7a1c/matplotlib-3.10.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c26dd9834e74d164d06433dc7be5d75a1e9890b926b3e57e74fa446e1a62c3e2", size = 8218223, upload-time = "2025-05-08T19:10:33.114Z" }, + { url = "https://files.pythonhosted.org/packages/5c/08/30a94afd828b6e02d0a52cae4a29d6e9ccfcf4c8b56cc28b021d3588873e/matplotlib-3.10.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:24853dad5b8c84c8c2390fc31ce4858b6df504156893292ce8092d190ef8151d", size = 8094985, upload-time = "2025-05-08T19:10:35.337Z" }, + { url = "https://files.pythonhosted.org/packages/89/44/f3bc6b53066c889d7a1a3ea8094c13af6a667c5ca6220ec60ecceec2dabe/matplotlib-3.10.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68f7878214d369d7d4215e2a9075fef743be38fa401d32e6020bab2dfabaa566", size = 8483109, upload-time = "2025-05-08T19:10:37.611Z" }, + { url = "https://files.pythonhosted.org/packages/ba/c7/473bc559beec08ebee9f86ca77a844b65747e1a6c2691e8c92e40b9f42a8/matplotlib-3.10.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6929fc618cb6db9cb75086f73b3219bbb25920cb24cee2ea7a12b04971a4158", size = 8618082, upload-time = "2025-05-08T19:10:39.892Z" }, + { url = "https://files.pythonhosted.org/packages/d8/e9/6ce8edd264c8819e37bbed8172e0ccdc7107fe86999b76ab5752276357a4/matplotlib-3.10.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6c7818292a5cc372a2dc4c795e5c356942eb8350b98ef913f7fda51fe175ac5d", size = 9413699, upload-time = "2025-05-08T19:10:42.376Z" }, + { url = "https://files.pythonhosted.org/packages/1b/92/9a45c91089c3cf690b5badd4be81e392ff086ccca8a1d4e3a08463d8a966/matplotlib-3.10.3-cp313-cp313t-win_amd64.whl", hash = "sha256:4f23ffe95c5667ef8a2b56eea9b53db7f43910fa4a2d5472ae0f72b64deab4d5", size = 8139044, upload-time = "2025-05-08T19:10:44.551Z" }, +] + +[[package]] +name = "matplotlib-inline" +version = "0.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159, upload-time = "2024-04-15T13:44:44.803Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899, upload-time = "2024-04-15T13:44:43.265Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, +] + +[[package]] +name = "murmurhash" +version = "1.0.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/e9/02efbc6dfc2dd2085da3daacf9a8c17e8356019eceaedbfa21555e32d2af/murmurhash-1.0.13.tar.gz", hash = "sha256:737246d41ee00ff74b07b0bd1f0888be304d203ce668e642c86aa64ede30f8b7", size = 13258, upload-time = "2025-05-22T12:35:57.019Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/53/56ce2d8d4b9ab89557cb1d00ffce346b80a2eb2d8c7944015e5c83eacdec/murmurhash-1.0.13-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bbe882e46cb3f86e092d8a1dd7a5a1c992da1ae3b39f7dd4507b6ce33dae7f92", size = 26859, upload-time = "2025-05-22T12:35:31.815Z" }, + { url = "https://files.pythonhosted.org/packages/f8/85/3a0ad54a61257c31496545ae6861515d640316f93681d1dd917e7be06634/murmurhash-1.0.13-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:52a33a12ecedc432493692c207c784b06b6427ffaa897fc90b7a76e65846478d", size = 26900, upload-time = "2025-05-22T12:35:34.267Z" }, + { url = "https://files.pythonhosted.org/packages/d0/cd/6651de26744b50ff11c79f0c0d41244db039625de53c0467a7a52876b2d8/murmurhash-1.0.13-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:950403a7f0dc2d9c8d0710f07c296f2daab66299d9677d6c65d6b6fa2cb30aaa", size = 131367, upload-time = "2025-05-22T12:35:35.258Z" }, + { url = "https://files.pythonhosted.org/packages/50/6c/01ded95ddce33811c9766cae4ce32e0a54288da1d909ee2bcaa6ed13b9f1/murmurhash-1.0.13-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fde9fb5d2c106d86ff3ef2e4a9a69c2a8d23ba46e28c6b30034dc58421bc107b", size = 128943, upload-time = "2025-05-22T12:35:36.358Z" }, + { url = "https://files.pythonhosted.org/packages/ab/27/e539a9622d7bea3ae22706c1eb80d4af80f9dddd93b54d151955c2ae4011/murmurhash-1.0.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3aa55d62773745616e1ab19345dece122f6e6d09224f7be939cc5b4c513c8473", size = 129108, upload-time = "2025-05-22T12:35:37.864Z" }, + { url = "https://files.pythonhosted.org/packages/7a/84/18af5662e07d06839ad4db18ce026e6f8ef850d7b0ba92817b28dad28ba6/murmurhash-1.0.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:060dfef1b405cf02c450f182fb629f76ebe7f79657cced2db5054bc29b34938b", size = 129175, upload-time = "2025-05-22T12:35:38.928Z" }, + { url = "https://files.pythonhosted.org/packages/fe/8d/b01d3ee1f1cf3957250223b7c6ce35454f38fbf4abe236bf04a3f769341d/murmurhash-1.0.13-cp312-cp312-win_amd64.whl", hash = "sha256:a8e79627d44a6e20a6487effc30bfe1c74754c13d179106e68cc6d07941b022c", size = 24869, upload-time = "2025-05-22T12:35:40.035Z" }, + { url = "https://files.pythonhosted.org/packages/00/b4/8919dfdc4a131ad38a57b2c5de69f4bd74538bf546637ee59ebaebe6e5a4/murmurhash-1.0.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b8a7f8befd901379b6dc57a9e49c5188454113747ad6aa8cdd951a6048e10790", size = 26852, upload-time = "2025-05-22T12:35:41.061Z" }, + { url = "https://files.pythonhosted.org/packages/b4/32/ce78bef5d6101568bcb12f5bb5103fabcbe23723ec52e76ff66132d5dbb7/murmurhash-1.0.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f741aab86007510199193eee4f87c5ece92bc5a6ca7d0fe0d27335c1203dface", size = 26900, upload-time = "2025-05-22T12:35:42.097Z" }, + { url = "https://files.pythonhosted.org/packages/0c/4c/0f47c0b4f6b31a1de84d65f9573832c78cd47b4b8ce25ab5596a8238d150/murmurhash-1.0.13-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82614f18fa6d9d83da6bb0918f3789a3e1555d0ce12c2548153e97f79b29cfc9", size = 130033, upload-time = "2025-05-22T12:35:43.113Z" }, + { url = "https://files.pythonhosted.org/packages/e0/cb/e47233e32fb792dcc9fb18a2cf65f795d47179b29c2b4a2034689f14c707/murmurhash-1.0.13-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91f22a48b9454712e0690aa0b76cf0156a5d5a083d23ec7e209cfaeef28f56ff", size = 130619, upload-time = "2025-05-22T12:35:44.229Z" }, + { url = "https://files.pythonhosted.org/packages/8f/f1/f89911bf304ba5d385ccd346cc7fbb1c1450a24f093b592c3bfe87768467/murmurhash-1.0.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c4bc7938627b8fcb3d598fe6657cc96d1e31f4eba6a871b523c1512ab6dacb3e", size = 127643, upload-time = "2025-05-22T12:35:45.369Z" }, + { url = "https://files.pythonhosted.org/packages/a4/24/262229221f6840c1a04a46051075e99675e591571abcca6b9a8b6aa1602b/murmurhash-1.0.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:58a61f1fc840f9ef704e638c39b8517bab1d21f1a9dbb6ba3ec53e41360e44ec", size = 127981, upload-time = "2025-05-22T12:35:46.503Z" }, + { url = "https://files.pythonhosted.org/packages/18/25/addbc1d28f83252732ac3e57334d42f093890b4c2cce483ba01a42bc607c/murmurhash-1.0.13-cp313-cp313-win_amd64.whl", hash = "sha256:c451a22f14c2f40e7abaea521ee24fa0e46fbec480c4304c25c946cdb6e81883", size = 24880, upload-time = "2025-05-22T12:35:47.625Z" }, +] + +[[package]] +name = "networkx" +version = "3.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/4f/ccdb8ad3a38e583f214547fd2f7ff1fc160c43a75af88e6aec213404b96a/networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037", size = 2471065, upload-time = "2025-05-29T11:35:07.804Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec", size = 2034406, upload-time = "2025-05-29T11:35:04.961Z" }, +] + +[[package]] +name = "nltk" +version = "3.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "joblib" }, + { name = "regex" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3c/87/db8be88ad32c2d042420b6fd9ffd4a149f9a0d7f0e86b3f543be2eeeedd2/nltk-3.9.1.tar.gz", hash = "sha256:87d127bd3de4bd89a4f81265e5fa59cb1b199b27440175370f7417d2bc7ae868", size = 2904691, upload-time = "2024-08-18T19:48:37.769Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/66/7d9e26593edda06e8cb531874633f7c2372279c3b0f46235539fe546df8b/nltk-3.9.1-py3-none-any.whl", hash = "sha256:4fa26829c5b00715afe3061398a8989dc643b92ce7dd93fb4585a70930d168a1", size = 1505442, upload-time = "2024-08-18T19:48:21.909Z" }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, + { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, + { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, + { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, + { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, + { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, + { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, + { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, + { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, + { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, + { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, + { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, + { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, + { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, + { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" }, + { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" }, + { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, + { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, + { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, + { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, + { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, + { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" }, + { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "palettable" +version = "3.3.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/3d/a5854d60850485bff12f28abfe0e17f503e866763bed61aed4990b604530/palettable-3.3.3.tar.gz", hash = "sha256:094dd7d9a5fc1cca4854773e5c1fc6a315b33bd5b3a8f47064928facaf0490a8", size = 106639, upload-time = "2023-04-19T23:13:35.864Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/f7/3367feadd4ab56783b0971c9b7edfbdd68e0c70ce877949a5dd2117ed4a0/palettable-3.3.3-py2.py3-none-any.whl", hash = "sha256:74e9e7d7fe5a9be065e02397558ed1777b2df0b793a6f4ce1a5ee74f74fb0caa", size = 332251, upload-time = "2023-04-19T23:13:33.996Z" }, +] + +[[package]] +name = "parse-literature" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "ipython" }, + { name = "mathics-omnibus" }, +] + +[package.metadata] +requires-dist = [ + { name = "ipython", specifier = ">=9.3.0" }, + { name = "mathics-omnibus", specifier = ">=8.0.0" }, +] + +[[package]] +name = "parso" +version = "0.8.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/94/68e2e17afaa9169cf6412ab0f28623903be73d1b32e208d9e8e541bb086d/parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d", size = 400609, upload-time = "2024-04-05T09:43:55.897Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650, upload-time = "2024-04-05T09:43:53.299Z" }, +] + +[[package]] +name = "patternlite" +version = "3.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nltk" }, + { name = "numpy" }, + { name = "scipy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/65/d872a6269072568f4e812fc071705b5134c6153e05b121ed3234a16eb1ca/PatternLite-3.6.tar.gz", hash = "sha256:bfc890760dd2dfec89f0b01167ef9a460b31ecb9ad8cebcd4b3798a96a62e260", size = 21968361, upload-time = "2019-12-06T12:46:24.911Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/f5/1894eb24102cae0e433c18366ec2a8d945b42cbf128303b67454db8587d8/PatternLite-3.6-py3-none-any.whl", hash = "sha256:c54cd29314635b0e6361861c33f036705c5449f08741c47d834317ba83bd98e0", size = 22072758, upload-time = "2019-12-06T12:46:18.742Z" }, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, +] + +[[package]] +name = "pillow" +version = "11.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/cb/bb5c01fcd2a69335b86c22142b2bccfc3464087efb7fd382eee5ffc7fdf7/pillow-11.2.1.tar.gz", hash = "sha256:a64dd61998416367b7ef979b73d3a85853ba9bec4c2925f74e588879a58716b6", size = 47026707, upload-time = "2025-04-12T17:50:03.289Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/40/052610b15a1b8961f52537cc8326ca6a881408bc2bdad0d852edeb6ed33b/pillow-11.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:78afba22027b4accef10dbd5eed84425930ba41b3ea0a86fa8d20baaf19d807f", size = 3190185, upload-time = "2025-04-12T17:48:00.417Z" }, + { url = "https://files.pythonhosted.org/packages/e5/7e/b86dbd35a5f938632093dc40d1682874c33dcfe832558fc80ca56bfcb774/pillow-11.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78092232a4ab376a35d68c4e6d5e00dfd73454bd12b230420025fbe178ee3b0b", size = 3030306, upload-time = "2025-04-12T17:48:02.391Z" }, + { url = "https://files.pythonhosted.org/packages/a4/5c/467a161f9ed53e5eab51a42923c33051bf8d1a2af4626ac04f5166e58e0c/pillow-11.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25a5f306095c6780c52e6bbb6109624b95c5b18e40aab1c3041da3e9e0cd3e2d", size = 4416121, upload-time = "2025-04-12T17:48:04.554Z" }, + { url = "https://files.pythonhosted.org/packages/62/73/972b7742e38ae0e2ac76ab137ca6005dcf877480da0d9d61d93b613065b4/pillow-11.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c7b29dbd4281923a2bfe562acb734cee96bbb129e96e6972d315ed9f232bef4", size = 4501707, upload-time = "2025-04-12T17:48:06.831Z" }, + { url = "https://files.pythonhosted.org/packages/e4/3a/427e4cb0b9e177efbc1a84798ed20498c4f233abde003c06d2650a6d60cb/pillow-11.2.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3e645b020f3209a0181a418bffe7b4a93171eef6c4ef6cc20980b30bebf17b7d", size = 4522921, upload-time = "2025-04-12T17:48:09.229Z" }, + { url = "https://files.pythonhosted.org/packages/fe/7c/d8b1330458e4d2f3f45d9508796d7caf0c0d3764c00c823d10f6f1a3b76d/pillow-11.2.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b2dbea1012ccb784a65349f57bbc93730b96e85b42e9bf7b01ef40443db720b4", size = 4612523, upload-time = "2025-04-12T17:48:11.631Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2f/65738384e0b1acf451de5a573d8153fe84103772d139e1e0bdf1596be2ea/pillow-11.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:da3104c57bbd72948d75f6a9389e6727d2ab6333c3617f0a89d72d4940aa0443", size = 4587836, upload-time = "2025-04-12T17:48:13.592Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c5/e795c9f2ddf3debb2dedd0df889f2fe4b053308bb59a3cc02a0cd144d641/pillow-11.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:598174aef4589af795f66f9caab87ba4ff860ce08cd5bb447c6fc553ffee603c", size = 4669390, upload-time = "2025-04-12T17:48:15.938Z" }, + { url = "https://files.pythonhosted.org/packages/96/ae/ca0099a3995976a9fce2f423166f7bff9b12244afdc7520f6ed38911539a/pillow-11.2.1-cp312-cp312-win32.whl", hash = "sha256:1d535df14716e7f8776b9e7fee118576d65572b4aad3ed639be9e4fa88a1cad3", size = 2332309, upload-time = "2025-04-12T17:48:17.885Z" }, + { url = "https://files.pythonhosted.org/packages/7c/18/24bff2ad716257fc03da964c5e8f05d9790a779a8895d6566e493ccf0189/pillow-11.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:14e33b28bf17c7a38eede290f77db7c664e4eb01f7869e37fa98a5aa95978941", size = 2676768, upload-time = "2025-04-12T17:48:19.655Z" }, + { url = "https://files.pythonhosted.org/packages/da/bb/e8d656c9543276517ee40184aaa39dcb41e683bca121022f9323ae11b39d/pillow-11.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:21e1470ac9e5739ff880c211fc3af01e3ae505859392bf65458c224d0bf283eb", size = 2415087, upload-time = "2025-04-12T17:48:21.991Z" }, + { url = "https://files.pythonhosted.org/packages/36/9c/447528ee3776e7ab8897fe33697a7ff3f0475bb490c5ac1456a03dc57956/pillow-11.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fdec757fea0b793056419bca3e9932eb2b0ceec90ef4813ea4c1e072c389eb28", size = 3190098, upload-time = "2025-04-12T17:48:23.915Z" }, + { url = "https://files.pythonhosted.org/packages/b5/09/29d5cd052f7566a63e5b506fac9c60526e9ecc553825551333e1e18a4858/pillow-11.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0e130705d568e2f43a17bcbe74d90958e8a16263868a12c3e0d9c8162690830", size = 3030166, upload-time = "2025-04-12T17:48:25.738Z" }, + { url = "https://files.pythonhosted.org/packages/71/5d/446ee132ad35e7600652133f9c2840b4799bbd8e4adba881284860da0a36/pillow-11.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bdb5e09068332578214cadd9c05e3d64d99e0e87591be22a324bdbc18925be0", size = 4408674, upload-time = "2025-04-12T17:48:27.908Z" }, + { url = "https://files.pythonhosted.org/packages/69/5f/cbe509c0ddf91cc3a03bbacf40e5c2339c4912d16458fcb797bb47bcb269/pillow-11.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d189ba1bebfbc0c0e529159631ec72bb9e9bc041f01ec6d3233d6d82eb823bc1", size = 4496005, upload-time = "2025-04-12T17:48:29.888Z" }, + { url = "https://files.pythonhosted.org/packages/f9/b3/dd4338d8fb8a5f312021f2977fb8198a1184893f9b00b02b75d565c33b51/pillow-11.2.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:191955c55d8a712fab8934a42bfefbf99dd0b5875078240943f913bb66d46d9f", size = 4518707, upload-time = "2025-04-12T17:48:31.874Z" }, + { url = "https://files.pythonhosted.org/packages/13/eb/2552ecebc0b887f539111c2cd241f538b8ff5891b8903dfe672e997529be/pillow-11.2.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:ad275964d52e2243430472fc5d2c2334b4fc3ff9c16cb0a19254e25efa03a155", size = 4610008, upload-time = "2025-04-12T17:48:34.422Z" }, + { url = "https://files.pythonhosted.org/packages/72/d1/924ce51bea494cb6e7959522d69d7b1c7e74f6821d84c63c3dc430cbbf3b/pillow-11.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:750f96efe0597382660d8b53e90dd1dd44568a8edb51cb7f9d5d918b80d4de14", size = 4585420, upload-time = "2025-04-12T17:48:37.641Z" }, + { url = "https://files.pythonhosted.org/packages/43/ab/8f81312d255d713b99ca37479a4cb4b0f48195e530cdc1611990eb8fd04b/pillow-11.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fe15238d3798788d00716637b3d4e7bb6bde18b26e5d08335a96e88564a36b6b", size = 4667655, upload-time = "2025-04-12T17:48:39.652Z" }, + { url = "https://files.pythonhosted.org/packages/94/86/8f2e9d2dc3d308dfd137a07fe1cc478df0a23d42a6c4093b087e738e4827/pillow-11.2.1-cp313-cp313-win32.whl", hash = "sha256:3fe735ced9a607fee4f481423a9c36701a39719252a9bb251679635f99d0f7d2", size = 2332329, upload-time = "2025-04-12T17:48:41.765Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ec/1179083b8d6067a613e4d595359b5fdea65d0a3b7ad623fee906e1b3c4d2/pillow-11.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:74ee3d7ecb3f3c05459ba95eed5efa28d6092d751ce9bf20e3e253a4e497e691", size = 2676388, upload-time = "2025-04-12T17:48:43.625Z" }, + { url = "https://files.pythonhosted.org/packages/23/f1/2fc1e1e294de897df39fa8622d829b8828ddad938b0eaea256d65b84dd72/pillow-11.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:5119225c622403afb4b44bad4c1ca6c1f98eed79db8d3bc6e4e160fc6339d66c", size = 2414950, upload-time = "2025-04-12T17:48:45.475Z" }, + { url = "https://files.pythonhosted.org/packages/c4/3e/c328c48b3f0ead7bab765a84b4977acb29f101d10e4ef57a5e3400447c03/pillow-11.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8ce2e8411c7aaef53e6bb29fe98f28cd4fbd9a1d9be2eeea434331aac0536b22", size = 3192759, upload-time = "2025-04-12T17:48:47.866Z" }, + { url = "https://files.pythonhosted.org/packages/18/0e/1c68532d833fc8b9f404d3a642991441d9058eccd5606eab31617f29b6d4/pillow-11.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9ee66787e095127116d91dea2143db65c7bb1e232f617aa5957c0d9d2a3f23a7", size = 3033284, upload-time = "2025-04-12T17:48:50.189Z" }, + { url = "https://files.pythonhosted.org/packages/b7/cb/6faf3fb1e7705fd2db74e070f3bf6f88693601b0ed8e81049a8266de4754/pillow-11.2.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9622e3b6c1d8b551b6e6f21873bdcc55762b4b2126633014cea1803368a9aa16", size = 4445826, upload-time = "2025-04-12T17:48:52.346Z" }, + { url = "https://files.pythonhosted.org/packages/07/94/8be03d50b70ca47fb434a358919d6a8d6580f282bbb7af7e4aa40103461d/pillow-11.2.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63b5dff3a68f371ea06025a1a6966c9a1e1ee452fc8020c2cd0ea41b83e9037b", size = 4527329, upload-time = "2025-04-12T17:48:54.403Z" }, + { url = "https://files.pythonhosted.org/packages/fd/a4/bfe78777076dc405e3bd2080bc32da5ab3945b5a25dc5d8acaa9de64a162/pillow-11.2.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:31df6e2d3d8fc99f993fd253e97fae451a8db2e7207acf97859732273e108406", size = 4549049, upload-time = "2025-04-12T17:48:56.383Z" }, + { url = "https://files.pythonhosted.org/packages/65/4d/eaf9068dc687c24979e977ce5677e253624bd8b616b286f543f0c1b91662/pillow-11.2.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:062b7a42d672c45a70fa1f8b43d1d38ff76b63421cbbe7f88146b39e8a558d91", size = 4635408, upload-time = "2025-04-12T17:48:58.782Z" }, + { url = "https://files.pythonhosted.org/packages/1d/26/0fd443365d9c63bc79feb219f97d935cd4b93af28353cba78d8e77b61719/pillow-11.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4eb92eca2711ef8be42fd3f67533765d9fd043b8c80db204f16c8ea62ee1a751", size = 4614863, upload-time = "2025-04-12T17:49:00.709Z" }, + { url = "https://files.pythonhosted.org/packages/49/65/dca4d2506be482c2c6641cacdba5c602bc76d8ceb618fd37de855653a419/pillow-11.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f91ebf30830a48c825590aede79376cb40f110b387c17ee9bd59932c961044f9", size = 4692938, upload-time = "2025-04-12T17:49:02.946Z" }, + { url = "https://files.pythonhosted.org/packages/b3/92/1ca0c3f09233bd7decf8f7105a1c4e3162fb9142128c74adad0fb361b7eb/pillow-11.2.1-cp313-cp313t-win32.whl", hash = "sha256:e0b55f27f584ed623221cfe995c912c61606be8513bfa0e07d2c674b4516d9dd", size = 2335774, upload-time = "2025-04-12T17:49:04.889Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ac/77525347cb43b83ae905ffe257bbe2cc6fd23acb9796639a1f56aa59d191/pillow-11.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:36d6b82164c39ce5482f649b437382c0fb2395eabc1e2b1702a6deb8ad647d6e", size = 2681895, upload-time = "2025-04-12T17:49:06.635Z" }, + { url = "https://files.pythonhosted.org/packages/67/32/32dc030cfa91ca0fc52baebbba2e009bb001122a1daa8b6a79ad830b38d3/pillow-11.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:225c832a13326e34f212d2072982bb1adb210e0cc0b153e688743018c94a2681", size = 2417234, upload-time = "2025-04-12T17:49:08.399Z" }, +] + +[[package]] +name = "pint" +version = "0.24.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "flexcache" }, + { name = "flexparser" }, + { name = "platformdirs" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/20/bb/52b15ddf7b7706ed591134a895dbf6e41c8348171fb635e655e0a4bbb0ea/pint-0.24.4.tar.gz", hash = "sha256:35275439b574837a6cd3020a5a4a73645eb125ce4152a73a2f126bf164b91b80", size = 342225, upload-time = "2024-11-07T16:29:46.061Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/16/bd2f5904557265882108dc2e04f18abc05ab0c2b7082ae9430091daf1d5c/Pint-0.24.4-py3-none-any.whl", hash = "sha256:aa54926c8772159fcf65f82cc0d34de6768c151b32ad1deb0331291c38fe7659", size = 302029, upload-time = "2024-11-07T16:29:43.976Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" }, +] + +[[package]] +name = "preshed" +version = "3.0.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cymem" }, + { name = "murmurhash" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/3a/db814f67a05b6d7f9c15d38edef5ec9b21415710705b393883de92aee5ef/preshed-3.0.10.tar.gz", hash = "sha256:5a5c8e685e941f4ffec97f1fbf32694b8107858891a4bc34107fac981d8296ff", size = 15039, upload-time = "2025-05-26T15:18:33.612Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/14/322a4f58bc25991a87f216acb1351800739b0794185d27508ee86c35f382/preshed-3.0.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6e9c46933d55c8898c8f7a6019a8062cd87ef257b075ada2dd5d1e57810189ea", size = 131367, upload-time = "2025-05-26T15:18:02.408Z" }, + { url = "https://files.pythonhosted.org/packages/38/80/67507653c35620cace913f617df6d6f658b87e8da83087b851557d65dd86/preshed-3.0.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5c4ebc4f8ef0114d55f2ffdce4965378129c7453d0203664aeeb03055572d9e4", size = 126535, upload-time = "2025-05-26T15:18:03.589Z" }, + { url = "https://files.pythonhosted.org/packages/db/b1/ab4f811aeaf20af0fa47148c1c54b62d7e8120d59025bd0a3f773bb67725/preshed-3.0.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ab5ab4c6dfd3746fb4328e7fbeb2a0544416b872db02903bfac18e6f5cd412f", size = 864907, upload-time = "2025-05-26T15:18:04.794Z" }, + { url = "https://files.pythonhosted.org/packages/fb/db/fe37c1f99cfb26805dd89381ddd54901307feceb267332eaaca228e9f9c1/preshed-3.0.10-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40586fd96ae3974c552a7cd78781b6844ecb1559ee7556586f487058cf13dd96", size = 869329, upload-time = "2025-05-26T15:18:06.353Z" }, + { url = "https://files.pythonhosted.org/packages/a7/fd/efb6a6233d1cd969966f3f65bdd8e662579c3d83114e5c356cec1927b1f7/preshed-3.0.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a606c24cda931306b98e0edfafed3309bffcf8d6ecfe07804db26024c4f03cd6", size = 846829, upload-time = "2025-05-26T15:18:07.716Z" }, + { url = "https://files.pythonhosted.org/packages/14/49/0e4ce5db3bf86b081abb08a404fb37b7c2dbfd7a73ec6c0bc71b650307eb/preshed-3.0.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:394015566f9354738be903447039e8dbc6d93ba5adf091af694eb03c4e726b1e", size = 874008, upload-time = "2025-05-26T15:18:09.364Z" }, + { url = "https://files.pythonhosted.org/packages/6f/17/76d6593fc2d055d4e413b68a8c87b70aa9b7697d4972cb8062559edcf6e9/preshed-3.0.10-cp312-cp312-win_amd64.whl", hash = "sha256:fd7e38225937e580420c84d1996dde9b4f726aacd9405093455c3a2fa60fede5", size = 116701, upload-time = "2025-05-26T15:18:11.905Z" }, + { url = "https://files.pythonhosted.org/packages/bf/5e/87671bc58c4f6c8cf0a5601ccd74b8bb50281ff28aa4ab3e3cad5cd9d06a/preshed-3.0.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:23e6e0581a517597f3f76bc24a4cdb0ba5509933d4f61c34fca49649dd71edf9", size = 129184, upload-time = "2025-05-26T15:18:13.331Z" }, + { url = "https://files.pythonhosted.org/packages/92/69/b3969a3c95778def5bf5126484a1f7d2ad324d1040077f55f56e027d8ea4/preshed-3.0.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:574e6d6056981540310ff181b47a2912f4bddc91bcace3c7a9c6726eafda24ca", size = 124258, upload-time = "2025-05-26T15:18:14.497Z" }, + { url = "https://files.pythonhosted.org/packages/32/df/6e828ec4565bf33bd4803a3eb3b1102830b739143e5d6c132bf7181a58ec/preshed-3.0.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bd658dd73e853d1bb5597976a407feafa681b9d6155bc9bc7b4c2acc2a6ee96", size = 825445, upload-time = "2025-05-26T15:18:15.71Z" }, + { url = "https://files.pythonhosted.org/packages/05/3d/478b585f304920e51f328c9231e22f30dc64baa68e079e08a46ab72be738/preshed-3.0.10-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b95396046328ffb461a68859ce2141aca4815b8624167832d28ced70d541626", size = 831690, upload-time = "2025-05-26T15:18:17.08Z" }, + { url = "https://files.pythonhosted.org/packages/c3/65/938f21f77227e8d398d46fb10b9d1b3467be859468ce8db138fc3d50589c/preshed-3.0.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3e6728b2028bbe79565eb6cf676b5bae5ce1f9cc56e4bf99bb28ce576f88054d", size = 808593, upload-time = "2025-05-26T15:18:18.535Z" }, + { url = "https://files.pythonhosted.org/packages/6c/1c/2a3961fc88bc72300ff7e4ca54689bda90d2d77cc994167cc09a310480b6/preshed-3.0.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c4ef96cb28bf5f08de9c070143113e168efccbb68fd4961e7d445f734c051a97", size = 837333, upload-time = "2025-05-26T15:18:19.937Z" }, + { url = "https://files.pythonhosted.org/packages/fa/8c/d3e30f80b2ef21f267f09f0b7d18995adccc928ede5b73ea3fe54e1303f4/preshed-3.0.10-cp313-cp313-win_amd64.whl", hash = "sha256:97e0e2edfd25a7dfba799b49b3c5cc248ad0318a76edd9d5fd2c82aa3d5c64ed", size = 115769, upload-time = "2025-05-26T15:18:21.842Z" }, +] + +[[package]] +name = "prompt-toolkit" +version = "3.0.51" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bb/6e/9d084c929dfe9e3bfe0c6a47e31f78a25c54627d64a66e884a8bf5474f1c/prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed", size = 428940, upload-time = "2025-04-15T09:18:47.731Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/4f/5249960887b1fbe561d9ff265496d170b55a735b76724f10ef19f9e40716/prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07", size = 387810, upload-time = "2025-04-15T09:18:44.753Z" }, +] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, +] + +[[package]] +name = "pure-eval" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, +] + +[[package]] +name = "pycountry" +version = "24.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/57/c389fa68c50590881a75b7883eeb3dc15e9e73a0fdc001cdd45c13290c92/pycountry-24.6.1.tar.gz", hash = "sha256:b61b3faccea67f87d10c1f2b0fc0be714409e8fcdcc1315613174f6466c10221", size = 6043910, upload-time = "2024-06-01T04:12:15.05Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/ec/1fb891d8a2660716aadb2143235481d15ed1cbfe3ad669194690b0604492/pycountry-24.6.1-py3-none-any.whl", hash = "sha256:f1a4fb391cd7214f8eefd39556d740adcc233c778a27f8942c8dca351d6ce06f", size = 6335189, upload-time = "2024-06-01T04:11:49.711Z" }, +] + +[[package]] +name = "pydantic" +version = "2.11.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.33.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" }, + { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" }, + { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" }, + { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" }, + { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" }, + { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" }, + { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" }, + { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" }, + { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" }, + { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" }, + { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" }, + { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" }, + { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, + { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, + { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, + { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, + { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, + { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, + { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, + { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, + { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, + { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, + { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, + { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, + { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, + { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, +] + +[[package]] +name = "pydot" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyparsing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/c3/6034ed1ebf2e3ba95a0e35fa7c43104e40444c0ed2b5325702c63e824dbf/pydot-4.0.0.tar.gz", hash = "sha256:12f16493337cade2f7631b87c8ccd299ba2e251f3ee5d0732a058df2887afe97", size = 161793, upload-time = "2025-05-04T11:13:03.214Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/16/984c0cf5073a23154b1f95c9d131b14c9fea83bfadae4ba8fc169daded11/pydot-4.0.0-py3-none-any.whl", hash = "sha256:cf86e13a6cfe2a96758a9702537f77e0ac1368db8ef277b4d3b34473ea425c97", size = 37535, upload-time = "2025-05-04T11:13:01.458Z" }, +] + +[[package]] +name = "pyenchant" +version = "3.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/a3/86763b6350727ca81c8fcc5bb5bccee416e902e0085dc7a902c81233717e/pyenchant-3.2.2.tar.gz", hash = "sha256:1cf830c6614362a78aab78d50eaf7c6c93831369c52e1bb64ffae1df0341e637", size = 49580, upload-time = "2021-10-05T17:25:25.553Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/4c/a741dddab6ad96f257d90cb4d23067ffadac526c9cab3a99ca6ce3c05477/pyenchant-3.2.2-py3-none-any.whl", hash = "sha256:5facc821ece957208a81423af7d6ec7810dad29697cb0d77aae81e4e11c8e5a6", size = 55660, upload-time = "2021-10-05T17:25:19.548Z" }, + { url = "https://files.pythonhosted.org/packages/01/44/1e9a273d230abf5c961750a75e42b449adfb61eb446f80b6523955d2a4a2/pyenchant-3.2.2-py3-none-win32.whl", hash = "sha256:5a636832987eaf26efe971968f4d1b78e81f62bca2bde0a9da210c7de43c3bce", size = 11884084, upload-time = "2021-10-05T17:25:23.844Z" }, + { url = "https://files.pythonhosted.org/packages/49/96/2087455de16b08e86fa7ce70b53ddac5fcc040c899d9ebad507a0efec52d/pyenchant-3.2.2-py3-none-win_amd64.whl", hash = "sha256:6153f521852e23a5add923dbacfbf4bebbb8d70c4e4bad609a8e0f9faeb915d1", size = 11890882, upload-time = "2021-10-05T17:25:17.013Z" }, +] + +[[package]] +name = "pyficache" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments" }, + { name = "term-background" }, + { name = "xdis" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/97/72159ece19a5feec21a2629ad7e02bace110113d832fcd1c754cf24e72f8/pyficache-2.4.0.tar.gz", hash = "sha256:c3ce7b49b9d26a34c255a8b6a361e234f1ebb20d37eecc41de1e9ba58ba553d8", size = 33583, upload-time = "2025-01-16T00:31:02.225Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/37/b6/acdbc40cdfc6f60e4ddabc6f78ee0d2077b5f7534fbbd00816fa854e3b33/pyficache-2.4.0-py3-none-any.whl", hash = "sha256:6074ef4b004cd0474b3bed34480b3632d4415b5ed6ba2e3e400792858a87b55d", size = 27325, upload-time = "2025-01-16T00:30:32.605Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload-time = "2025-01-06T17:26:30.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" }, +] + +[[package]] +name = "pymathics-graph" +version = "8.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mathics3" }, + { name = "matplotlib" }, + { name = "networkx" }, + { name = "pydot" }, + { name = "scipy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/f6/35dd01c1ce39d228ec579c093b6dcea98cf835145ac7f944b3be7e9a3ab4/pymathics_graph-8.0.1.tar.gz", hash = "sha256:9b773cbe27dfb97c9767f8d182b573f65011002d40a98726cc8e5632d63f6413", size = 59844, upload-time = "2025-02-08T15:40:55.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/4d/9873c74d4b5c8448b1eac3e6639bb084fb66c48cef97f561da4fdad4c86d/pymathics_graph-8.0.1-py3-none-any.whl", hash = "sha256:6c1c97a2df827eb6afa5550fba4fff2b806c0bce98ba6bb91097433d8a60ccb7", size = 53730, upload-time = "2025-02-08T15:40:53.737Z" }, +] + +[[package]] +name = "pymathics-natlang" +version = "8.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "joblib" }, + { name = "langid" }, + { name = "llvmlite" }, + { name = "mathics3" }, + { name = "nltk" }, + { name = "patternlite" }, + { name = "pycountry" }, + { name = "pyenchant" }, + { name = "spacy" }, + { name = "wasabi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e8/a3/d60ef3d51218eb77ab4696392b1c765ebd6e147851aeea39055bb64a65df/pymathics_natlang-8.0.1.tar.gz", hash = "sha256:d31da297592a10dae07529b891cad01e32ccd11d0c144c6e9f8675e61d082a23", size = 41528, upload-time = "2025-02-08T18:01:21.229Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/14/758adfa8520cef6d1bb7d315780e829468266eac8289bbcad13f29fe9087/pymathics_natlang-8.0.1-py3-none-any.whl", hash = "sha256:2ae2a2781c234eea9fb89270ca7e968094b6559ddecbfc86359bcc965d486441", size = 33636, upload-time = "2025-02-08T18:01:19.426Z" }, +] + +[[package]] +name = "pympler" +version = "1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pywin32", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/37/c384631908029676d8e7213dd956bb686af303a80db7afbc9be36bc49495/pympler-1.1.tar.gz", hash = "sha256:1eaa867cb8992c218430f1708fdaccda53df064144d1c5656b1e6f1ee6000424", size = 179954, upload-time = "2024-06-28T19:56:06.563Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/4f/a6a2e2b202d7fd97eadfe90979845b8706676b41cbd3b42ba75adf329d1f/Pympler-1.1-py3-none-any.whl", hash = "sha256:5b223d6027d0619584116a0cbc28e8d2e378f7a79c1e5e024f9ff3b673c58506", size = 165766, upload-time = "2024-06-28T19:56:05.087Z" }, +] + +[[package]] +name = "pyparsing" +version = "3.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/22/f1129e69d94ffff626bdb5c835506b3a5b4f3d070f17ea295e12c2c6f60f/pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be", size = 1088608, upload-time = "2025-03-25T05:01:28.114Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf", size = 111120, upload-time = "2025-03-25T05:01:24.908Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "pywin32" +version = "310" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/ec/4fdbe47932f671d6e348474ea35ed94227fb5df56a7c30cbbb42cd396ed0/pywin32-310-cp312-cp312-win32.whl", hash = "sha256:8a75a5cc3893e83a108c05d82198880704c44bbaee4d06e442e471d3c9ea4f3d", size = 8796239, upload-time = "2025-03-17T00:55:58.807Z" }, + { url = "https://files.pythonhosted.org/packages/e3/e5/b0627f8bb84e06991bea89ad8153a9e50ace40b2e1195d68e9dff6b03d0f/pywin32-310-cp312-cp312-win_amd64.whl", hash = "sha256:bf5c397c9a9a19a6f62f3fb821fbf36cac08f03770056711f765ec1503972060", size = 9503839, upload-time = "2025-03-17T00:56:00.8Z" }, + { url = "https://files.pythonhosted.org/packages/1f/32/9ccf53748df72301a89713936645a664ec001abd35ecc8578beda593d37d/pywin32-310-cp312-cp312-win_arm64.whl", hash = "sha256:2349cc906eae872d0663d4d6290d13b90621eaf78964bb1578632ff20e152966", size = 8459470, upload-time = "2025-03-17T00:56:02.601Z" }, + { url = "https://files.pythonhosted.org/packages/1c/09/9c1b978ffc4ae53999e89c19c77ba882d9fce476729f23ef55211ea1c034/pywin32-310-cp313-cp313-win32.whl", hash = "sha256:5d241a659c496ada3253cd01cfaa779b048e90ce4b2b38cd44168ad555ce74ab", size = 8794384, upload-time = "2025-03-17T00:56:04.383Z" }, + { url = "https://files.pythonhosted.org/packages/45/3c/b4640f740ffebadd5d34df35fecba0e1cfef8fde9f3e594df91c28ad9b50/pywin32-310-cp313-cp313-win_amd64.whl", hash = "sha256:667827eb3a90208ddbdcc9e860c81bde63a135710e21e4cb3348968e4bd5249e", size = 9503039, upload-time = "2025-03-17T00:56:06.207Z" }, + { url = "https://files.pythonhosted.org/packages/b4/f4/f785020090fb050e7fb6d34b780f2231f302609dc964672f72bfaeb59a28/pywin32-310-cp313-cp313-win_arm64.whl", hash = "sha256:e308f831de771482b7cf692a1f308f8fca701b2d8f9dde6cc440c7da17e47b33", size = 8458152, upload-time = "2025-03-17T00:56:07.819Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, +] + +[[package]] +name = "regex" +version = "2024.11.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494, upload-time = "2024-11-06T20:12:31.635Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/30/9a87ce8336b172cc232a0db89a3af97929d06c11ceaa19d97d84fa90a8f8/regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a", size = 483781, upload-time = "2024-11-06T20:10:07.07Z" }, + { url = "https://files.pythonhosted.org/packages/01/e8/00008ad4ff4be8b1844786ba6636035f7ef926db5686e4c0f98093612add/regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9", size = 288455, upload-time = "2024-11-06T20:10:09.117Z" }, + { url = "https://files.pythonhosted.org/packages/60/85/cebcc0aff603ea0a201667b203f13ba75d9fc8668fab917ac5b2de3967bc/regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2", size = 284759, upload-time = "2024-11-06T20:10:11.155Z" }, + { url = "https://files.pythonhosted.org/packages/94/2b/701a4b0585cb05472a4da28ee28fdfe155f3638f5e1ec92306d924e5faf0/regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4", size = 794976, upload-time = "2024-11-06T20:10:13.24Z" }, + { url = "https://files.pythonhosted.org/packages/4b/bf/fa87e563bf5fee75db8915f7352e1887b1249126a1be4813837f5dbec965/regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577", size = 833077, upload-time = "2024-11-06T20:10:15.37Z" }, + { url = "https://files.pythonhosted.org/packages/a1/56/7295e6bad94b047f4d0834e4779491b81216583c00c288252ef625c01d23/regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3", size = 823160, upload-time = "2024-11-06T20:10:19.027Z" }, + { url = "https://files.pythonhosted.org/packages/fb/13/e3b075031a738c9598c51cfbc4c7879e26729c53aa9cca59211c44235314/regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e", size = 796896, upload-time = "2024-11-06T20:10:21.85Z" }, + { url = "https://files.pythonhosted.org/packages/24/56/0b3f1b66d592be6efec23a795b37732682520b47c53da5a32c33ed7d84e3/regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe", size = 783997, upload-time = "2024-11-06T20:10:24.329Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a1/eb378dada8b91c0e4c5f08ffb56f25fcae47bf52ad18f9b2f33b83e6d498/regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e", size = 781725, upload-time = "2024-11-06T20:10:28.067Z" }, + { url = "https://files.pythonhosted.org/packages/83/f2/033e7dec0cfd6dda93390089864732a3409246ffe8b042e9554afa9bff4e/regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29", size = 789481, upload-time = "2024-11-06T20:10:31.612Z" }, + { url = "https://files.pythonhosted.org/packages/83/23/15d4552ea28990a74e7696780c438aadd73a20318c47e527b47a4a5a596d/regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39", size = 852896, upload-time = "2024-11-06T20:10:34.054Z" }, + { url = "https://files.pythonhosted.org/packages/e3/39/ed4416bc90deedbfdada2568b2cb0bc1fdb98efe11f5378d9892b2a88f8f/regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51", size = 860138, upload-time = "2024-11-06T20:10:36.142Z" }, + { url = "https://files.pythonhosted.org/packages/93/2d/dd56bb76bd8e95bbce684326302f287455b56242a4f9c61f1bc76e28360e/regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad", size = 787692, upload-time = "2024-11-06T20:10:38.394Z" }, + { url = "https://files.pythonhosted.org/packages/0b/55/31877a249ab7a5156758246b9c59539abbeba22461b7d8adc9e8475ff73e/regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54", size = 262135, upload-time = "2024-11-06T20:10:40.367Z" }, + { url = "https://files.pythonhosted.org/packages/38/ec/ad2d7de49a600cdb8dd78434a1aeffe28b9d6fc42eb36afab4a27ad23384/regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b", size = 273567, upload-time = "2024-11-06T20:10:43.467Z" }, + { url = "https://files.pythonhosted.org/packages/90/73/bcb0e36614601016552fa9344544a3a2ae1809dc1401b100eab02e772e1f/regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84", size = 483525, upload-time = "2024-11-06T20:10:45.19Z" }, + { url = "https://files.pythonhosted.org/packages/0f/3f/f1a082a46b31e25291d830b369b6b0c5576a6f7fb89d3053a354c24b8a83/regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4", size = 288324, upload-time = "2024-11-06T20:10:47.177Z" }, + { url = "https://files.pythonhosted.org/packages/09/c9/4e68181a4a652fb3ef5099e077faf4fd2a694ea6e0f806a7737aff9e758a/regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0", size = 284617, upload-time = "2024-11-06T20:10:49.312Z" }, + { url = "https://files.pythonhosted.org/packages/fc/fd/37868b75eaf63843165f1d2122ca6cb94bfc0271e4428cf58c0616786dce/regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0", size = 795023, upload-time = "2024-11-06T20:10:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/c4/7c/d4cd9c528502a3dedb5c13c146e7a7a539a3853dc20209c8e75d9ba9d1b2/regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7", size = 833072, upload-time = "2024-11-06T20:10:52.926Z" }, + { url = "https://files.pythonhosted.org/packages/4f/db/46f563a08f969159c5a0f0e722260568425363bea43bb7ae370becb66a67/regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7", size = 823130, upload-time = "2024-11-06T20:10:54.828Z" }, + { url = "https://files.pythonhosted.org/packages/db/60/1eeca2074f5b87df394fccaa432ae3fc06c9c9bfa97c5051aed70e6e00c2/regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c", size = 796857, upload-time = "2024-11-06T20:10:56.634Z" }, + { url = "https://files.pythonhosted.org/packages/10/db/ac718a08fcee981554d2f7bb8402f1faa7e868c1345c16ab1ebec54b0d7b/regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3", size = 784006, upload-time = "2024-11-06T20:10:59.369Z" }, + { url = "https://files.pythonhosted.org/packages/c2/41/7da3fe70216cea93144bf12da2b87367590bcf07db97604edeea55dac9ad/regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07", size = 781650, upload-time = "2024-11-06T20:11:02.042Z" }, + { url = "https://files.pythonhosted.org/packages/a7/d5/880921ee4eec393a4752e6ab9f0fe28009435417c3102fc413f3fe81c4e5/regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e", size = 789545, upload-time = "2024-11-06T20:11:03.933Z" }, + { url = "https://files.pythonhosted.org/packages/dc/96/53770115e507081122beca8899ab7f5ae28ae790bfcc82b5e38976df6a77/regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6", size = 853045, upload-time = "2024-11-06T20:11:06.497Z" }, + { url = "https://files.pythonhosted.org/packages/31/d3/1372add5251cc2d44b451bd94f43b2ec78e15a6e82bff6a290ef9fd8f00a/regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4", size = 860182, upload-time = "2024-11-06T20:11:09.06Z" }, + { url = "https://files.pythonhosted.org/packages/ed/e3/c446a64984ea9f69982ba1a69d4658d5014bc7a0ea468a07e1a1265db6e2/regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d", size = 787733, upload-time = "2024-11-06T20:11:11.256Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f1/e40c8373e3480e4f29f2692bd21b3e05f296d3afebc7e5dcf21b9756ca1c/regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", size = 262122, upload-time = "2024-11-06T20:11:13.161Z" }, + { url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545, upload-time = "2024-11-06T20:11:15Z" }, +] + +[[package]] +name = "requests" +version = "2.32.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, +] + +[[package]] +name = "rich" +version = "14.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078, upload-time = "2025-03-30T14:15:14.23Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229, upload-time = "2025-03-30T14:15:12.283Z" }, +] + +[[package]] +name = "scipy" +version = "1.15.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/37/4b/683aa044c4162e10ed7a7ea30527f2cbd92e6999c10a8ed8edb253836e9c/scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019", size = 38766735, upload-time = "2025-05-08T16:06:06.471Z" }, + { url = "https://files.pythonhosted.org/packages/7b/7e/f30be3d03de07f25dc0ec926d1681fed5c732d759ac8f51079708c79e680/scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6", size = 30173284, upload-time = "2025-05-08T16:06:11.686Z" }, + { url = "https://files.pythonhosted.org/packages/07/9c/0ddb0d0abdabe0d181c1793db51f02cd59e4901da6f9f7848e1f96759f0d/scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477", size = 22446958, upload-time = "2025-05-08T16:06:15.97Z" }, + { url = "https://files.pythonhosted.org/packages/af/43/0bce905a965f36c58ff80d8bea33f1f9351b05fad4beaad4eae34699b7a1/scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c", size = 25242454, upload-time = "2025-05-08T16:06:20.394Z" }, + { url = "https://files.pythonhosted.org/packages/56/30/a6f08f84ee5b7b28b4c597aca4cbe545535c39fe911845a96414700b64ba/scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45", size = 35210199, upload-time = "2025-05-08T16:06:26.159Z" }, + { url = "https://files.pythonhosted.org/packages/0b/1f/03f52c282437a168ee2c7c14a1a0d0781a9a4a8962d84ac05c06b4c5b555/scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49", size = 37309455, upload-time = "2025-05-08T16:06:32.778Z" }, + { url = "https://files.pythonhosted.org/packages/89/b1/fbb53137f42c4bf630b1ffdfc2151a62d1d1b903b249f030d2b1c0280af8/scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e", size = 36885140, upload-time = "2025-05-08T16:06:39.249Z" }, + { url = "https://files.pythonhosted.org/packages/2e/2e/025e39e339f5090df1ff266d021892694dbb7e63568edcfe43f892fa381d/scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539", size = 39710549, upload-time = "2025-05-08T16:06:45.729Z" }, + { url = "https://files.pythonhosted.org/packages/e6/eb/3bf6ea8ab7f1503dca3a10df2e4b9c3f6b3316df07f6c0ded94b281c7101/scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed", size = 40966184, upload-time = "2025-05-08T16:06:52.623Z" }, + { url = "https://files.pythonhosted.org/packages/73/18/ec27848c9baae6e0d6573eda6e01a602e5649ee72c27c3a8aad673ebecfd/scipy-1.15.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759", size = 38728256, upload-time = "2025-05-08T16:06:58.696Z" }, + { url = "https://files.pythonhosted.org/packages/74/cd/1aef2184948728b4b6e21267d53b3339762c285a46a274ebb7863c9e4742/scipy-1.15.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62", size = 30109540, upload-time = "2025-05-08T16:07:04.209Z" }, + { url = "https://files.pythonhosted.org/packages/5b/d8/59e452c0a255ec352bd0a833537a3bc1bfb679944c4938ab375b0a6b3a3e/scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb", size = 22383115, upload-time = "2025-05-08T16:07:08.998Z" }, + { url = "https://files.pythonhosted.org/packages/08/f5/456f56bbbfccf696263b47095291040655e3cbaf05d063bdc7c7517f32ac/scipy-1.15.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730", size = 25163884, upload-time = "2025-05-08T16:07:14.091Z" }, + { url = "https://files.pythonhosted.org/packages/a2/66/a9618b6a435a0f0c0b8a6d0a2efb32d4ec5a85f023c2b79d39512040355b/scipy-1.15.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825", size = 35174018, upload-time = "2025-05-08T16:07:19.427Z" }, + { url = "https://files.pythonhosted.org/packages/b5/09/c5b6734a50ad4882432b6bb7c02baf757f5b2f256041da5df242e2d7e6b6/scipy-1.15.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7", size = 37269716, upload-time = "2025-05-08T16:07:25.712Z" }, + { url = "https://files.pythonhosted.org/packages/77/0a/eac00ff741f23bcabd352731ed9b8995a0a60ef57f5fd788d611d43d69a1/scipy-1.15.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11", size = 36872342, upload-time = "2025-05-08T16:07:31.468Z" }, + { url = "https://files.pythonhosted.org/packages/fe/54/4379be86dd74b6ad81551689107360d9a3e18f24d20767a2d5b9253a3f0a/scipy-1.15.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126", size = 39670869, upload-time = "2025-05-08T16:07:38.002Z" }, + { url = "https://files.pythonhosted.org/packages/87/2e/892ad2862ba54f084ffe8cc4a22667eaf9c2bcec6d2bff1d15713c6c0703/scipy-1.15.3-cp313-cp313-win_amd64.whl", hash = "sha256:b90ab29d0c37ec9bf55424c064312930ca5f4bde15ee8619ee44e69319aab163", size = 40988851, upload-time = "2025-05-08T16:08:33.671Z" }, + { url = "https://files.pythonhosted.org/packages/1b/e9/7a879c137f7e55b30d75d90ce3eb468197646bc7b443ac036ae3fe109055/scipy-1.15.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8", size = 38863011, upload-time = "2025-05-08T16:07:44.039Z" }, + { url = "https://files.pythonhosted.org/packages/51/d1/226a806bbd69f62ce5ef5f3ffadc35286e9fbc802f606a07eb83bf2359de/scipy-1.15.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5", size = 30266407, upload-time = "2025-05-08T16:07:49.891Z" }, + { url = "https://files.pythonhosted.org/packages/e5/9b/f32d1d6093ab9eeabbd839b0f7619c62e46cc4b7b6dbf05b6e615bbd4400/scipy-1.15.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e", size = 22540030, upload-time = "2025-05-08T16:07:54.121Z" }, + { url = "https://files.pythonhosted.org/packages/e7/29/c278f699b095c1a884f29fda126340fcc201461ee8bfea5c8bdb1c7c958b/scipy-1.15.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb", size = 25218709, upload-time = "2025-05-08T16:07:58.506Z" }, + { url = "https://files.pythonhosted.org/packages/24/18/9e5374b617aba742a990581373cd6b68a2945d65cc588482749ef2e64467/scipy-1.15.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723", size = 34809045, upload-time = "2025-05-08T16:08:03.929Z" }, + { url = "https://files.pythonhosted.org/packages/e1/fe/9c4361e7ba2927074360856db6135ef4904d505e9b3afbbcb073c4008328/scipy-1.15.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb", size = 36703062, upload-time = "2025-05-08T16:08:09.558Z" }, + { url = "https://files.pythonhosted.org/packages/b7/8e/038ccfe29d272b30086b25a4960f757f97122cb2ec42e62b460d02fe98e9/scipy-1.15.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4", size = 36393132, upload-time = "2025-05-08T16:08:15.34Z" }, + { url = "https://files.pythonhosted.org/packages/10/7e/5c12285452970be5bdbe8352c619250b97ebf7917d7a9a9e96b8a8140f17/scipy-1.15.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5", size = 38979503, upload-time = "2025-05-08T16:08:21.513Z" }, + { url = "https://files.pythonhosted.org/packages/81/06/0a5e5349474e1cbc5757975b21bd4fad0e72ebf138c5592f191646154e06/scipy-1.15.3-cp313-cp313t-win_amd64.whl", hash = "sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca", size = 40308097, upload-time = "2025-05-08T16:08:27.627Z" }, +] + +[[package]] +name = "setuptools" +version = "80.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "smart-open" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/21/30/1f41c3d3b8cec82024b4b277bfd4e5b18b765ae7279eb9871fa25c503778/smart_open-7.1.0.tar.gz", hash = "sha256:a4f09f84f0f6d3637c6543aca7b5487438877a21360e7368ccf1f704789752ba", size = 72044, upload-time = "2024-12-17T13:19:17.71Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/18/9a8d9f01957aa1f8bbc5676d54c2e33102d247e146c1a3679d3bd5cc2e3a/smart_open-7.1.0-py3-none-any.whl", hash = "sha256:4b8489bb6058196258bafe901730c7db0dcf4f083f316e97269c66f45502055b", size = 61746, upload-time = "2024-12-17T13:19:21.076Z" }, +] + +[[package]] +name = "spacy" +version = "3.8.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "catalogue" }, + { name = "cymem" }, + { name = "jinja2" }, + { name = "langcodes" }, + { name = "murmurhash" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "preshed" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "setuptools" }, + { name = "spacy-legacy" }, + { name = "spacy-loggers" }, + { name = "srsly" }, + { name = "thinc" }, + { name = "tqdm" }, + { name = "typer" }, + { name = "wasabi" }, + { name = "weasel" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1e/9e/fb4e1cefe3fbd51ea6a243e5a3d2bc629baa9a28930bf4be6fe5672fa1ca/spacy-3.8.7.tar.gz", hash = "sha256:700fd174c6c552276be142c48e70bb53cae24c4dd86003c4432af9cb93e4c908", size = 1316143, upload-time = "2025-05-23T08:55:39.538Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/10/89852f40f926e0902c11c34454493ba0d15530b322711e754b89a6d7dfe6/spacy-3.8.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:88b397e37793cea51df298e6c651a763e49877a25bead5ba349761531a456687", size = 6265335, upload-time = "2025-05-23T08:54:42.876Z" }, + { url = "https://files.pythonhosted.org/packages/16/fb/b5d54522969a632c06f4af354763467553b66d5bf0671ac39f3cceb3fd54/spacy-3.8.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f70b676955fa6959347ca86ed6edd8ff0d6eb2ba20561fdfec76924bd3e540f9", size = 5906035, upload-time = "2025-05-23T08:54:44.824Z" }, + { url = "https://files.pythonhosted.org/packages/3a/03/70f06753fd65081404ade30408535eb69f627a36ffce2107116d1aa16239/spacy-3.8.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4b5a624797ade30c25b5b69daa35a93ee24bcc56bd79b0884b2565f76f35d6", size = 33420084, upload-time = "2025-05-23T08:54:46.889Z" }, + { url = "https://files.pythonhosted.org/packages/f9/19/b60e1ebf4985ee2b33d85705b89a5024942b65dad04dbdc3fb46f168b410/spacy-3.8.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9d83e006df66decccefa3872fa958b3756228fb216d83783595444cf42ca10c", size = 33922188, upload-time = "2025-05-23T08:54:49.781Z" }, + { url = "https://files.pythonhosted.org/packages/8f/a3/1fb1a49dc6d982d96fffc30c3a31bb431526008eea72ac3773f6518720a6/spacy-3.8.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0dca25deba54f3eb5dcfbf63bf16e613e6c601da56f91c4a902d38533c098941", size = 31939285, upload-time = "2025-05-23T08:54:53.162Z" }, + { url = "https://files.pythonhosted.org/packages/2d/55/6cf1aff8e5c01ee683e828f3ccd9282d2aff7ca1143a9349ee3d0c1291ff/spacy-3.8.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5eef3f805a1c118d9b709a23e2d378f5f20da5a0d6258c9cfdc87c4cb234b4fc", size = 32988845, upload-time = "2025-05-23T08:54:57.776Z" }, + { url = "https://files.pythonhosted.org/packages/8c/47/c17ee61b51aa8497d8af0999224b4b62485111a55ec105a06886685b2c68/spacy-3.8.7-cp312-cp312-win_amd64.whl", hash = "sha256:25d7a68e445200c9e9dc0044f8b7278ec0ef01ccc7cb5a95d1de2bd8e3ed6be2", size = 13918682, upload-time = "2025-05-23T08:55:00.387Z" }, + { url = "https://files.pythonhosted.org/packages/2a/95/7125bea6d432c601478bf922f7a568762c8be425bbde5b66698260ab0358/spacy-3.8.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dda7d57f42ec57c19fbef348095a9c82504e4777bca7b8db4b0d8318ba280fc7", size = 6235950, upload-time = "2025-05-23T08:55:02.92Z" }, + { url = "https://files.pythonhosted.org/packages/96/c3/d2362846154d4d341136774831605df02d61f49ac637524a15f4f2794874/spacy-3.8.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:de0e0bddb810ed05bce44bcb91460eabe52bc56323da398d2ca74288a906da35", size = 5878106, upload-time = "2025-05-23T08:55:04.496Z" }, + { url = "https://files.pythonhosted.org/packages/50/b6/b2943acfbfc4fc12642dac9feb571e712dd1569ab481db8f3daedee045fe/spacy-3.8.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a2e58f92b684465777a7c1a65d5578b1dc36fe55c48d9964fb6d46cc9449768", size = 33085866, upload-time = "2025-05-23T08:55:06.65Z" }, + { url = "https://files.pythonhosted.org/packages/65/98/c4415cbb217ac0b502dbb3372136015c699dd16a0c47cd6d338cd15f4bed/spacy-3.8.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46330da2eb357d6979f40ea8fc16ee5776ee75cd0c70aac2a4ea10c80364b8f3", size = 33398424, upload-time = "2025-05-23T08:55:10.477Z" }, + { url = "https://files.pythonhosted.org/packages/12/45/12a198858f1f11c21844876e039ba90df59d550527c72996d418c1faf78d/spacy-3.8.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:86b6a6ad23ca5440ef9d29c2b1e3125e28722c927db612ae99e564d49202861c", size = 31530066, upload-time = "2025-05-23T08:55:13.329Z" }, + { url = "https://files.pythonhosted.org/packages/9c/df/80524f99822eb96c9649200042ec5912357eec100cf0cd678a2e9ef0ecb3/spacy-3.8.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ccfe468cbb370888153df145ce3693af8e54dae551940df49057258081b2112f", size = 32613343, upload-time = "2025-05-23T08:55:16.711Z" }, + { url = "https://files.pythonhosted.org/packages/02/99/881f6f24c279a5a70b8d69aaf8266fd411a0a58fd1c8848112aaa348f6f6/spacy-3.8.7-cp313-cp313-win_amd64.whl", hash = "sha256:ca81e416ff35209769e8b5dd5d13acc52e4f57dd9d028364bccbbe157c2ae86b", size = 13911250, upload-time = "2025-05-23T08:55:19.606Z" }, +] + +[[package]] +name = "spacy-legacy" +version = "3.0.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d9/79/91f9d7cc8db5642acad830dcc4b49ba65a7790152832c4eceb305e46d681/spacy-legacy-3.0.12.tar.gz", hash = "sha256:b37d6e0c9b6e1d7ca1cf5bc7152ab64a4c4671f59c85adaf7a3fcb870357a774", size = 23806, upload-time = "2023-01-23T09:04:15.104Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/55/12e842c70ff8828e34e543a2c7176dac4da006ca6901c9e8b43efab8bc6b/spacy_legacy-3.0.12-py2.py3-none-any.whl", hash = "sha256:476e3bd0d05f8c339ed60f40986c07387c0a71479245d6d0f4298dbd52cda55f", size = 29971, upload-time = "2023-01-23T09:04:13.45Z" }, +] + +[[package]] +name = "spacy-loggers" +version = "1.0.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/67/3d/926db774c9c98acf66cb4ed7faf6c377746f3e00b84b700d0868b95d0712/spacy-loggers-1.0.5.tar.gz", hash = "sha256:d60b0bdbf915a60e516cc2e653baeff946f0cfc461b452d11a4d5458c6fe5f24", size = 20811, upload-time = "2023-09-11T12:26:52.323Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/78/d1a1a026ef3af911159398c939b1509d5c36fe524c7b644f34a5146c4e16/spacy_loggers-1.0.5-py3-none-any.whl", hash = "sha256:196284c9c446cc0cdb944005384270d775fdeaf4f494d8e269466cfa497ef645", size = 22343, upload-time = "2023-09-11T12:26:50.586Z" }, +] + +[[package]] +name = "spark-parser" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3c/e1/a443990c6c32cb7fa7d3896405d2924c4921c74c1ffd5b90ffaeb42f778a/spark_parser-1.9.0.tar.gz", hash = "sha256:dc66d48c4265c4133db41a9c5fe9c1c502b3b20167df158a0f234cd31712cf64", size = 80108, upload-time = "2024-10-08T13:51:02.148Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/c6/9d34faf67c92e14f1feaa036f6bc8beed0af71af1287060060af1b47bf9c/spark_parser-1.9.0-py310-none-any.whl", hash = "sha256:a1ab71c2df145b21c2c1c8aa5f001e36206bec259ec836504263ab7450881ad1", size = 45955, upload-time = "2024-10-08T13:45:04.437Z" }, + { url = "https://files.pythonhosted.org/packages/4b/bb/a13ae1bd3d257720e19d5d8a3dfa34cee38c5d9dd59dcb2c001bd0c6c310/spark_parser-1.9.0-py311-none-any.whl", hash = "sha256:30ab1a6005a783ca39f73ad477efb1e12a1cb0123607543e71690d9fd6fecf49", size = 63422, upload-time = "2024-10-08T13:45:05.451Z" }, + { url = "https://files.pythonhosted.org/packages/f1/9f/771e5add2467b6cadbcb560290bb6d9a4b069c3ca8483181245f5ea2d83e/spark_parser-1.9.0-py312-none-any.whl", hash = "sha256:036b8227c5b09a919a7eae50576f91d533124de1dbf9a9c654a2be5864f1c947", size = 63423, upload-time = "2024-10-08T13:45:07.059Z" }, + { url = "https://files.pythonhosted.org/packages/1c/3d/074fef587d8c392e4e5d3599670fec88e01ebb5a5ddc1ada05b80f01a921/spark_parser-1.9.0-py313-none-any.whl", hash = "sha256:fc2e188601a6c8f7a68e2d7bd97ca377f6dc271a67607ff11e6beef39d0e9d1d", size = 63427, upload-time = "2024-10-14T10:49:03.625Z" }, +] + +[[package]] +name = "sqlparse" +version = "0.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/40/edede8dd6977b0d3da179a342c198ed100dd2aba4be081861ee5911e4da4/sqlparse-0.5.3.tar.gz", hash = "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272", size = 84999, upload-time = "2024-12-10T12:05:30.728Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/5c/bfd6bd0bf979426d405cc6e71eceb8701b148b16c21d2dc3c261efc61c7b/sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca", size = 44415, upload-time = "2024-12-10T12:05:27.824Z" }, +] + +[[package]] +name = "srsly" +version = "2.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "catalogue" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b7/e8/eb51b1349f50bac0222398af0942613fdc9d1453ae67cbe4bf9936a1a54b/srsly-2.5.1.tar.gz", hash = "sha256:ab1b4bf6cf3e29da23dae0493dd1517fb787075206512351421b89b4fc27c77e", size = 466464, upload-time = "2025-01-17T09:26:26.919Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/f6/bebc20d75bd02121fc0f65ad8c92a5dd2570e870005e940faa55a263e61a/srsly-2.5.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:683b54ed63d7dfee03bc2abc4b4a5f2152f81ec217bbadbac01ef1aaf2a75790", size = 636717, upload-time = "2025-01-17T09:25:40.236Z" }, + { url = "https://files.pythonhosted.org/packages/b6/e8/9372317a4742c70b87b413335adfcdfb2bee4f88f3faba89fabb9e6abf21/srsly-2.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:459d987130e57e83ce9e160899afbeb871d975f811e6958158763dd9a8a20f23", size = 634697, upload-time = "2025-01-17T09:25:43.605Z" }, + { url = "https://files.pythonhosted.org/packages/d5/00/c6a7b99ab27b051a27bd26fe1a8c1885225bb8980282bf9cb99f70610368/srsly-2.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:184e3c98389aab68ff04aab9095bd5f1a8e5a72cc5edcba9d733bac928f5cf9f", size = 1134655, upload-time = "2025-01-17T09:25:45.238Z" }, + { url = "https://files.pythonhosted.org/packages/c2/e6/861459e8241ec3b78c111081bd5efa414ef85867e17c45b6882954468d6e/srsly-2.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c2a3e4856e63b7efd47591d049aaee8e5a250e098917f50d93ea68853fab78", size = 1143544, upload-time = "2025-01-17T09:25:47.485Z" }, + { url = "https://files.pythonhosted.org/packages/2d/85/8448fe874dd2042a4eceea5315cfff3af03ac77ff5073812071852c4e7e2/srsly-2.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:366b4708933cd8d6025c13c2cea3331f079c7bb5c25ec76fca392b6fc09818a0", size = 1098330, upload-time = "2025-01-17T09:25:52.55Z" }, + { url = "https://files.pythonhosted.org/packages/ef/7e/04d0e1417da140b2ac4053a3d4fcfc86cd59bf4829f69d370bb899f74d5d/srsly-2.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c8a0b03c64eb6e150d772c5149befbadd981cc734ab13184b0561c17c8cef9b1", size = 1110670, upload-time = "2025-01-17T09:25:54.02Z" }, + { url = "https://files.pythonhosted.org/packages/96/1a/a8cd627eaa81a91feb6ceab50155f4ceff3eef6107916cb87ef796958427/srsly-2.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:7952538f6bba91b9d8bf31a642ac9e8b9ccc0ccbb309feb88518bfb84bb0dc0d", size = 632598, upload-time = "2025-01-17T09:25:55.499Z" }, + { url = "https://files.pythonhosted.org/packages/42/94/cab36845aad6e2c22ecee1178accaa365657296ff87305b805648fd41118/srsly-2.5.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84b372f7ef1604b4a5b3cee1571993931f845a5b58652ac01bcb32c52586d2a8", size = 634883, upload-time = "2025-01-17T09:25:58.363Z" }, + { url = "https://files.pythonhosted.org/packages/67/8b/501f51f4eaee7e1fd7327764799cb0a42f5d0de042a97916d30dbff770fc/srsly-2.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6ac3944c112acb3347a39bfdc2ebfc9e2d4bace20fe1c0b764374ac5b83519f2", size = 632842, upload-time = "2025-01-17T09:25:59.777Z" }, + { url = "https://files.pythonhosted.org/packages/07/be/5b8fce4829661e070a7d3e262d2e533f0e297b11b8993d57240da67d7330/srsly-2.5.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6118f9c4b221cde0a990d06a42c8a4845218d55b425d8550746fe790acf267e9", size = 1118516, upload-time = "2025-01-17T09:26:01.234Z" }, + { url = "https://files.pythonhosted.org/packages/91/60/a34e97564eac352c0e916c98f44b6f566b7eb6a9fb60bcd60ffa98530762/srsly-2.5.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7481460110d9986781d9e4ac0f5f991f1d6839284a80ad268625f9a23f686950", size = 1127974, upload-time = "2025-01-17T09:26:04.007Z" }, + { url = "https://files.pythonhosted.org/packages/70/a2/f642334db0cabd187fa86b8773257ee6993c6009338a6831d4804e2c5b3c/srsly-2.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e57b8138082f09e35db60f99757e16652489e9e3692471d8e0c39aa95180688", size = 1086098, upload-time = "2025-01-17T09:26:05.612Z" }, + { url = "https://files.pythonhosted.org/packages/0d/9b/be48e185c5a010e71b5135e4cdf317ff56b8ac4bc08f394bbf882ac13b05/srsly-2.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bab90b85a63a1fe0bbc74d373c8bb9bb0499ddfa89075e0ebe8d670f12d04691", size = 1100354, upload-time = "2025-01-17T09:26:07.215Z" }, + { url = "https://files.pythonhosted.org/packages/3a/e2/745aeba88a8513017fbac2fd2f9f07b8a36065e51695f818541eb795ec0c/srsly-2.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:e73712be1634b5e1de6f81c273a7d47fe091ad3c79dc779c03d3416a5c117cee", size = 630634, upload-time = "2025-01-17T09:26:10.018Z" }, +] + +[[package]] +name = "stack-data" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asttokens" }, + { name = "executing" }, + { name = "pure-eval" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, +] + +[[package]] +name = "stopit" +version = "1.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/58/e8bb0b0fb05baf07bbac1450c447d753da65f9701f551dca79823ce15d50/stopit-1.1.2.tar.gz", hash = "sha256:f7f39c583fd92027bd9d06127b259aee7a5b7945c1f1fa56263811e1e766996d", size = 18281, upload-time = "2018-02-09T00:32:14.204Z" } + +[[package]] +name = "sympy" +version = "1.13.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/11/8a/5a7fd6284fa8caac23a26c9ddf9c30485a48169344b4bd3b0f02fef1890f/sympy-1.13.3.tar.gz", hash = "sha256:b27fd2c6530e0ab39e275fc9b683895367e51d5da91baa8d3d64db2565fec4d9", size = 7533196, upload-time = "2024-09-18T21:54:25.591Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/ff/c87e0622b1dadea79d2fb0b25ade9ed98954c9033722eb707053d310d4f3/sympy-1.13.3-py3-none-any.whl", hash = "sha256:54612cf55a62755ee71824ce692986f23c88ffa77207b30c1368eda4a7060f73", size = 6189483, upload-time = "2024-09-18T21:54:23.097Z" }, +] + +[[package]] +name = "term-background" +version = "1.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/18/7dc8c71e6fac31a34a94225cb4f5f912439b1ef825f2332b34d7119d83ee/term_background-1.0.2.tar.gz", hash = "sha256:96dcba8b664f272c7f1db183be70c1825e936990b65566999583f14ca326c75c", size = 12320, upload-time = "2024-10-10T16:52:32.949Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/37/641ddef6347f9fd42638743dc745857ff1fb682dd7d1a7262de494dda259/term_background-1.0.2-py310-none-any.whl", hash = "sha256:e894ac4af47cdf2b5d5fab64470f912972f306af011f21687644d70d28698cc5", size = 12194, upload-time = "2024-10-10T16:50:12.598Z" }, + { url = "https://files.pythonhosted.org/packages/1a/36/e0ccd50bdc44a68ea04233a3bec64238ebcdd9e1e0158e31adf141c84496/term_background-1.0.2-py311-none-any.whl", hash = "sha256:c80af48eea39bc19d134e52923264d3c3ad0b14fb33cd6b9ba9ad1eb45cea754", size = 5335, upload-time = "2024-10-10T16:50:13.426Z" }, + { url = "https://files.pythonhosted.org/packages/14/b6/b5b5b397fe5e302176cdaa8b446e7d387272f0962c55ab4f4404cc12b7bc/term_background-1.0.2-py312-none-any.whl", hash = "sha256:6b1f07bd95600786a671753a179e1ab38d971cac1a31758ab39968262fd4f259", size = 12375, upload-time = "2024-10-10T16:50:14.67Z" }, + { url = "https://files.pythonhosted.org/packages/ca/7b/515211e4f4ea8c5380b2139c2eda5088d38dd0b8e3cab8a60c3ec7674c25/term_background-1.0.2-py313-none-any.whl", hash = "sha256:61b8dee21ea1496e15eebd71a1f1727459920c19e0c52a183b4ca0143fa5adb7", size = 12380, upload-time = "2024-10-14T11:06:41.992Z" }, + { url = "https://files.pythonhosted.org/packages/9f/ee/42b93f7120f1fbf30136deec18296db119f03eb2b5d7a93bba936b10f023/term_background-1.0.2-py34-none-any.whl", hash = "sha256:ff955ca76b72d3928598149aa8b5db942204904c6b45f3cc3c050ba4996db4b7", size = 12179, upload-time = "2024-10-10T16:51:24.423Z" }, + { url = "https://files.pythonhosted.org/packages/31/ce/9525eab178887653ed08568ef45d852ee8a7fc7103aa674b69ed59d63a7c/term_background-1.0.2-py35-none-any.whl", hash = "sha256:8b9836d90dd98f25f46afd24ed6e286ccd16d56f5dbb5d300d1d969fa2aceb2a", size = 12176, upload-time = "2024-10-10T16:51:25.868Z" }, + { url = "https://files.pythonhosted.org/packages/59/f8/73b31fb3ec42d3b8f4f0ee27e86fac140171620f9f05420654e8cc86bb42/term_background-1.0.2-py36-none-any.whl", hash = "sha256:540e48ec34fea7edf2482d23ede5ad359c1587f1c49bb7a23a35a5d47455c99d", size = 12213, upload-time = "2024-10-10T16:51:26.983Z" }, + { url = "https://files.pythonhosted.org/packages/2f/77/db99756367b5ef00418fdc14e3e5fc521266203ee89d8d3bdcc3bfc1b892/term_background-1.0.2-py37-none-any.whl", hash = "sha256:71d029eceb97313586324b16cfecf313478fa9cb8ed1379bbd3d2c81f3a8a64d", size = 12193, upload-time = "2024-10-10T16:51:28.601Z" }, + { url = "https://files.pythonhosted.org/packages/b4/91/9898424d425d8b6a51d64419a260b48cb8767ae374d90ebf8fd32b117b48/term_background-1.0.2-py38-none-any.whl", hash = "sha256:3f4c2c4f84398d091329142d6b16e139a8c136c8c66af1c554fabcafadc7738a", size = 12198, upload-time = "2024-10-10T16:51:29.795Z" }, + { url = "https://files.pythonhosted.org/packages/03/e2/0b7a43b2f0c87807de51045093424297172e1644faca3ccb7ecf79fb3ac0/term_background-1.0.2-py39-none-any.whl", hash = "sha256:029291ba62573c637227801ecd43cb7388355610ffb7710d68a9406be210be80", size = 12214, upload-time = "2024-10-10T16:51:30.518Z" }, +] + +[[package]] +name = "thinc" +version = "8.3.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "blis" }, + { name = "catalogue" }, + { name = "confection" }, + { name = "cymem" }, + { name = "murmurhash" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "preshed" }, + { name = "pydantic" }, + { name = "setuptools" }, + { name = "srsly" }, + { name = "wasabi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/01/f4/7607f76c2e156a34b1961a941eb8407b84da4f515cc0903b44d44edf4f45/thinc-8.3.6.tar.gz", hash = "sha256:49983f9b7ddc4343a9532694a9118dd216d7a600520a21849a43b6c268ec6cad", size = 194218, upload-time = "2025-04-04T11:50:45.751Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/c8/a9250944fb9a0a4c65b5d456f3a87ee6c249b53962757d77c28df8fadb46/thinc-8.3.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c54705e45a710e49758192592a3e0a80482edfdf5c61fc99f5d27ae822f652c5", size = 890177, upload-time = "2025-04-04T11:50:07.543Z" }, + { url = "https://files.pythonhosted.org/packages/3b/89/1ac54b18d4de79872c633302a10825695a36cd2e552cb8d4fea820b7a357/thinc-8.3.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:91acdbf3041c0ac1775ede570535a779cdf1312c317cd054d7b9d200da685c23", size = 839410, upload-time = "2025-04-04T11:50:09.26Z" }, + { url = "https://files.pythonhosted.org/packages/37/76/e1a76ab42e4637c4b8988d59784cdc1169a532d3043c36d2faf1a8d95228/thinc-8.3.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5a1db861614f91ff127feecce681c2213777b2d3d1ee6644bcc8a886acf0595", size = 4195748, upload-time = "2025-04-04T11:50:10.92Z" }, + { url = "https://files.pythonhosted.org/packages/00/a9/c59ac3260e7aff6b9dc80f495f1846a80b490595db06d040b05205d1f7f8/thinc-8.3.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:512e461989df8a30558367061d63ae6f1a6b4abe3c016a3360ee827e824254e0", size = 4261270, upload-time = "2025-04-04T11:50:12.953Z" }, + { url = "https://files.pythonhosted.org/packages/e0/8e/e86c5cbc6ebe238aa747ef9e20a969f6faba9ebbe1cbce059119f9614dd6/thinc-8.3.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a087aea2a63e6b9ccde61163d5922553b58908e96f8ad49cd0fd2edeb43e063f", size = 5067567, upload-time = "2025-04-04T11:50:18.317Z" }, + { url = "https://files.pythonhosted.org/packages/fe/8a/16670e4de36231aab5b052c734ad716be29aab2c0d2f3d8dd9c8dd27fafc/thinc-8.3.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b1d85dd5d94bb75006864c7d99fd5b75d05b1602d571e7fcdb42d4521f962048", size = 5309405, upload-time = "2025-04-04T11:50:20.075Z" }, + { url = "https://files.pythonhosted.org/packages/58/08/5439dd15b661610d8a3b919f18065ebf0d664b6a54a3794206622a74c910/thinc-8.3.6-cp312-cp312-win_amd64.whl", hash = "sha256:1170d85294366127d97a27dd5896f4abe90e2a5ea2b7988de9a5bb8e1128d222", size = 1749275, upload-time = "2025-04-04T11:50:21.769Z" }, + { url = "https://files.pythonhosted.org/packages/a6/03/0ba9bec3057f4a9c0b7ba53839aebcbbbc28de3b91330cb8de74a885b8f6/thinc-8.3.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d8743ee8ad2d59fda018b57e5da102d6098bbeb0f70476f3fd8ceb9d215d88b9", size = 883375, upload-time = "2025-04-04T11:50:23.273Z" }, + { url = "https://files.pythonhosted.org/packages/ae/79/ac31cd25d1d973b824de10ebbc56788688aecdd8f56800daf8edfff45097/thinc-8.3.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:89dbeb2ca94f1033e90999a70e2bc9dd5390d5341dc1a3a4b8793d03855265c3", size = 832654, upload-time = "2025-04-04T11:50:24.871Z" }, + { url = "https://files.pythonhosted.org/packages/f8/0d/fb5e8e49dfb53cc02ce907f81002031c6f4fe7e7aa44b1004ea695630017/thinc-8.3.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89a5460695067aa6e4182515cfd2018263db77cc17b7031d50ed696e990797a8", size = 4158592, upload-time = "2025-04-04T11:50:26.403Z" }, + { url = "https://files.pythonhosted.org/packages/e5/42/c87990ca214b9910f33b110d3b1ac213407388d35376bc955ad45e5de764/thinc-8.3.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0aa8e32f49234569fd10c35b562ee2f9c0d51225365a6e604a5a67396a49f2c1", size = 4236211, upload-time = "2025-04-04T11:50:27.943Z" }, + { url = "https://files.pythonhosted.org/packages/fa/10/9975bcee4dd4634bfb87df0447d7fa86d6c9b2d9228e56d4adb98cc19cbc/thinc-8.3.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f432158b80cf75a096980470b790b51d81daf9c2822598adebfc3cb58588fd6c", size = 5049197, upload-time = "2025-04-04T11:50:29.583Z" }, + { url = "https://files.pythonhosted.org/packages/9b/34/e1b384009eb8ad2192770157961cd0c2e2712fedf49e1dfd902e3d9b9973/thinc-8.3.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:61fb33a22aba40366fa9018ab34580f74fc40be821ab8af77ac1fdbeac17243b", size = 5278543, upload-time = "2025-04-04T11:50:31.524Z" }, + { url = "https://files.pythonhosted.org/packages/f0/26/f77ef4bd174bfeac491237a4ca3f74ba2ee2f672004f76cff90f8407a489/thinc-8.3.6-cp313-cp313-win_amd64.whl", hash = "sha256:ddd7041946a427f6a9b0b49419353d02ad7eb43fe16724bfcc3bdeb9562040b1", size = 1746883, upload-time = "2025-04-04T11:50:33.038Z" }, +] + +[[package]] +name = "tqdm" +version = "4.67.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, +] + +[[package]] +name = "tracer" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/a8/9f47e80c5fb2a908dd6f23f3eb22a182956425a75ca70f88be0384315361/tracer-2.0.0.tar.gz", hash = "sha256:baec4888666191ed7fb25c38c526a81d6c748787cacb649d6f67fd75da17687a", size = 26255, upload-time = "2024-10-14T16:42:07.764Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/97/2c0a11e1175b7473b4dfb743fe5c3b59455c002c25f8582ed2e17f4d48f9/tracer-2.0.0-py311-none-any.whl", hash = "sha256:3cf6876ab71c83e4bd361883b306dd1b536486ecdaec2e508b73e1a09d64bff7", size = 9772, upload-time = "2024-10-14T16:41:02.087Z" }, + { url = "https://files.pythonhosted.org/packages/bc/9e/4a09e896e3208060cfa0aebd3d48d70494186967779980dccf0145f8bf48/tracer-2.0.0-py312-none-any.whl", hash = "sha256:8cf13a8305b9491d355c9f39a767c8bb069481f4bf4ac8d99c56a797f61dcc8a", size = 22087, upload-time = "2024-10-14T16:41:03.434Z" }, + { url = "https://files.pythonhosted.org/packages/40/c9/a1df0f756378dc8a54c3ebe43aa9d7ab18b4fd8be3d43622f218edf9f1f8/tracer-2.0.0-py313-none-any.whl", hash = "sha256:146e5f8d33a3c22dc1d274c956dd7c40f9763aada3e310e3b2beae0fb228bfa6", size = 22090, upload-time = "2024-10-14T16:41:04.357Z" }, +] + +[[package]] +name = "traitlets" +version = "5.14.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, +] + +[[package]] +name = "trepan3k" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "columnize" }, + { name = "pyficache" }, + { name = "pygments" }, + { name = "spark-parser" }, + { name = "term-background" }, + { name = "tracer" }, + { name = "xdis" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/7a/ee387cfc09857785e6926a0809a372655cd47003fee6626ebe11c639c8c0/trepan3k-1.3.1.tar.gz", hash = "sha256:26d9adc7e618848f749654c2541c132c6572489af232045ec72df3080b0dc610", size = 427621, upload-time = "2025-01-16T23:06:11.618Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/bf/097a53738f3d825289ea1b1b1f8943a73e7432c15db94710340427c76990/trepan3k-1.3.1-py3-none-any.whl", hash = "sha256:e2036d216b649b550f63ff47358b48f3cecedf61ef5870e2e59864acaa24fbdd", size = 586588, upload-time = "2025-01-16T23:06:43.062Z" }, +] + +[[package]] +name = "typer" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "rich" }, + { name = "shellingham" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/8c/7d682431efca5fd290017663ea4588bf6f2c6aad085c7f108c5dbc316e70/typer-0.16.0.tar.gz", hash = "sha256:af377ffaee1dbe37ae9440cb4e8f11686ea5ce4e9bae01b84ae7c63b87f1dd3b", size = 102625, upload-time = "2025-05-26T14:30:31.824Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/42/3efaf858001d2c2913de7f354563e3a3a2f0decae3efe98427125a8f441e/typer-0.16.0-py3-none-any.whl", hash = "sha256:1f79bed11d4d02d4310e3c1b7ba594183bcedb0ac73b27a9e5f28f6fb5b98855", size = 46317, upload-time = "2025-05-26T14:30:30.523Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423, upload-time = "2025-06-02T14:52:11.399Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839, upload-time = "2025-06-02T14:52:10.026Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, +] + +[[package]] +name = "tzdata" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, +] + +[[package]] +name = "urllib3" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672, upload-time = "2025-04-10T15:23:39.232Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload-time = "2025-04-10T15:23:37.377Z" }, +] + +[[package]] +name = "wasabi" +version = "0.10.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/69/41/0c31737ee1a29c8b829690ebb4ab988b1f489aa2c3efa115a732a9dd7997/wasabi-0.10.1.tar.gz", hash = "sha256:c8e372781be19272942382b14d99314d175518d7822057cb7a97010c4259d249", size = 28380, upload-time = "2022-07-28T08:17:54.968Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/74/bd566f876c2de097e75d525c2696fb9829009987a0d93a4fb3576778a0a8/wasabi-0.10.1-py3-none-any.whl", hash = "sha256:fe862cc24034fbc9f04717cd312ab884f71f51a8ecabebc3449b751c2a649d83", size = 26075, upload-time = "2022-07-28T08:17:53.504Z" }, +] + +[[package]] +name = "wcwidth" +version = "0.2.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301, upload-time = "2024-01-06T02:10:57.829Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166, upload-time = "2024-01-06T02:10:55.763Z" }, +] + +[[package]] +name = "weasel" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cloudpathlib" }, + { name = "confection" }, + { name = "packaging" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "smart-open" }, + { name = "srsly" }, + { name = "typer" }, + { name = "wasabi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a7/1a/9c522dd61b52939c217925d3e55c95f9348b73a66a956f52608e1e59a2c0/weasel-0.4.1.tar.gz", hash = "sha256:aabc210f072e13f6744e5c3a28037f93702433405cd35673f7c6279147085aa9", size = 38417, upload-time = "2024-05-15T08:52:54.765Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/87/abd57374044e1f627f0a905ac33c1a7daab35a3a815abfea4e1bafd3fdb1/weasel-0.4.1-py3-none-any.whl", hash = "sha256:24140a090ea1ac512a2b2f479cc64192fd1d527a7f3627671268d08ed5ac418c", size = 50270, upload-time = "2024-05-15T08:52:52.977Z" }, +] + +[[package]] +name = "wrapt" +version = "1.17.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", size = 55531, upload-time = "2025-01-14T10:35:45.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", size = 53799, upload-time = "2025-01-14T10:33:57.4Z" }, + { url = "https://files.pythonhosted.org/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", size = 38821, upload-time = "2025-01-14T10:33:59.334Z" }, + { url = "https://files.pythonhosted.org/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", size = 38919, upload-time = "2025-01-14T10:34:04.093Z" }, + { url = "https://files.pythonhosted.org/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", size = 88721, upload-time = "2025-01-14T10:34:07.163Z" }, + { url = "https://files.pythonhosted.org/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", size = 80899, upload-time = "2025-01-14T10:34:09.82Z" }, + { url = "https://files.pythonhosted.org/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", size = 89222, upload-time = "2025-01-14T10:34:11.258Z" }, + { url = "https://files.pythonhosted.org/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", size = 86707, upload-time = "2025-01-14T10:34:12.49Z" }, + { url = "https://files.pythonhosted.org/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", size = 79685, upload-time = "2025-01-14T10:34:15.043Z" }, + { url = "https://files.pythonhosted.org/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", size = 87567, upload-time = "2025-01-14T10:34:16.563Z" }, + { url = "https://files.pythonhosted.org/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", size = 36672, upload-time = "2025-01-14T10:34:17.727Z" }, + { url = "https://files.pythonhosted.org/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", size = 38865, upload-time = "2025-01-14T10:34:19.577Z" }, + { url = "https://files.pythonhosted.org/packages/ce/b9/0ffd557a92f3b11d4c5d5e0c5e4ad057bd9eb8586615cdaf901409920b14/wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", size = 53800, upload-time = "2025-01-14T10:34:21.571Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ef/8be90a0b7e73c32e550c73cfb2fa09db62234227ece47b0e80a05073b375/wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", size = 38824, upload-time = "2025-01-14T10:34:22.999Z" }, + { url = "https://files.pythonhosted.org/packages/36/89/0aae34c10fe524cce30fe5fc433210376bce94cf74d05b0d68344c8ba46e/wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", size = 38920, upload-time = "2025-01-14T10:34:25.386Z" }, + { url = "https://files.pythonhosted.org/packages/3b/24/11c4510de906d77e0cfb5197f1b1445d4fec42c9a39ea853d482698ac681/wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", size = 88690, upload-time = "2025-01-14T10:34:28.058Z" }, + { url = "https://files.pythonhosted.org/packages/71/d7/cfcf842291267bf455b3e266c0c29dcb675b5540ee8b50ba1699abf3af45/wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", size = 80861, upload-time = "2025-01-14T10:34:29.167Z" }, + { url = "https://files.pythonhosted.org/packages/d5/66/5d973e9f3e7370fd686fb47a9af3319418ed925c27d72ce16b791231576d/wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", size = 89174, upload-time = "2025-01-14T10:34:31.702Z" }, + { url = "https://files.pythonhosted.org/packages/a7/d3/8e17bb70f6ae25dabc1aaf990f86824e4fd98ee9cadf197054e068500d27/wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", size = 86721, upload-time = "2025-01-14T10:34:32.91Z" }, + { url = "https://files.pythonhosted.org/packages/6f/54/f170dfb278fe1c30d0ff864513cff526d624ab8de3254b20abb9cffedc24/wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", size = 79763, upload-time = "2025-01-14T10:34:34.903Z" }, + { url = "https://files.pythonhosted.org/packages/4a/98/de07243751f1c4a9b15c76019250210dd3486ce098c3d80d5f729cba029c/wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", size = 87585, upload-time = "2025-01-14T10:34:36.13Z" }, + { url = "https://files.pythonhosted.org/packages/f9/f0/13925f4bd6548013038cdeb11ee2cbd4e37c30f8bfd5db9e5a2a370d6e20/wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", size = 36676, upload-time = "2025-01-14T10:34:37.962Z" }, + { url = "https://files.pythonhosted.org/packages/bf/ae/743f16ef8c2e3628df3ddfd652b7d4c555d12c84b53f3d8218498f4ade9b/wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", size = 38871, upload-time = "2025-01-14T10:34:39.13Z" }, + { url = "https://files.pythonhosted.org/packages/3d/bc/30f903f891a82d402ffb5fda27ec1d621cc97cb74c16fea0b6141f1d4e87/wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", size = 56312, upload-time = "2025-01-14T10:34:40.604Z" }, + { url = "https://files.pythonhosted.org/packages/8a/04/c97273eb491b5f1c918857cd26f314b74fc9b29224521f5b83f872253725/wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", size = 40062, upload-time = "2025-01-14T10:34:45.011Z" }, + { url = "https://files.pythonhosted.org/packages/4e/ca/3b7afa1eae3a9e7fefe499db9b96813f41828b9fdb016ee836c4c379dadb/wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", size = 40155, upload-time = "2025-01-14T10:34:47.25Z" }, + { url = "https://files.pythonhosted.org/packages/89/be/7c1baed43290775cb9030c774bc53c860db140397047cc49aedaf0a15477/wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", size = 113471, upload-time = "2025-01-14T10:34:50.934Z" }, + { url = "https://files.pythonhosted.org/packages/32/98/4ed894cf012b6d6aae5f5cc974006bdeb92f0241775addad3f8cd6ab71c8/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", size = 101208, upload-time = "2025-01-14T10:34:52.297Z" }, + { url = "https://files.pythonhosted.org/packages/ea/fd/0c30f2301ca94e655e5e057012e83284ce8c545df7661a78d8bfca2fac7a/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", size = 109339, upload-time = "2025-01-14T10:34:53.489Z" }, + { url = "https://files.pythonhosted.org/packages/75/56/05d000de894c4cfcb84bcd6b1df6214297b8089a7bd324c21a4765e49b14/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", size = 110232, upload-time = "2025-01-14T10:34:55.327Z" }, + { url = "https://files.pythonhosted.org/packages/53/f8/c3f6b2cf9b9277fb0813418e1503e68414cd036b3b099c823379c9575e6d/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", size = 100476, upload-time = "2025-01-14T10:34:58.055Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b1/0bb11e29aa5139d90b770ebbfa167267b1fc548d2302c30c8f7572851738/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", size = 106377, upload-time = "2025-01-14T10:34:59.3Z" }, + { url = "https://files.pythonhosted.org/packages/6a/e1/0122853035b40b3f333bbb25f1939fc1045e21dd518f7f0922b60c156f7c/wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", size = 37986, upload-time = "2025-01-14T10:35:00.498Z" }, + { url = "https://files.pythonhosted.org/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750, upload-time = "2025-01-14T10:35:03.378Z" }, + { url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594, upload-time = "2025-01-14T10:35:44.018Z" }, +] + +[[package]] +name = "xdis" +version = "6.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/94/de880a3be7219203bcc81769613d950614e446990ab48dea9d33477f7848/xdis-6.1.4.tar.gz", hash = "sha256:6e749661ac114c7b66edf4bd200e95066963c14ea039119651eb081a8da08ce6", size = 437460, upload-time = "2025-05-26T15:27:46.866Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/8e/5988895297d6bf0464fd8fbe9eeeba9feb486cd633005100bd2cb9cbd8fe/xdis-6.1.4-py310-none-any.whl", hash = "sha256:fe0c07b82d4e2b9cbc28b19aa3d74ecfed8e3c9448d08fe91495d756350668f6", size = 224058, upload-time = "2025-05-26T15:26:35.391Z" }, + { url = "https://files.pythonhosted.org/packages/f9/2c/449ddd0499da041edf6b2ebcf1388b0ece46630819e94b2fa5710fce7ca9/xdis-6.1.4-py311-none-any.whl", hash = "sha256:b767726edb151ea60a081ad4acf673f5ac171882bf28dadf829110259b7646f7", size = 181268, upload-time = "2025-05-26T15:26:37.237Z" }, + { url = "https://files.pythonhosted.org/packages/5c/61/d6ce5daf33ac21c8a9809d6ca5cf63acca5c2f3bd0bba56f90c76f2b538f/xdis-6.1.4-py312-none-any.whl", hash = "sha256:b58ddf1698d9c22e573c7c97b7d1917162ceddc5c14b5a12c8d5042b3d3fb803", size = 188308, upload-time = "2025-05-26T15:26:38.181Z" }, + { url = "https://files.pythonhosted.org/packages/8e/f5/b6933dbd5da1f8e42c6cc704dff4551e458fd7075649bd503267f5c20a96/xdis-6.1.4-py313-none-any.whl", hash = "sha256:fac7b8e67c070102ba534f2630a39498afefe5bfc7d27741518f2b2b27c0d207", size = 188308, upload-time = "2025-05-26T15:26:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/47/43/63578140a59229c4a1d7439049d37e02863a7a0475be71041aa10e368988/xdis-6.1.4-py34-none-any.whl", hash = "sha256:0a6e393aa659f4003a9455a4d32c3b8a24cc02ab87863bfe17dbe43f0c29e354", size = 222948, upload-time = "2025-05-26T15:26:40.329Z" }, + { url = "https://files.pythonhosted.org/packages/ca/cc/3bf31c1a3e1bccf061296431c1a45b8cfbd1c66840ee86d0f7110a237656/xdis-6.1.4-py35-none-any.whl", hash = "sha256:2b030cbda54107a2b379f0ee76ad656b8799ebec72a3118d2d0b006c577e16f1", size = 222949, upload-time = "2025-05-26T15:26:41.378Z" }, + { url = "https://files.pythonhosted.org/packages/db/f9/35bb4a914439ea39392e55e2ef1825b51c78a54b89bb7375cc3ad81a4e05/xdis-6.1.4-py36-none-any.whl", hash = "sha256:64980c9e92e8134fc6d912af106249da2ae1209ee0cb4e127e0a6a74ee508b8f", size = 224080, upload-time = "2025-05-26T15:26:42.866Z" }, + { url = "https://files.pythonhosted.org/packages/f2/be/2a63f96542f5603a513bbb8f975afce9e87c184d426471a30d02e20a7324/xdis-6.1.4-py37-none-any.whl", hash = "sha256:fc929a900ba5806cbc5efa0d50b33be4b906fa03301bf5e6e2f06c8b11347d17", size = 224059, upload-time = "2025-05-26T15:26:44.6Z" }, + { url = "https://files.pythonhosted.org/packages/97/cf/8a46db3c71950aa7fa353203f78b9ab35870a2b2be19e2f025db31394f5a/xdis-6.1.4-py38-none-any.whl", hash = "sha256:b36dd09186eefe955347ba546735163b8e56f878c5702ec73ead4b6b45c5a309", size = 224062, upload-time = "2025-05-26T15:26:46.076Z" }, + { url = "https://files.pythonhosted.org/packages/a9/e9/dec1970a136784ebb1cf7f1381ab3cf3d2c4ae6ab0ad0f0d8bcc1e2787c7/xdis-6.1.4-py39-none-any.whl", hash = "sha256:b98fc10371a7ba3036ae5f8958ec8da3ec8c45115da2404e1fb561abeec323ac", size = 224077, upload-time = "2025-05-26T15:26:47.11Z" }, +] diff --git a/src2/PostNewtonian.jl b/src2/PostNewtonian.jl new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src2/PostNewtonian.jl @@ -0,0 +1 @@ + diff --git a/src2/core/PNExpansion.jl b/src2/core/PNExpansion.jl new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src2/core/PNExpansion.jl @@ -0,0 +1 @@ + diff --git a/src2/core/PNExpression.jl b/src2/core/PNExpression.jl new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src2/core/PNExpression.jl @@ -0,0 +1 @@ + diff --git a/src2/core/PNReference.jl b/src2/core/PNReference.jl new file mode 100644 index 00000000..c9134611 --- /dev/null +++ b/src2/core/PNReference.jl @@ -0,0 +1,38 @@ +""" +This file defines the `@pn_reference` macro, which is used to create a module for a specific +Post-Newtonian reference. Its purpose is to *un*define even the most basic operations, such +as addition, multiplication, and so on, to ensure that all operations are sufficiently +overridden by `@pn_expression`. For example, integer division `1/3` will result in a +`Float64` by default. But `@pn_expression` will extract the number type `NT` of the +`pnsystem` argument, and redefine integer division to return something compatible with `NT` +instead — possibly a `Double64` or `Sympy` expression, for example. + +The [julia docs +say](https://docs.julialang.org/en/v1/manual/modules/#Default-top-level-definitions-and-bare-modules) +that a standard module is essentially this: + +```julia +baremodule Mod + +using Base + +eval(x) = Core.eval(Mod, x) +include(p) = Base.include(Mod, p) + +... + +end +``` +We just want to replace that `using` with `import`. This will ensure that only operations +that are already handled by `@pn_expression` will be defined; if they aren't, there will be +an error. + +Similarly, we want the author to *have to* define every single variable to be used in the PN +expression. In most cases, the variable can just be imported from `common_variables` under +its own name. There may be cases, however, where the name needs to change, or a variable +needs to be defined just in the given module. In any case, this will make it easier for +`@pn_expression` to look through the current module for symbols that appear in the +expression that need to be replaced with a call to that function. + + +""" diff --git a/src2/core/PNTerm.jl b/src2/core/PNTerm.jl new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src2/core/PNTerm.jl @@ -0,0 +1 @@ + diff --git a/src2/interface/orbital_evolution.jl b/src2/interface/orbital_evolution.jl new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src2/interface/orbital_evolution.jl @@ -0,0 +1 @@ + diff --git a/src2/interface/pn.jl b/src2/interface/pn.jl new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src2/interface/pn.jl @@ -0,0 +1 @@ + diff --git a/src2/interface/waveform.jl b/src2/interface/waveform.jl new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src2/interface/waveform.jl @@ -0,0 +1 @@ + diff --git a/src2/literature/PetersMathews1963/PetersMathews1963.jl b/src2/literature/PetersMathews1963/PetersMathews1963.jl new file mode 100644 index 00000000..0cfcb026 --- /dev/null +++ b/src2/literature/PetersMathews1963/PetersMathews1963.jl @@ -0,0 +1,3 @@ +@pn_reference module PetersMathews1963 + +end diff --git a/src2/literature/common_variables.jl b/src2/literature/common_variables.jl new file mode 100644 index 00000000..80cfe481 --- /dev/null +++ b/src2/literature/common_variables.jl @@ -0,0 +1,10 @@ +module CommonVariables + +# Import the fundamental variables, which are generally part of the `state` vector of a +# PNSystem. +import PostNewtonian: M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ + +# Now, we define all the common variables used throughout the literature as functions of +# those fundamental variables. + +end diff --git a/src2/literature/ref1/ref1.jl b/src2/literature/ref1/ref1.jl new file mode 100644 index 00000000..1d23dc7a --- /dev/null +++ b/src2/literature/ref1/ref1.jl @@ -0,0 +1,10 @@ +module Ref1 + +import PostNewtonian: M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ +import CommonVariables: χₛ + +# import common variables +# define variables not in common variables +# write individual expressions as separate functions with `@pn_expression` + +end diff --git a/src2/pn_expressions/angular_momentum.jl b/src2/pn_expressions/angular_momentum.jl new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src2/pn_expressions/angular_momentum.jl @@ -0,0 +1 @@ + diff --git a/src2/pn_expressions/dynamics.jl b/src2/pn_expressions/dynamics.jl new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src2/pn_expressions/dynamics.jl @@ -0,0 +1 @@ + diff --git a/src2/pn_expressions/energy.jl b/src2/pn_expressions/energy.jl new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src2/pn_expressions/energy.jl @@ -0,0 +1 @@ + diff --git a/src2/pn_expressions/flux.jl b/src2/pn_expressions/flux.jl new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src2/pn_expressions/flux.jl @@ -0,0 +1 @@ + diff --git a/src2/pn_expressions/precession.jl b/src2/pn_expressions/precession.jl new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src2/pn_expressions/precession.jl @@ -0,0 +1 @@ + diff --git a/src2/pn_expressions/waveforms.jl b/src2/pn_expressions/waveforms.jl new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src2/pn_expressions/waveforms.jl @@ -0,0 +1 @@ + diff --git a/src2/pn_systems/BBH.jl b/src2/pn_systems/BBH.jl new file mode 100644 index 00000000..c8e4f373 --- /dev/null +++ b/src2/pn_systems/BBH.jl @@ -0,0 +1,61 @@ +abstract type AbstractBBH{NT,ST,PNOrder} <: PNSystem{NT,ST,PNOrder} end + +""" + BBH{T, PNOrder} + +The [`PNSystem`](@ref) subtype describing a binary black hole system. + +The `state` vector here holds the fundamental variables `M₁`, `M₂`, `χ⃗₁`, `χ⃗₂`, `R`, `v`, +with the spins unpacked into three components each, and `R` unpacked into four — for a total +of 13 elements. + +Optionally, `Φ` may also be tracked as the 14th element of the `state` vector. This is just +the integral of the orbital angular frequency `Ω`, and holds little interest for general +systems beyond a convenient description of how "far" the system has evolved. +""" +struct BBH{NT,ST,PNOrder} <: AbstractBBH{NT,ST,PNOrder} + state::ST + + BBH{NT,ST,PNOrder}(state) where {NT,ST,PNOrder} = new{NT,ST,PNOrder}(state) + function BBH(; M₁, M₂, χ⃗₁, χ⃗₂, v, R=Rotor(1), Φ=0, PNOrder=typemax(Int), kwargs...) + (NT, ST, PNOrder, state) = prepare_system(; M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ, PNOrder) + return new{NT,ST,PNOrder}(state) + end + function BBH(state; Λ₁=0, Λ₂=0, PNOrder=typemax(Int)) + @assert length(state) == 14 + @assert Λ₁ == 0 + @assert Λ₂ == 0 + return new{eltype(state),typeof(state),prepare_pn_order(PNOrder)}(state) + end +end +const BHBH = BBH + +state(pnsystem::BBH) = pnsystem.state + +function symbols(::Type{BBH}) + (:M₁, :M₂, :χ⃗₁ˣ, :χ⃗₁ʸ, :χ⃗₁ᶻ, :χ⃗₂ˣ, :χ⃗₂ʸ, :χ⃗₂ᶻ, :Rʷ, :Rˣ, :Rʸ, :Rᶻ, :v, :Φ) +end +function ascii_symbols(::Type{BBH}) + (:M1, :M2, :chi1x, :chi1y, :chi1z, :chi2x, :chi2y, :chi2z, :Rw, :Rx, :Ry, :Rz, :v, :Phi) +end + +M₁(pnsystem::BBH) = @inbounds state(pnsystem)[1] +M₂(pnsystem::BBH) = @inbounds state(pnsystem)[2] +χ⃗₁(pnsystem::BBH) = @inbounds state(pnsystem)[3:5] +χ⃗₁ˣ(pnsystem::BBH) = @inbounds state(pnsystem)[3] +χ⃗₁ʸ(pnsystem::BBH) = @inbounds state(pnsystem)[4] +χ⃗₁ᶻ(pnsystem::BBH) = @inbounds state(pnsystem)[5] +χ⃗₂(pnsystem::BBH) = @inbounds state(pnsystem)[6:8] +χ⃗₂ˣ(pnsystem::BBH) = @inbounds state(pnsystem)[6] +χ⃗₂ʸ(pnsystem::BBH) = @inbounds state(pnsystem)[7] +χ⃗₂ᶻ(pnsystem::BBH) = @inbounds state(pnsystem)[8] +R(pnsystem::BBH) = @inbounds state(pnsystem)[9:12] +Rʷ(pnsystem::BBH) = @inbounds state(pnsystem)[9] +Rˣ(pnsystem::BBH) = @inbounds state(pnsystem)[10] +Rʸ(pnsystem::BBH) = @inbounds state(pnsystem)[11] +Rᶻ(pnsystem::BBH) = @inbounds state(pnsystem)[12] +v(pnsystem::BBH) = @inbounds state(pnsystem)[13] +Φ(pnsystem::BBH) = @inbounds state(pnsystem)[14] + +# Λ₁(pnsystem::BBH) = @inbounds state(pnsystem)[15] +# Λ₂(pnsystem::BBH) = @inbounds state(pnsystem)[16] diff --git a/src2/pn_systems/PNSystem.jl b/src2/pn_systems/PNSystem.jl new file mode 100644 index 00000000..62106265 --- /dev/null +++ b/src2/pn_systems/PNSystem.jl @@ -0,0 +1,164 @@ +# TODO: Write the docstrings for all the general state functions +# TODO: Change to subtyping SVector or MVector +# - similar_type(::Type{MyPNSystem}, ::Type{NewElType}) +# - similar(::Type{MyPNSystem}, ::Type{NewElType}) for MVector +# - setindex! for MVector +# - zero-parameter constructor for MVector + +""" + PNSystem{NT, PNOrder} + +Base type for all PN systems, such as `BBH`, `BHNS`, and `NSNS`. + +These objects encode all essential properties of the binary, including its current state. +As such, they can be used as inputs to the various [fundamental](@ref Fundamental-variables) +and [derived variables](@ref Derived-variables), as well as [PN expressions](@ref) and +[dynamics](@ref Dynamics) functions. + +All subtypes should contain a `state` vector holding all of the fundamental variables for +the given type of system. The parameter `ST` is the type of the `state` vector — for +example, `Vector{Float64}`. `PNOrder` is a `Rational` giving the order to which PN +expansions should be carried. +""" +abstract type PNSystem{NT,PNOrder} <: Vector{NT} end + +""" + state(pnsystem::PNSystem) + +Return the state vector of `pnsystem`, which is a vector of fundamental variables for the +given PN system. + +Note that the built-in `PNSystem` subtypes have a `state` field that is a vector, so this +function will just return that vector. However, that may not always be true for +user-defined subtypes. +""" +function state(::T) where {T<:PNSystem} + error("`state` is not yet defined for PNSystem subtype `$T`.") +end +Base.vec(pnsystem::PNSystem) = state(pnsystem) + +Base.one(::PNSystem{NT}) where {NT} = one(NT) +Base.zero(::PNSystem{NT}) where {NT} = zero(NT) + +""" + G(pnsystem) + +Return Newton's gravitational constant for the given `pnsystem`. + +By default, the value is one *with the same number type as `pnsystem`*. It can be +overridden for subtypes of `PNSystem` that use different units or conventions. + +However, note that this function should specialize on the number type of `pnsystem`, rather +than just returning the integer `1`, because there may be expressions with factors such as +`G/3` which will immediately convert to `Float64` if `G` is just `1`, so the result will not +have the expected precision. +""" +G(::PNSystem{NT}) where {NT} = one(NT) + +""" + c(pnsystem) + +Return the speed of light for the given `pnsystem`. + +By default, the value is one *with the same number type as `pnsystem`*. It can be +overridden for subtypes of `PNSystem` that use different units or conventions. + +However, note that this function should specialize on the number type of `pnsystem`, rather +than just returning the integer `1`, because there may be expressions with factors such as +`c/3` which will immediately convert to `Float64` if `c` is just `1`, so the result will not +have the expected precision. +""" +c(::PNSystem{NT}) where {NT} = one(NT) + +""" + symbols(pnsystem::PNSystem) + symbols(::Type{<:PNSystem}) + ascii_symbols(pnsystem::PNSystem) + ascii_symbols(::Type{<:PNSystem}) + +Return a Tuple of symbols corresponding to the variables tracked by `pnsystem`, in the order +in which they are stored in the `state` vector. +""" +symbols(pnsystem::PNSystem) = symbols(typeof(pnsystem)) +function symbols(::Type{T}) where {T<:PNSystem} + error("`symbols`is not yet defined for PNSystem subtype `$T`.") +end +ascii_symbols(pnsystem::PNSystem) = ascii_symbols(typeof(pnsystem)) +function ascii_symbols(::Type{T}) where {T<:PNSystem} + error("`ascii_symbols` is not yet defined for PNSystem subtype `$T`.") +end + +# Define all fundamental variables as functions of a PNSystem, but undefined for the +# abstract type. +for var ∈ ( + :M₁, + :M₂, + :χ⃗₁, + :χ⃗₁ˣ, + :χ⃗₁ʸ, + :χ⃗₁ᶻ, + :χ⃗₂, + :χ⃗₂ˣ, + :χ⃗₂ʸ, + :χ⃗₂ᶻ, + :R, + :Rʷ, + :Rˣ, + :Rʸ, + :Rᶻ, + :v, + :Φ, + :Λ₁, + :Λ₂, +) + @eval begin + function $var(::T) where {T<:PNSystem} + error("$var is not (yet) defined for PNSystem subtype `$T`.") + end + end +end + +const M1 = M₁ +const M2 = M₂ +const chi1 = χ⃗₁ +const chi1x = χ⃗₁ˣ +const chi1y = χ⃗₁ʸ +const chi1z = χ⃗₁ᶻ +const chi2 = χ⃗₂ +const chi2x = χ⃗₂ˣ +const chi2y = χ⃗₂ʸ +const chi2z = χ⃗₂ᶻ +# const R = R +const Rw = Rʷ +const Rx = Rˣ +const Ry = Rʸ +const Rz = Rᶻ +# const v = v +const Phi = Φ +const Lambda1 = Λ₁ +const Lambda2 = Λ₂ + +### Interfaces: https://docs.julialang.org/en/v1/manual/interfaces +# Iteration +Base.iterate(pnsystem::PNSystem) = iterate(state(pnsystem)) +Base.iterate(pnsystem::PNSystem, state) = iterate(state(pnsystem), state) +Base.length(pnsystem::PNSystem) = length(state(pnsystem)) +Base.IteratorSize(::Type{T}) where {T<:PNSystem} = Base.IteratorSize(Vector) +Base.length(pnsystem::PNSystem) = length(state(pnsystem)) +Base.size(pnsystem::PNSystem) = size(state(pnsystem)) +Base.size(pnsystem::PNSystem, dim) = size(state(pnsystem), dim) +Base.IteratorEltype(::Type{T}) where {T<:PNSystem} = Base.IteratorEltype(Vector) +Base.eltype(::Type{T}) where {NT,T<:PNSystem{NT}} = NT +Base.isdone(pnsystem::PNSystem) = isdone(state(pnsystem)) +Base.isdone(pnsystem::PNSystem, iterstate) = isdone(state(pnsystem), iterstate) +# Indexing +Base.getindex(pnsystem::PNSystem, i) = @propagate_inbounds getindex(state(pnsystem), i) +Base.setindex!(pnsys::PNSystem, v, i) = @propagate_inbounds setindex!(state(pnsys), v, i) +Base.firstindex(pnsystem::PNSystem) = firstindex(state(pnsystem)) +Base.lastindex(pnsystem::PNSystem) = lastindex(state(pnsystem)) +# Abstract arrays +Base.size(pnsystem::PNSystem) = size(state(pnsystem)) +Base.IndexStyle(::Type{T}) where {T<:PNSystem} = Base.IndexStyle(Vector) +Base.length(pnsystem::PNSystem) = length(state(pnsystem)) +# Base.similar(pnsystem::PNSystem) = similar(state(pnsystem)) +Base.axes(pnsystem::PNSystem) = axes(state(pnsystem)) From c2dfc3194a4768c3ea83f458ed8b019836be5ecf Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Wed, 25 Jun 2025 12:19:13 -0400 Subject: [PATCH 03/77] Reorganize src; fully broken package --- src/PostNewtonian.jl | 215 +---------- src/compatibility_layers.jl | 1 - src/{pn_expansion.jl => core/PNExpansion.jl} | 0 {src2 => src}/core/PNExpression.jl | 0 {src2 => src}/core/PNReference.jl | 8 +- {src2 => src}/core/PNTerm.jl | 0 src2/PostNewtonian.jl => src/core/core.jl | 0 src/{ => core}/utilities/combine_solutions.jl | 0 src/{ => core}/utilities/macros.jl | 0 src/{ => core}/utilities/mathconstants.jl | 0 src/{ => core}/utilities/misc.jl | 0 .../utilities/termination_criteria.jl | 0 .../utilities/truncated_series_inversion.jl | 0 .../utilities/truncated_series_monoid.jl | 0 src/dynamics.jl | 3 - src/fundamental_variables.jl | 163 --------- .../assorted_binaries/examples.jl | 0 .../assorted_binaries/random.jl | 0 .../compatibility_layers/gwframes.jl | 0 .../dynamics/orbital_evolution.jl | 0 .../dynamics/right_hand_sides.jl | 0 .../dynamics/up_down_instability.jl | 0 .../interface/interface.jl | 0 {src2 => src}/interface/orbital_evolution.jl | 0 {src2 => src}/interface/pn.jl | 0 src/{waveforms.jl => interface/waveform.jl} | 0 .../common_variables/common_variables.jl} | 11 +- .../common_variables}/horizons.jl | 0 .../common_variables}/mass_combinations.jl | 0 .../common_variables}/orbital_elements.jl | 0 .../common_variables}/spin_combinations.jl | 0 .../common_variables}/tidal_coupling.jl | 0 .../literature/literature.jl | 0 src/pn_expressions.jl | 7 - .../pn_expressions/angular_momentum.jl | 0 {src2 => src}/pn_expressions/dynamics.jl | 0 .../pn_expressions/pn_expressions.jl | 0 .../flux.jl => src/pn_expressions/waveform.jl | 0 src/pn_systems.jl | 312 ---------------- src/pn_systems/BBH.jl | 117 ++++++ src/pn_systems/BHNS.jl | 80 ++++ src/pn_systems/FDPNsystem.jl | 34 ++ src/pn_systems/NSNS.jl | 98 +++++ src/pn_systems/PNSystem.jl | 112 ++++++ src/pn_systems/pn_order.jl | 42 +++ src/pn_systems/pn_systems.jl | 8 + src/pn_systems/state_variables.jl | 342 ++++++++++++++++++ src/pn_systems/vector_interface.jl | 249 +++++++++++++ src/utilities.jl | 9 - .../PetersMathews1963/PetersMathews1963.jl | 3 - src2/literature/common_variables.jl | 10 - src2/literature/ref1/ref1.jl | 10 - src2/pn_expressions/precession.jl | 1 - src2/pn_expressions/waveforms.jl | 1 - src2/pn_systems/BBH.jl | 61 ---- src2/pn_systems/PNSystem.jl | 164 --------- 56 files changed, 1100 insertions(+), 961 deletions(-) delete mode 100644 src/compatibility_layers.jl rename src/{pn_expansion.jl => core/PNExpansion.jl} (100%) rename {src2 => src}/core/PNExpression.jl (100%) rename {src2 => src}/core/PNReference.jl (81%) rename {src2 => src}/core/PNTerm.jl (100%) rename src2/PostNewtonian.jl => src/core/core.jl (100%) rename src/{ => core}/utilities/combine_solutions.jl (100%) rename src/{ => core}/utilities/macros.jl (100%) rename src/{ => core}/utilities/mathconstants.jl (100%) rename src/{ => core}/utilities/misc.jl (100%) rename src/{ => core}/utilities/termination_criteria.jl (100%) rename src/{ => core}/utilities/truncated_series_inversion.jl (100%) rename src/{ => core}/utilities/truncated_series_monoid.jl (100%) delete mode 100644 src/dynamics.jl delete mode 100644 src/fundamental_variables.jl rename src/{ => interface}/assorted_binaries/examples.jl (100%) rename src/{ => interface}/assorted_binaries/random.jl (100%) rename src/{ => interface}/compatibility_layers/gwframes.jl (100%) rename src/{ => interface}/dynamics/orbital_evolution.jl (100%) rename src/{ => interface}/dynamics/right_hand_sides.jl (100%) rename src/{ => interface}/dynamics/up_down_instability.jl (100%) rename src2/core/PNExpansion.jl => src/interface/interface.jl (100%) rename {src2 => src}/interface/orbital_evolution.jl (100%) rename {src2 => src}/interface/pn.jl (100%) rename src/{waveforms.jl => interface/waveform.jl} (100%) rename src/{derived_variables.jl => literature/common_variables/common_variables.jl} (76%) rename src/{derived_variables => literature/common_variables}/horizons.jl (100%) rename src/{derived_variables => literature/common_variables}/mass_combinations.jl (100%) rename src/{derived_variables => literature/common_variables}/orbital_elements.jl (100%) rename src/{derived_variables => literature/common_variables}/spin_combinations.jl (100%) rename src/{derived_variables => literature/common_variables}/tidal_coupling.jl (100%) rename src2/interface/waveform.jl => src/literature/literature.jl (100%) delete mode 100644 src/pn_expressions.jl rename {src2 => src}/pn_expressions/angular_momentum.jl (100%) rename {src2 => src}/pn_expressions/dynamics.jl (100%) rename src2/pn_expressions/energy.jl => src/pn_expressions/pn_expressions.jl (100%) rename src2/pn_expressions/flux.jl => src/pn_expressions/waveform.jl (100%) delete mode 100644 src/pn_systems.jl create mode 100644 src/pn_systems/BBH.jl create mode 100644 src/pn_systems/BHNS.jl create mode 100644 src/pn_systems/FDPNsystem.jl create mode 100644 src/pn_systems/NSNS.jl create mode 100644 src/pn_systems/PNSystem.jl create mode 100644 src/pn_systems/pn_order.jl create mode 100644 src/pn_systems/pn_systems.jl create mode 100644 src/pn_systems/state_variables.jl create mode 100644 src/pn_systems/vector_interface.jl delete mode 100644 src/utilities.jl delete mode 100644 src2/literature/PetersMathews1963/PetersMathews1963.jl delete mode 100644 src2/literature/common_variables.jl delete mode 100644 src2/literature/ref1/ref1.jl delete mode 100644 src2/pn_expressions/precession.jl delete mode 100644 src2/pn_expressions/waveforms.jl delete mode 100644 src2/pn_systems/BBH.jl delete mode 100644 src2/pn_systems/PNSystem.jl diff --git a/src/PostNewtonian.jl b/src/PostNewtonian.jl index d4859708..a910c6c1 100644 --- a/src/PostNewtonian.jl +++ b/src/PostNewtonian.jl @@ -1,214 +1,9 @@ module PostNewtonian -# Always explicitly address functions similar to functions defined in this package, -# which come from these packages: -using MacroTools: MacroTools -using FastDifferentiation: FastDifferentiation -using RuntimeGeneratedFunctions: RuntimeGeneratedFunctions - -# Otherwise, we just explicitly import specific functions: -using DataInterpolations: CubicSpline -using InteractiveUtils: methodswith -using LinearAlgebra: mul! -using Random: AbstractRNG, default_rng -using Quaternionic: QuatVec, Rotor, abs2vec, components, normalize, ⋅, × -using SphericalFunctions: D!, Diterator, Dprep, Yiterator -using OrdinaryDiffEqVerner: Vern9 -using SciMLBase: - ODEFunction, - ODEProblem, - solve, - remake, - terminate!, - CallbackSet, - DiscreteCallback, - VectorContinuousCallback, - ODESolution, - parameterless_type, - FullSpecialize, - AbstractDiffEqInterpolation, - build_solution, - get_du -using SciMLBase.ReturnCode: ReturnCode -using SymbolicIndexingInterface: SymbolCache -using RecursiveArrayTools: DiffEqArray -using StaticArrays: SVector, MVector -using TestItems: @testitem - -# See the "Code structure" section of the documentation for a description of the simple -# hierarchy into which this code is organized. The different levels of that hierarchy are -# reflected cleanly in the files `include`d below. - -# It's more common in PN to use `ln` — which I also prefer, as `log` seems ambiguous. -const ln = log - -include("utilities.jl") -export termination_forwards, - termination_backwards, dtmin_terminator, decreasing_v_terminator, nonfinite_terminator -using .MathConstants - -include("pn_systems.jl") -export PNSystem, pn_order, BBH, BHBH, BHNS, NSNS, BNS, FDPNSystem, fd_pnsystem - -include("pn_expansion.jl") -export PNExpansion, PNTerm, PNExpansionParameter - -include("fundamental_variables.jl") -using .FundamentalVariables -#export M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ, Λ₁, Λ₂ # Avoid clashes: don't export - -include("derived_variables.jl") -using .DerivedVariables -export total_mass, # M, # Avoid clashes: don't export nicer names for important variables - reduced_mass, # μ, - reduced_mass_ratio, # ν, - mass_difference_ratio, # δ, - mass_ratio, # q, - chirp_mass, # ℳ, - # X1, X₁, - # X2, X₂, - n_hat, - n̂, - lambda_hat, - λ̂, - ell_hat, - ℓ̂, - Omega, - Ω, - S⃗₁, - S⃗₂, - S⃗, - Σ⃗, - χ⃗, - χ⃗ₛ, - χ⃗ₐ, - chi_perp, - χₚₑᵣₚ, - chi_eff, - χₑ, - chi_p, - χₚ, - S⃗₀⁺, - S⃗₀⁻, - S₀⁺ₙ, - S₀⁻ₙ, - S₀⁺λ, - S₀⁻λ, - S₀⁺ₗ, - S₀⁻ₗ, - χ₁², - χ₂², - χ₁, - χ₂, - χ₁₂, - χ₁ₗ, - χ₂ₗ, - χₛₗ, - χₐₗ, - Sₙ, - Σₙ, - Sλ, - Σλ, - Sₗ, - Σₗ, - sₗ, - σₗ, - S₁ₙ, - S₁λ, - S₁ₗ, - S₂ₙ, - S₂λ, - S₂ₗ, - rₕ₁, - rₕ₂, - Ωₕ₁, - Ωₕ₂, - sin²θ₁, - sin²θ₂, - ϕ̇̂₁, - ϕ̇̂₂, - Î₀₁, - Î₀₂, - κ₁, - κ₂, - κ₊, - κ₋, - λ₁, - λ₂, - λ₊, - λ₋, - Λ̃, - Lambda_tilde - -include("pn_expressions.jl") -export gw_energy_flux, - 𝓕, - tidal_heating, - binding_energy, - 𝓔, - binding_energy_deriv, - 𝓔′, - Omega_p, - Ω⃗ₚ, - Omega_chi1, - Ω⃗ᵪ₁, - Omega_chi2, - Ω⃗ᵪ₂, - #𝛡, aₗ, Ω⃗ᵪ # Too obscure to bother with - γₚₙ, - inverse_separation, - γₚₙ′, - inverse_separation_deriv, - γₚₙ⁻¹, - inverse_separation_inverse, - separation, # r, - separation_deriv, # r′, - separation_dot, # ṙ, - separation_inverse, # r⁻¹, - mode_weights!, - h! - -include("dynamics.jl") -export up_down_instability, - estimated_time_to_merger, fISCO, ΩISCO, uniform_in_phase, orbital_evolution - -include("waveforms.jl") -export coorbital_waveform, - inertial_waveform, - coorbital_waveform_computation_storage, - inertial_waveform_computation_storage, - coorbital_waveform!, - inertial_waveform! - -include("compatibility_layers.jl") -export GWFrames - -include("assorted_binaries/examples.jl") -export superkick, hangup_kick -include("assorted_binaries/random.jl") -# Base.rand is the only function in that file, hence no need for exports - -include("precompilation.jl") - -include("predefinitions_Symbolics.jl") - -if !isdefined(Base, :get_extension) - using Requires -end - -@static if !isdefined(Base, :get_extension) - # COV_EXCL_START - - function __init__() - @require Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" include( - "../ext/PostNewtonianSymbolicsExt.jl" - ) - @require ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" include( - "../ext/PostNewtonianForwardDiffExt.jl" - ) - end - - # COV_EXCL_STOP -end +include("core/core.jl") +include("pn_systems/pn_systems.jl") +include("literature/literature.jl") +include("pn_expressions/pn_expressions.jl") +include("interface/interface.jl") end # module PostNewtonian diff --git a/src/compatibility_layers.jl b/src/compatibility_layers.jl deleted file mode 100644 index dccb1c83..00000000 --- a/src/compatibility_layers.jl +++ /dev/null @@ -1 +0,0 @@ -include("compatibility_layers/gwframes.jl") diff --git a/src/pn_expansion.jl b/src/core/PNExpansion.jl similarity index 100% rename from src/pn_expansion.jl rename to src/core/PNExpansion.jl diff --git a/src2/core/PNExpression.jl b/src/core/PNExpression.jl similarity index 100% rename from src2/core/PNExpression.jl rename to src/core/PNExpression.jl diff --git a/src2/core/PNReference.jl b/src/core/PNReference.jl similarity index 81% rename from src2/core/PNReference.jl rename to src/core/PNReference.jl index c9134611..0ec4afb3 100644 --- a/src2/core/PNReference.jl +++ b/src/core/PNReference.jl @@ -30,9 +30,9 @@ an error. Similarly, we want the author to *have to* define every single variable to be used in the PN expression. In most cases, the variable can just be imported from `common_variables` under its own name. There may be cases, however, where the name needs to change, or a variable -needs to be defined just in the given module. In any case, this will make it easier for -`@pn_expression` to look through the current module for symbols that appear in the -expression that need to be replaced with a call to that function. - +needs to be defined just in the given module. One prominent example is that some authors +use `η` where we use `ν`. In any case, this will make it easier for `@pn_expression` to +look through the current module for symbols that appear in the expression that need to be +replaced with a call to that function. """ diff --git a/src2/core/PNTerm.jl b/src/core/PNTerm.jl similarity index 100% rename from src2/core/PNTerm.jl rename to src/core/PNTerm.jl diff --git a/src2/PostNewtonian.jl b/src/core/core.jl similarity index 100% rename from src2/PostNewtonian.jl rename to src/core/core.jl diff --git a/src/utilities/combine_solutions.jl b/src/core/utilities/combine_solutions.jl similarity index 100% rename from src/utilities/combine_solutions.jl rename to src/core/utilities/combine_solutions.jl diff --git a/src/utilities/macros.jl b/src/core/utilities/macros.jl similarity index 100% rename from src/utilities/macros.jl rename to src/core/utilities/macros.jl diff --git a/src/utilities/mathconstants.jl b/src/core/utilities/mathconstants.jl similarity index 100% rename from src/utilities/mathconstants.jl rename to src/core/utilities/mathconstants.jl diff --git a/src/utilities/misc.jl b/src/core/utilities/misc.jl similarity index 100% rename from src/utilities/misc.jl rename to src/core/utilities/misc.jl diff --git a/src/utilities/termination_criteria.jl b/src/core/utilities/termination_criteria.jl similarity index 100% rename from src/utilities/termination_criteria.jl rename to src/core/utilities/termination_criteria.jl diff --git a/src/utilities/truncated_series_inversion.jl b/src/core/utilities/truncated_series_inversion.jl similarity index 100% rename from src/utilities/truncated_series_inversion.jl rename to src/core/utilities/truncated_series_inversion.jl diff --git a/src/utilities/truncated_series_monoid.jl b/src/core/utilities/truncated_series_monoid.jl similarity index 100% rename from src/utilities/truncated_series_monoid.jl rename to src/core/utilities/truncated_series_monoid.jl diff --git a/src/dynamics.jl b/src/dynamics.jl deleted file mode 100644 index e03d7e17..00000000 --- a/src/dynamics.jl +++ /dev/null @@ -1,3 +0,0 @@ -include("dynamics/up_down_instability.jl") -include("dynamics/right_hand_sides.jl") -include("dynamics/orbital_evolution.jl") diff --git a/src/fundamental_variables.jl b/src/fundamental_variables.jl deleted file mode 100644 index db75b10d..00000000 --- a/src/fundamental_variables.jl +++ /dev/null @@ -1,163 +0,0 @@ -module FundamentalVariables - -using ..PostNewtonian -using ..PostNewtonian: PNSystem, BHNS, NSNS, FDPNSystem -using ..PostNewtonian: M₁index, M₂index, χ⃗₁indices, χ⃗₂indices, Rindices, vindex, Φindex -using Quaternionic: Quaternionic, QuatVec, Rotor - -export M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ, Λ₁, Λ₂, M1, M2, chi1, chi2, Phi, Lambda1, Lambda2 - -## NOTE: -## This indices used below are intimately bound to choices made in the definitions of -## the various `PNSystem`s. Any changes there must be mirrored here, and vice versa. - -""" - M₁(pnsystem) - M1(pnsystem) - -Mass of object 1 in this system. -""" -M₁(s::PNSystem) = M₁(s.state) -M₁(state::AbstractVector) = @inbounds state[M₁index] -const M1 = M₁ - -""" - M₂(pnsystem) - M2(pnsystem) - -Mass of object 2 in this system. -""" -M₂(s::PNSystem) = M₂(s.state) -M₂(state::AbstractVector) = @inbounds state[M₂index] -const M2 = M₂ - -""" - χ⃗₁(pnsystem) - chi1(pnsystem) - -Dimensionless spin vector of object 1 in this system, as a `QuatVec`. -""" -χ⃗₁(s::PNSystem) = χ⃗₁(s.state) -χ⃗₁(state::AbstractVector) = @inbounds QuatVec(view(state, χ⃗₁indices)...) -const chi1 = χ⃗₁ - -""" - χ⃗₂(pnsystem) - chi2(pnsystem) - -Dimensionless spin vector of object 2 in this system, as a `QuatVec`. -""" -χ⃗₂(s::PNSystem) = χ⃗₂(s.state) -χ⃗₂(state::AbstractVector) = @inbounds QuatVec(view(state, χ⃗₂indices)...) -const chi2 = χ⃗₂ - -""" - R(pnsystem) - -Orientation of the binary, as a `Rotor`. - -At any instant, the binary is represented by the right-handed triad ``(n̂, λ̂, ℓ̂)``, where -[``n̂``](@ref PostNewtonian.n̂) is the unit vector pointing from object 2 to object 1, and -the instantaneous velocities of the binary's elements are in the ``n̂``-``λ̂`` plane. This -`Rotor` will rotate the ``x̂`` vector to be along ``n̂``, the ``ŷ`` vector to be along -``λ̂``, and the ``ẑ`` vector to be along ``ℓ̂``. - -Note that the angular velocity associated to `R` is given by ``Ω⃗ = 2 Ṙ R̄ = Ω ℓ̂ + ϖ n̂``. -(Any component of ``Ω⃗`` along ``λ̂`` would violate the condition that the velocities be in -the ``n̂``-``λ̂`` plane.) Here, the scalar quantity ``Ω`` is the orbital angular frequency, -and ``ϖ`` is the precession angular frequency. - -See also [`n̂`](@ref PostNewtonian.n̂), [`λ̂`](@ref PostNewtonian.λ̂), [`ℓ̂`](@ref -PostNewtonian.ℓ̂), [`Ω`](@ref PostNewtonian.Ω), and [`𝛡`](@ref PostNewtonian.𝛡)``=ϖ n̂``. -""" -R(s::PNSystem) = R(s.state) -R(state::AbstractVector) = @inbounds Rotor(view(state, Rindices)...) - -@doc raw""" - v(pnsystem) - v(;Ω, M=1) - -Post-Newtonian velocity parameter. This is related to the orbital angular frequency -``\Omega`` as -```math -v \colonequals (M\,\Omega)^{1/3}, -``` -where ``M`` is the total mass of the binary. - -Note that if you want to pass the value ``Ω`` (rather than a `PNSystem`), you must pass it -as a keyword argument — as in `v(Ω=0.1)`. - -See also [`Ω`](@ref). -""" -v(s::PNSystem) = v(s.state) -v(state::AbstractVector) = @inbounds state[vindex] -v(; Ω, M=1) = ∛(M * Ω) - -""" - Φ(pnsystem) - Phi(pnsystem) - -Integrated orbital phase of the system. It is computed as the integral of [`Ω`](@ref). -""" -Φ(s::PNSystem) = Φ(s.state) -Φ(state::AbstractVector) = @inbounds state[Φindex] -const Phi = Φ - -@doc raw""" - Λ₁(pnsystem) - Lambda1(pnsystem) - -Quadrupolar tidal-coupling parameter of object 1 in this system. - -We imagine object 1 begin placed in an (adiabatic) external field with Newtonian potential -``\phi``, resulting in a tidal field measured by ``\partial_i \partial_j \phi`` evaluated at -the center of mass of the object. This induces a quadrupole moment ``Q_{ij}`` in object 1, -which can be related to the tidal field as -```math -Q_{ij} = -\frac{G^4}{c^{10}} \Lambda_1 M_1^5 \partial_i \partial_j \phi, -``` -where ``M_1`` is the mass of object 1. This tidal-coupling parameter ``\Lambda_1`` can be -related to the Love number ``k_2`` (where the subscript 2 refers to the fact that this is -for the ``\ell=2`` quadrupole, rather than object 2) as -```math -\Lambda_1 = \frac{2}{3} \frac{c^{10}}{G^5} \frac{R_1^5}{M_1^5} k_2, -``` -where ``R_1`` is the radius of object 1. Note that ``\Lambda_1`` is dimensionless. For -black holes, it is precisely zero; for neutron stars it may range up to 1; more exotic -objects may have significantly larger values. - -Note that — as of this writing — only `NSNS` systems can have a nonzero value for this -quantity. (`BHNS` systems can only have a nonzero value for ``\Lambda_2``.) All other -types return `0`, which Julia can use to eliminate code that would then be 0. Thus, it is -safe and efficient to use this quantity in any PN expression that specializes on the type of -`pnsystem`. - -See also [`Λ₂`](@ref) and [`Λ̃`](@ref). -""" -Λ₁(pn::PNSystem) = zero(eltype(pn)) -Λ₁(pn::NSNS) = pn.Λ₁ -Λ₁(pn::FDPNSystem) = pn.Λ₁ -const Lambda1 = Λ₁ - -@doc raw""" - Λ₂(pnsystem) - Lambda2(pnsystem) - -Quadrupolar tidal coupling parameter of object 2 in this system. - -See [`Λ₁`](@ref) for details about the definition, swapping "object 1" with "object 2". - -Note that — as of this writing — only `BHNS` and `NSNS` systems can have a nonzero value for -this quantity. All other types return `0`, which Julia can use to eliminate code that would -then be 0. Thus, it is safe and efficient to use this quantity in any PN expression that -specializes on the type of `pnsystem`. - -See also [`Λ₁`](@ref) and [`Λ̃`](@ref). -""" -Λ₂(pn::PNSystem) = zero(eltype(pn)) -Λ₂(pn::BHNS) = pn.Λ₂ -Λ₂(pn::NSNS) = pn.Λ₂ -Λ₂(pn::FDPNSystem) = pn.Λ₂ -const Lambda2 = Λ₂ - -end diff --git a/src/assorted_binaries/examples.jl b/src/interface/assorted_binaries/examples.jl similarity index 100% rename from src/assorted_binaries/examples.jl rename to src/interface/assorted_binaries/examples.jl diff --git a/src/assorted_binaries/random.jl b/src/interface/assorted_binaries/random.jl similarity index 100% rename from src/assorted_binaries/random.jl rename to src/interface/assorted_binaries/random.jl diff --git a/src/compatibility_layers/gwframes.jl b/src/interface/compatibility_layers/gwframes.jl similarity index 100% rename from src/compatibility_layers/gwframes.jl rename to src/interface/compatibility_layers/gwframes.jl diff --git a/src/dynamics/orbital_evolution.jl b/src/interface/dynamics/orbital_evolution.jl similarity index 100% rename from src/dynamics/orbital_evolution.jl rename to src/interface/dynamics/orbital_evolution.jl diff --git a/src/dynamics/right_hand_sides.jl b/src/interface/dynamics/right_hand_sides.jl similarity index 100% rename from src/dynamics/right_hand_sides.jl rename to src/interface/dynamics/right_hand_sides.jl diff --git a/src/dynamics/up_down_instability.jl b/src/interface/dynamics/up_down_instability.jl similarity index 100% rename from src/dynamics/up_down_instability.jl rename to src/interface/dynamics/up_down_instability.jl diff --git a/src2/core/PNExpansion.jl b/src/interface/interface.jl similarity index 100% rename from src2/core/PNExpansion.jl rename to src/interface/interface.jl diff --git a/src2/interface/orbital_evolution.jl b/src/interface/orbital_evolution.jl similarity index 100% rename from src2/interface/orbital_evolution.jl rename to src/interface/orbital_evolution.jl diff --git a/src2/interface/pn.jl b/src/interface/pn.jl similarity index 100% rename from src2/interface/pn.jl rename to src/interface/pn.jl diff --git a/src/waveforms.jl b/src/interface/waveform.jl similarity index 100% rename from src/waveforms.jl rename to src/interface/waveform.jl diff --git a/src/derived_variables.jl b/src/literature/common_variables/common_variables.jl similarity index 76% rename from src/derived_variables.jl rename to src/literature/common_variables/common_variables.jl index 9091c887..59da72cc 100644 --- a/src/derived_variables.jl +++ b/src/literature/common_variables/common_variables.jl @@ -1,6 +1,13 @@ -module DerivedVariables +module CommonVariables + +# Import the fundamental variables, which are generally part of the `state` vector of a +# PNSystem. +import ..PostNewtonian: + M₁, M₂, χ⃗₁, χ⃗₁ˣ, χ⃗₁ʸ, χ⃗₁ᶻ, χ⃗₂, χ⃗₂ˣ, χ⃗₂ʸ, χ⃗₂ᶻ, R, Rʷ, Rˣ, Rʸ, Rᶻ, v, Φ, Λ₁, Λ₂ + +# Now, we define all the common variables used throughout the literature as functions of +# those fundamental variables. -using ..PostNewtonian: VecOrPNSystem using ..PostNewtonian.FundamentalVariables using Quaternionic: 𝐢, 𝐣, 𝐤, QuatVec, ⋅, ×, abs2vec, absvec diff --git a/src/derived_variables/horizons.jl b/src/literature/common_variables/horizons.jl similarity index 100% rename from src/derived_variables/horizons.jl rename to src/literature/common_variables/horizons.jl diff --git a/src/derived_variables/mass_combinations.jl b/src/literature/common_variables/mass_combinations.jl similarity index 100% rename from src/derived_variables/mass_combinations.jl rename to src/literature/common_variables/mass_combinations.jl diff --git a/src/derived_variables/orbital_elements.jl b/src/literature/common_variables/orbital_elements.jl similarity index 100% rename from src/derived_variables/orbital_elements.jl rename to src/literature/common_variables/orbital_elements.jl diff --git a/src/derived_variables/spin_combinations.jl b/src/literature/common_variables/spin_combinations.jl similarity index 100% rename from src/derived_variables/spin_combinations.jl rename to src/literature/common_variables/spin_combinations.jl diff --git a/src/derived_variables/tidal_coupling.jl b/src/literature/common_variables/tidal_coupling.jl similarity index 100% rename from src/derived_variables/tidal_coupling.jl rename to src/literature/common_variables/tidal_coupling.jl diff --git a/src2/interface/waveform.jl b/src/literature/literature.jl similarity index 100% rename from src2/interface/waveform.jl rename to src/literature/literature.jl diff --git a/src/pn_expressions.jl b/src/pn_expressions.jl deleted file mode 100644 index 5ab622e6..00000000 --- a/src/pn_expressions.jl +++ /dev/null @@ -1,7 +0,0 @@ -include("utilities/macros.jl") -include("pn_expressions/flux.jl") -include("pn_expressions/tidal_heating.jl") -include("pn_expressions/binding_energy.jl") -include("pn_expressions/separation.jl") -include("pn_expressions/precession.jl") -include("pn_expressions/mode_weights.jl") diff --git a/src2/pn_expressions/angular_momentum.jl b/src/pn_expressions/angular_momentum.jl similarity index 100% rename from src2/pn_expressions/angular_momentum.jl rename to src/pn_expressions/angular_momentum.jl diff --git a/src2/pn_expressions/dynamics.jl b/src/pn_expressions/dynamics.jl similarity index 100% rename from src2/pn_expressions/dynamics.jl rename to src/pn_expressions/dynamics.jl diff --git a/src2/pn_expressions/energy.jl b/src/pn_expressions/pn_expressions.jl similarity index 100% rename from src2/pn_expressions/energy.jl rename to src/pn_expressions/pn_expressions.jl diff --git a/src2/pn_expressions/flux.jl b/src/pn_expressions/waveform.jl similarity index 100% rename from src2/pn_expressions/flux.jl rename to src/pn_expressions/waveform.jl diff --git a/src/pn_systems.jl b/src/pn_systems.jl deleted file mode 100644 index f860ded7..00000000 --- a/src/pn_systems.jl +++ /dev/null @@ -1,312 +0,0 @@ -""" - PNSystem{ST, PNOrder} - -Base type for all PN systems, such as `BBH`, `BHNS`, and `NSNS`. - -These objects encode all essential properties of the binary, including its current state. -As such, they can be used as inputs to the various [fundamental](@ref Fundamental-variables) -and [derived variables](@ref Derived-variables), as well as [PN expressions](@ref) and -[dynamics](@ref Dynamics) functions. - -All subtypes should contain a `state` vector holding all of the fundamental variables for -the given type of system. The parameter `ST` is the type of the `state` vector — for -example, `Vector{Float64}`. `PNOrder` is a `Rational` giving the order to which PN -expansions should be carried. -""" -abstract type PNSystem{ST,PNOrder} end - -const VecOrPNSystem = Union{AbstractVector,PNSystem} - -const pnsystem_symbols = ( - :M₁, :M₂, :χ⃗₁ˣ, :χ⃗₁ʸ, :χ⃗₁ᶻ, :χ⃗₂ˣ, :χ⃗₂ʸ, :χ⃗₂ᶻ, :Rʷ, :Rˣ, :Rʸ, :Rᶻ, :v, :Φ -) - -for (i, s) ∈ enumerate(pnsystem_symbols) - sindex = Symbol("$(s)index") - @eval const $sindex = $i -end - -const χ⃗₁indices = χ⃗₁ˣindex:χ⃗₁ᶻindex -const χ⃗₂indices = χ⃗₂ˣindex:χ⃗₂ᶻindex -const Rindices = Rʷindex:Rᶻindex - -Base.eltype(::Type{PNT}) where {ST,PNT<:PNSystem{ST}} = eltype(ST) -Base.one(x::T) where {T<:PNSystem} = one(T) -Base.zero(x::T) where {T<:PNSystem} = zero(T) -Base.float(x::T) where {T<:PNSystem} = float(T) -Base.one(::Type{PNT}) where {PNT<:PNSystem} = one(eltype(PNT)) -Base.zero(::Type{PNT}) where {PNT<:PNSystem} = zero(eltype(PNT)) -Base.float(::Type{PNT}) where {PNT<:PNSystem} = float(eltype(PNT)) -pn_order(::PNSystem{ST,PNOrder}) where {ST,PNOrder} = PNOrder -order_index(pn::PNSystem) = 1 + Int(2pn_order(pn)) - -""" - causes_domain_error!(u̇, p) - -Ensure that these parameters correspond to a physically valid set of PN parameters. - -If the parameters are not valid, this function should modify `u̇` to indicate that the -current step is invalid. This is done by filling `u̇` with `NaN`s, which will be detected -by the ODE solver and cause it to try a different (smaller) step size. - -Currently, the only check that is done is to test that these parameters result in a PN -parameter v>0. In the future, this function may be expanded to include other checks. -""" -function causes_domain_error!(u̇, p::PNSystem{VT}) where {VT} - if p.state[vindex] ≤ 0 # If this is expanded, document the change in the docstring. - u̇ .= convert(eltype(VT), NaN) - true - else - false - end -end - -function prepare_system(; M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ=0, PNOrder=typemax(Int)) - state = [M₁; M₂; vec(QuatVec(χ⃗₁)); vec(QuatVec(χ⃗₂)); components(Rotor(R)); v; Φ] - ST = typeof(state) - PNOrder = prepare_pn_order(PNOrder) - return (ST, PNOrder, state) -end - -function prepare_pn_order(PNOrder) - if PNOrder != typemax(Int) - round(Int, 2PNOrder)//2 - else - (typemax(Int) - 2)//2 - end -end - -""" - BBH{T, PNOrder} - -The [`PNSystem`](@ref) subtype describing a binary black hole system. - -The `state` vector here holds the fundamental variables `M₁`, `M₂`, `χ⃗₁`, `χ⃗₂`, `R`, `v`, -with the spins unpacked into three components each, and `R` unpacked into four — for a total -of 13 elements. - -Optionally, `Φ` may also be tracked as the 14th element of the `state` vector. This is just -the integral of the orbital angular frequency `Ω`, and holds little interest for general -systems beyond a convenient description of how "far" the system has evolved. -""" -struct BBH{T,PNOrder} <: PNSystem{T,PNOrder} - state::T - - BBH{T,PNOrder}(state) where {T,PNOrder} = new{T,PNOrder}(state) - function BBH(; M₁, M₂, χ⃗₁, χ⃗₂, v, R=Rotor(1), Φ=0, PNOrder=typemax(Int), kwargs...) - (T, PNOrder, state) = prepare_system(; M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ, PNOrder) - return new{T,PNOrder}(state) - end - function BBH(state; Λ₁=0, Λ₂=0, PNOrder=typemax(Int)) - @assert length(state) == 14 - @assert Λ₁ == 0 - @assert Λ₂ == 0 - return new{typeof(state),prepare_pn_order(PNOrder)}(state) - end -end -const BHBH = BBH - -""" - BHNS{T, PNOrder} - -The [`PNSystem`](@ref) subtype describing a black-hole—neutron-star binary system. - -The `state` vector is the same as for a [`BBH`](@ref). There is an additional field `Λ₂` -holding the (constant) tidal-coupling parameter of the neutron star. - -Note that the neutron star is *always* object 2 — meaning that `M₂`, `χ⃗₂`, and `Λ₂` always -refer to it; `M₁` and `χ⃗₁` always refer to the black hole. See also [`NSNS`](@ref). -""" -struct BHNS{ST,PNOrder,ET} <: PNSystem{ST,PNOrder} - state::ST - Λ₂::ET - - BHNS{T,PNOrder,ET}(state) where {T,PNOrder,ET} = new{T,PNOrder,ET}(state) - BHNS{T,PNOrder}(state) where {T,PNOrder} = new{T,PNOrder,eltype(T)}(state) - function BHNS(; - M₁, M₂, χ⃗₁, χ⃗₂, v, R=Rotor(1), Λ₂, Φ=0, PNOrder=typemax(Int), kwargs... - ) - ST, PNOrder, state = prepare_system(; M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ, PNOrder) - ET = eltype(ST) - return new{ST,PNOrder,ET}(state, convert(ET, Λ₂)) - end - function BHNS(state; Λ₂, Λ₁=0, PNOrder=typemax(Int)) - @assert length(state) == 14 - ST, PNOrder = typeof(state), prepare_pn_order(PNOrder) - ET = eltype(ST) - return new{ST,PNOrder,ET}(state, convert(ET, Λ₂)) - end -end - -""" - NSNS{T, PNOrder} - -The [`PNSystem`](@ref) subtype describing a neutron-star—neutron-star binary system. - -The `state` vector is the same as for a [`BBH`](@ref). There are two additional fields `Λ₁` -and `Λ₂` holding the (constant) tidal-coupling parameters of the neutron stars. See also -[`BHNS`](@ref). -""" -struct NSNS{ST,PNOrder,ET} <: PNSystem{ST,PNOrder} - state::ST - Λ₁::ET - Λ₂::ET - - NSNS{T,PNOrder,ET}(state) where {T,PNOrder,ET} = new{T,PNOrder,ET}(state) - NSNS{T,PNOrder}(state) where {T,PNOrder} = new{T,PNOrder,eltype(T)}(state) - function NSNS(; - M₁, M₂, χ⃗₁, χ⃗₂, v, R=Rotor(1), Λ₁, Λ₂, Φ=0, PNOrder=typemax(Int), kwargs... - ) - ST, PNOrder, state = prepare_system(; M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ, PNOrder) - ET = eltype(ST) - return new{ST,PNOrder,ET}(state, convert(ET, Λ₁), convert(ET, Λ₂)) - end - function NSNS(state; Λ₁, Λ₂, PNOrder=typemax(Int)) - @assert length(state) == 14 - ST, PNOrder = typeof(state), prepare_pn_order(PNOrder) - ET = eltype(state) - return new{ST,PNOrder,ET}(state, convert(ET, Λ₁), convert(ET, Λ₂)) - end -end -const BNS = NSNS - -""" - FDPNSystem{FT, PNOrder}(state, Λ₁, Λ₂) - -A `PNSystem` that contains information as variables from -[`FastDifferentiation.jl`](https://docs.juliahub.com/General/FastDifferentiation/stable/). - -See also [`fd_pnsystem`](@ref) for a particular instance of this type. Note that this type -also involves the type `FT`, which will be the float type of actual numbers that eventually -get fed into (and will be passed out from) functions that use this system. The correct type -of `FDPNSystem` is used in calculating `𝓔′`. -""" -struct FDPNSystem{FT,PNOrder} <: PNSystem{Vector{FastDifferentiation.Node},PNOrder} - state::Vector{FastDifferentiation.Node} - Λ₁::FastDifferentiation.Node - Λ₂::FastDifferentiation.Node - - function FDPNSystem(FT, PNOrder=typemax(Int)) - FastDifferentiation.@variables M₁ M₂ χ⃗₁ˣ χ⃗₁ʸ χ⃗₁ᶻ χ⃗₂ˣ χ⃗₂ʸ χ⃗₂ᶻ Rʷ Rˣ Rʸ Rᶻ v Φ Λ₁ Λ₂ - return new{FT,prepare_pn_order(PNOrder)}( - [M₁, M₂, χ⃗₁ˣ, χ⃗₁ʸ, χ⃗₁ᶻ, χ⃗₂ˣ, χ⃗₂ʸ, χ⃗₂ᶻ, Rʷ, Rˣ, Rʸ, Rᶻ, v, Φ], Λ₁, Λ₂ - ) - end -end -Base.eltype(::FDPNSystem{FT}) where {FT} = FT - -""" - fd_pnsystem - -A symbolic `PNSystem` that contains symbolic information for all types of `PNSystem`s. - -In particular, note that this object has (essentially) infinite `PNOrder`, has nonzero -values for quantities like `Λ₁` and `Λ₂`, and assumes that the eventual output will be in -`Float64`. If you want different choices, you may need to call [`FDPNSystem`](@ref) -yourself, or even construct a different specialized subtype of `PNSystem` (it's not hard). - -# Examples -```jldoctest -julia> using PostNewtonian: M₁, M₂, χ⃗₁, χ⃗₂, FDPNSystem - -julia> fd_pnsystem = FDPNSystem(Float64) -FDPNSystem{Float64, 9223372036854775805//2}(FastDifferentiation.Node[M₁, M₂, χ⃗₁ˣ, χ⃗₁ʸ, χ⃗₁ᶻ, χ⃗₂ˣ, χ⃗₂ʸ, χ⃗₂ᶻ, Rʷ, Rˣ, Rʸ, Rᶻ, v, Φ], Λ₁, Λ₂) - -julia> M₁(fd_pnsystem), M₂(fd_pnsystem) -(M₁, M₂) - -julia> χ⃗₁(fd_pnsystem) - + χ⃗₁ˣ𝐢 + χ⃗₁ʸ𝐣 + χ⃗₁ᶻ𝐤 - -julia> χ⃗₂(fd_pnsystem) - + χ⃗₂ˣ𝐢 + χ⃗₂ʸ𝐣 + χ⃗₂ᶻ𝐤 -``` -""" -const fd_pnsystem = FDPNSystem(Float64) - -function SVector(pnsystem::PNSystem) - return SVector{16,eltype(pnsystem)}( - pnsystem.state[1], - pnsystem.state[2], - pnsystem.state[3], - pnsystem.state[4], - pnsystem.state[5], - pnsystem.state[6], - pnsystem.state[7], - pnsystem.state[8], - pnsystem.state[9], - pnsystem.state[10], - pnsystem.state[11], - pnsystem.state[12], - pnsystem.state[13], - pnsystem.state[14], - Λ₁(pnsystem), - Λ₂(pnsystem), - ) -end -function SVector(pnsystem::FDPNSystem) - return SVector{16,FastDifferentiation.Node}( - pnsystem.state[1], - pnsystem.state[2], - pnsystem.state[3], - pnsystem.state[4], - pnsystem.state[5], - pnsystem.state[6], - pnsystem.state[7], - pnsystem.state[8], - pnsystem.state[9], - pnsystem.state[10], - pnsystem.state[11], - pnsystem.state[12], - pnsystem.state[13], - pnsystem.state[14], - Λ₁(pnsystem), - Λ₂(pnsystem), - ) -end - -@testitem "PNSystem constructors" begin - using Quaternionic - - pnA = BBH(; - M₁=1.0f0, M₂=2.0f0, χ⃗₁=Float32[3.0, 4.0, 5.0], χ⃗₂=Float32[6.0, 7.0, 8.0], v=0.23f0 - ) - @test pnA.state == - Float32[1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; 1.0; 0.0; 0.0; 0.0; 0.23; 0.0] - - pnB = BBH(; - M₁=1.0f0, - M₂=2.0f0, - χ⃗₁=Float32[3.0, 4.0, 5.0], - χ⃗₂=Float32[6.0, 7.0, 8.0], - v=0.23f0, - Φ=9.0f0, - ) - @test pnB.state == - Float32[1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; 1.0; 0.0; 0.0; 0.0; 0.23; 9.0] - - R = randn(RotorF32) - pn1 = BBH(; - M₁=1.0f0, - M₂=2.0f0, - χ⃗₁=Float32[3.0, 4.0, 5.0], - χ⃗₂=Float32[6.0, 7.0, 8.0], - R=R, - v=0.23f0, - ) - @test pn1.state ≈ [1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; components(R)...; 0.23; 0.0] - - pn2 = BBH(; - M₁=1.0f0, - M₂=2.0f0, - χ⃗₁=Float32[3.0, 4.0, 5.0], - χ⃗₂=Float32[6.0, 7.0, 8.0], - R=R, - v=0.23f0, - Φ=9.0f0, - ) - @test pn2.state ≈ [1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; components(R)...; 0.23; 9.0] - - pn1.state[end] = 9.0f0 - @test pn1.state == pn2.state -end diff --git a/src/pn_systems/BBH.jl b/src/pn_systems/BBH.jl new file mode 100644 index 00000000..8221f5b6 --- /dev/null +++ b/src/pn_systems/BBH.jl @@ -0,0 +1,117 @@ +""" + BBH{NT, ST, PNOrder} <: PNSystem{NT, ST, PNOrder} + +The [`PNSystem`](@ref) subtype describing a binary black hole system. + +The `state` vector here holds the fundamental state variables characterizing the masses, +spins, orientation, velocity, and orbital phase of the system. The spins unpacked into +three components each. The orientation is described by the four components of the `Rotor` +`R`. This gives us a total of 14 elements: + + M₁, M₂, χ⃗₁ˣ, χ⃗₁ʸ, χ⃗₁ᶻ, χ⃗₂ˣ, χ⃗₂ʸ, χ⃗₂ᶻ, Rʷ, Rˣ, Rʸ, Rᶻ, v, Φ + +The "orbital phase" `Φ` is tracked as the 14th element of the `state` vector. This is just +the integral of the (scalar) orbital angular frequency `Ω`, and holds little interest for +general systems beyond a convenient description of how "far" the system has evolved. For +nonprecessing systems, `Φ` would be sufficient to describe the system's position, which is +more completely described by the `Rotor` `R`. However, for precessing systems, it is +difficult to extract this quantity from `R`. +""" +struct BBH{NT,ST,PNOrder} <: PNSystem{NT,ST,PNOrder} + state::ST + + function BBH{NT,ST,PNOrder}(state) where {NT,ST,PNOrder} + if eachindex(state) != Base.OneTo(14) + error( + "The `state` vector for `BBH` must be indexed from 1 to 14; " * + "input is indexed `$(eachindex(state))`.", + ) + end + new{NT,ST,PNOrder}(state) + end + function BBH(; M₁, M₂, χ⃗₁, χ⃗₂, v, R=Rotor(1), Φ=0, PNOrder=typemax(Int), kwargs...) + (NT, ST, PNOrder, state) = prepare_system(; M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ, PNOrder) + return new{NT,ST,PNOrder}(state) + end + function BBH(state; Λ₁=0, Λ₂=0, PNOrder=typemax(Int)) + if eachindex(state) != Base.OneTo(14) + error( + "The `state` vector for `BBH` must be indexed from 1 to 14; " * + "input is indexed `$(eachindex(state))`.", + ) + end + @assert Λ₁ == 0 + @assert Λ₂ == 0 + return new{eltype(state),typeof(state),prepare_pn_order(PNOrder)}(state) + end +end +const BHBH = BBH + +# The following are methods of functions defined in `state_variables.jl`, specialized for +# `BBH` systems. +state(pnsystem::BBH) = pnsystem.state +function symbols(::Type{<:BBH}) + (:M₁, :M₂, :χ⃗₁ˣ, :χ⃗₁ʸ, :χ⃗₁ᶻ, :χ⃗₂ˣ, :χ⃗₂ʸ, :χ⃗₂ᶻ, :Rʷ, :Rˣ, :Rʸ, :Rᶻ, :v, :Φ) +end +function ascii_symbols(::Type{<:BBH}) + (:M1, :M2, :chi1x, :chi1y, :chi1z, :chi2x, :chi2y, :chi2z, :Rw, :Rx, :Ry, :Rz, :v, :Phi) +end +for (i, symbol) ∈ enumerate(symbols(BBH)) + # This will define, e.g., `M₁(pnsystem::BBH) = pnsystem.state[1]`. We + # could do this manually, but this is more concise and less error-prone. + @eval begin + $(symbol)(pnsystem::BBH) = @inbounds pnsystem.state[$i] + function symbol_index(::Type{T}, ::Val{Symbol($symbol)}) where {T<:BBH} + $i + end + end +end + +Λ₁(pnsystem::BBH) = zero(pnsystem) +Λ₂(pnsystem::BBH) = zero(pnsystem) + +@testitem "BBH constructors" begin + using Quaternionic + + pnA = BBH(; + M₁=1.0f0, M₂=2.0f0, χ⃗₁=Float32[3.0, 4.0, 5.0], χ⃗₂=Float32[6.0, 7.0, 8.0], v=0.23f0 + ) + @test pnA.state == + Float32[1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; 1.0; 0.0; 0.0; 0.0; 0.23; 0.0] + + pnB = BBH(; + M₁=1.0f0, + M₂=2.0f0, + χ⃗₁=Float32[3.0, 4.0, 5.0], + χ⃗₂=Float32[6.0, 7.0, 8.0], + v=0.23f0, + Φ=9.0f0, + ) + @test pnB.state == + Float32[1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; 1.0; 0.0; 0.0; 0.0; 0.23; 9.0] + + R = randn(RotorF32) + pn1 = BBH(; + M₁=1.0f0, + M₂=2.0f0, + χ⃗₁=Float32[3.0, 4.0, 5.0], + χ⃗₂=Float32[6.0, 7.0, 8.0], + R=R, + v=0.23f0, + ) + @test pn1.state ≈ [1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; components(R)...; 0.23; 0.0] + + pn2 = BBH(; + M₁=1.0f0, + M₂=2.0f0, + χ⃗₁=Float32[3.0, 4.0, 5.0], + χ⃗₂=Float32[6.0, 7.0, 8.0], + R=R, + v=0.23f0, + Φ=9.0f0, + ) + @test pn2.state ≈ [1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; components(R)...; 0.23; 9.0] + + pn1.state[end] = 9.0f0 + @test pn1.state == pn2.state +end diff --git a/src/pn_systems/BHNS.jl b/src/pn_systems/BHNS.jl new file mode 100644 index 00000000..cccfb12f --- /dev/null +++ b/src/pn_systems/BHNS.jl @@ -0,0 +1,80 @@ +""" + BHNS{NT, ST, PNOrder} <: PNSystem{NT, ST, PNOrder} + +The [`PNSystem`](@ref) subtype describing a black-hole—neutron-star binary system. + +The `state` vector is the same as for a [`BBH`](@ref), with an additional field `Λ₂` holding +the (constant) tidal-coupling parameter of the neutron star. + +Note that the neutron star is *always* object 2 — meaning that `M₂`, `χ⃗₂`, and `Λ₂` always +refer to it; `M₁` and `χ⃗₁` always refer to the black hole. (It's "BHNS", not "NSBH".) See +also [`NSNS`](@ref). +""" +struct BHNS{NT,ST,PNOrder} <: PNSystem{NT,ST,PNOrder} + state::ST + + function BHNS{NT,ST,PNOrder}(state) where {NT,ST,PNOrder} + if eachindex(state) != Base.OneTo(15) + error( + "The `state` vector for `BHNS` must be indexed from 1 to 15; " * + "input is indexed `$(eachindex(state))`.", + ) + end + new{NT,ST,PNOrder}(state) + end + function BHNS(; + M₁, M₂, χ⃗₁, χ⃗₂, v, R=Rotor(1), Φ=0, Λ₂, PNOrder=typemax(Int), kwargs... + ) + NT, ST, PNOrder, state = prepare_system(; M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ, Λ₂, PNOrder) + return new{NT,ST,PNOrder}(state) + end + function BHNS(state; PNOrder=typemax(Int)) + if eachindex(state) != Base.OneTo(15) + error( + "The `state` vector for `BHNS` must be indexed from 1 to 15; " * + "input is indexed `$(eachindex(state))`.", + ) + end + NT, ST, PNOrder = eltype(state), typeof(state), prepare_pn_order(PNOrder) + return new{NT,ST,PNOrder}(state) + end +end + +# The following are methods of functions defined in `state_variables.jl`, specialized for +# `BHNS` systems. +state(pnsystem::BHNS) = pnsystem.state +function symbols(::Type{<:BHNS}) + (:M₁, :M₂, :χ⃗₁ˣ, :χ⃗₁ʸ, :χ⃗₁ᶻ, :χ⃗₂ˣ, :χ⃗₂ʸ, :χ⃗₂ᶻ, :Rʷ, :Rˣ, :Rʸ, :Rᶻ, :v, :Φ, :Λ₂) +end +function ascii_symbols(::Type{<:BHNS}) + ( + :M1, + :M2, + :chi1x, + :chi1y, + :chi1z, + :chi2x, + :chi2y, + :chi2z, + :Rw, + :Rx, + :Ry, + :Rz, + :v, + :Phi, + :Lambda2, + ) +end +for (i, symbol) ∈ enumerate(symbols(BHNS)) + # This will define, e.g., `M₁(pnsystem::BHNS) = pnsystem.state[1]`. We + # could do this manually, but this is more concise and less error-prone. + @eval begin + $(symbol)(pnsystem::BHNS) = @inbounds pnsystem.state[$i] + function symbol_index(::Type{T}, ::Val{Symbol($symbol)}) where {T<:BHNS} + $i + end + end +end + +Λ₁(pnsystem::BHNS) = zero(pnsystem) +Λ₂(pnsystem::BHNS) = @inbounds pnsystem.state[15] diff --git a/src/pn_systems/FDPNsystem.jl b/src/pn_systems/FDPNsystem.jl new file mode 100644 index 00000000..098aa415 --- /dev/null +++ b/src/pn_systems/FDPNsystem.jl @@ -0,0 +1,34 @@ +""" + FDPNSystem{NT, PN, PNOrder} <: PNSystem{FDNode, Vector{FDNode}, PNOrder} + +A `PNSystem` that contains information as variables from +[`FastDifferentiation.jl`](https://docs.juliahub.com/General/FastDifferentiation/stable/). + +Note that this type also involves the type parameter `PN`, which is actually the type of a +`PNSystem`, and its type parameter `NT`, which will be the number type of actual numbers +that eventually get fed into (and will be passed out from) functions that use this system. + +One important example of what this type is used for is computing the derivative of the +orbital binding energy, `𝓔′` — and in particular, for generating the corresponding function +method to apply to a given `PNSystem`. +""" +struct FDPNSystem{NT,PN<:PNSystem{NT},PNOrder} <: PNSystem{FDNode,Vector{FDNode},PNOrder} + state::Vector{FDNode} + + function FDPNSystem(::Type{PN}, PNOrder=typemax(Int)) where {NT,PN<:PNSystem{NT}} + return new{NT,prepare_pn_order(PNOrder)}([FDNode(s) for s ∈ symbols(PN)]) + end +end + +symbols(pnsystem::FDPNSystem{NT,PN}) where {NT,PN} = symbols(PN) + +function symbol_index(pnsystem::FDPNSystem{NT,PN}, s::Symbol) where {NT,PN} + symbol_index(PN, Val(s)) +end + +## TODO: See if this method is needed + +## The old code had this, but I think it would probably just cause errors. It might be +## relied upon in the functions where we take derivatives — 𝓔′code and γₚₙ₀′ — but even if +## so, maybe we could work around it with another function. +#Base.eltype(::FDPNSystem{FT}) where {FT} = FT diff --git a/src/pn_systems/NSNS.jl b/src/pn_systems/NSNS.jl new file mode 100644 index 00000000..ca40d3e7 --- /dev/null +++ b/src/pn_systems/NSNS.jl @@ -0,0 +1,98 @@ +""" + NSNS{NT, ST, PNOrder} <: PNSystem{NT, ST, PNOrder} + +The [`PNSystem`](@ref) subtype describing a neutron-star—neutron-star binary system. + +The `state` vector is the same as for a [`BBH`](@ref), with two additional fields `Λ₁` +and `Λ₂` holding the (constant) tidal-coupling parameters of the neutron stars. See also +[`BHNS`](@ref). +""" +struct NSNS{NT,ST,PNOrder} <: PNSystem{NT,ST,PNOrder} + state::ST + + function NSNS{NT,ST,PNOrder}(state) where {NT,ST,PNOrder} + if eachindex(state) != Base.OneTo(16) + error( + "The `state` vector for `NSNS` must be indexed from 1 to 16; " * + "input is indexed `$(eachindex(state))`.", + ) + end + new{NT,ST,PNOrder}(state) + end + function NSNS(; + M₁, M₂, χ⃗₁, χ⃗₂, v, R=Rotor(1), Φ=0, Λ₁, Λ₂, PNOrder=typemax(Int), kwargs... + ) + NT, ST, PNOrder, state = prepare_system(; + M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ, Λ₁, Λ₂, PNOrder + ) + return new{NT,ST,PNOrder}(state) + end + function NSNS(state; PNOrder=typemax(Int)) + if eachindex(state) != Base.OneTo(16) + error( + "The `state` vector for `NSNS` must be indexed from 1 to 16; " * + "input is indexed `$(eachindex(state))`.", + ) + end + NT, ST, PNOrder = eltype(state), typeof(state), prepare_pn_order(PNOrder) + return new{NT,ST,PNOrder}(state) + end +end +const BNS = NSNS + +# The following are methods of functions defined in `state_variables.jl`, specialized for +# `NSNS` systems. +state(pnsystem::NSNS) = pnsystem.state +function symbols(::Type{<:NSNS}) + ( + :M₁, + :M₂, + :χ⃗₁ˣ, + :χ⃗₁ʸ, + :χ⃗₁ᶻ, + :χ⃗₂ˣ, + :χ⃗₂ʸ, + :χ⃗₂ᶻ, + :Rʷ, + :Rˣ, + :Rʸ, + :Rᶻ, + :v, + :Φ, + :Λ₁, + :Λ₂, + ) +end +function ascii_symbols(::Type{<:NSNS}) + ( + :M1, + :M2, + :chi1x, + :chi1y, + :chi1z, + :chi2x, + :chi2y, + :chi2z, + :Rw, + :Rx, + :Ry, + :Rz, + :v, + :Phi, + :Lambda1, + :Lambda2, + ) +end +for (i, symbol) ∈ enumerate(symbols(NSNS)) + # This will define, e.g., `M₁(pnsystem::NSNS) = pnsystem.state[1]`. We + # could do this manually, but this is more concise and less error-prone. + @eval begin + $(symbol)(pnsystem::NSNS) = @inbounds pnsystem.state[$i] + function symbol_index(::Type{T}, ::Val{Symbol($symbol)}) where {T<:NSNS} + $i + end + end +end + +Λ₁(pnsystem::NSNS) = @inbounds pnsystem.state[15] +Λ₂(pnsystem::NSNS) = @inbounds pnsystem.state[16] diff --git a/src/pn_systems/PNSystem.jl b/src/pn_systems/PNSystem.jl new file mode 100644 index 00000000..c10285ef --- /dev/null +++ b/src/pn_systems/PNSystem.jl @@ -0,0 +1,112 @@ +""" + PNSystem{NT, PNOrder, ST} + +Base type for all PN systems, such as `BBH`, `BHNS`, and `NSNS`. + +These objects encode all essential properties of the binary, including its current state. +As such, they can be used as inputs to the various [fundamental](@ref Fundamental-variables) +and [derived variables](@ref Derived-variables), as well as [PN expressions](@ref) and +[dynamics](@ref Dynamics) functions. + +The parameter `NT` is the number type of the system, such as `Float64` or `Dual{SomeTag, +Float64, 7}`. The parameter `ST <: DenseVector{NT}` is the type returned by the `state` +function, which probably just returns the `state` vector stored in the concrete subtype. As +such, this will probably be `MVector{N, NT}` or `SVector{N, NT}`, where `N` is the number of +elements in the state. `PNOrder` is a `Rational` giving the order to which PN expansions +should be carried out when using the given object. +""" +abstract type PNSystem{NT,PNOrder,ST<:DenseVector{NT}} <: DenseVector{NT} end + +""" + state(pnsystem::PNSystem) + +Return the state vector of `pnsystem`, which is a vector of fundamental variables for the +given PN system. + +Note that the built-in `PNSystem` subtypes have a `state` field that is a vector, so this +function will just return that vector. However, that may not always be true for +user-defined subtypes. +""" +function state(::T) where {T<:PNSystem} + error("`state` is not yet defined for PNSystem subtype `$T`.") +end +Base.vec(pnsystem::PNSystem) = state(pnsystem) + +Base.eltype(::Type{PNT}) where {NT,PNT<:PNSystem{NT}} = NT +Base.one(::Type{PNT}) where {PNT<:PNSystem} = one(eltype(PNT)) +Base.one(x::T) where {T<:PNSystem} = one(T) +Base.zero(::Type{PNT}) where {PNT<:PNSystem} = zero(eltype(PNT)) +Base.zero(x::T) where {T<:PNSystem} = zero(T) +Base.float(::Type{PNT}) where {PNT<:PNSystem} = float(eltype(PNT)) +Base.float(x::T) where {T<:PNSystem} = float(T) + +""" + symbols(pnsystem::PNSystem) + symbols(::Type{<:PNSystem}) + ascii_symbols(pnsystem::PNSystem) + ascii_symbols(::Type{<:PNSystem}) + +Return a Tuple of symbols corresponding to the variables tracked by `pnsystem`, in the order +in which they are stored in the `state` vector. + +The `ascii_symbols` function returns those symbols in ASCII form, enabling interaction with +external systems (e.g., Python) that do not support many Unicode symbols. + +```jldoctest +julia> using PostNewtonian: BBH + +julia> pnsystem = BBH(randn(14); PNOrder=7//2); + +julia> symbols(pnsystem) +(:M₁, :M₂, :χ⃗₁ˣ, :χ⃗₁ʸ, :χ⃗₁ᶻ, :χ⃗₂ˣ, :χ⃗₂ʸ, :χ⃗₂ᶻ, :Rʷ, :Rˣ, :Rʸ, :Rᶻ, :v, :Φ) + +julia> ascii_symbols(pnsystem) +(:M1, :M2, :chi1x, :chi1y, :chi1z, :chi2x, :chi2y, :chi2z, :Rw, :Rx, :Ry, :Rz, :v, :Phi) +``` +""" +symbols(pnsystem::PNSystem) = symbols(typeof(pnsystem)) +function symbols(::Type{T}) where {T<:PNSystem} + error("`symbols` is not yet defined for PNSystem subtype `$T`.") +end +ascii_symbols(pnsystem::PNSystem) = ascii_symbols(typeof(pnsystem)) +function ascii_symbols(::Type{T}) where {T<:PNSystem} + error("`ascii_symbols` is not yet defined for PNSystem subtype `$T`.") +end + +""" + pnsystem::PNSystem(; kwargs...) + +State-modifying copy constructor for `PNSystem` objects. + +Note that this cannot modify the type's parameters, including the number type `NT`, the +state type `ST`, or the `PNOrder` of the system. However, it can modify any of the state +variables by symbol or by ASCII symbol. This function will raise an AssertionError if +any of the keys in `kwargs` is not a valid symbol for the given `PNSystem` type. + +```jldoctest +julia> using PostNewtonian: BBH + +julia> pnsystem = BBH(ones(14)/2; PNOrder=7//2) +BBH{Vector{Float64}, 7//2}([0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5]) + +julia> pnsystem2 = pnsystem(M₁=0.2, M₂=0.8, chi1x=0.1) +BBH{Vector{Float64}, 7//2}([0.2, 0.8, 0.1, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5]) +``` +""" +function (pnsystem::PNSystem{NT,PNOrder,ST})(; kwargs...) where {NT,PNOrder,ST} + all_symbols = Set(symbols(pnsystem)) ∪ Set(ascii_symbols(pnsystem)) + @assert keys(kwargs) ⊆ all_symbols ( + "PNSystem of type $(typeof(pnsystem)) does not have these symbols, which were " * + "input as keyword arguments:\n $(setdiff(keys(kwargs), all_symbols))\n" * + "Maybe you passed `String`s instead of `Symbol`s?\n" * + "The available symbols for this type are\n" * + " $(symbols(pnsystem))\n" * + "and their ASCII equivalents:\n" * + " $(ascii_symbols(pnsystem))" + ) + state = Tuple( + get(kwargs, symbol, get(kwargs, ascii_symbol, pnsystem[symbol])) for + (symbol, ascii_symbol) ∈ zip(symbols(pnsystem), ascii_symbols(pnsystem)) + ) + typeof(pnsystem)(ST(SVector(state))) +end diff --git a/src/pn_systems/pn_order.jl b/src/pn_systems/pn_order.jl new file mode 100644 index 00000000..2c213fd1 --- /dev/null +++ b/src/pn_systems/pn_order.jl @@ -0,0 +1,42 @@ +""" + pn_order(pnsystem::PNSystem) + +Return the PN order of the given `pnsystem`. + +This is a `Rational{Int}` that indicates the order to which the PN expansions should be +carried out when using the given object. +""" +pn_order(::PNSystem{NT,ST,PNOrder}) where {NT,ST,PNOrder} = PNOrder + +""" + order_index(pnsystem::PNSystem) + +Return the order index of the given `pnsystem`. + +This is defined as the (one-based) index into an iterable of PN terms starting at 0pN, then +0.5pN, etc. Specifically, this is defined as `1 + Int(2pn_order(pnsystem))`. +""" +order_index(pn::PNSystem) = 1 + Int(2pn_order(pn)) + +""" + max_pn_order + +The maximum PN order that can be used without overflowing the `Int` type. +""" +const max_pn_order = (typemax(Int) - 2) // 2 + +""" + prepare_pn_order(PNOrder) + +Convert the input to a half-integer of type `Rational{Int}`. + +If `PNOrder` is larger than `max_pn_order`, it is set to `max_pn_order`, to avoid overflow +when computing the order index. +""" +function prepare_pn_order(PNOrder) + if PNOrder < max_pn_order + round(Int, 2PNOrder) // 2 + else + max_pn_order + end +end diff --git a/src/pn_systems/pn_systems.jl b/src/pn_systems/pn_systems.jl new file mode 100644 index 00000000..12ebbaab --- /dev/null +++ b/src/pn_systems/pn_systems.jl @@ -0,0 +1,8 @@ +include("PNSystem.jl") +include("pn_order.jl") +include("vector_interface.jl") +include("FDPNSystem.jl") +include("state_variables.jl") +include("BBH.jl") +include("BHNS.jl") +include("NSNS.jl") diff --git a/src/pn_systems/state_variables.jl b/src/pn_systems/state_variables.jl new file mode 100644 index 00000000..e06b2c44 --- /dev/null +++ b/src/pn_systems/state_variables.jl @@ -0,0 +1,342 @@ +""" + G(pnsystem) + +Return Newton's gravitational constant for the given `pnsystem`. + +By default, the value is one *with the same number type as `pnsystem`*. It can be +overridden for subtypes of `PNSystem` that use different units or conventions. + +However, note that this function should specialize on the number type of `pnsystem`, rather +than just returning the integer `1`, because there may be expressions with factors such as +`G/3` which will immediately convert to `Float64` if `G` is just `1`, so the result will not +have the expected precision. +""" +G(::PNSystem{NT}) where {NT} = one(NT) +G(::FDPNSystem{NT,PN}) where {NT,PN} = one(PN) + +""" + c(pnsystem) + +Return the speed of light for the given `pnsystem`. + +By default, the value is one *with the same number type as `pnsystem`*. It can be +overridden for subtypes of `PNSystem` that use different units or conventions. + +However, note that this function should specialize on the number type of `pnsystem`, rather +than just returning the integer `1`, because there may be expressions with factors such as +`c/3` which will immediately convert to `Float64` if `c` is just `1`, so the result will not +have the expected precision. +""" +c(::PNSystem{NT}) where {NT} = one(NT) +c(::FDPNSystem{NT,PN}) where {NT,PN} = one(PN) + +""" + M₁(pnsystem) + M1(pnsystem) + +Mass of object 1 in this system. +""" +function M₁(::T) where {T<:PNSystem} + error("M₁ is not (yet) defined for PNSystem subtype `$T`.") +end +M₁(fdpnsystem::FDPNSystem) = fdpnsystem[:M₁] +const M1 = M₁ + +""" + M₂(pnsystem) + M2(pnsystem) + +Mass of object 2 in this system. +""" +function M₂(::T) where {T<:PNSystem} + error("M₂ is not (yet) defined for PNSystem subtype `$T`.") +end +M₂(fdpnsystem::FDPNSystem) = fdpnsystem[:M₂] +const M2 = M₂ + +""" + χ⃗₁ˣ(pnsystem) + chi1x(pnsystem) + +`x`-component of dimensionless spin vector of object 1 in this system, as a `QuatVec`. + +See [`χ⃗₁`](@ref) for details. +""" +function χ⃗₁ˣ(::T) where {T<:PNSystem} + error("χ⃗₁ˣ is not (yet) defined for PNSystem subtype `$T`.") +end +χ⃗₁ˣ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ⃗₁ˣ] +const chi1x = χ⃗₁ˣ + +""" + χ⃗₁ʸ(pnsystem) + chi1y(pnsystem) + +`y`-component of dimensionless spin vector of object 1 in this system, as a `QuatVec`. + +See [`χ⃗₁`](@ref) for details. +""" +function χ⃗₁ʸ(::T) where {T<:PNSystem} + error("χ⃗₁ʸ is not (yet) defined for PNSystem subtype `$T`.") +end +χ⃗₁ʸ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ⃗₁ʸ] +const chi1y = χ⃗₁ʸ + +""" + χ⃗₁ᶻ(pnsystem) + chi1z(pnsystem) + +`z`-component of dimensionless spin vector of object 1 in this system, as a `QuatVec`. + +See [`χ⃗₁`](@ref) for details. +""" +function χ⃗₁ᶻ(::T) where {T<:PNSystem} + error("χ⃗₁ᶻ is not (yet) defined for PNSystem subtype `$T`.") +end +χ⃗₁ᶻ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ⃗₁ᶻ] +const chi1z = χ⃗₁ᶻ + +""" + χ⃗₂ˣ(pnsystem) + chi2x(pnsystem) + +`x`-component of dimensionless spin vector of object 2 in this system, as a `QuatVec`. + +See [`χ⃗₂`](@ref) for details. +""" +function χ⃗₂ˣ(::T) where {T<:PNSystem} + error("χ⃗₂ˣ is not (yet) defined for PNSystem subtype `$T`.") +end +χ⃗₂ˣ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ⃗₂ˣ] +const chi2x = χ⃗₂ˣ + +""" + χ⃗₂ʸ(pnsystem) + chi2y(pnsystem) + +`y`-component of dimensionless spin vector of object 2 in this system, as a `QuatVec`. + +See [`χ⃗₂`](@ref) for details. +""" +function χ⃗₂ʸ(::T) where {T<:PNSystem} + error("χ⃗₂ʸ is not (yet) defined for PNSystem subtype `$T`.") +end +χ⃗₂ʸ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ⃗₂ʸ] +const chi2y = χ⃗₂ʸ + +""" + χ⃗₂ᶻ(pnsystem) + chi2z(pnsystem) + +`z`-component of dimensionless spin vector of object 2 in this system, as a `QuatVec`. + +See [`χ⃗₂`](@ref) for details. +""" +function χ⃗₂ᶻ(::T) where {T<:PNSystem} + error("χ⃗₂ᶻ is not (yet) defined for PNSystem subtype `$T`.") +end +χ⃗₂ᶻ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ⃗₂ᶻ] +const chi2z = χ⃗₂ᶻ + +""" + Rʷ(pnsystem) + Rw(pnsystem) + +Scalar component of the orientation `Rotor` of the binary. + +See [`R`](@ref) for details. +""" +function Rʷ(::T) where {T<:PNSystem} + error("Rʷ is not (yet) defined for PNSystem subtype `$T`.") +end +Rʷ(fdpnsystem::FDPNSystem) = fdpnsystem[:Rʷ] +const Rw = Rʷ + +""" + Rˣ(pnsystem) + Rx(pnsystem) + +`x`-component of the orientation `Rotor` of the binary. + +See [`R`](@ref) for details. +""" +function Rˣ(::T) where {T<:PNSystem} + error("Rˣ is not (yet) defined for PNSystem subtype `$T`.") +end +Rˣ(fdpnsystem::FDPNSystem) = fdpnsystem[:Rˣ] +const Rx = Rˣ + +""" + Rʸ(pnsystem) + Ry(pnsystem) + +`y`-component of the orientation `Rotor` of the binary. + +See [`R`](@ref) for details. +""" +function Rʸ(::T) where {T<:PNSystem} + error("Rʸ is not (yet) defined for PNSystem subtype `$T`.") +end +Rʸ(fdpnsystem::FDPNSystem) = fdpnsystem[:Rʸ] +const Ry = Rʸ + +""" + Rᶻ(pnsystem) + Rz(pnsystem) + +`z`-component of the orientation `Rotor` of the binary. + +See [`R`](@ref) for details. +""" +function Rᶻ(::T) where {T<:PNSystem} + error("Rᶻ is not (yet) defined for PNSystem subtype `$T`.") +end +Rᶻ(fdpnsystem::FDPNSystem) = fdpnsystem[:Rᶻ] +const Rz = Rᶻ + +@doc raw""" + v(pnsystem) + v(;Ω, M=1) + +Post-Newtonian velocity parameter. This is related to the orbital angular frequency +``\Omega`` as +```math +v \colonequals (M\,\Omega)^{1/3}, +``` +where ``M`` is the total mass of the binary. + +Note that if you want to pass the value ``Ω`` (rather than a `PNSystem`), you must pass it +as a keyword argument — as in `v(Ω=0.1)`. + +See also [`Ω`](@ref). +""" +function v(::T) where {T<:PNSystem} + error("v is not (yet) defined for PNSystem subtype `$T`.") +end +v(fdpnsystem::FDPNSystem) = fdpnsystem[:v] +v(; Ω, M=1) = ∛(M * Ω) + +""" + Φ(pnsystem) + Phi(pnsystem) + +Integrated orbital phase of the system. It is computed as the integral of [`Ω`](@ref). +""" +function Φ(::T) where {T<:PNSystem} + error("Φ is not (yet) defined for PNSystem subtype `$T`.") +end +Φ(fdpnsystem::FDPNSystem) = fdpnsystem[:Φ] +const Phi = Φ + +@doc raw""" + Λ₁(pnsystem) + Lambda1(pnsystem) + +Quadrupolar tidal-coupling parameter of object 1 in this system. + +We imagine object 1 begin placed in an (adiabatic) external field with Newtonian potential +``\phi``, resulting in a tidal field measured by ``\partial_i \partial_j \phi`` evaluated at +the center of mass of the object. This induces a quadrupole moment ``Q_{ij}`` in object 1, +which can be related to the tidal field as +```math +Q_{ij} = -\frac{G^4}{c^{10}} \Lambda_1 M_1^5 \partial_i \partial_j \phi, +``` +where ``M_1`` is the mass of object 1. This tidal-coupling parameter ``\Lambda_1`` can be +related to the Love number ``k_2`` (where the subscript 2 refers to the fact that this is +for the ``\ell=2`` quadrupole, rather than object 2) as +```math +\Lambda_1 = \frac{2}{3} \frac{c^{10}}{G^5} \frac{R_1^5}{M_1^5} k_2, +``` +where ``R_1`` is the radius of object 1. Note that ``\Lambda_1`` is dimensionless. For +black holes, it is precisely zero; for neutron stars it may range up to 1; more exotic +objects may have significantly larger values. + +Note that — as of this writing — only `NSNS` systems can have a nonzero value for this +quantity. (`BHNS` systems can only have a nonzero value for ``\Lambda_2``.) All other +types return `0`, which Julia can use to eliminate code that would then be 0. Thus, it is +safe and efficient to use this quantity in any PN expression that specializes on the type of +`pnsystem`. + +See also [`Λ₂`](@ref) and [`Λ̃`](@ref). +""" +function Λ₁(::T) where {T<:PNSystem} + error("Λ₁ is not (yet) defined for PNSystem subtype `$T`.") +end +Λ₁(fdpnsystem::FDPNSystem) = fdpnsystem[:Λ₁] +const Lambda1 = Λ₁ + +@doc raw""" + Λ₂(pnsystem) + Lambda2(pnsystem) + +Quadrupolar tidal coupling parameter of object 2 in this system. + +See [`Λ₁`](@ref) for details about the definition, swapping "object 1" with "object 2". + +Note that — as of this writing — only `BHNS` and `NSNS` systems can have a nonzero value for +this quantity. All other types return `0`, which Julia can use to eliminate code that would +then be 0. Thus, it is safe and efficient to use this quantity in any PN expression that +specializes on the type of `pnsystem`. + +See also [`Λ₁`](@ref) and [`Λ̃`](@ref). +""" +function Λ₂(::T) where {T<:PNSystem} + error("Λ₂ is not (yet) defined for PNSystem subtype `$T`.") +end +Λ₂(fdpnsystem::FDPNSystem) = fdpnsystem[:Λ₂] +const Lambda2 = Λ₂ + +################################################################# +# Not actually state variables, but aggregates of state variables + +""" + χ⃗₁(pnsystem) + chi1(pnsystem) + +Dimensionless spin vector of object 1 in this system, as a `QuatVec`. + +See also [`χ⃗₁ˣ`](@ref), [`χ⃗₁ʸ`](@ref), and [`χ⃗₁ᶻ`](@ref) for the individual components. +""" +function χ⃗₁(::T) where {T<:PNSystem} + QuatVec(χ⃗₁ˣ(pnsystem), χ⃗₁ʸ(pnsystem), χ⃗₁ᶻ(pnsystem)) +end +const chi1 = χ⃗₁ + +""" + χ⃗₂(pnsystem) + chi2(pnsystem) + +Dimensionless spin vector of object 2 in this system, as a `QuatVec`. + +See also [`χ⃗₂ˣ`](@ref), [`χ⃗₂ʸ`](@ref), and [`χ⃗₂ᶻ`](@ref) for the individual components. +""" +function χ⃗₂(::T) where {T<:PNSystem} + QuatVec(χ⃗₂ˣ(pnsystem), χ⃗₂ʸ(pnsystem), χ⃗₂ᶻ(pnsystem)) +end +const chi2 = χ⃗₂ + +""" + R(pnsystem) + +Orientation of the binary, as a `Rotor`. + +At any instant, the binary is represented by the right-handed triad ``(n̂, λ̂, ℓ̂)``, where +[``n̂``](@ref PostNewtonian.n̂) is the unit vector pointing from object 2 to object 1, and +the instantaneous velocities of the binary's elements are in the ``n̂``-``λ̂`` plane. This +`Rotor` will rotate the ``x̂`` vector to be along ``n̂``, the ``ŷ`` vector to be along +``λ̂``, and the ``ẑ`` vector to be along ``ℓ̂``. + +Note that the angular velocity associated to `R` is given by ``Ω⃗ = 2 Ṙ R̄ = Ω ℓ̂ + ϖ n̂``. +(Any component of ``Ω⃗`` along ``λ̂`` would violate the condition that the velocities be in +the ``n̂``-``λ̂`` plane.) Here, the scalar quantity ``Ω`` is the orbital angular frequency, +and ``ϖ`` is the precession angular frequency. + +See also [`n̂`](@ref PostNewtonian.n̂), [`λ̂`](@ref PostNewtonian.λ̂), [`ℓ̂`](@ref +PostNewtonian.ℓ̂), [`Ω`](@ref PostNewtonian.Ω), and [`𝛡`](@ref PostNewtonian.𝛡)``=ϖ n̂``. +""" +function R(pnsystem::T) where {NT,T<:PNSystem{NT}} + # We use this explicit constructor (with type parameter) to avoid normalization + # that would probably just complicate derivatives. + Rotor{NT}(Rʷ(pnsystem), Rˣ(pnsystem), Rʸ(pnsystem), Rᶻ(pnsystem)) +end +R(fdpnsystem::FDPNSystem) = fdpnsystem[:R] diff --git a/src/pn_systems/vector_interface.jl b/src/pn_systems/vector_interface.jl new file mode 100644 index 00000000..03fd570c --- /dev/null +++ b/src/pn_systems/vector_interface.jl @@ -0,0 +1,249 @@ +# Base.ismutable(pnsystem::PNSystem{NT, ST}) where {NT, ST} = ismutable(state(pnsystem)) +# Base.ismutabletype(::Type{<:PNSystem{NT, ST}}) where {NT, ST} = ismutabletype(ST) + +""" + symbol_index(::Type{T}, s::Symbol) where {T<:PNSystem} + symbol_index(::Type{T}, ::Val{s}) where {T<:PNSystem} + +Return the index of the symbol `s` in the state vector of the given `PNSystem` type `T`. + +Note that the default implementation is slow; `symbol_index(::Type{T}, ::Val{s})` should be +overridden for every symbol (and ASCII equivalent, if desired) for concrete `PNSystem` +types. +""" +function symbol_index(::Type{T}, ::Val{S}) where {T<:PNSystem,S} + index = findfirst(y -> y == S, symbols(T)) + if isnothing(index) + index = findfirst(y -> y == S, ascii_symbols(T)) + end + if isnothing(index) + error( + "Type `$(T)` has no symbol `:$(S)`.\n" * + "Its symbols are `$(symbols(T))`.\n" * + "The ASCII equivalents are `$(ascii_symbols(T))`.\n", + ) + else + @warn "Please define `PostNewtonian.symbol_index(::Type{$T}, ::Val{$S})`" + index + end +end + +Base.getindex(pnsystem::PNSystem, s::Symbol) = getindex(pnsystem, Val(s)) +function Base.getindex(pnsystem::T, ::Val{S}) where {T<:PNSystem,S} + # If `S` is not actually a symbol in `pnsystem`, `symbol_index` will error, so we know + # that the `index` is inbounds if it returns. + index = symbol_index(T, Val(S)) + @inbounds state(pnsystem)[index] +end + +Base.setindex!(pnsystem::PNSystem, v, s::Symbol) = setindex!(pnsystem, v, Val(s)) +function Base.setindex!(pnsystem::T, v, ::Val{S}) where {NT,T<:PNSystem{NT},S} + index = symbol_index(T, Val(S)) + @inbounds setindex!(state(pnsystem), v, index) +end + +### Interfaces: https://docs.julialang.org/en/v1/manual/interfaces +# Iteration +Base.iterate(pnsystem::PNSystem) = iterate(state(pnsystem)) +Base.iterate(pnsystem::PNSystem, state) = iterate(state(pnsystem), state) +Base.IteratorSize(::Type{T}) where {T<:PNSystem} = Base.HasShape{1}() +Base.length(pnsystem::PNSystem) = length(state(pnsystem)) +Base.ndims(pnsystem::PNSystem) = ndims(state(pnsystem)) +Base.size(pnsystem::PNSystem) = size(state(pnsystem)) +Base.size(pnsystem::PNSystem, dim) = size(state(pnsystem), dim) +Base.IteratorEltype(::Type{T}) where {T<:PNSystem} = Base.HasEltype() +Base.eltype(::Type{<:PNSystem{NT}}) where {NT} = NT +Base.isdone(pnsystem::PNSystem) = isdone(state(pnsystem)) +Base.isdone(pnsystem::PNSystem, iterstate) = isdone(state(pnsystem), iterstate) +# Indexing +Base.getindex(pnsystem::PNSystem, i::Int) = @propagate_inbounds getindex(state(pnsystem), i) +Base.setindex!(pn::PNSystem, v, i::Int) = @propagate_inbounds setindex!(state(pn), v, i) +Base.firstindex(pnsystem::PNSystem) = firstindex(state(pnsystem)) +Base.lastindex(pnsystem::PNSystem) = lastindex(state(pnsystem)) +Base.eachindex(pnsystem::PNSystem) = eachindex(state(pnsystem)) +# Abstract arrays +Base.IndexStyle(::Type{T}) where {T<:PNSystem} = Base.IndexLinear() +Base.length(pnsystem::PNSystem) = length(state(pnsystem)) +# Base.similar(pnsystem::PNSystem) = similar(state(pnsystem)) +Base.axes(pnsystem::PNSystem) = axes(state(pnsystem)) +# Strided Arrays +Base.strides(pnsystem::PNSystem) = strides(state(pnsystem)) +function Base.unsafe_convert(::Type{Ptr{T}}, A::PNSystem) where {T} + Base.unsafe_convert(Ptr{T}, state(A)) +end +Base.elsize(::Type{<:PNSystem{T}}) where {T} = sizeof(T) +Base.stride(pnsystem::PNSystem, k::Int) = stride(state(pnsystem), k) + +function PreallocationTools.get_tmp( + dc::PreallocationTools.DiffCache, u::LArray{T,N,D,Syms} +) where {T<:ForwardDiff.Dual,N,D,Syms} + nelem = div(sizeof(T), sizeof(eltype(dc.dual_du))) * length(dc.du) + if nelem > length(dc.dual_du) + PreallocationTools.enlargedualcache!(dc, nelem) + end + _x = ArrayInterface.restructure(dc.du, reinterpret(T, view(dc.dual_du, 1:nelem))) + LabelledArrays.LArray{T,N,D,Syms}(_x) +end + +function RecursiveArrayTools.recursive_unitless_eltype( + a::Type{LArray{T,N,D,Syms}} +) where {T,N,D,Syms} + LArray{typeof(one(T)),N,D,Syms} +end + +##################################### +# NamedTuple compatibility +##################################### +## SLArray to named tuple +function Base.convert(::Type{NamedTuple}, x::SLArray{S,T,N,L,Syms}) where {S,T,N,L,Syms} + tup = NTuple{length(Syms),T}(x.__x) + NamedTuple{Syms,typeof(tup)}(tup) +end +Base.keys(x::SLArray{S,T,N,L,Syms}) where {S,T,N,L,Syms} = Syms + +## pairs iterator +function Base.pairs(x::LArray{T,N,D,Syms}) where {T,N,D,Syms} + # (label => getproperty(x, label) for label in Syms) # not type stable? + (Syms[i] => x[i] for i ∈ 1:length(Syms)) +end + +function Base.iterate(x::SLArray, args...) + iterate(convert(NamedTuple, x), args...) +end + +##################################### +# Array Interface +##################################### +function Base.print_array(io::IO, w::WignerMatrix{NT,IT}) where {NT,IT<:Rational} + Base.print_array(io, parent(w)) +end + +Base.size(x::LArray) = size(getfield(x, :__x)) +Base.@propagate_inbounds Base.getindex(x::LArray, i...) = getfield(x, :__x)[i...] +Base.@propagate_inbounds function Base.setindex!(x::LArray, y, i...) + getfield(x, :__x)[i...] = y + return x +end + +Base.propertynames(::LArray{T,N,D,Syms}) where {T,N,D,Syms} = Syms +symnames(::Type{LArray{T,N,D,Syms}}) where {T,N,D,Syms} = Syms + +Base.@propagate_inbounds function Base.getproperty(x::LArray, s::Symbol) + if s == :__x + return getfield(x, :__x) + end + return getindex(x, Val(s)) +end + +Base.@propagate_inbounds function Base.setproperty!(x::LArray, s::Symbol, y) + if s == :__x + return setfield!(x, :__x, y) + end + setindex!(x, y, Val(s)) +end + +Base.@propagate_inbounds Base.getindex(x::LArray, s::Symbol) = getindex(x, Val(s)) +Base.@propagate_inbounds Base.getindex(x::LArray, s::Val) = __getindex(x, s) +Base.@propagate_inbounds Base.setindex!(x::LArray, v, s::Symbol) = setindex!(x, v, Val(s)) + +@generated function Base.setindex!(x::LArray, y, ::Val{s}) where {s} + syms = symnames(x) + if syms isa NamedTuple + idxs = syms[s] + return quote + Base.@_propagate_inbounds_meta + setindex!(getfield(x, :__x), y, $idxs) + return x + end + else # Tuple + idx = findfirst(y -> y == s, symnames(x)) + return quote + Base.@_propagate_inbounds_meta + setindex!(getfield(x, :__x), y, $idx) + return x + end + end +end + +Base.@propagate_inbounds function Base.getindex(x::LArray, s::AbstractArray{Symbol,1}) + [getindex(x, si) for si ∈ s] +end + +function Base.similar( + x::LArray{T,K,D,Syms}, ::Type{S}, dims::NTuple{N,Int} +) where {T,K,D,Syms,S,N} + tmp = similar(x.__x, S, dims) + LArray{S,N,typeof(tmp),Syms}(tmp) +end + +function StaticArrays.similar_type( + ::Type{SLArray{S,T,N,L,Syms}}, T2, ::Size{S} +) where {S,T,N,L,Syms} + SLArray{S,T2,N,L,Syms} +end + +# Allow copying LArray of uninitialized data, as with regular Array +Base.copy(x::LArray) = typeof(x)(copy(getfield(x, :__x))) +Base.copyto!(x::LArray, y::LArray) = copyto!(getfield(x, :__x), getfield(y, :__x)) + +# enable the usage of LAPACK +function Base.unsafe_convert(::Type{Ptr{T}}, a::LArray{T,N,D,S}) where {T,N,D,S} + Base.unsafe_convert(Ptr{T}, getfield(a, :__x)) +end + +Base.convert(::Type{T}, x) where {T<:LArray} = T(x) +Base.convert(::Type{T}, x::T) where {T<:LArray} = x +Base.convert(::Type{<:Array}, x::LArray) = convert(Array, getfield(x, :__x)) +function Base.convert( + ::Type{AbstractArray{T,N}}, x::LArray{S,N,<:Any,Syms} +) where {T,S,N,Syms} + LArray{Syms}(convert(AbstractArray{T,N}, getfield(x, :__x))) +end +Base.convert(::Type{AbstractArray{T,N}}, x::LArray{T,N}) where {T,N} = x + +function ArrayInterface.restructure( + x::LArray{T,N,D,Syms}, y::LArray{T2,N2,D2,Syms} +) where {T,N,D,T2,N2,D2,Syms} + reshape(y, size(x)...) +end + +##################################### +# Broadcast +##################################### +struct LAStyle{T,N,L} <: Broadcast.AbstractArrayStyle{N} end +LAStyle{T,N,L}(x::Val{i}) where {T,N,L,i} = LAStyle{T,N,L}() +Base.BroadcastStyle(::Type{LArray{T,N,D,L}}) where {T,N,D,L} = LAStyle{T,N,L}() +function Base.BroadcastStyle( + ::LabelledArrays.LAStyle{T,N,L}, ::LabelledArrays.LAStyle{E,N,L} +) where {T,E,N,L} + LAStyle{promote_type(T, E),N,L}() +end + +@generated function labels2axes(::Val{t}) where {t} + if t isa NamedTuple && all(x -> x isa Union{Integer,UnitRange}, values(t)) # range labelling + (Base.OneTo(maximum(Iterators.flatten(v for v ∈ values(t)))),) + elseif t isa NTuple{<:Any,Symbol} + axes(t) + else + error( + "$t label isn't supported for broadcasting. Try to formulate it in terms of linear indexing.", + ) + end +end +function Base.similar( + bc::Broadcast.Broadcasted{LAStyle{T,N,L}}, ::Type{ElType} +) where {T,N,L,ElType} + tmp = similar(Array{ElType}, axes(bc)) + if axes(bc) != labels2axes(Val(L)) + return tmp + else + return LArray{ElType,N,typeof(tmp),L}(tmp) + end +end + +# Broadcasting checks for aliasing with Base.dataids but the fallback +# for AbstractArrays is very slow. Instead, we just call dataids on the +# wrapped buffer +Base.dataids(pnsystem::PNSystem) = Base.dataids(state(pnsystem)) + +Base.elsize(::Type{<:LArray{T}}) where {T} = sizeof(T) diff --git a/src/utilities.jl b/src/utilities.jl deleted file mode 100644 index 615a5069..00000000 --- a/src/utilities.jl +++ /dev/null @@ -1,9 +0,0 @@ -include("utilities/mathconstants.jl") -include("utilities/misc.jl") -include("utilities/truncated_series_monoid.jl") -include("utilities/truncated_series_inversion.jl") -include("utilities/combine_solutions.jl") -include("utilities/termination_criteria.jl") - -# Don't include macros.jl yet; we need to get contents of `FundamentalVariables` and -# `DerivedVariables`, so that's included by `pn_expressions.jl`. diff --git a/src2/literature/PetersMathews1963/PetersMathews1963.jl b/src2/literature/PetersMathews1963/PetersMathews1963.jl deleted file mode 100644 index 0cfcb026..00000000 --- a/src2/literature/PetersMathews1963/PetersMathews1963.jl +++ /dev/null @@ -1,3 +0,0 @@ -@pn_reference module PetersMathews1963 - -end diff --git a/src2/literature/common_variables.jl b/src2/literature/common_variables.jl deleted file mode 100644 index 80cfe481..00000000 --- a/src2/literature/common_variables.jl +++ /dev/null @@ -1,10 +0,0 @@ -module CommonVariables - -# Import the fundamental variables, which are generally part of the `state` vector of a -# PNSystem. -import PostNewtonian: M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ - -# Now, we define all the common variables used throughout the literature as functions of -# those fundamental variables. - -end diff --git a/src2/literature/ref1/ref1.jl b/src2/literature/ref1/ref1.jl deleted file mode 100644 index 1d23dc7a..00000000 --- a/src2/literature/ref1/ref1.jl +++ /dev/null @@ -1,10 +0,0 @@ -module Ref1 - -import PostNewtonian: M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ -import CommonVariables: χₛ - -# import common variables -# define variables not in common variables -# write individual expressions as separate functions with `@pn_expression` - -end diff --git a/src2/pn_expressions/precession.jl b/src2/pn_expressions/precession.jl deleted file mode 100644 index 8b137891..00000000 --- a/src2/pn_expressions/precession.jl +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src2/pn_expressions/waveforms.jl b/src2/pn_expressions/waveforms.jl deleted file mode 100644 index 8b137891..00000000 --- a/src2/pn_expressions/waveforms.jl +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src2/pn_systems/BBH.jl b/src2/pn_systems/BBH.jl deleted file mode 100644 index c8e4f373..00000000 --- a/src2/pn_systems/BBH.jl +++ /dev/null @@ -1,61 +0,0 @@ -abstract type AbstractBBH{NT,ST,PNOrder} <: PNSystem{NT,ST,PNOrder} end - -""" - BBH{T, PNOrder} - -The [`PNSystem`](@ref) subtype describing a binary black hole system. - -The `state` vector here holds the fundamental variables `M₁`, `M₂`, `χ⃗₁`, `χ⃗₂`, `R`, `v`, -with the spins unpacked into three components each, and `R` unpacked into four — for a total -of 13 elements. - -Optionally, `Φ` may also be tracked as the 14th element of the `state` vector. This is just -the integral of the orbital angular frequency `Ω`, and holds little interest for general -systems beyond a convenient description of how "far" the system has evolved. -""" -struct BBH{NT,ST,PNOrder} <: AbstractBBH{NT,ST,PNOrder} - state::ST - - BBH{NT,ST,PNOrder}(state) where {NT,ST,PNOrder} = new{NT,ST,PNOrder}(state) - function BBH(; M₁, M₂, χ⃗₁, χ⃗₂, v, R=Rotor(1), Φ=0, PNOrder=typemax(Int), kwargs...) - (NT, ST, PNOrder, state) = prepare_system(; M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ, PNOrder) - return new{NT,ST,PNOrder}(state) - end - function BBH(state; Λ₁=0, Λ₂=0, PNOrder=typemax(Int)) - @assert length(state) == 14 - @assert Λ₁ == 0 - @assert Λ₂ == 0 - return new{eltype(state),typeof(state),prepare_pn_order(PNOrder)}(state) - end -end -const BHBH = BBH - -state(pnsystem::BBH) = pnsystem.state - -function symbols(::Type{BBH}) - (:M₁, :M₂, :χ⃗₁ˣ, :χ⃗₁ʸ, :χ⃗₁ᶻ, :χ⃗₂ˣ, :χ⃗₂ʸ, :χ⃗₂ᶻ, :Rʷ, :Rˣ, :Rʸ, :Rᶻ, :v, :Φ) -end -function ascii_symbols(::Type{BBH}) - (:M1, :M2, :chi1x, :chi1y, :chi1z, :chi2x, :chi2y, :chi2z, :Rw, :Rx, :Ry, :Rz, :v, :Phi) -end - -M₁(pnsystem::BBH) = @inbounds state(pnsystem)[1] -M₂(pnsystem::BBH) = @inbounds state(pnsystem)[2] -χ⃗₁(pnsystem::BBH) = @inbounds state(pnsystem)[3:5] -χ⃗₁ˣ(pnsystem::BBH) = @inbounds state(pnsystem)[3] -χ⃗₁ʸ(pnsystem::BBH) = @inbounds state(pnsystem)[4] -χ⃗₁ᶻ(pnsystem::BBH) = @inbounds state(pnsystem)[5] -χ⃗₂(pnsystem::BBH) = @inbounds state(pnsystem)[6:8] -χ⃗₂ˣ(pnsystem::BBH) = @inbounds state(pnsystem)[6] -χ⃗₂ʸ(pnsystem::BBH) = @inbounds state(pnsystem)[7] -χ⃗₂ᶻ(pnsystem::BBH) = @inbounds state(pnsystem)[8] -R(pnsystem::BBH) = @inbounds state(pnsystem)[9:12] -Rʷ(pnsystem::BBH) = @inbounds state(pnsystem)[9] -Rˣ(pnsystem::BBH) = @inbounds state(pnsystem)[10] -Rʸ(pnsystem::BBH) = @inbounds state(pnsystem)[11] -Rᶻ(pnsystem::BBH) = @inbounds state(pnsystem)[12] -v(pnsystem::BBH) = @inbounds state(pnsystem)[13] -Φ(pnsystem::BBH) = @inbounds state(pnsystem)[14] - -# Λ₁(pnsystem::BBH) = @inbounds state(pnsystem)[15] -# Λ₂(pnsystem::BBH) = @inbounds state(pnsystem)[16] diff --git a/src2/pn_systems/PNSystem.jl b/src2/pn_systems/PNSystem.jl deleted file mode 100644 index 62106265..00000000 --- a/src2/pn_systems/PNSystem.jl +++ /dev/null @@ -1,164 +0,0 @@ -# TODO: Write the docstrings for all the general state functions -# TODO: Change to subtyping SVector or MVector -# - similar_type(::Type{MyPNSystem}, ::Type{NewElType}) -# - similar(::Type{MyPNSystem}, ::Type{NewElType}) for MVector -# - setindex! for MVector -# - zero-parameter constructor for MVector - -""" - PNSystem{NT, PNOrder} - -Base type for all PN systems, such as `BBH`, `BHNS`, and `NSNS`. - -These objects encode all essential properties of the binary, including its current state. -As such, they can be used as inputs to the various [fundamental](@ref Fundamental-variables) -and [derived variables](@ref Derived-variables), as well as [PN expressions](@ref) and -[dynamics](@ref Dynamics) functions. - -All subtypes should contain a `state` vector holding all of the fundamental variables for -the given type of system. The parameter `ST` is the type of the `state` vector — for -example, `Vector{Float64}`. `PNOrder` is a `Rational` giving the order to which PN -expansions should be carried. -""" -abstract type PNSystem{NT,PNOrder} <: Vector{NT} end - -""" - state(pnsystem::PNSystem) - -Return the state vector of `pnsystem`, which is a vector of fundamental variables for the -given PN system. - -Note that the built-in `PNSystem` subtypes have a `state` field that is a vector, so this -function will just return that vector. However, that may not always be true for -user-defined subtypes. -""" -function state(::T) where {T<:PNSystem} - error("`state` is not yet defined for PNSystem subtype `$T`.") -end -Base.vec(pnsystem::PNSystem) = state(pnsystem) - -Base.one(::PNSystem{NT}) where {NT} = one(NT) -Base.zero(::PNSystem{NT}) where {NT} = zero(NT) - -""" - G(pnsystem) - -Return Newton's gravitational constant for the given `pnsystem`. - -By default, the value is one *with the same number type as `pnsystem`*. It can be -overridden for subtypes of `PNSystem` that use different units or conventions. - -However, note that this function should specialize on the number type of `pnsystem`, rather -than just returning the integer `1`, because there may be expressions with factors such as -`G/3` which will immediately convert to `Float64` if `G` is just `1`, so the result will not -have the expected precision. -""" -G(::PNSystem{NT}) where {NT} = one(NT) - -""" - c(pnsystem) - -Return the speed of light for the given `pnsystem`. - -By default, the value is one *with the same number type as `pnsystem`*. It can be -overridden for subtypes of `PNSystem` that use different units or conventions. - -However, note that this function should specialize on the number type of `pnsystem`, rather -than just returning the integer `1`, because there may be expressions with factors such as -`c/3` which will immediately convert to `Float64` if `c` is just `1`, so the result will not -have the expected precision. -""" -c(::PNSystem{NT}) where {NT} = one(NT) - -""" - symbols(pnsystem::PNSystem) - symbols(::Type{<:PNSystem}) - ascii_symbols(pnsystem::PNSystem) - ascii_symbols(::Type{<:PNSystem}) - -Return a Tuple of symbols corresponding to the variables tracked by `pnsystem`, in the order -in which they are stored in the `state` vector. -""" -symbols(pnsystem::PNSystem) = symbols(typeof(pnsystem)) -function symbols(::Type{T}) where {T<:PNSystem} - error("`symbols`is not yet defined for PNSystem subtype `$T`.") -end -ascii_symbols(pnsystem::PNSystem) = ascii_symbols(typeof(pnsystem)) -function ascii_symbols(::Type{T}) where {T<:PNSystem} - error("`ascii_symbols` is not yet defined for PNSystem subtype `$T`.") -end - -# Define all fundamental variables as functions of a PNSystem, but undefined for the -# abstract type. -for var ∈ ( - :M₁, - :M₂, - :χ⃗₁, - :χ⃗₁ˣ, - :χ⃗₁ʸ, - :χ⃗₁ᶻ, - :χ⃗₂, - :χ⃗₂ˣ, - :χ⃗₂ʸ, - :χ⃗₂ᶻ, - :R, - :Rʷ, - :Rˣ, - :Rʸ, - :Rᶻ, - :v, - :Φ, - :Λ₁, - :Λ₂, -) - @eval begin - function $var(::T) where {T<:PNSystem} - error("$var is not (yet) defined for PNSystem subtype `$T`.") - end - end -end - -const M1 = M₁ -const M2 = M₂ -const chi1 = χ⃗₁ -const chi1x = χ⃗₁ˣ -const chi1y = χ⃗₁ʸ -const chi1z = χ⃗₁ᶻ -const chi2 = χ⃗₂ -const chi2x = χ⃗₂ˣ -const chi2y = χ⃗₂ʸ -const chi2z = χ⃗₂ᶻ -# const R = R -const Rw = Rʷ -const Rx = Rˣ -const Ry = Rʸ -const Rz = Rᶻ -# const v = v -const Phi = Φ -const Lambda1 = Λ₁ -const Lambda2 = Λ₂ - -### Interfaces: https://docs.julialang.org/en/v1/manual/interfaces -# Iteration -Base.iterate(pnsystem::PNSystem) = iterate(state(pnsystem)) -Base.iterate(pnsystem::PNSystem, state) = iterate(state(pnsystem), state) -Base.length(pnsystem::PNSystem) = length(state(pnsystem)) -Base.IteratorSize(::Type{T}) where {T<:PNSystem} = Base.IteratorSize(Vector) -Base.length(pnsystem::PNSystem) = length(state(pnsystem)) -Base.size(pnsystem::PNSystem) = size(state(pnsystem)) -Base.size(pnsystem::PNSystem, dim) = size(state(pnsystem), dim) -Base.IteratorEltype(::Type{T}) where {T<:PNSystem} = Base.IteratorEltype(Vector) -Base.eltype(::Type{T}) where {NT,T<:PNSystem{NT}} = NT -Base.isdone(pnsystem::PNSystem) = isdone(state(pnsystem)) -Base.isdone(pnsystem::PNSystem, iterstate) = isdone(state(pnsystem), iterstate) -# Indexing -Base.getindex(pnsystem::PNSystem, i) = @propagate_inbounds getindex(state(pnsystem), i) -Base.setindex!(pnsys::PNSystem, v, i) = @propagate_inbounds setindex!(state(pnsys), v, i) -Base.firstindex(pnsystem::PNSystem) = firstindex(state(pnsystem)) -Base.lastindex(pnsystem::PNSystem) = lastindex(state(pnsystem)) -# Abstract arrays -Base.size(pnsystem::PNSystem) = size(state(pnsystem)) -Base.IndexStyle(::Type{T}) where {T<:PNSystem} = Base.IndexStyle(Vector) -Base.length(pnsystem::PNSystem) = length(state(pnsystem)) -# Base.similar(pnsystem::PNSystem) = similar(state(pnsystem)) -Base.axes(pnsystem::PNSystem) = axes(state(pnsystem)) From 113c7faf5f0ea6221e40f6182553ea2fc8a49c9e Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Wed, 25 Jun 2025 20:13:41 -0400 Subject: [PATCH 04/77] Copy InlineExports --- src/core/utilities/InlineExports.jl | 230 ++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 src/core/utilities/InlineExports.jl diff --git a/src/core/utilities/InlineExports.jl b/src/core/utilities/InlineExports.jl new file mode 100644 index 00000000..c6dccf48 --- /dev/null +++ b/src/core/utilities/InlineExports.jl @@ -0,0 +1,230 @@ +# NOTE: This file is a modified version of the `InlineExports.jl` package, which is +# licensed under the MIT License. The original source can be found at: +# https://github.com/dalum/InlineExports.jl + +module InlineExports + +import Base: @__doc__ + +export @export, @public + +eval(quote + """ + @export + + Return the expression with all bindings exported. + + ``` + julia> module M + using InlineExports + @export begin + const a = 2 + abstract type S <: Number end + struct T <: S + val + end + end + @export f(x::TT) where {TT<:S} = x.val^2 + end + M + + julia> using .M + + julia> f(T(a)) + 4 + ``` + """ + macro $(Symbol("export"))(expr::Expr) + r = handle(expr) + if r isa Symbol + return quote + export $(esc(r)) + @__doc__ $(esc(expr)) + end + else + return quote + export $(map(esc, r)...) + @__doc__ $(esc(expr)) + end + end + end +end) + +eval(quote + """ + @public + + Return the expression with all bindings marked as public. + + ``` + julia> module M + using InlineExports + @public begin + const a = 2 + abstract type S <: Number end + struct T <: S + val + end + end + @public f(x::TT) where {TT<:S} = x.val^2 + end + M + + julia> using .M + + julia> f(T(a)) + 4 + ``` + """ + macro $(Symbol("public"))(expr::Expr) + r = handle(expr) + if r isa Symbol + return quote + public $ (esc(r)) + @__doc__ $(esc(expr)) + end + else + return quote + public $ (map(esc, r)...) + @__doc__ $(esc(expr)) + end + end + end +end) + +handle(::Any) = nothing +handle(x::Symbol) = x +handle(expr::Expr) = handle(Val(expr.head), expr) + +handle(::Val{:block}, expr) = filter(x -> x !== nothing, map(handle, expr.args)) +handle(::Val{:const}, expr) = handle(expr.args[1]) +handle(::Val{:(::)}, expr) = handle(expr.args[1]) +handle(::Val{:(=)}, expr) = handle(expr.args[1]) +handle(::Val{:function}, expr) = handle(expr.args[1]) +handle(::Val{:where}, expr) = handle(expr.args[1]) +handle(::Val{:macro}, expr) = Symbol("@", handle(expr.args[1])) +handle(::Val{:struct}, expr) = handle(expr.args[2]) +handle(::Union{Val{:abstract},Val{:primitive}}, expr) = handle(expr.args[1]) + +handle(::Val{:<:}, expr) = handle(expr.args[1]) +handle(::Val{:curly}, expr) = handle(expr.args[1]) +handle(::Val{:call}, expr) = handle(expr.args[1]) +handle(::Val{:macrocall}, expr) = filter(x -> x !== nothing, map(handle, expr.args[3:end])) + +end # module InlineExports + +@testitem "InlineExports" begin + module Bla + using ..InlineExports: @export, @public + + @export begin + "`const a` doc" + const a = 2 + "`abstract type S` doc" + abstract type S <: Number end + "`struct T` doc" + struct T <: S + val + end + end + """ + f(x) + + Here a doc! + """ + @export f(x::TT) where {TT<:S} = x.val^2 + + @public begin + "`const b` doc" + const b = 3 + "`abstract type U` doc" + abstract type U <: Number end + "`struct V` doc" + struct V <: U + val + end + end + """ + g(x) + + There a doc! + """ + @public g(x::VV) where {VV<:U} = x.val^3 + + begin + "`const c` doc" + const c = 5 + "`abstract type W` doc" + abstract type W <: Number end + "`struct X` doc" + struct X <: W + val + end + end + """ + h(x) + + Everywhere a doc, doc! + """ + h(x::XX) where {XX<:W} = x.val^5 + + end + + using .Bla + + @test f(T(a)) == 4 + @test Bla.g(Bla.V(Bla.b)) == 27 + @test Bla.h(Bla.X(Bla.c)) == 125 + + @test Base.isexported(Bla, :a) + @test Base.isexported(Bla, :S) + @test Base.isexported(Bla, :T) + @test Base.isexported(Bla, :f) + @test !Base.isexported(Bla, :b) + @test !Base.isexported(Bla, :U) + @test !Base.isexported(Bla, :V) + @test !Base.isexported(Bla, :g) + @test !Base.isexported(Bla, :c) + @test !Base.isexported(Bla, :W) + @test !Base.isexported(Bla, :X) + @test !Base.isexported(Bla, :h) + + @test Base.ispublic(Bla, :a) + @test Base.ispublic(Bla, :S) + @test Base.ispublic(Bla, :T) + @test Base.ispublic(Bla, :f) + @test Base.ispublic(Bla, :b) + @test Base.ispublic(Bla, :U) + @test Base.ispublic(Bla, :V) + @test Base.ispublic(Bla, :g) + @test !Base.ispublic(Bla, :c) + @test !Base.ispublic(Bla, :W) + @test !Base.ispublic(Bla, :X) + @test !Base.ispublic(Bla, :h) + + @test Base.hasproperty(Bla, :a) + @test Base.hasproperty(Bla, :S) + @test Base.hasproperty(Bla, :T) + @test Base.hasproperty(Bla, :f) + @test Base.hasproperty(Bla, :b) + @test Base.hasproperty(Bla, :U) + @test Base.hasproperty(Bla, :V) + @test Base.hasproperty(Bla, :g) + @test Base.hasproperty(Bla, :c) + @test Base.hasproperty(Bla, :W) + @test Base.hasproperty(Bla, :X) + @test Base.hasproperty(Bla, :h) + + @test Base.@__doc__ Bla.a == "`const a` doc" + @test Base.@__doc__ Bla.b == "`const b` doc" + @test Base.@__doc__ Bla.c == "`const c` doc" + @test Base.@__doc__ Bla.S == "`abstract type S` doc" + @test Base.@__doc__ Bla.U == "`abstract type U` doc" + @test Base.@__doc__ Bla.W == "`abstract type W` doc" + @test Base.@__doc__ Bla.T == "`struct T` doc" + @test Base.@__doc__ Bla.V == "`struct V` doc" + @test Base.@__doc__ Bla.X == "`struct X` doc" + @test Base.@__doc__ Bla.f == "f(x)\n\nHere a doc!" + @test Base.@__doc__ Bla.g == "g(x)\n\nThere a doc!" + @test Base.@__doc__ Bla.h == "h(x)\n\nEverywhere a doc, doc!" +end From 7e2f6c86d61c0ded665347b60976c33efd217223 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Wed, 25 Jun 2025 20:16:50 -0400 Subject: [PATCH 05/77] Simplify code; handle docstrings properly --- src/core/utilities/InlineExports.jl | 119 ++++++++-------------------- 1 file changed, 34 insertions(+), 85 deletions(-) diff --git a/src/core/utilities/InlineExports.jl b/src/core/utilities/InlineExports.jl index c6dccf48..92ce083d 100644 --- a/src/core/utilities/InlineExports.jl +++ b/src/core/utilities/InlineExports.jl @@ -1,96 +1,35 @@ -# NOTE: This file is a modified version of the `InlineExports.jl` package, which is -# licensed under the MIT License. The original source can be found at: -# https://github.com/dalum/InlineExports.jl +# NOTE: This file borrows from the `InlineExports.jl` package, which is licensed under the +# MIT License. The original source can be found at https://github.com/dalum/InlineExports.jl module InlineExports -import Base: @__doc__ - -export @export, @public +export @public, @export +# Because of how `export` is handled, we can't define a macro with that name directly. +# Here, we trick the parser to allow us to do so. eval(quote - """ - @export - - Return the expression with all bindings exported. - - ``` - julia> module M - using InlineExports - @export begin - const a = 2 - abstract type S <: Number end - struct T <: S - val - end - end - @export f(x::TT) where {TT<:S} = x.val^2 - end - M - - julia> using .M - - julia> f(T(a)) - 4 - ``` - """ macro $(Symbol("export"))(expr::Expr) - r = handle(expr) - if r isa Symbol - return quote - export $(esc(r)) - @__doc__ $(esc(expr)) - end - else - return quote - export $(map(esc, r)...) - @__doc__ $(esc(expr)) - end - end + return handle(expr, :export) end end) -eval(quote - """ - @public - - Return the expression with all bindings marked as public. - - ``` - julia> module M - using InlineExports - @public begin - const a = 2 - abstract type S <: Number end - struct T <: S - val - end - end - @public f(x::TT) where {TT<:S} = x.val^2 - end - M - - julia> using .M - - julia> f(T(a)) - 4 - ``` - """ - macro $(Symbol("public"))(expr::Expr) - r = handle(expr) - if r isa Symbol - return quote - public $ (esc(r)) - @__doc__ $(esc(expr)) - end - else - return quote - public $ (map(esc, r)...) - @__doc__ $(esc(expr)) - end - end +# For some reason, `public` is handled differently, so we don't need to play any tricks. +macro public(expr::Expr) + return esc(public_handler(expr, :public)) +end + +function handle(expr::Expr, export_or_public::Symbol) + r = handle(expr) + ep = if r isa Symbol + Expr(export_or_public, r) + else + Expr(export_or_public, r...) end -end) + return esc(quote + Base.@__doc__ $expr + $ep + end) +end handle(::Any) = nothing handle(x::Symbol) = x @@ -109,9 +48,19 @@ handle(::Union{Val{:abstract},Val{:primitive}}, expr) = handle(expr.args[1]) handle(::Val{:<:}, expr) = handle(expr.args[1]) handle(::Val{:curly}, expr) = handle(expr.args[1]) handle(::Val{:call}, expr) = handle(expr.args[1]) -handle(::Val{:macrocall}, expr) = filter(x -> x !== nothing, map(handle, expr.args[3:end])) +function handle(::Val{:macrocall}, expr) + if expr.args[1]==Symbol("@doc") || + (expr.args[1] == Core.GlobalRef(Core, Symbol("@doc"))) + if length(expr.args) != 4 + error("@doc expression found with $(length(expr.args)) args:\n$expr") + end + handle(expr.args[4]) + else + filter(x -> x !== nothing, map(handle, expr.args[3:end])) + end +end -end # module InlineExports +end # module InlineExports @testitem "InlineExports" begin module Bla From f97017708d16bc82549100aa2b7c1611a428cbbe Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Thu, 26 Jun 2025 20:24:46 -0400 Subject: [PATCH 06/77] Get PNSystems working --- Project.toml | 8 +- src/PostNewtonian.jl | 7 + src/pn_systems/BBH.jl | 51 ++-- src/pn_systems/BHNS.jl | 141 ++++++++--- src/pn_systems/FDPNsystem.jl | 12 +- src/pn_systems/NSNS.jl | 163 +++++++++---- src/pn_systems/PNSystem.jl | 15 +- src/pn_systems/Quasispherical.jl | 40 ++++ src/pn_systems/pn_order.jl | 9 +- src/pn_systems/pn_systems.jl | 1 + src/pn_systems/state_variables.jl | 76 +++--- src/pn_systems/vector_interface.jl | 368 +++++++++++++++-------------- 12 files changed, 550 insertions(+), 341 deletions(-) create mode 100644 src/pn_systems/Quasispherical.jl diff --git a/Project.toml b/Project.toml index 8f5cb798..b3109328 100644 --- a/Project.toml +++ b/Project.toml @@ -6,6 +6,7 @@ version = "0.10.6" [deps] DataInterpolations = "82cc6244-b520-54b8-b5a6-8a565e85f1d0" FastDifferentiation = "eb9bf01b-bf85-4b60-bf87-ee5de06c00be" +InlineExports = "809d7502-0c9d-5bc2-962a-5201dc498f73" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" @@ -26,9 +27,9 @@ ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b" Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" -[extensions] -PostNewtonianForwardDiffExt = ["ForwardDiff"] -PostNewtonianSymbolicsExt = ["Symbolics", "SymbolicUtils"] +# [extensions] +# PostNewtonianForwardDiffExt = ["ForwardDiff"] +# PostNewtonianSymbolicsExt = ["Symbolics", "SymbolicUtils"] [compat] Aqua = "0.8" @@ -39,6 +40,7 @@ FastDifferentiation = "0.3.15, 0.4" FileIO = "1.16.3" ForwardDiff = "0.10.38, 1" HDF5 = "0.17.2" +InlineExports = "0.1.2" InteractiveUtils = "1" LinearAlgebra = "1" Logging = "1" diff --git a/src/PostNewtonian.jl b/src/PostNewtonian.jl index a910c6c1..5e527b3d 100644 --- a/src/PostNewtonian.jl +++ b/src/PostNewtonian.jl @@ -1,5 +1,12 @@ module PostNewtonian +using Base: @propagate_inbounds +using FastDifferentiation: Node as FDNode +using InlineExports: @public, @export +using Quaternionic: Quaternionic, QuatVec, Rotor, abs2vec, components, normalize, ⋅, × +using StaticArrays: @MVector, MVector, SVector +using TestItems: @testitem + include("core/core.jl") include("pn_systems/pn_systems.jl") include("literature/literature.jl") diff --git a/src/pn_systems/BBH.jl b/src/pn_systems/BBH.jl index 8221f5b6..d4bdbe9f 100644 --- a/src/pn_systems/BBH.jl +++ b/src/pn_systems/BBH.jl @@ -17,45 +17,46 @@ nonprecessing systems, `Φ` would be sufficient to describe the system's positio more completely described by the `Rotor` `R`. However, for precessing systems, it is difficult to extract this quantity from `R`. """ -struct BBH{NT,ST,PNOrder} <: PNSystem{NT,ST,PNOrder} +@export struct BBH{NT,PNOrder,ST} <: Quasispherical{NT,PNOrder,ST} state::ST - function BBH{NT,ST,PNOrder}(state) where {NT,ST,PNOrder} + function BBH{NT,PNOrder,ST}(state) where {NT,PNOrder,ST} if eachindex(state) != Base.OneTo(14) error( "The `state` vector for `BBH` must be indexed from 1 to 14; " * "input is indexed `$(eachindex(state))`.", ) end - new{NT,ST,PNOrder}(state) + new{NT,PNOrder,ST}(state) end - function BBH(; M₁, M₂, χ⃗₁, χ⃗₂, v, R=Rotor(1), Φ=0, PNOrder=typemax(Int), kwargs...) - (NT, ST, PNOrder, state) = prepare_system(; M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ, PNOrder) - return new{NT,ST,PNOrder}(state) + function BBH(; M₁, M₂, χ⃗₁, χ⃗₂, v, R=Rotor(1), Φ=0, PNOrder=typemax(Int), Λ₁=0, Λ₂=0) + if Λ₁ != 0 || Λ₂ != 0 + error( + "`BBH` does not support tidal-coupling parameters `Λ₁` or `Λ₂`; " * + "use `BHNS` or `NSNS` instead.", + ) + end + (NT, PNOrder, state) = prepare_Quasispherical(; M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ, PNOrder) + return new{NT,PNOrder,typeof(state)}(state) end - function BBH(state; Λ₁=0, Λ₂=0, PNOrder=typemax(Int)) + function BBH(state; PNOrder=typemax(Int)) if eachindex(state) != Base.OneTo(14) error( "The `state` vector for `BBH` must be indexed from 1 to 14; " * "input is indexed `$(eachindex(state))`.", ) end - @assert Λ₁ == 0 - @assert Λ₂ == 0 - return new{eltype(state),typeof(state),prepare_pn_order(PNOrder)}(state) + NT, PNOrder, ST = eltype(state), prepare_pn_order(PNOrder), typeof(state) + return new{NT,PNOrder,ST}(state) end end -const BHBH = BBH +@public const BHBH = BBH # The following are methods of functions defined in `state_variables.jl`, specialized for # `BBH` systems. state(pnsystem::BBH) = pnsystem.state -function symbols(::Type{<:BBH}) - (:M₁, :M₂, :χ⃗₁ˣ, :χ⃗₁ʸ, :χ⃗₁ᶻ, :χ⃗₂ˣ, :χ⃗₂ʸ, :χ⃗₂ᶻ, :Rʷ, :Rˣ, :Rʸ, :Rᶻ, :v, :Φ) -end -function ascii_symbols(::Type{<:BBH}) - (:M1, :M2, :chi1x, :chi1y, :chi1z, :chi2x, :chi2y, :chi2z, :Rw, :Rx, :Ry, :Rz, :v, :Phi) -end +symbols(::Type{<:BBH}) = symbols(Quasispherical) +ascii_symbols(::Type{<:BBH}) = ascii_symbols(Quasispherical) for (i, symbol) ∈ enumerate(symbols(BBH)) # This will define, e.g., `M₁(pnsystem::BBH) = pnsystem.state[1]`. We # could do this manually, but this is more concise and less error-prone. @@ -67,16 +68,20 @@ for (i, symbol) ∈ enumerate(symbols(BBH)) end end +# Define any state-variable methods we may need for variables that are not actually in the +# state vector. Λ₁(pnsystem::BBH) = zero(pnsystem) Λ₂(pnsystem::BBH) = zero(pnsystem) @testitem "BBH constructors" begin + using PostNewtonian: state using Quaternionic pnA = BBH(; M₁=1.0f0, M₂=2.0f0, χ⃗₁=Float32[3.0, 4.0, 5.0], χ⃗₂=Float32[6.0, 7.0, 8.0], v=0.23f0 ) - @test pnA.state == + @test eltype(pnA) == Float32 + @test state(pnA) == Float32[1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; 1.0; 0.0; 0.0; 0.0; 0.23; 0.0] pnB = BBH(; @@ -87,7 +92,7 @@ end v=0.23f0, Φ=9.0f0, ) - @test pnB.state == + @test state(pnB) == Float32[1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; 1.0; 0.0; 0.0; 0.0; 0.23; 9.0] R = randn(RotorF32) @@ -99,7 +104,7 @@ end R=R, v=0.23f0, ) - @test pn1.state ≈ [1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; components(R)...; 0.23; 0.0] + @test state(pn1) ≈ [1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; components(R)...; 0.23; 0.0] pn2 = BBH(; M₁=1.0f0, @@ -110,8 +115,8 @@ end v=0.23f0, Φ=9.0f0, ) - @test pn2.state ≈ [1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; components(R)...; 0.23; 9.0] + @test state(pn2) ≈ [1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; components(R)...; 0.23; 9.0] - pn1.state[end] = 9.0f0 - @test pn1.state == pn2.state + state(pn1)[end] = 9.0f0 + @test state(pn1) == state(pn2) end diff --git a/src/pn_systems/BHNS.jl b/src/pn_systems/BHNS.jl index cccfb12f..9414bb1d 100644 --- a/src/pn_systems/BHNS.jl +++ b/src/pn_systems/BHNS.jl @@ -10,23 +10,28 @@ Note that the neutron star is *always* object 2 — meaning that `M₂`, `χ⃗ refer to it; `M₁` and `χ⃗₁` always refer to the black hole. (It's "BHNS", not "NSBH".) See also [`NSNS`](@ref). """ -struct BHNS{NT,ST,PNOrder} <: PNSystem{NT,ST,PNOrder} +@export struct BHNS{NT,PNOrder,ST} <: Quasispherical{NT,PNOrder,ST} state::ST - function BHNS{NT,ST,PNOrder}(state) where {NT,ST,PNOrder} + function BHNS{NT,PNOrder,ST}(state) where {NT,PNOrder,ST} if eachindex(state) != Base.OneTo(15) error( "The `state` vector for `BHNS` must be indexed from 1 to 15; " * "input is indexed `$(eachindex(state))`.", ) end - new{NT,ST,PNOrder}(state) + new{NT,PNOrder,ST}(state) end - function BHNS(; - M₁, M₂, χ⃗₁, χ⃗₂, v, R=Rotor(1), Φ=0, Λ₂, PNOrder=typemax(Int), kwargs... - ) - NT, ST, PNOrder, state = prepare_system(; M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ, Λ₂, PNOrder) - return new{NT,ST,PNOrder}(state) + function BHNS(; M₁, M₂, χ⃗₁, χ⃗₂, v, R=Rotor(1), Φ=0, Λ₁=0, Λ₂, PNOrder=typemax(Int)) + if Λ₁ != 0 + error( + "`BHNS` does not support a tidal-coupling parameter `Λ₁` for object 1; " * + "use `NSNS` instead.", + ) + end + (NT, PNOrder, state) = prepare_Quasispherical(; M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ, PNOrder) + state = vcat(state, Λ₂) + return new{eltype(state),PNOrder,typeof(state)}(state) end function BHNS(state; PNOrder=typemax(Int)) if eachindex(state) != Base.OneTo(15) @@ -35,8 +40,8 @@ struct BHNS{NT,ST,PNOrder} <: PNSystem{NT,ST,PNOrder} "input is indexed `$(eachindex(state))`.", ) end - NT, ST, PNOrder = eltype(state), typeof(state), prepare_pn_order(PNOrder) - return new{NT,ST,PNOrder}(state) + NT, PNOrder, ST = eltype(state), prepare_pn_order(PNOrder), typeof(state) + return new{NT,PNOrder,ST}(state) end end @@ -44,26 +49,10 @@ end # `BHNS` systems. state(pnsystem::BHNS) = pnsystem.state function symbols(::Type{<:BHNS}) - (:M₁, :M₂, :χ⃗₁ˣ, :χ⃗₁ʸ, :χ⃗₁ᶻ, :χ⃗₂ˣ, :χ⃗₂ʸ, :χ⃗₂ᶻ, :Rʷ, :Rˣ, :Rʸ, :Rᶻ, :v, :Φ, :Λ₂) + (symbols(Quasispherical)..., :Λ₂) end function ascii_symbols(::Type{<:BHNS}) - ( - :M1, - :M2, - :chi1x, - :chi1y, - :chi1z, - :chi2x, - :chi2y, - :chi2z, - :Rw, - :Rx, - :Ry, - :Rz, - :v, - :Phi, - :Lambda2, - ) + (ascii_symbols(Quasispherical)..., :Lambda2) end for (i, symbol) ∈ enumerate(symbols(BHNS)) # This will define, e.g., `M₁(pnsystem::BHNS) = pnsystem.state[1]`. We @@ -76,5 +65,99 @@ for (i, symbol) ∈ enumerate(symbols(BHNS)) end end +# Define any state-variable methods we may need for variables that are not actually in the +# state vector. Λ₁(pnsystem::BHNS) = zero(pnsystem) -Λ₂(pnsystem::BHNS) = @inbounds pnsystem.state[15] + +@testitem "BHNS constructors" begin + using PostNewtonian: state + using Quaternionic + + # minimal constructor: default Φ=0, R=Rotor(1) + pnA = BHNS( + M₁=1.0f0, + M₂=2.0f0, + χ⃗₁=Float32[3.0, 4.0, 5.0], + χ⃗₂=Float32[6.0, 7.0, 8.0], + v=0.23f0, + Λ₂=4.0f0, + ) + @test eltype(pnA) == Float32 + @test state(pnA) == Float32[ + 1.0; + 2.0; + 3.0; + 4.0; + 5.0; + 6.0; + 7.0; + 8.0; + 1.0; + 0.0; + 0.0; + 0.0; + 0.23; + 0.0; + 4.0 + ] + + # explicit orbital phase + pnB = BHNS( + M₁=1.0f0, + M₂=2.0f0, + χ⃗₁=Float32[3.0, 4.0, 5.0], + χ⃗₂=Float32[6.0, 7.0, 8.0], + v=0.23f0, + Φ=9.0f0, + Λ₂=4.0f0, + ) + @test state(pnB) == Float32[ + 1.0; + 2.0; + 3.0; + 4.0; + 5.0; + 6.0; + 7.0; + 8.0; + 1.0; + 0.0; + 0.0; + 0.0; + 0.23; + 9.0; + 4.0 + ] + + # custom rotor, default Φ + R = randn(RotorF32) + pn1 = BHNS( + M₁=1.0f0, + M₂=2.0f0, + χ⃗₁=Float32[3.0, 4.0, 5.0], + χ⃗₂=Float32[6.0, 7.0, 8.0], + R=R, + v=0.23f0, + Λ₂=4.0f0, + ) + @test state(pn1) ≈ + [1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; components(R)...; 0.23; 0.0; 4.0] + + # custom rotor and Φ + pn2 = BHNS( + M₁=1.0f0, + M₂=2.0f0, + χ⃗₁=Float32[3.0, 4.0, 5.0], + χ⃗₂=Float32[6.0, 7.0, 8.0], + R=R, + v=0.23f0, + Φ=9.0f0, + Λ₂=4.0f0, + ) + @test state(pn2) ≈ + [1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; components(R)...; 0.23; 9.0; 4.0] + + # mutating the second-to-last element (Φ) to match pn2 + pn1[end - 1] = 9.0f0 + @test state(pn1) == state(pn2) +end diff --git a/src/pn_systems/FDPNsystem.jl b/src/pn_systems/FDPNsystem.jl index 098aa415..5227d685 100644 --- a/src/pn_systems/FDPNsystem.jl +++ b/src/pn_systems/FDPNsystem.jl @@ -12,17 +12,19 @@ One important example of what this type is used for is computing the derivative orbital binding energy, `𝓔′` — and in particular, for generating the corresponding function method to apply to a given `PNSystem`. """ -struct FDPNSystem{NT,PN<:PNSystem{NT},PNOrder} <: PNSystem{FDNode,Vector{FDNode},PNOrder} +@export struct FDPNSystem{NT,PNOrder,PN<:Type{<:PNSystem{NT,PNOrder}}} <: + PNSystem{FDNode,PNOrder,Vector{FDNode}} state::Vector{FDNode} - function FDPNSystem(::Type{PN}, PNOrder=typemax(Int)) where {NT,PN<:PNSystem{NT}} - return new{NT,prepare_pn_order(PNOrder)}([FDNode(s) for s ∈ symbols(PN)]) + FDPNSystem(pnsystem::PNSystem) = FDPNSystem(typeof(pnsystem)) + function FDPNSystem(::Type{PN}) where {NT,PNOrder,PN<:PNSystem{NT,PNOrder}} + return new{NT,PNOrder,PN}([FDNode(s) for s ∈ symbols(PN)]) end end -symbols(pnsystem::FDPNSystem{NT,PN}) where {NT,PN} = symbols(PN) +symbols(pnsystem::FDPNSystem{NT,PNOrder,PN}) where {NT,PNOrder,PN} = symbols(PN) -function symbol_index(pnsystem::FDPNSystem{NT,PN}, s::Symbol) where {NT,PN} +function symbol_index(pnsystem::FDPNSystem{NT,PNOrder,PN}, s::Symbol) where {NT,PNOrder,PN} symbol_index(PN, Val(s)) end diff --git a/src/pn_systems/NSNS.jl b/src/pn_systems/NSNS.jl index ca40d3e7..d614fb52 100644 --- a/src/pn_systems/NSNS.jl +++ b/src/pn_systems/NSNS.jl @@ -1,5 +1,5 @@ """ - NSNS{NT, ST, PNOrder} <: PNSystem{NT, ST, PNOrder} + NSNS{NT, PNOrder, ST} <: PNSystem{NT, PNOrder, ST} The [`PNSystem`](@ref) subtype describing a neutron-star—neutron-star binary system. @@ -7,25 +7,22 @@ The `state` vector is the same as for a [`BBH`](@ref), with two additional field and `Λ₂` holding the (constant) tidal-coupling parameters of the neutron stars. See also [`BHNS`](@ref). """ -struct NSNS{NT,ST,PNOrder} <: PNSystem{NT,ST,PNOrder} +@export struct NSNS{NT,PNOrder,ST} <: Quasispherical{NT,PNOrder,ST} state::ST - function NSNS{NT,ST,PNOrder}(state) where {NT,ST,PNOrder} + function NSNS{NT,PNOrder,ST}(state) where {NT,PNOrder,ST} if eachindex(state) != Base.OneTo(16) error( "The `state` vector for `NSNS` must be indexed from 1 to 16; " * "input is indexed `$(eachindex(state))`.", ) end - new{NT,ST,PNOrder}(state) + new{NT,PNOrder,ST}(state) end - function NSNS(; - M₁, M₂, χ⃗₁, χ⃗₂, v, R=Rotor(1), Φ=0, Λ₁, Λ₂, PNOrder=typemax(Int), kwargs... - ) - NT, ST, PNOrder, state = prepare_system(; - M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ, Λ₁, Λ₂, PNOrder - ) - return new{NT,ST,PNOrder}(state) + function NSNS(; M₁, M₂, χ⃗₁, χ⃗₂, v, R=Rotor(1), Φ=0, Λ₁, Λ₂, PNOrder=typemax(Int)) + (NT, PNOrder, state) = prepare_Quasispherical(; M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ, PNOrder) + state = vcat(state, Λ₁, Λ₂) + return new{eltype(state),PNOrder,typeof(state)}(state) end function NSNS(state; PNOrder=typemax(Int)) if eachindex(state) != Base.OneTo(16) @@ -34,54 +31,20 @@ struct NSNS{NT,ST,PNOrder} <: PNSystem{NT,ST,PNOrder} "input is indexed `$(eachindex(state))`.", ) end - NT, ST, PNOrder = eltype(state), typeof(state), prepare_pn_order(PNOrder) - return new{NT,ST,PNOrder}(state) + NT, PNOrder, ST = eltype(state), prepare_pn_order(PNOrder), typeof(state) + return new{NT,PNOrder,ST}(state) end end -const BNS = NSNS +@public const BNS = NSNS # The following are methods of functions defined in `state_variables.jl`, specialized for # `NSNS` systems. state(pnsystem::NSNS) = pnsystem.state function symbols(::Type{<:NSNS}) - ( - :M₁, - :M₂, - :χ⃗₁ˣ, - :χ⃗₁ʸ, - :χ⃗₁ᶻ, - :χ⃗₂ˣ, - :χ⃗₂ʸ, - :χ⃗₂ᶻ, - :Rʷ, - :Rˣ, - :Rʸ, - :Rᶻ, - :v, - :Φ, - :Λ₁, - :Λ₂, - ) + (symbols(Quasispherical)..., :Λ₁, :Λ₂) end function ascii_symbols(::Type{<:NSNS}) - ( - :M1, - :M2, - :chi1x, - :chi1y, - :chi1z, - :chi2x, - :chi2y, - :chi2z, - :Rw, - :Rx, - :Ry, - :Rz, - :v, - :Phi, - :Lambda1, - :Lambda2, - ) + (ascii_symbols(Quasispherical)..., :Lambda1, :Lambda2) end for (i, symbol) ∈ enumerate(symbols(NSNS)) # This will define, e.g., `M₁(pnsystem::NSNS) = pnsystem.state[1]`. We @@ -94,5 +57,101 @@ for (i, symbol) ∈ enumerate(symbols(NSNS)) end end -Λ₁(pnsystem::NSNS) = @inbounds pnsystem.state[15] -Λ₂(pnsystem::NSNS) = @inbounds pnsystem.state[16] +@testitem "NSNS constructors" begin + using PostNewtonian: state + using Quaternionic + + # minimal constructor: default Φ=0, R=Rotor(1) + pnA = NSNS( + M₁=1.0f0, + M₂=2.0f0, + χ⃗₁=Float32[3.0, 4.0, 5.0], + χ⃗₂=Float32[6.0, 7.0, 8.0], + v=0.23f0, + Λ₁=5.0f0, + Λ₂=6.0f0, + ) + @test eltype(pnA) == Float32 + @test state(pnA) == Float32[ + 1.0; + 2.0; + 3.0; + 4.0; + 5.0; + 6.0; + 7.0; + 8.0; + 1.0; + 0.0; + 0.0; + 0.0; + 0.23; + 0.0; + 5.0; + 6.0 + ] + + # explicit orbital phase + pnB = NSNS( + M₁=1.0f0, + M₂=2.0f0, + χ⃗₁=Float32[3.0, 4.0, 5.0], + χ⃗₂=Float32[6.0, 7.0, 8.0], + v=0.23f0, + Φ=9.0f0, + Λ₁=5.0f0, + Λ₂=6.0f0, + ) + @test state(pnB) == Float32[ + 1.0; + 2.0; + 3.0; + 4.0; + 5.0; + 6.0; + 7.0; + 8.0; + 1.0; + 0.0; + 0.0; + 0.0; + 0.23; + 9.0; + 5.0; + 6.0 + ] + + # custom rotor, default Φ + R = randn(RotorF32) + pn1 = NSNS( + M₁=1.0f0, + M₂=2.0f0, + χ⃗₁=Float32[3.0, 4.0, 5.0], + χ⃗₂=Float32[6.0, 7.0, 8.0], + R=R, + v=0.23f0, + Λ₁=5.0f0, + Λ₂=6.0f0, + ) + @test state(pn1) ≈ + [1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; components(R)...; 0.23; 0.0; 5.0; 6.0] + + # custom rotor and Φ + pn2 = NSNS( + M₁=1.0f0, + M₂=2.0f0, + χ⃗₁=Float32[3.0, 4.0, 5.0], + χ⃗₂=Float32[6.0, 7.0, 8.0], + R=R, + v=0.23f0, + Φ=9.0f0, + Λ₁=5.0f0, + Λ₂=6.0f0, + ) + @test state(pn2) ≈ + [1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; components(R)...; 0.23; 9.0; 5.0; 6.0] + + # mutating the second-to-last element (Φ) to match pn2 + pn1[end - 2] = 9.0f0 + @test state(pn1) == state(pn2) +end diff --git a/src/pn_systems/PNSystem.jl b/src/pn_systems/PNSystem.jl index c10285ef..023d35e1 100644 --- a/src/pn_systems/PNSystem.jl +++ b/src/pn_systems/PNSystem.jl @@ -9,13 +9,14 @@ and [derived variables](@ref Derived-variables), as well as [PN expressions](@re [dynamics](@ref Dynamics) functions. The parameter `NT` is the number type of the system, such as `Float64` or `Dual{SomeTag, -Float64, 7}`. The parameter `ST <: DenseVector{NT}` is the type returned by the `state` +Float64, 7}`. The parameter `ST <: AbstractVector{NT}` is the type returned by the `state` function, which probably just returns the `state` vector stored in the concrete subtype. As such, this will probably be `MVector{N, NT}` or `SVector{N, NT}`, where `N` is the number of elements in the state. `PNOrder` is a `Rational` giving the order to which PN expansions should be carried out when using the given object. """ -abstract type PNSystem{NT,PNOrder,ST<:DenseVector{NT}} <: DenseVector{NT} end +@export abstract type PNSystem{NT<:Number,PNOrder,ST<:AbstractVector{NT}} <: + AbstractVector{NT} end """ state(pnsystem::PNSystem) @@ -26,8 +27,10 @@ given PN system. Note that the built-in `PNSystem` subtypes have a `state` field that is a vector, so this function will just return that vector. However, that may not always be true for user-defined subtypes. + +This function should have a specialized method for each concrete subtype of `PNSystem`. """ -function state(::T) where {T<:PNSystem} +@public function state(::T) where {T<:PNSystem} error("`state` is not yet defined for PNSystem subtype `$T`.") end Base.vec(pnsystem::PNSystem) = state(pnsystem) @@ -52,6 +55,8 @@ in which they are stored in the `state` vector. The `ascii_symbols` function returns those symbols in ASCII form, enabling interaction with external systems (e.g., Python) that do not support many Unicode symbols. +These functions should have specialized methods for each concrete subtype of `PNSystem`. +For example, the `BBH` system has 14 state variables, as seen in the following example: ```jldoctest julia> using PostNewtonian: BBH @@ -64,11 +69,11 @@ julia> ascii_symbols(pnsystem) (:M1, :M2, :chi1x, :chi1y, :chi1z, :chi2x, :chi2y, :chi2z, :Rw, :Rx, :Ry, :Rz, :v, :Phi) ``` """ -symbols(pnsystem::PNSystem) = symbols(typeof(pnsystem)) +@export symbols(pnsystem::PNSystem) = symbols(typeof(pnsystem)) function symbols(::Type{T}) where {T<:PNSystem} error("`symbols` is not yet defined for PNSystem subtype `$T`.") end -ascii_symbols(pnsystem::PNSystem) = ascii_symbols(typeof(pnsystem)) +@export ascii_symbols(pnsystem::PNSystem) = ascii_symbols(typeof(pnsystem)) function ascii_symbols(::Type{T}) where {T<:PNSystem} error("`ascii_symbols` is not yet defined for PNSystem subtype `$T`.") end diff --git a/src/pn_systems/Quasispherical.jl b/src/pn_systems/Quasispherical.jl new file mode 100644 index 00000000..6db4daab --- /dev/null +++ b/src/pn_systems/Quasispherical.jl @@ -0,0 +1,40 @@ +# NOTE: This type is documented separately, below, so that we can call +# `symbols(Quasispherical)` and `ascii_symbols(Quasispherical)` in the docstring. +@public abstract type Quasispherical{NT,PNOrder,ST} <: PNSystem{NT,PNOrder,ST} end + +function symbols(::Type{<:Quasispherical}) + (:M₁, :M₂, :χ⃗₁ˣ, :χ⃗₁ʸ, :χ⃗₁ᶻ, :χ⃗₂ˣ, :χ⃗₂ʸ, :χ⃗₂ᶻ, :Rʷ, :Rˣ, :Rʸ, :Rᶻ, :v, :Φ) +end +function ascii_symbols(::Type{<:Quasispherical}) + (:M1, :M2, :chi1x, :chi1y, :chi1z, :chi2x, :chi2y, :chi2z, :Rw, :Rx, :Ry, :Rz, :v, :Phi) +end + +@doc """ + Quasispherical{NT,PNOrder,ST} <: PNSystem{NT,PNOrder,ST} + +This is an abstract type for quasispherical post-Newtonian systems. The built-in `BBH`, +`BHNS`, and `NSNS` systems subtype this. + +The quantities common to all subtypes are + + $(symbols(Quasispherical)) + +which have the ASCII equivalents + + $(ascii_symbols(Quasispherical)) + +Note `BBH` has exactly these symbols, while `BHNS` and `NSNS` have additional symbols for +tidal-coupling parameters, `Λ₁` and `Λ₂` (or `Lambda1` and `Lambda2`). +""" +Quasispherical + +function prepare_Quasispherical(; M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ=0, PNOrder=typemax(Int)) + χ⃗₁ = QuatVec(χ⃗₁) + χ⃗₂ = QuatVec(χ⃗₂) + state = MVector{14}( + M₁, M₂, χ⃗₁[2], χ⃗₁[3], χ⃗₁[4], χ⃗₂[2], χ⃗₂[3], χ⃗₂[4], R[1], R[2], R[3], R[4], v, Φ + ) + NT = eltype(state) + PNOrder = prepare_pn_order(PNOrder) + return (NT, PNOrder, state) +end diff --git a/src/pn_systems/pn_order.jl b/src/pn_systems/pn_order.jl index 2c213fd1..207fe030 100644 --- a/src/pn_systems/pn_order.jl +++ b/src/pn_systems/pn_order.jl @@ -6,7 +6,8 @@ Return the PN order of the given `pnsystem`. This is a `Rational{Int}` that indicates the order to which the PN expansions should be carried out when using the given object. """ -pn_order(::PNSystem{NT,ST,PNOrder}) where {NT,ST,PNOrder} = PNOrder +@export pn_order(::Type{<:PNSystem{NT,PNOrder}}) where {NT,PNOrder} = PNOrder +pn_order(pnsystem) = pn_order(typeof(pnsystem)) """ order_index(pnsystem::PNSystem) @@ -16,14 +17,14 @@ Return the order index of the given `pnsystem`. This is defined as the (one-based) index into an iterable of PN terms starting at 0pN, then 0.5pN, etc. Specifically, this is defined as `1 + Int(2pn_order(pnsystem))`. """ -order_index(pn::PNSystem) = 1 + Int(2pn_order(pn)) +@export order_index(pnsystem::PNSystem) = 1 + Int(2pn_order(pnsystem)) """ max_pn_order The maximum PN order that can be used without overflowing the `Int` type. """ -const max_pn_order = (typemax(Int) - 2) // 2 +@export const max_pn_order = (typemax(Int) - 2) // 2 """ prepare_pn_order(PNOrder) @@ -33,7 +34,7 @@ Convert the input to a half-integer of type `Rational{Int}`. If `PNOrder` is larger than `max_pn_order`, it is set to `max_pn_order`, to avoid overflow when computing the order index. """ -function prepare_pn_order(PNOrder) +@public function prepare_pn_order(PNOrder) if PNOrder < max_pn_order round(Int, 2PNOrder) // 2 else diff --git a/src/pn_systems/pn_systems.jl b/src/pn_systems/pn_systems.jl index 12ebbaab..1479cadd 100644 --- a/src/pn_systems/pn_systems.jl +++ b/src/pn_systems/pn_systems.jl @@ -3,6 +3,7 @@ include("pn_order.jl") include("vector_interface.jl") include("FDPNSystem.jl") include("state_variables.jl") +include("Quasispherical.jl") include("BBH.jl") include("BHNS.jl") include("NSNS.jl") diff --git a/src/pn_systems/state_variables.jl b/src/pn_systems/state_variables.jl index e06b2c44..02faec20 100644 --- a/src/pn_systems/state_variables.jl +++ b/src/pn_systems/state_variables.jl @@ -11,7 +11,7 @@ than just returning the integer `1`, because there may be expressions with facto `G/3` which will immediately convert to `Float64` if `G` is just `1`, so the result will not have the expected precision. """ -G(::PNSystem{NT}) where {NT} = one(NT) +@public G(::PNSystem{NT}) where {NT} = one(NT) G(::FDPNSystem{NT,PN}) where {NT,PN} = one(PN) """ @@ -27,7 +27,7 @@ than just returning the integer `1`, because there may be expressions with facto `c/3` which will immediately convert to `Float64` if `c` is just `1`, so the result will not have the expected precision. """ -c(::PNSystem{NT}) where {NT} = one(NT) +@public c(::PNSystem{NT}) where {NT} = one(NT) c(::FDPNSystem{NT,PN}) where {NT,PN} = one(PN) """ @@ -36,11 +36,11 @@ c(::FDPNSystem{NT,PN}) where {NT,PN} = one(PN) Mass of object 1 in this system. """ -function M₁(::T) where {T<:PNSystem} +@public function M₁(::T) where {T<:PNSystem} error("M₁ is not (yet) defined for PNSystem subtype `$T`.") end M₁(fdpnsystem::FDPNSystem) = fdpnsystem[:M₁] -const M1 = M₁ +@public const M1 = M₁ """ M₂(pnsystem) @@ -48,11 +48,11 @@ const M1 = M₁ Mass of object 2 in this system. """ -function M₂(::T) where {T<:PNSystem} +@public function M₂(::T) where {T<:PNSystem} error("M₂ is not (yet) defined for PNSystem subtype `$T`.") end M₂(fdpnsystem::FDPNSystem) = fdpnsystem[:M₂] -const M2 = M₂ +@public const M2 = M₂ """ χ⃗₁ˣ(pnsystem) @@ -62,11 +62,11 @@ const M2 = M₂ See [`χ⃗₁`](@ref) for details. """ -function χ⃗₁ˣ(::T) where {T<:PNSystem} +@public function χ⃗₁ˣ(::T) where {T<:PNSystem} error("χ⃗₁ˣ is not (yet) defined for PNSystem subtype `$T`.") end χ⃗₁ˣ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ⃗₁ˣ] -const chi1x = χ⃗₁ˣ +@public const chi1x = χ⃗₁ˣ """ χ⃗₁ʸ(pnsystem) @@ -76,11 +76,11 @@ const chi1x = χ⃗₁ˣ See [`χ⃗₁`](@ref) for details. """ -function χ⃗₁ʸ(::T) where {T<:PNSystem} +@public function χ⃗₁ʸ(::T) where {T<:PNSystem} error("χ⃗₁ʸ is not (yet) defined for PNSystem subtype `$T`.") end χ⃗₁ʸ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ⃗₁ʸ] -const chi1y = χ⃗₁ʸ +@public const chi1y = χ⃗₁ʸ """ χ⃗₁ᶻ(pnsystem) @@ -90,11 +90,11 @@ const chi1y = χ⃗₁ʸ See [`χ⃗₁`](@ref) for details. """ -function χ⃗₁ᶻ(::T) where {T<:PNSystem} +@public function χ⃗₁ᶻ(::T) where {T<:PNSystem} error("χ⃗₁ᶻ is not (yet) defined for PNSystem subtype `$T`.") end χ⃗₁ᶻ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ⃗₁ᶻ] -const chi1z = χ⃗₁ᶻ +@public const chi1z = χ⃗₁ᶻ """ χ⃗₂ˣ(pnsystem) @@ -104,11 +104,11 @@ const chi1z = χ⃗₁ᶻ See [`χ⃗₂`](@ref) for details. """ -function χ⃗₂ˣ(::T) where {T<:PNSystem} +@public function χ⃗₂ˣ(::T) where {T<:PNSystem} error("χ⃗₂ˣ is not (yet) defined for PNSystem subtype `$T`.") end χ⃗₂ˣ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ⃗₂ˣ] -const chi2x = χ⃗₂ˣ +@public const chi2x = χ⃗₂ˣ """ χ⃗₂ʸ(pnsystem) @@ -118,11 +118,11 @@ const chi2x = χ⃗₂ˣ See [`χ⃗₂`](@ref) for details. """ -function χ⃗₂ʸ(::T) where {T<:PNSystem} +@public function χ⃗₂ʸ(::T) where {T<:PNSystem} error("χ⃗₂ʸ is not (yet) defined for PNSystem subtype `$T`.") end χ⃗₂ʸ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ⃗₂ʸ] -const chi2y = χ⃗₂ʸ +@public const chi2y = χ⃗₂ʸ """ χ⃗₂ᶻ(pnsystem) @@ -132,11 +132,11 @@ const chi2y = χ⃗₂ʸ See [`χ⃗₂`](@ref) for details. """ -function χ⃗₂ᶻ(::T) where {T<:PNSystem} +@public function χ⃗₂ᶻ(::T) where {T<:PNSystem} error("χ⃗₂ᶻ is not (yet) defined for PNSystem subtype `$T`.") end χ⃗₂ᶻ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ⃗₂ᶻ] -const chi2z = χ⃗₂ᶻ +@public const chi2z = χ⃗₂ᶻ """ Rʷ(pnsystem) @@ -146,11 +146,11 @@ Scalar component of the orientation `Rotor` of the binary. See [`R`](@ref) for details. """ -function Rʷ(::T) where {T<:PNSystem} +@public function Rʷ(::T) where {T<:PNSystem} error("Rʷ is not (yet) defined for PNSystem subtype `$T`.") end Rʷ(fdpnsystem::FDPNSystem) = fdpnsystem[:Rʷ] -const Rw = Rʷ +@public const Rw = Rʷ """ Rˣ(pnsystem) @@ -160,11 +160,11 @@ const Rw = Rʷ See [`R`](@ref) for details. """ -function Rˣ(::T) where {T<:PNSystem} +@public function Rˣ(::T) where {T<:PNSystem} error("Rˣ is not (yet) defined for PNSystem subtype `$T`.") end Rˣ(fdpnsystem::FDPNSystem) = fdpnsystem[:Rˣ] -const Rx = Rˣ +@public const Rx = Rˣ """ Rʸ(pnsystem) @@ -174,11 +174,11 @@ const Rx = Rˣ See [`R`](@ref) for details. """ -function Rʸ(::T) where {T<:PNSystem} +@public function Rʸ(::T) where {T<:PNSystem} error("Rʸ is not (yet) defined for PNSystem subtype `$T`.") end Rʸ(fdpnsystem::FDPNSystem) = fdpnsystem[:Rʸ] -const Ry = Rʸ +@public const Ry = Rʸ """ Rᶻ(pnsystem) @@ -188,11 +188,11 @@ const Ry = Rʸ See [`R`](@ref) for details. """ -function Rᶻ(::T) where {T<:PNSystem} +@public function Rᶻ(::T) where {T<:PNSystem} error("Rᶻ is not (yet) defined for PNSystem subtype `$T`.") end Rᶻ(fdpnsystem::FDPNSystem) = fdpnsystem[:Rᶻ] -const Rz = Rᶻ +@public const Rz = Rᶻ @doc raw""" v(pnsystem) @@ -210,7 +210,7 @@ as a keyword argument — as in `v(Ω=0.1)`. See also [`Ω`](@ref). """ -function v(::T) where {T<:PNSystem} +@public function v(::T) where {T<:PNSystem} error("v is not (yet) defined for PNSystem subtype `$T`.") end v(fdpnsystem::FDPNSystem) = fdpnsystem[:v] @@ -222,11 +222,11 @@ v(; Ω, M=1) = ∛(M * Ω) Integrated orbital phase of the system. It is computed as the integral of [`Ω`](@ref). """ -function Φ(::T) where {T<:PNSystem} +@public function Φ(::T) where {T<:PNSystem} error("Φ is not (yet) defined for PNSystem subtype `$T`.") end Φ(fdpnsystem::FDPNSystem) = fdpnsystem[:Φ] -const Phi = Φ +@public const Phi = Φ @doc raw""" Λ₁(pnsystem) @@ -259,11 +259,11 @@ safe and efficient to use this quantity in any PN expression that specializes on See also [`Λ₂`](@ref) and [`Λ̃`](@ref). """ -function Λ₁(::T) where {T<:PNSystem} +@public function Λ₁(::T) where {T<:PNSystem} error("Λ₁ is not (yet) defined for PNSystem subtype `$T`.") end Λ₁(fdpnsystem::FDPNSystem) = fdpnsystem[:Λ₁] -const Lambda1 = Λ₁ +@public const Lambda1 = Λ₁ @doc raw""" Λ₂(pnsystem) @@ -280,11 +280,11 @@ specializes on the type of `pnsystem`. See also [`Λ₁`](@ref) and [`Λ̃`](@ref). """ -function Λ₂(::T) where {T<:PNSystem} +@public function Λ₂(::T) where {T<:PNSystem} error("Λ₂ is not (yet) defined for PNSystem subtype `$T`.") end Λ₂(fdpnsystem::FDPNSystem) = fdpnsystem[:Λ₂] -const Lambda2 = Λ₂ +@public const Lambda2 = Λ₂ ################################################################# # Not actually state variables, but aggregates of state variables @@ -297,10 +297,10 @@ Dimensionless spin vector of object 1 in this system, as a `QuatVec`. See also [`χ⃗₁ˣ`](@ref), [`χ⃗₁ʸ`](@ref), and [`χ⃗₁ᶻ`](@ref) for the individual components. """ -function χ⃗₁(::T) where {T<:PNSystem} +@public function χ⃗₁(::T) where {T<:PNSystem} QuatVec(χ⃗₁ˣ(pnsystem), χ⃗₁ʸ(pnsystem), χ⃗₁ᶻ(pnsystem)) end -const chi1 = χ⃗₁ +@public const chi1 = χ⃗₁ """ χ⃗₂(pnsystem) @@ -310,10 +310,10 @@ Dimensionless spin vector of object 2 in this system, as a `QuatVec`. See also [`χ⃗₂ˣ`](@ref), [`χ⃗₂ʸ`](@ref), and [`χ⃗₂ᶻ`](@ref) for the individual components. """ -function χ⃗₂(::T) where {T<:PNSystem} +@public function χ⃗₂(::T) where {T<:PNSystem} QuatVec(χ⃗₂ˣ(pnsystem), χ⃗₂ʸ(pnsystem), χ⃗₂ᶻ(pnsystem)) end -const chi2 = χ⃗₂ +@public const chi2 = χ⃗₂ """ R(pnsystem) @@ -334,7 +334,7 @@ and ``ϖ`` is the precession angular frequency. See also [`n̂`](@ref PostNewtonian.n̂), [`λ̂`](@ref PostNewtonian.λ̂), [`ℓ̂`](@ref PostNewtonian.ℓ̂), [`Ω`](@ref PostNewtonian.Ω), and [`𝛡`](@ref PostNewtonian.𝛡)``=ϖ n̂``. """ -function R(pnsystem::T) where {NT,T<:PNSystem{NT}} +@public function R(pnsystem::T) where {NT,T<:PNSystem{NT}} # We use this explicit constructor (with type parameter) to avoid normalization # that would probably just complicate derivatives. Rotor{NT}(Rʷ(pnsystem), Rˣ(pnsystem), Rʸ(pnsystem), Rᶻ(pnsystem)) diff --git a/src/pn_systems/vector_interface.jl b/src/pn_systems/vector_interface.jl index 03fd570c..5f6bb65d 100644 --- a/src/pn_systems/vector_interface.jl +++ b/src/pn_systems/vector_interface.jl @@ -1,9 +1,13 @@ -# Base.ismutable(pnsystem::PNSystem{NT, ST}) where {NT, ST} = ismutable(state(pnsystem)) -# Base.ismutabletype(::Type{<:PNSystem{NT, ST}}) where {NT, ST} = ismutabletype(ST) +function Base.ismutable(pnsystem::PNSystem{NT,PNOrder,ST}) where {NT,PNOrder,ST} + ismutable(state(pnsystem)) +end +function Base.ismutabletype(::Type{<:PNSystem{NT,PNOrder,ST}}) where {NT,PNOrder,ST} + ismutabletype(ST) +end """ symbol_index(::Type{T}, s::Symbol) where {T<:PNSystem} - symbol_index(::Type{T}, ::Val{s}) where {T<:PNSystem} + symbol_index(::Type{T}, ::Val{S}) where {T<:PNSystem,S} Return the index of the symbol `s` in the state vector of the given `PNSystem` type `T`. @@ -11,7 +15,7 @@ Note that the default implementation is slow; `symbol_index(::Type{T}, ::Val{s}) overridden for every symbol (and ASCII equivalent, if desired) for concrete `PNSystem` types. """ -function symbol_index(::Type{T}, ::Val{S}) where {T<:PNSystem,S} +@export function symbol_index(::Type{T}, ::Val{S}) where {T<:PNSystem,S} index = findfirst(y -> y == S, symbols(T)) if isnothing(index) index = findfirst(y -> y == S, ascii_symbols(T)) @@ -37,7 +41,7 @@ function Base.getindex(pnsystem::T, ::Val{S}) where {T<:PNSystem,S} end Base.setindex!(pnsystem::PNSystem, v, s::Symbol) = setindex!(pnsystem, v, Val(s)) -function Base.setindex!(pnsystem::T, v, ::Val{S}) where {NT,T<:PNSystem{NT},S} +function Base.setindex!(pnsystem::T, v, ::Val{S}) where {T<:PNSystem,S} index = symbol_index(T, Val(S)) @inbounds setindex!(state(pnsystem), v, index) end @@ -52,18 +56,18 @@ Base.ndims(pnsystem::PNSystem) = ndims(state(pnsystem)) Base.size(pnsystem::PNSystem) = size(state(pnsystem)) Base.size(pnsystem::PNSystem, dim) = size(state(pnsystem), dim) Base.IteratorEltype(::Type{T}) where {T<:PNSystem} = Base.HasEltype() -Base.eltype(::Type{<:PNSystem{NT}}) where {NT} = NT +# Base.eltype(::Type{<:PNSystem{NT}}) where {NT} = NT ## Already defined in `PNSystem.jl` Base.isdone(pnsystem::PNSystem) = isdone(state(pnsystem)) Base.isdone(pnsystem::PNSystem, iterstate) = isdone(state(pnsystem), iterstate) # Indexing -Base.getindex(pnsystem::PNSystem, i::Int) = @propagate_inbounds getindex(state(pnsystem), i) -Base.setindex!(pn::PNSystem, v, i::Int) = @propagate_inbounds setindex!(state(pn), v, i) +@propagate_inbounds Base.getindex(pnsystem::PNSystem, i::Int) = getindex(state(pnsystem), i) +@propagate_inbounds Base.setindex!(pn::PNSystem, v, i::Int) = setindex!(state(pn), v, i) Base.firstindex(pnsystem::PNSystem) = firstindex(state(pnsystem)) Base.lastindex(pnsystem::PNSystem) = lastindex(state(pnsystem)) Base.eachindex(pnsystem::PNSystem) = eachindex(state(pnsystem)) # Abstract arrays Base.IndexStyle(::Type{T}) where {T<:PNSystem} = Base.IndexLinear() -Base.length(pnsystem::PNSystem) = length(state(pnsystem)) +# Base.length(pnsystem::PNSystem) = length(state(pnsystem)) ## Already defined above # Base.similar(pnsystem::PNSystem) = similar(state(pnsystem)) Base.axes(pnsystem::PNSystem) = axes(state(pnsystem)) # Strided Arrays @@ -74,176 +78,176 @@ end Base.elsize(::Type{<:PNSystem{T}}) where {T} = sizeof(T) Base.stride(pnsystem::PNSystem, k::Int) = stride(state(pnsystem), k) -function PreallocationTools.get_tmp( - dc::PreallocationTools.DiffCache, u::LArray{T,N,D,Syms} -) where {T<:ForwardDiff.Dual,N,D,Syms} - nelem = div(sizeof(T), sizeof(eltype(dc.dual_du))) * length(dc.du) - if nelem > length(dc.dual_du) - PreallocationTools.enlargedualcache!(dc, nelem) - end - _x = ArrayInterface.restructure(dc.du, reinterpret(T, view(dc.dual_du, 1:nelem))) - LabelledArrays.LArray{T,N,D,Syms}(_x) -end - -function RecursiveArrayTools.recursive_unitless_eltype( - a::Type{LArray{T,N,D,Syms}} -) where {T,N,D,Syms} - LArray{typeof(one(T)),N,D,Syms} -end - -##################################### -# NamedTuple compatibility -##################################### -## SLArray to named tuple -function Base.convert(::Type{NamedTuple}, x::SLArray{S,T,N,L,Syms}) where {S,T,N,L,Syms} - tup = NTuple{length(Syms),T}(x.__x) - NamedTuple{Syms,typeof(tup)}(tup) -end -Base.keys(x::SLArray{S,T,N,L,Syms}) where {S,T,N,L,Syms} = Syms - -## pairs iterator -function Base.pairs(x::LArray{T,N,D,Syms}) where {T,N,D,Syms} - # (label => getproperty(x, label) for label in Syms) # not type stable? - (Syms[i] => x[i] for i ∈ 1:length(Syms)) -end - -function Base.iterate(x::SLArray, args...) - iterate(convert(NamedTuple, x), args...) -end - -##################################### -# Array Interface -##################################### -function Base.print_array(io::IO, w::WignerMatrix{NT,IT}) where {NT,IT<:Rational} - Base.print_array(io, parent(w)) -end - -Base.size(x::LArray) = size(getfield(x, :__x)) -Base.@propagate_inbounds Base.getindex(x::LArray, i...) = getfield(x, :__x)[i...] -Base.@propagate_inbounds function Base.setindex!(x::LArray, y, i...) - getfield(x, :__x)[i...] = y - return x -end - -Base.propertynames(::LArray{T,N,D,Syms}) where {T,N,D,Syms} = Syms -symnames(::Type{LArray{T,N,D,Syms}}) where {T,N,D,Syms} = Syms - -Base.@propagate_inbounds function Base.getproperty(x::LArray, s::Symbol) - if s == :__x - return getfield(x, :__x) - end - return getindex(x, Val(s)) -end - -Base.@propagate_inbounds function Base.setproperty!(x::LArray, s::Symbol, y) - if s == :__x - return setfield!(x, :__x, y) - end - setindex!(x, y, Val(s)) -end - -Base.@propagate_inbounds Base.getindex(x::LArray, s::Symbol) = getindex(x, Val(s)) -Base.@propagate_inbounds Base.getindex(x::LArray, s::Val) = __getindex(x, s) -Base.@propagate_inbounds Base.setindex!(x::LArray, v, s::Symbol) = setindex!(x, v, Val(s)) - -@generated function Base.setindex!(x::LArray, y, ::Val{s}) where {s} - syms = symnames(x) - if syms isa NamedTuple - idxs = syms[s] - return quote - Base.@_propagate_inbounds_meta - setindex!(getfield(x, :__x), y, $idxs) - return x - end - else # Tuple - idx = findfirst(y -> y == s, symnames(x)) - return quote - Base.@_propagate_inbounds_meta - setindex!(getfield(x, :__x), y, $idx) - return x - end - end -end - -Base.@propagate_inbounds function Base.getindex(x::LArray, s::AbstractArray{Symbol,1}) - [getindex(x, si) for si ∈ s] -end - -function Base.similar( - x::LArray{T,K,D,Syms}, ::Type{S}, dims::NTuple{N,Int} -) where {T,K,D,Syms,S,N} - tmp = similar(x.__x, S, dims) - LArray{S,N,typeof(tmp),Syms}(tmp) -end - -function StaticArrays.similar_type( - ::Type{SLArray{S,T,N,L,Syms}}, T2, ::Size{S} -) where {S,T,N,L,Syms} - SLArray{S,T2,N,L,Syms} -end - -# Allow copying LArray of uninitialized data, as with regular Array -Base.copy(x::LArray) = typeof(x)(copy(getfield(x, :__x))) -Base.copyto!(x::LArray, y::LArray) = copyto!(getfield(x, :__x), getfield(y, :__x)) - -# enable the usage of LAPACK -function Base.unsafe_convert(::Type{Ptr{T}}, a::LArray{T,N,D,S}) where {T,N,D,S} - Base.unsafe_convert(Ptr{T}, getfield(a, :__x)) -end - -Base.convert(::Type{T}, x) where {T<:LArray} = T(x) -Base.convert(::Type{T}, x::T) where {T<:LArray} = x -Base.convert(::Type{<:Array}, x::LArray) = convert(Array, getfield(x, :__x)) -function Base.convert( - ::Type{AbstractArray{T,N}}, x::LArray{S,N,<:Any,Syms} -) where {T,S,N,Syms} - LArray{Syms}(convert(AbstractArray{T,N}, getfield(x, :__x))) -end -Base.convert(::Type{AbstractArray{T,N}}, x::LArray{T,N}) where {T,N} = x - -function ArrayInterface.restructure( - x::LArray{T,N,D,Syms}, y::LArray{T2,N2,D2,Syms} -) where {T,N,D,T2,N2,D2,Syms} - reshape(y, size(x)...) -end - -##################################### -# Broadcast -##################################### -struct LAStyle{T,N,L} <: Broadcast.AbstractArrayStyle{N} end -LAStyle{T,N,L}(x::Val{i}) where {T,N,L,i} = LAStyle{T,N,L}() -Base.BroadcastStyle(::Type{LArray{T,N,D,L}}) where {T,N,D,L} = LAStyle{T,N,L}() -function Base.BroadcastStyle( - ::LabelledArrays.LAStyle{T,N,L}, ::LabelledArrays.LAStyle{E,N,L} -) where {T,E,N,L} - LAStyle{promote_type(T, E),N,L}() -end - -@generated function labels2axes(::Val{t}) where {t} - if t isa NamedTuple && all(x -> x isa Union{Integer,UnitRange}, values(t)) # range labelling - (Base.OneTo(maximum(Iterators.flatten(v for v ∈ values(t)))),) - elseif t isa NTuple{<:Any,Symbol} - axes(t) - else - error( - "$t label isn't supported for broadcasting. Try to formulate it in terms of linear indexing.", - ) - end -end -function Base.similar( - bc::Broadcast.Broadcasted{LAStyle{T,N,L}}, ::Type{ElType} -) where {T,N,L,ElType} - tmp = similar(Array{ElType}, axes(bc)) - if axes(bc) != labels2axes(Val(L)) - return tmp - else - return LArray{ElType,N,typeof(tmp),L}(tmp) - end -end - -# Broadcasting checks for aliasing with Base.dataids but the fallback -# for AbstractArrays is very slow. Instead, we just call dataids on the -# wrapped buffer -Base.dataids(pnsystem::PNSystem) = Base.dataids(state(pnsystem)) - -Base.elsize(::Type{<:LArray{T}}) where {T} = sizeof(T) +# function PreallocationTools.get_tmp( +# dc::PreallocationTools.DiffCache, u::LArray{T,N,D,Syms} +# ) where {T<:ForwardDiff.Dual,N,D,Syms} +# nelem = div(sizeof(T), sizeof(eltype(dc.dual_du))) * length(dc.du) +# if nelem > length(dc.dual_du) +# PreallocationTools.enlargedualcache!(dc, nelem) +# end +# _x = ArrayInterface.restructure(dc.du, reinterpret(T, view(dc.dual_du, 1:nelem))) +# LabelledArrays.LArray{T,N,D,Syms}(_x) +# end + +# function RecursiveArrayTools.recursive_unitless_eltype( +# a::Type{LArray{T,N,D,Syms}} +# ) where {T,N,D,Syms} +# LArray{typeof(one(T)),N,D,Syms} +# end + +# ##################################### +# # NamedTuple compatibility +# ##################################### +# ## SLArray to named tuple +# function Base.convert(::Type{NamedTuple}, x::SLArray{S,T,N,L,Syms}) where {S,T,N,L,Syms} +# tup = NTuple{length(Syms),T}(x.__x) +# NamedTuple{Syms,typeof(tup)}(tup) +# end +# Base.keys(x::SLArray{S,T,N,L,Syms}) where {S,T,N,L,Syms} = Syms + +# ## pairs iterator +# function Base.pairs(x::LArray{T,N,D,Syms}) where {T,N,D,Syms} +# # (label => getproperty(x, label) for label in Syms) # not type stable? +# (Syms[i] => x[i] for i ∈ 1:length(Syms)) +# end + +# function Base.iterate(x::SLArray, args...) +# iterate(convert(NamedTuple, x), args...) +# end + +# ##################################### +# # Array Interface +# ##################################### +# function Base.print_array(io::IO, w::WignerMatrix{NT,IT}) where {NT,IT<:Rational} +# Base.print_array(io, parent(w)) +# end + +# Base.size(x::LArray) = size(getfield(x, :__x)) +# Base.@propagate_inbounds Base.getindex(x::LArray, i...) = getfield(x, :__x)[i...] +# Base.@propagate_inbounds function Base.setindex!(x::LArray, y, i...) +# getfield(x, :__x)[i...] = y +# return x +# end + +# Base.propertynames(::LArray{T,N,D,Syms}) where {T,N,D,Syms} = Syms +# symnames(::Type{LArray{T,N,D,Syms}}) where {T,N,D,Syms} = Syms + +# Base.@propagate_inbounds function Base.getproperty(x::LArray, s::Symbol) +# if s == :__x +# return getfield(x, :__x) +# end +# return getindex(x, Val(s)) +# end + +# Base.@propagate_inbounds function Base.setproperty!(x::LArray, s::Symbol, y) +# if s == :__x +# return setfield!(x, :__x, y) +# end +# setindex!(x, y, Val(s)) +# end + +# Base.@propagate_inbounds Base.getindex(x::LArray, s::Symbol) = getindex(x, Val(s)) +# Base.@propagate_inbounds Base.getindex(x::LArray, s::Val) = __getindex(x, s) +# Base.@propagate_inbounds Base.setindex!(x::LArray, v, s::Symbol) = setindex!(x, v, Val(s)) + +# @generated function Base.setindex!(x::LArray, y, ::Val{s}) where {s} +# syms = symnames(x) +# if syms isa NamedTuple +# idxs = syms[s] +# return quote +# Base.@_propagate_inbounds_meta +# setindex!(getfield(x, :__x), y, $idxs) +# return x +# end +# else # Tuple +# idx = findfirst(y -> y == s, symnames(x)) +# return quote +# Base.@_propagate_inbounds_meta +# setindex!(getfield(x, :__x), y, $idx) +# return x +# end +# end +# end + +# Base.@propagate_inbounds function Base.getindex(x::LArray, s::AbstractArray{Symbol,1}) +# [getindex(x, si) for si ∈ s] +# end + +# function Base.similar( +# x::LArray{T,K,D,Syms}, ::Type{S}, dims::NTuple{N,Int} +# ) where {T,K,D,Syms,S,N} +# tmp = similar(x.__x, S, dims) +# LArray{S,N,typeof(tmp),Syms}(tmp) +# end + +# function StaticArrays.similar_type( +# ::Type{SLArray{S,T,N,L,Syms}}, T2, ::Size{S} +# ) where {S,T,N,L,Syms} +# SLArray{S,T2,N,L,Syms} +# end + +# # Allow copying LArray of uninitialized data, as with regular Array +# Base.copy(x::LArray) = typeof(x)(copy(getfield(x, :__x))) +# Base.copyto!(x::LArray, y::LArray) = copyto!(getfield(x, :__x), getfield(y, :__x)) + +# # enable the usage of LAPACK +# function Base.unsafe_convert(::Type{Ptr{T}}, a::LArray{T,N,D,S}) where {T,N,D,S} +# Base.unsafe_convert(Ptr{T}, getfield(a, :__x)) +# end + +# Base.convert(::Type{T}, x) where {T<:LArray} = T(x) +# Base.convert(::Type{T}, x::T) where {T<:LArray} = x +# Base.convert(::Type{<:Array}, x::LArray) = convert(Array, getfield(x, :__x)) +# function Base.convert( +# ::Type{AbstractArray{T,N}}, x::LArray{S,N,<:Any,Syms} +# ) where {T,S,N,Syms} +# LArray{Syms}(convert(AbstractArray{T,N}, getfield(x, :__x))) +# end +# Base.convert(::Type{AbstractArray{T,N}}, x::LArray{T,N}) where {T,N} = x + +# function ArrayInterface.restructure( +# x::LArray{T,N,D,Syms}, y::LArray{T2,N2,D2,Syms} +# ) where {T,N,D,T2,N2,D2,Syms} +# reshape(y, size(x)...) +# end + +# ##################################### +# # Broadcast +# ##################################### +# struct LAStyle{T,N,L} <: Broadcast.AbstractArrayStyle{N} end +# LAStyle{T,N,L}(x::Val{i}) where {T,N,L,i} = LAStyle{T,N,L}() +# Base.BroadcastStyle(::Type{LArray{T,N,D,L}}) where {T,N,D,L} = LAStyle{T,N,L}() +# function Base.BroadcastStyle( +# ::LabelledArrays.LAStyle{T,N,L}, ::LabelledArrays.LAStyle{E,N,L} +# ) where {T,E,N,L} +# LAStyle{promote_type(T, E),N,L}() +# end + +# @generated function labels2axes(::Val{t}) where {t} +# if t isa NamedTuple && all(x -> x isa Union{Integer,UnitRange}, values(t)) # range labelling +# (Base.OneTo(maximum(Iterators.flatten(v for v ∈ values(t)))),) +# elseif t isa NTuple{<:Any,Symbol} +# axes(t) +# else +# error( +# "$t label isn't supported for broadcasting. Try to formulate it in terms of linear indexing.", +# ) +# end +# end +# function Base.similar( +# bc::Broadcast.Broadcasted{LAStyle{T,N,L}}, ::Type{ElType} +# ) where {T,N,L,ElType} +# tmp = similar(Array{ElType}, axes(bc)) +# if axes(bc) != labels2axes(Val(L)) +# return tmp +# else +# return LArray{ElType,N,typeof(tmp),L}(tmp) +# end +# end + +# # Broadcasting checks for aliasing with Base.dataids but the fallback +# # for AbstractArrays is very slow. Instead, we just call dataids on the +# # wrapped buffer +# Base.dataids(pnsystem::PNSystem) = Base.dataids(state(pnsystem)) + +# Base.elsize(::Type{<:LArray{T}}) where {T} = sizeof(T) From 71285d0e00193de59ea951078e63bf176b89fe5a Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Sat, 28 Jun 2025 11:32:27 -0400 Subject: [PATCH 07/77] Test PN orders --- src/pn_systems/pn_order.jl | 39 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/pn_systems/pn_order.jl b/src/pn_systems/pn_order.jl index 207fe030..8bf22e93 100644 --- a/src/pn_systems/pn_order.jl +++ b/src/pn_systems/pn_order.jl @@ -41,3 +41,42 @@ when computing the order index. max_pn_order end end + +@testitem "PN Orders" begin + using PostNewtonian: prepare_pn_order + + @test max_pn_order == (typemax(Int) - 2) // 2 + @test max_pn_order > 4611686018427387902 + @test max_pn_order < 4611686018427387903 + + @test prepare_pn_order(0//2) == 0//2 + @test prepare_pn_order(1//2) == 1//2 + @test prepare_pn_order(2//2) == 2//2 + @test prepare_pn_order(7//2) == 7//2 + @test prepare_pn_order(31//2) == 31//2 + + @test prepare_pn_order(0.0) == 0//2 + @test prepare_pn_order(0.5) == 1//2 + for i ∈ 0:31 + @test prepare_pn_order(i/2) == i//2 + end + + @test prepare_pn_order(0) == 0//1 + @test prepare_pn_order(1) == 1//1 + @test prepare_pn_order(2) == 2//1 + @test prepare_pn_order(7) == 7//1 + @test prepare_pn_order(31) == 31//1 + + @test prepare_pn_order(typemax(Int)) == max_pn_order + @test prepare_pn_order(typemax(Int) - 1) == max_pn_order + @test prepare_pn_order(typemax(Int) - 2) == max_pn_order + @test prepare_pn_order(typemax(Int) // 2) == max_pn_order + + for i ∈ 0:31 + bbh = BBH(randn(14), i//2) + @test order_index(bbh) == 1 + i + @test pn_order(bbh) ≈ i/2 + @test pn_order(bbh) == i//2 + @test pn_order(BBH{Float16,i//2,Vector{Float16}}) == i//2 + end +end From f912fe655134f2914ce584314a358eb7f32917e3 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Sat, 28 Jun 2025 11:40:57 -0400 Subject: [PATCH 08/77] Simplify constructors; improve method definitions --- src/pn_systems/BBH.jl | 36 +++++++++++++++++++------------- src/pn_systems/BHNS.jl | 36 +++++++++++++++++++------------- src/pn_systems/NSNS.jl | 36 +++++++++++++++++++------------- src/pn_systems/PNSystem.jl | 1 - src/pn_systems/Quasispherical.jl | 4 ++++ 5 files changed, 67 insertions(+), 46 deletions(-) diff --git a/src/pn_systems/BBH.jl b/src/pn_systems/BBH.jl index d4bdbe9f..6d043447 100644 --- a/src/pn_systems/BBH.jl +++ b/src/pn_systems/BBH.jl @@ -39,16 +39,16 @@ difficult to extract this quantity from `R`. (NT, PNOrder, state) = prepare_Quasispherical(; M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ, PNOrder) return new{NT,PNOrder,typeof(state)}(state) end - function BBH(state; PNOrder=typemax(Int)) - if eachindex(state) != Base.OneTo(14) - error( - "The `state` vector for `BBH` must be indexed from 1 to 14; " * - "input is indexed `$(eachindex(state))`.", - ) - end - NT, PNOrder, ST = eltype(state), prepare_pn_order(PNOrder), typeof(state) - return new{NT,PNOrder,ST}(state) - end + # function BBH(state, PNOrder=typemax(Int)) + # if eachindex(state) != Base.OneTo(14) + # error( + # "The `state` vector for `BBH` must be indexed from 1 to 14; " * + # "input is indexed `$(eachindex(state))`.", + # ) + # end + # NT, PNOrder, ST = eltype(state), prepare_pn_order(PNOrder), typeof(state) + # return new{NT,PNOrder,ST}(state) + # end end @public const BHBH = BBH @@ -57,13 +57,19 @@ end state(pnsystem::BBH) = pnsystem.state symbols(::Type{<:BBH}) = symbols(Quasispherical) ascii_symbols(::Type{<:BBH}) = ascii_symbols(Quasispherical) -for (i, symbol) ∈ enumerate(symbols(BBH)) - # This will define, e.g., `M₁(pnsystem::BBH) = pnsystem.state[1]`. We - # could do this manually, but this is more concise and less error-prone. +for (i, (symbol, ascii_symbol)) ∈ enumerate(zip(symbols(BBH), ascii_symbols(BBH))) + # We could do this manually, but this is more concise and less error-prone. @eval begin + # Define, e.g., `M₁(pnsystem::BBH) = pnsystem.state[1]`. $(symbol)(pnsystem::BBH) = @inbounds pnsystem.state[$i] - function symbol_index(::Type{T}, ::Val{Symbol($symbol)}) where {T<:BBH} - $i + + # Specialize `symbol_index` for Val{:M₁}, Val{:M₂}, etc. + symbol_index(::Type{T}, ::Val{$(QuoteNode(symbol))}) where {T<:BBH} = $i + end + if symbol ≠ ascii_symbol + @eval begin + # Specialize `symbol_index` for Val{:M1}, Val{:M2}, etc. + symbol_index(::Type{T}, ::Val{$(QuoteNode(ascii_symbol))}) where {T<:BBH} = $i end end end diff --git a/src/pn_systems/BHNS.jl b/src/pn_systems/BHNS.jl index 9414bb1d..3280b583 100644 --- a/src/pn_systems/BHNS.jl +++ b/src/pn_systems/BHNS.jl @@ -33,16 +33,16 @@ also [`NSNS`](@ref). state = vcat(state, Λ₂) return new{eltype(state),PNOrder,typeof(state)}(state) end - function BHNS(state; PNOrder=typemax(Int)) - if eachindex(state) != Base.OneTo(15) - error( - "The `state` vector for `BHNS` must be indexed from 1 to 15; " * - "input is indexed `$(eachindex(state))`.", - ) - end - NT, PNOrder, ST = eltype(state), prepare_pn_order(PNOrder), typeof(state) - return new{NT,PNOrder,ST}(state) - end + # function BHNS(state, PNOrder=typemax(Int)) + # if eachindex(state) != Base.OneTo(15) + # error( + # "The `state` vector for `BHNS` must be indexed from 1 to 15; " * + # "input is indexed `$(eachindex(state))`.", + # ) + # end + # NT, PNOrder, ST = eltype(state), prepare_pn_order(PNOrder), typeof(state) + # return new{NT,PNOrder,ST}(state) + # end end # The following are methods of functions defined in `state_variables.jl`, specialized for @@ -54,13 +54,19 @@ end function ascii_symbols(::Type{<:BHNS}) (ascii_symbols(Quasispherical)..., :Lambda2) end -for (i, symbol) ∈ enumerate(symbols(BHNS)) - # This will define, e.g., `M₁(pnsystem::BHNS) = pnsystem.state[1]`. We - # could do this manually, but this is more concise and less error-prone. +for (i, (symbol, ascii_symbol)) ∈ enumerate(zip(symbols(BHNS), ascii_symbols(BHNS))) + # We could do this manually, but this is more concise and less error-prone. @eval begin + # Define, e.g., `M₁(pnsystem::BHNS) = pnsystem.state[1]`. $(symbol)(pnsystem::BHNS) = @inbounds pnsystem.state[$i] - function symbol_index(::Type{T}, ::Val{Symbol($symbol)}) where {T<:BHNS} - $i + + # Specialize `symbol_index` for Val{:M₁}, Val{:M₂}, etc. + symbol_index(::Type{T}, ::Val{$(QuoteNode(symbol))}) where {T<:BHNS} = $i + end + if symbol ≠ ascii_symbol + @eval begin + # Specialize `symbol_index` for Val{:M1}, Val{:M2}, etc. + symbol_index(::Type{T}, ::Val{$(QuoteNode(ascii_symbol))}) where {T<:BHNS} = $i end end end diff --git a/src/pn_systems/NSNS.jl b/src/pn_systems/NSNS.jl index d614fb52..e8f34a3f 100644 --- a/src/pn_systems/NSNS.jl +++ b/src/pn_systems/NSNS.jl @@ -24,16 +24,16 @@ and `Λ₂` holding the (constant) tidal-coupling parameters of the neutron star state = vcat(state, Λ₁, Λ₂) return new{eltype(state),PNOrder,typeof(state)}(state) end - function NSNS(state; PNOrder=typemax(Int)) - if eachindex(state) != Base.OneTo(16) - error( - "The `state` vector for `NSNS` must be indexed from 1 to 16; " * - "input is indexed `$(eachindex(state))`.", - ) - end - NT, PNOrder, ST = eltype(state), prepare_pn_order(PNOrder), typeof(state) - return new{NT,PNOrder,ST}(state) - end + # function NSNS(state, PNOrder=typemax(Int)) + # if eachindex(state) != Base.OneTo(16) + # error( + # "The `state` vector for `NSNS` must be indexed from 1 to 16; " * + # "input is indexed `$(eachindex(state))`.", + # ) + # end + # NT, PNOrder, ST = eltype(state), prepare_pn_order(PNOrder), typeof(state) + # return new{NT,PNOrder,ST}(state) + # end end @public const BNS = NSNS @@ -46,13 +46,19 @@ end function ascii_symbols(::Type{<:NSNS}) (ascii_symbols(Quasispherical)..., :Lambda1, :Lambda2) end -for (i, symbol) ∈ enumerate(symbols(NSNS)) - # This will define, e.g., `M₁(pnsystem::NSNS) = pnsystem.state[1]`. We - # could do this manually, but this is more concise and less error-prone. +for (i, (symbol, ascii_symbol)) ∈ enumerate(zip(symbols(NSNS), ascii_symbols(NSNS))) + # We could do this manually, but this is more concise and less error-prone. @eval begin + # Define, e.g., `M₁(pnsystem::NSNS) = pnsystem.state[1]`. $(symbol)(pnsystem::NSNS) = @inbounds pnsystem.state[$i] - function symbol_index(::Type{T}, ::Val{Symbol($symbol)}) where {T<:NSNS} - $i + + # Specialize `symbol_index` for Val{:M₁}, Val{:M₂}, etc. + symbol_index(::Type{T}, ::Val{$(QuoteNode(symbol))}) where {T<:NSNS} = $i + end + if symbol ≠ ascii_symbol + @eval begin + # Specialize `symbol_index` for Val{:M1}, Val{:M2}, etc. + symbol_index(::Type{T}, ::Val{$(QuoteNode(ascii_symbol))}) where {T<:NSNS} = $i end end end diff --git a/src/pn_systems/PNSystem.jl b/src/pn_systems/PNSystem.jl index 023d35e1..d6be7234 100644 --- a/src/pn_systems/PNSystem.jl +++ b/src/pn_systems/PNSystem.jl @@ -33,7 +33,6 @@ This function should have a specialized method for each concrete subtype of `PNS @public function state(::T) where {T<:PNSystem} error("`state` is not yet defined for PNSystem subtype `$T`.") end -Base.vec(pnsystem::PNSystem) = state(pnsystem) Base.eltype(::Type{PNT}) where {NT,PNT<:PNSystem{NT}} = NT Base.one(::Type{PNT}) where {PNT<:PNSystem} = one(eltype(PNT)) diff --git a/src/pn_systems/Quasispherical.jl b/src/pn_systems/Quasispherical.jl index 6db4daab..4872f378 100644 --- a/src/pn_systems/Quasispherical.jl +++ b/src/pn_systems/Quasispherical.jl @@ -28,6 +28,10 @@ tidal-coupling parameters, `Λ₁` and `Λ₂` (or `Lambda1` and `Lambda2`). """ Quasispherical +function (::Type{T})(state, PNOrder=typemax(Int)) where {T<:Quasispherical} + T{eltype(state),prepare_pn_order(PNOrder),typeof(state)}(state) +end + function prepare_Quasispherical(; M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ=0, PNOrder=typemax(Int)) χ⃗₁ = QuatVec(χ⃗₁) χ⃗₂ = QuatVec(χ⃗₂) From 2583e4638d88e97c5fd13c3da3417c473d321cd4 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Sat, 28 Jun 2025 11:41:49 -0400 Subject: [PATCH 09/77] Remove unused methods; start tests --- src/pn_systems/vector_interface.jl | 292 +++++++++++++++-------------- 1 file changed, 148 insertions(+), 144 deletions(-) diff --git a/src/pn_systems/vector_interface.jl b/src/pn_systems/vector_interface.jl index 5f6bb65d..11d732de 100644 --- a/src/pn_systems/vector_interface.jl +++ b/src/pn_systems/vector_interface.jl @@ -5,6 +5,7 @@ function Base.ismutabletype(::Type{<:PNSystem{NT,PNOrder,ST}}) where {NT,PNOrder ismutabletype(ST) end +### Symbol-based indexing """ symbol_index(::Type{T}, s::Symbol) where {T<:PNSystem} symbol_index(::Type{T}, ::Val{S}) where {T<:PNSystem,S} @@ -31,15 +32,13 @@ types. index end end - Base.getindex(pnsystem::PNSystem, s::Symbol) = getindex(pnsystem, Val(s)) -function Base.getindex(pnsystem::T, ::Val{S}) where {T<:PNSystem,S} +function Base.getindex(pnsystem::T, s::Val{S}) where {T<:PNSystem,S} # If `S` is not actually a symbol in `pnsystem`, `symbol_index` will error, so we know # that the `index` is inbounds if it returns. - index = symbol_index(T, Val(S)) + index = symbol_index(T, s) @inbounds state(pnsystem)[index] end - Base.setindex!(pnsystem::PNSystem, v, s::Symbol) = setindex!(pnsystem, v, Val(s)) function Base.setindex!(pnsystem::T, v, ::Val{S}) where {T<:PNSystem,S} index = symbol_index(T, Val(S)) @@ -48,8 +47,8 @@ end ### Interfaces: https://docs.julialang.org/en/v1/manual/interfaces # Iteration -Base.iterate(pnsystem::PNSystem) = iterate(state(pnsystem)) -Base.iterate(pnsystem::PNSystem, state) = iterate(state(pnsystem), state) +# Base.iterate(pnsystem::PNSystem) = iterate(state(pnsystem)) +# Base.iterate(pnsystem::PNSystem, state) = iterate(state(pnsystem), state) Base.IteratorSize(::Type{T}) where {T<:PNSystem} = Base.HasShape{1}() Base.length(pnsystem::PNSystem) = length(state(pnsystem)) Base.ndims(pnsystem::PNSystem) = ndims(state(pnsystem)) @@ -60,8 +59,16 @@ Base.IteratorEltype(::Type{T}) where {T<:PNSystem} = Base.HasEltype() Base.isdone(pnsystem::PNSystem) = isdone(state(pnsystem)) Base.isdone(pnsystem::PNSystem, iterstate) = isdone(state(pnsystem), iterstate) # Indexing -@propagate_inbounds Base.getindex(pnsystem::PNSystem, i::Int) = getindex(state(pnsystem), i) -@propagate_inbounds Base.setindex!(pn::PNSystem, v, i::Int) = setindex!(state(pn), v, i) +@propagate_inbounds Base.getindex(pnsystem::PNSystem, i::T) where {T} = getindex( + state(pnsystem), i +) +@propagate_inbounds Base.setindex!(pn::PNSystem, v, i::T) where {T} = setindex!( + state(pn), v, i +) +@propagate_inbounds Base.getindex(pnsystem::PNSystem, i...) = getindex( + state(pnsystem), i... +) +@propagate_inbounds Base.setindex!(pn::PNSystem, v, i...) = setindex!(state(pn), v, i...) Base.firstindex(pnsystem::PNSystem) = firstindex(state(pnsystem)) Base.lastindex(pnsystem::PNSystem) = lastindex(state(pnsystem)) Base.eachindex(pnsystem::PNSystem) = eachindex(state(pnsystem)) @@ -72,144 +79,32 @@ Base.IndexStyle(::Type{T}) where {T<:PNSystem} = Base.IndexLinear() Base.axes(pnsystem::PNSystem) = axes(state(pnsystem)) # Strided Arrays Base.strides(pnsystem::PNSystem) = strides(state(pnsystem)) -function Base.unsafe_convert(::Type{Ptr{T}}, A::PNSystem) where {T} - Base.unsafe_convert(Ptr{T}, state(A)) +function Base.unsafe_convert(::Type{Ptr{ST}}, pnsystem::PNSystem{ST}) where {ST} + Base.unsafe_convert(Ptr{ST}, state(pnsystem)) end Base.elsize(::Type{<:PNSystem{T}}) where {T} = sizeof(T) Base.stride(pnsystem::PNSystem, k::Int) = stride(state(pnsystem), k) -# function PreallocationTools.get_tmp( -# dc::PreallocationTools.DiffCache, u::LArray{T,N,D,Syms} -# ) where {T<:ForwardDiff.Dual,N,D,Syms} -# nelem = div(sizeof(T), sizeof(eltype(dc.dual_du))) * length(dc.du) -# if nelem > length(dc.dual_du) -# PreallocationTools.enlargedualcache!(dc, nelem) -# end -# _x = ArrayInterface.restructure(dc.du, reinterpret(T, view(dc.dual_du, 1:nelem))) -# LabelledArrays.LArray{T,N,D,Syms}(_x) -# end - -# function RecursiveArrayTools.recursive_unitless_eltype( -# a::Type{LArray{T,N,D,Syms}} -# ) where {T,N,D,Syms} -# LArray{typeof(one(T)),N,D,Syms} -# end - -# ##################################### -# # NamedTuple compatibility -# ##################################### -# ## SLArray to named tuple -# function Base.convert(::Type{NamedTuple}, x::SLArray{S,T,N,L,Syms}) where {S,T,N,L,Syms} -# tup = NTuple{length(Syms),T}(x.__x) -# NamedTuple{Syms,typeof(tup)}(tup) -# end -# Base.keys(x::SLArray{S,T,N,L,Syms}) where {S,T,N,L,Syms} = Syms - -# ## pairs iterator -# function Base.pairs(x::LArray{T,N,D,Syms}) where {T,N,D,Syms} -# # (label => getproperty(x, label) for label in Syms) # not type stable? -# (Syms[i] => x[i] for i ∈ 1:length(Syms)) -# end - -# function Base.iterate(x::SLArray, args...) -# iterate(convert(NamedTuple, x), args...) -# end - -# ##################################### -# # Array Interface -# ##################################### -# function Base.print_array(io::IO, w::WignerMatrix{NT,IT}) where {NT,IT<:Rational} -# Base.print_array(io, parent(w)) -# end - -# Base.size(x::LArray) = size(getfield(x, :__x)) -# Base.@propagate_inbounds Base.getindex(x::LArray, i...) = getfield(x, :__x)[i...] -# Base.@propagate_inbounds function Base.setindex!(x::LArray, y, i...) -# getfield(x, :__x)[i...] = y -# return x -# end - -# Base.propertynames(::LArray{T,N,D,Syms}) where {T,N,D,Syms} = Syms -# symnames(::Type{LArray{T,N,D,Syms}}) where {T,N,D,Syms} = Syms - -# Base.@propagate_inbounds function Base.getproperty(x::LArray, s::Symbol) -# if s == :__x -# return getfield(x, :__x) -# end -# return getindex(x, Val(s)) -# end - -# Base.@propagate_inbounds function Base.setproperty!(x::LArray, s::Symbol, y) -# if s == :__x -# return setfield!(x, :__x, y) -# end -# setindex!(x, y, Val(s)) -# end - -# Base.@propagate_inbounds Base.getindex(x::LArray, s::Symbol) = getindex(x, Val(s)) -# Base.@propagate_inbounds Base.getindex(x::LArray, s::Val) = __getindex(x, s) -# Base.@propagate_inbounds Base.setindex!(x::LArray, v, s::Symbol) = setindex!(x, v, Val(s)) - -# @generated function Base.setindex!(x::LArray, y, ::Val{s}) where {s} -# syms = symnames(x) -# if syms isa NamedTuple -# idxs = syms[s] -# return quote -# Base.@_propagate_inbounds_meta -# setindex!(getfield(x, :__x), y, $idxs) -# return x -# end -# else # Tuple -# idx = findfirst(y -> y == s, symnames(x)) -# return quote -# Base.@_propagate_inbounds_meta -# setindex!(getfield(x, :__x), y, $idx) -# return x -# end -# end -# end - -# Base.@propagate_inbounds function Base.getindex(x::LArray, s::AbstractArray{Symbol,1}) -# [getindex(x, si) for si ∈ s] -# end - -# function Base.similar( -# x::LArray{T,K,D,Syms}, ::Type{S}, dims::NTuple{N,Int} -# ) where {T,K,D,Syms,S,N} -# tmp = similar(x.__x, S, dims) -# LArray{S,N,typeof(tmp),Syms}(tmp) -# end +# NamedTuple interface +function Base.convert(::Type{NamedTuple}, pnsystem::PNSystem{N,P,S}) where {N,P,S} + NamedTuple{symbols(pnsystem),N}(state(pnsystem)) +end +Base.keys(pnsystem::PNSystem) = symbols(pnsystem) +function Base.pairs(pnsystem::PNSystem{N,P,S}) where {N,P,S} + (s=>v for (s, v) ∈ zip(symbols(pnsystem), state(pnsystem))) +end -# function StaticArrays.similar_type( -# ::Type{SLArray{S,T,N,L,Syms}}, T2, ::Size{S} -# ) where {S,T,N,L,Syms} -# SLArray{S,T2,N,L,Syms} -# end +Base.@propagate_inbounds function Base.getindex( + pnsystem::PNSystem, s::AbstractVector{Symbol} +) + # We trick broadcasting into treating `pnsystem` as a scalar by putting it into a + # 1-tuple. + getindex.((pnsystem,), s) +end # # Allow copying LArray of uninitialized data, as with regular Array -# Base.copy(x::LArray) = typeof(x)(copy(getfield(x, :__x))) -# Base.copyto!(x::LArray, y::LArray) = copyto!(getfield(x, :__x), getfield(y, :__x)) - -# # enable the usage of LAPACK -# function Base.unsafe_convert(::Type{Ptr{T}}, a::LArray{T,N,D,S}) where {T,N,D,S} -# Base.unsafe_convert(Ptr{T}, getfield(a, :__x)) -# end - -# Base.convert(::Type{T}, x) where {T<:LArray} = T(x) -# Base.convert(::Type{T}, x::T) where {T<:LArray} = x -# Base.convert(::Type{<:Array}, x::LArray) = convert(Array, getfield(x, :__x)) -# function Base.convert( -# ::Type{AbstractArray{T,N}}, x::LArray{S,N,<:Any,Syms} -# ) where {T,S,N,Syms} -# LArray{Syms}(convert(AbstractArray{T,N}, getfield(x, :__x))) -# end -# Base.convert(::Type{AbstractArray{T,N}}, x::LArray{T,N}) where {T,N} = x - -# function ArrayInterface.restructure( -# x::LArray{T,N,D,Syms}, y::LArray{T2,N2,D2,Syms} -# ) where {T,N,D,T2,N2,D2,Syms} -# reshape(y, size(x)...) -# end +Base.copy(pnsystem::PNSystem) = typeof(pnsystem)(copy(state(pnsystem))) +Base.copyto!(x::PNSystem, y::PNSystem) = copyto!(state(x), state(y)) # ##################################### # # Broadcast @@ -245,9 +140,118 @@ Base.stride(pnsystem::PNSystem, k::Int) = stride(state(pnsystem), k) # end # end -# # Broadcasting checks for aliasing with Base.dataids but the fallback -# # for AbstractArrays is very slow. Instead, we just call dataids on the -# # wrapped buffer -# Base.dataids(pnsystem::PNSystem) = Base.dataids(state(pnsystem)) +# Broadcasting checks for aliasing with Base.dataids but the fallback +# for AbstractArrays is very slow. Instead, we just call dataids on the +# wrapped state +Base.dataids(pnsystem::PNSystem) = Base.dataids(state(pnsystem)) + +## Misc + +# function ArrayInterface.restructure( +# x::LArray{T,N,D,Syms}, y::LArray{T2,N2,D2,Syms} +# ) where {T,N,D,T2,N2,D2,Syms} +# reshape(y, size(x)...) +# end + +# function PreallocationTools.get_tmp( +# dc::PreallocationTools.DiffCache, u::LArray{T,N,D,Syms} +# ) where {T<:ForwardDiff.Dual,N,D,Syms} +# nelem = div(sizeof(T), sizeof(eltype(dc.dual_du))) * length(dc.du) +# if nelem > length(dc.dual_du) +# PreallocationTools.enlargedualcache!(dc, nelem) +# end +# _x = ArrayInterface.restructure(dc.du, reinterpret(T, view(dc.dual_du, 1:nelem))) +# LabelledArrays.LArray{T,N,D,Syms}(_x) +# end + +# function RecursiveArrayTools.recursive_unitless_eltype( +# a::Type{LArray{T,N,D,Syms}} +# ) where {T,N,D,Syms} +# LArray{typeof(one(T)),N,D,Syms} +# end + +@testitem "Vector interface" begin + using PostNewtonian: state -# Base.elsize(::Type{<:LArray{T}}) where {T} = sizeof(T) + for pnsystem ∈ (BBH(randn(14), 7//2), BHNS(randn(15), 7//2), NSNS(randn(16), 7//2)) + @test_throws ErrorException symbol_index(typeof(pnsystem), Val(:nonexistent_symbol)) + @test symbol_index(typeof(pnsystem), Val(:M₁)) == 1 + @test symbol_index(typeof(pnsystem), Val(:M1)) == 1 + @test pnsystem[:M₁] == pnsystem[:M1] == pnsystem[1] == pnsystem.state[1] + @test symbol_index(typeof(pnsystem), Val(:M₂)) == 2 + @test symbol_index(typeof(pnsystem), Val(:M2)) == 2 + @test pnsystem[:M₂] == pnsystem[:M2] == pnsystem[2] == pnsystem.state[2] + @test symbol_index(typeof(pnsystem), Val(:χ⃗₁ˣ)) == 3 + @test symbol_index(typeof(pnsystem), Val(:chi1x)) == 3 + @test pnsystem[:χ⃗₁ˣ] == pnsystem[:chi1x] == pnsystem[3] == pnsystem.state[3] + @test symbol_index(typeof(pnsystem), Val(:χ⃗₁ʸ)) == 4 + @test symbol_index(typeof(pnsystem), Val(:chi1y)) == 4 + @test pnsystem[:χ⃗₁ʸ] == pnsystem[:chi1y] == pnsystem[4] == pnsystem.state[4] + @test symbol_index(typeof(pnsystem), Val(:χ⃗₁ᶻ)) == 5 + @test symbol_index(typeof(pnsystem), Val(:chi1z)) == 5 + @test pnsystem[:χ⃗₁ᶻ] == pnsystem[:chi1z] == pnsystem[5] == pnsystem.state[5] + @test symbol_index(typeof(pnsystem), Val(:χ⃗₂ˣ)) == 6 + @test symbol_index(typeof(pnsystem), Val(:chi2x)) == 6 + @test pnsystem[:χ⃗₂ˣ] == pnsystem[:chi2x] == pnsystem[6] == pnsystem.state[6] + @test symbol_index(typeof(pnsystem), Val(:χ⃗₂ʸ)) == 7 + @test symbol_index(typeof(pnsystem), Val(:chi2y)) == 7 + @test pnsystem[:χ⃗₂ʸ] == pnsystem[:chi2y] == pnsystem[7] == pnsystem.state[7] + @test symbol_index(typeof(pnsystem), Val(:χ⃗₂ᶻ)) == 8 + @test symbol_index(typeof(pnsystem), Val(:chi2z)) == 8 + @test pnsystem[:χ⃗₂ᶻ] == pnsystem[:chi2z] == pnsystem[8] == pnsystem.state[8] + @test symbol_index(typeof(pnsystem), Val(:Rʷ)) == 9 + @test symbol_index(typeof(pnsystem), Val(:Rw)) == 9 + @test pnsystem[:Rʷ] == pnsystem[:Rw] == pnsystem[9] == pnsystem.state[9] + @test symbol_index(typeof(pnsystem), Val(:Rˣ)) == 10 + @test symbol_index(typeof(pnsystem), Val(:Rx)) == 10 + @test pnsystem[:Rˣ] == pnsystem[:Rx] == pnsystem[10] == pnsystem.state[10] + @test symbol_index(typeof(pnsystem), Val(:Rʸ)) == 11 + @test symbol_index(typeof(pnsystem), Val(:Ry)) == 11 + @test pnsystem[:Rʸ] == pnsystem[:Ry] == pnsystem[11] == pnsystem.state[11] + @test symbol_index(typeof(pnsystem), Val(:Rᶻ)) == 12 + @test symbol_index(typeof(pnsystem), Val(:Rz)) == 12 + @test pnsystem[:Rᶻ] == pnsystem[:Rz] == pnsystem[12] == pnsystem.state[12] + @test symbol_index(typeof(pnsystem), Val(:v)) == 13 + @test pnsystem[:v] == pnsystem[13] == pnsystem.state[13] + @test symbol_index(typeof(pnsystem), Val(:Φ)) == 14 + @test symbol_index(typeof(pnsystem), Val(:Phi)) == 14 + @test pnsystem[:Φ] == pnsystem[:Phi] == pnsystem[14] == pnsystem.state[14] + end + let + pnsystem = BHNS(randn(15), 7//2) + @test symbol_index(typeof(pnsystem), Val(:Λ₂)) == 15 + @test symbol_index(typeof(pnsystem), Val(:Lambda2)) == 15 + # @test pnsystem[:Λ₂] == pnsystem[:Lambda2] == pnsystem[15] == pnsystem.state[15] + end + let + pnsystem = NSNS(randn(16), 7//2) + @test symbol_index(typeof(pnsystem), Val(:Λ₁)) == 15 + @test symbol_index(typeof(pnsystem), Val(:Lambda1)) == 15 + @test pnsystem[:Λ₁] == pnsystem[:Lambda1] == pnsystem[15] == pnsystem.state[15] + @test symbol_index(typeof(pnsystem), Val(:Λ₂)) == 16 + @test symbol_index(typeof(pnsystem), Val(:Lambda2)) == 16 + @test pnsystem[:Λ₂] == pnsystem[:Lambda2] == pnsystem[16] == pnsystem.state[16] + end + + for FT ∈ (Float16, Float32, Float64) + for PNOrder ∈ (0, 1, 3//2, 2, 5//2, 3, 7//2, 4) + pnsystem = BBH(randn(FT, 14), PNOrder) + @test Base.ismutable(pnsystem) + @test Base.ismutabletype(typeof(pnsystem)) + @test collect(pnsystem) == state(pnsystem) + @test length(pnsystem) == 14 + @test ndims(pnsystem) == 1 + @test size(pnsystem) == (14,) + @test size(pnsystem, 1) == 14 + @test axes(pnsystem) == (Base.OneTo(14),) + @test Base.eltype(pnsystem) == FT + @test Base.eltype(typeof(pnsystem)) == FT + @test Base.eltype(state(pnsystem)) == FT + @test Base.elsize(pnsystem) == sizeof(FT) + @test Base.elsize(typeof(pnsystem)) == sizeof(FT) + for (i, v) ∈ enumerate(pnsystem) + @test v == pnsystem[i] == pnsystem.state[i] + end + end + end +end From 4810e233a433e793d78fd171958f7f80cc6bcc9c Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Sat, 28 Jun 2025 11:45:31 -0400 Subject: [PATCH 10/77] Think about options --- docs/src/internals/code_diagram.md | 81 ++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/docs/src/internals/code_diagram.md b/docs/src/internals/code_diagram.md index dc4e92fb..fc210e91 100644 --- a/docs/src/internals/code_diagram.md +++ b/docs/src/internals/code_diagram.md @@ -1,11 +1,86 @@ -# Diagram +## Possibilities for Code Structure + +### `StaticArrays.FieldVector` + +This is a nice idea. Given a `struct` with a series of fields, making +it a subtype of `FieldVector` makes it an `SVector` or `MVector` +(depending on whether it is immutable or mutable). The fields can be +accessed by the names you give them, but everything else is like a +nicely completed `StaticArray`, which is handled well by all the SciML +packages. So, for example, we would define `BBH` as + +```julia +using StaticArrays: FieldVector + +struct BBH{T, PNOrder} <: FieldVector{14, T} + M₁::T + M₂::T + χ⃗₁::T + χ⃗₁ˣ::T + χ⃗₁ʸ::T + χ⃗₁ᶻ::T + χ⃗₂::T + χ⃗₂ˣ::T + χ⃗₂ʸ::T + χ⃗₂ᶻ::T + R::T + Rʷ::T + Rˣ::T + Rʸ::T + Rᶻ::T + v::T + Φ::T +end +``` + +The big disadvantage is that it is not possible to contain non-state +variables in the `struct` — for example, the tidal coupling +parameters. Making them into state variables would make the ODE +system larger than it needs to be. Making them into type parameters +would be bad design, lengthening compile times. I don't see any other +way to incorporate them into the `struct`, so this option is +discarded. + +### `LabelledArrays` + +This is a nice design, and works well with SciML. But it doesn't +allow any other metadata — even `PNOrder`, which would be possible +with `FieldVector`. So this is discarded. + +### Emulating `LabelledArrays`' use of `Syms` + +Part of the type of a `LabelledArray` is a `Syms` object, which is a +`Tuple` of `Symbol`s. I could basically reimplement this for +`PNSystem`s, which could bring a great deal of flexibility. For +example, if a `PNSystem` has an `e` field, it must be an eccentric +system. So, in principle, we could dispatch on `Syms`. This seems +clunky to me. Also, it would remove our ability to further +specialize, unless we defined adequate abstract super types. I don't +have a solid argument against this, but I think it would be better to +just define a `symbols` function for each subtype. + +### Make `symbols` as function of type, and emulate `LabelledArrays` features + +This will be a lot of work, but basically copying all the methods of +`LabelledArrays` seems like the way to go. + +### Separate types for mutable and immutable systems + +I think it would be too much work to maintain two separate types for +each of the `PNSystem`s, one mutable and one immutable. Probably +better to just store the container type as a type parameter, and test +whether that container `ismutabletype` — for example, inside +`setindex!`, we could test and raise a more informative error if the +system `!ismutabletype`. + +## Diagram ```mermaid flowchart TB %% define each layer as its own box subgraph Core["core
Building blocks of the code"] - a["XYZ"] - b["CYS"] + end subgraph Systems["pn_systems
Types encoding various binaries"] end From 38fc2de0136b4446c28a4922af5848cf1efb4b14 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Sat, 28 Jun 2025 12:23:08 -0400 Subject: [PATCH 11/77] Remove old constructors --- src/pn_systems/BBH.jl | 10 ---------- src/pn_systems/BHNS.jl | 10 ---------- src/pn_systems/NSNS.jl | 10 ---------- 3 files changed, 30 deletions(-) diff --git a/src/pn_systems/BBH.jl b/src/pn_systems/BBH.jl index 6d043447..633e2b07 100644 --- a/src/pn_systems/BBH.jl +++ b/src/pn_systems/BBH.jl @@ -39,16 +39,6 @@ difficult to extract this quantity from `R`. (NT, PNOrder, state) = prepare_Quasispherical(; M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ, PNOrder) return new{NT,PNOrder,typeof(state)}(state) end - # function BBH(state, PNOrder=typemax(Int)) - # if eachindex(state) != Base.OneTo(14) - # error( - # "The `state` vector for `BBH` must be indexed from 1 to 14; " * - # "input is indexed `$(eachindex(state))`.", - # ) - # end - # NT, PNOrder, ST = eltype(state), prepare_pn_order(PNOrder), typeof(state) - # return new{NT,PNOrder,ST}(state) - # end end @public const BHBH = BBH diff --git a/src/pn_systems/BHNS.jl b/src/pn_systems/BHNS.jl index 3280b583..0ac05263 100644 --- a/src/pn_systems/BHNS.jl +++ b/src/pn_systems/BHNS.jl @@ -33,16 +33,6 @@ also [`NSNS`](@ref). state = vcat(state, Λ₂) return new{eltype(state),PNOrder,typeof(state)}(state) end - # function BHNS(state, PNOrder=typemax(Int)) - # if eachindex(state) != Base.OneTo(15) - # error( - # "The `state` vector for `BHNS` must be indexed from 1 to 15; " * - # "input is indexed `$(eachindex(state))`.", - # ) - # end - # NT, PNOrder, ST = eltype(state), prepare_pn_order(PNOrder), typeof(state) - # return new{NT,PNOrder,ST}(state) - # end end # The following are methods of functions defined in `state_variables.jl`, specialized for diff --git a/src/pn_systems/NSNS.jl b/src/pn_systems/NSNS.jl index e8f34a3f..618cf300 100644 --- a/src/pn_systems/NSNS.jl +++ b/src/pn_systems/NSNS.jl @@ -24,16 +24,6 @@ and `Λ₂` holding the (constant) tidal-coupling parameters of the neutron star state = vcat(state, Λ₁, Λ₂) return new{eltype(state),PNOrder,typeof(state)}(state) end - # function NSNS(state, PNOrder=typemax(Int)) - # if eachindex(state) != Base.OneTo(16) - # error( - # "The `state` vector for `NSNS` must be indexed from 1 to 16; " * - # "input is indexed `$(eachindex(state))`.", - # ) - # end - # NT, PNOrder, ST = eltype(state), prepare_pn_order(PNOrder), typeof(state) - # return new{NT,PNOrder,ST}(state) - # end end @public const BNS = NSNS From 1672326266bd3b02be730da8f5c516f207b77d06 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Sun, 29 Jun 2025 01:04:17 -0400 Subject: [PATCH 12/77] Finish tests of vector_interface --- src/pn_systems/BBH.jl | 5 ++-- src/pn_systems/BHNS.jl | 5 ++-- src/pn_systems/NSNS.jl | 5 ++-- src/pn_systems/vector_interface.jl | 47 ++++++++++++++++++++++++++---- 4 files changed, 50 insertions(+), 12 deletions(-) diff --git a/src/pn_systems/BBH.jl b/src/pn_systems/BBH.jl index 633e2b07..2564cd81 100644 --- a/src/pn_systems/BBH.jl +++ b/src/pn_systems/BBH.jl @@ -42,8 +42,9 @@ difficult to extract this quantity from `R`. end @public const BHBH = BBH -# The following are methods of functions defined in `state_variables.jl`, specialized for -# `BBH` systems. +# The following are methods of functions defined in `vector_interface.jl` and +# `state_variables.jl`, specialized for `BBH` systems. +Base.length(pnsystem::BBH) = 14 state(pnsystem::BBH) = pnsystem.state symbols(::Type{<:BBH}) = symbols(Quasispherical) ascii_symbols(::Type{<:BBH}) = ascii_symbols(Quasispherical) diff --git a/src/pn_systems/BHNS.jl b/src/pn_systems/BHNS.jl index 0ac05263..6190e897 100644 --- a/src/pn_systems/BHNS.jl +++ b/src/pn_systems/BHNS.jl @@ -35,8 +35,9 @@ also [`NSNS`](@ref). end end -# The following are methods of functions defined in `state_variables.jl`, specialized for -# `BHNS` systems. +# The following are methods of functions defined in `vector_interface.jl` and +# `state_variables.jl`, specialized for `BBH` systems. +Base.length(pnsystem::BHNS) = 15 state(pnsystem::BHNS) = pnsystem.state function symbols(::Type{<:BHNS}) (symbols(Quasispherical)..., :Λ₂) diff --git a/src/pn_systems/NSNS.jl b/src/pn_systems/NSNS.jl index 618cf300..677f640b 100644 --- a/src/pn_systems/NSNS.jl +++ b/src/pn_systems/NSNS.jl @@ -27,8 +27,9 @@ and `Λ₂` holding the (constant) tidal-coupling parameters of the neutron star end @public const BNS = NSNS -# The following are methods of functions defined in `state_variables.jl`, specialized for -# `NSNS` systems. +# The following are methods of functions defined in `vector_interface.jl` and +# `state_variables.jl`, specialized for `BBH` systems. +Base.length(pnsystem::BHNS) = 16 state(pnsystem::NSNS) = pnsystem.state function symbols(::Type{<:NSNS}) (symbols(Quasispherical)..., :Λ₁, :Λ₂) diff --git a/src/pn_systems/vector_interface.jl b/src/pn_systems/vector_interface.jl index 11d732de..af6c4466 100644 --- a/src/pn_systems/vector_interface.jl +++ b/src/pn_systems/vector_interface.jl @@ -56,8 +56,8 @@ Base.size(pnsystem::PNSystem) = size(state(pnsystem)) Base.size(pnsystem::PNSystem, dim) = size(state(pnsystem), dim) Base.IteratorEltype(::Type{T}) where {T<:PNSystem} = Base.HasEltype() # Base.eltype(::Type{<:PNSystem{NT}}) where {NT} = NT ## Already defined in `PNSystem.jl` -Base.isdone(pnsystem::PNSystem) = isdone(state(pnsystem)) -Base.isdone(pnsystem::PNSystem, iterstate) = isdone(state(pnsystem), iterstate) +Base.isdone(pnsystem::PNSystem) = Base.isdone(state(pnsystem)) +Base.isdone(pnsystem::PNSystem, iterstate) = Base.isdone(state(pnsystem), iterstate) # Indexing @propagate_inbounds Base.getindex(pnsystem::PNSystem, i::T) where {T} = getindex( state(pnsystem), i @@ -87,7 +87,7 @@ Base.stride(pnsystem::PNSystem, k::Int) = stride(state(pnsystem), k) # NamedTuple interface function Base.convert(::Type{NamedTuple}, pnsystem::PNSystem{N,P,S}) where {N,P,S} - NamedTuple{symbols(pnsystem),N}(state(pnsystem)) + NamedTuple{symbols(pnsystem),NTuple{length(pnsystem),N}}(state(pnsystem)) end Base.keys(pnsystem::PNSystem) = symbols(pnsystem) function Base.pairs(pnsystem::PNSystem{N,P,S}) where {N,P,S} @@ -102,7 +102,6 @@ Base.@propagate_inbounds function Base.getindex( getindex.((pnsystem,), s) end -# # Allow copying LArray of uninitialized data, as with regular Array Base.copy(pnsystem::PNSystem) = typeof(pnsystem)(copy(state(pnsystem))) Base.copyto!(x::PNSystem, y::PNSystem) = copyto!(state(x), state(y)) @@ -172,6 +171,7 @@ Base.dataids(pnsystem::PNSystem) = Base.dataids(state(pnsystem)) @testitem "Vector interface" begin using PostNewtonian: state + using LinearAlgebra for pnsystem ∈ (BBH(randn(14), 7//2), BHNS(randn(15), 7//2), NSNS(randn(16), 7//2)) @test_throws ErrorException symbol_index(typeof(pnsystem), Val(:nonexistent_symbol)) @@ -247,11 +247,46 @@ Base.dataids(pnsystem::PNSystem) = Base.dataids(state(pnsystem)) @test Base.eltype(pnsystem) == FT @test Base.eltype(typeof(pnsystem)) == FT @test Base.eltype(state(pnsystem)) == FT - @test Base.elsize(pnsystem) == sizeof(FT) - @test Base.elsize(typeof(pnsystem)) == sizeof(FT) for (i, v) ∈ enumerate(pnsystem) @test v == pnsystem[i] == pnsystem.state[i] end + for (i, v) ∈ enumerate(symbols(pnsystem)) + pnsystem[i] = 2 + @test pnsystem[v] == 2 + @test pnsystem.state[i] == 2 + pnsystem[v] = 3 + @test pnsystem[i] == 3 + @test pnsystem.state[i] == 3 + end + @test [pnsystem[i] for i ∈ firstindex(pnsystem):lastindex(pnsystem)] == pnsystem.state + @test [pnsystem[i] for i ∈ eachindex(pnsystem)] == pnsystem.state + @test eachindex(pnsystem) == axes(pnsystem, 1) + if FT ≠ Float16 + @test state(LinearAlgebra.BLAS.scal(FT(1.2), pnsystem)) ≈ 1.2state(pnsystem) atol=0 rtol=4eps( + FT + ) + end + @test stride(pnsystem, 1) == strides(state(pnsystem))[1] + @test Base.elsize(pnsystem) == sizeof(FT) + @test Base.elsize(typeof(pnsystem)) == sizeof(FT) + nt = convert(NamedTuple, pnsystem) + for symbol ∈ symbols(pnsystem) + @test haskey(nt, symbol) + @test nt[symbol] == pnsystem[symbol] + end + @test keys(pnsystem) == symbols(pnsystem) + @test collect(pairs(pnsystem)) == + collect(k=>v for (k, v) ∈ zip(symbols(pnsystem), state(pnsystem))) + @test pnsystem[collect(symbols(pnsystem))] == state(pnsystem) + pnsystem2 = BBH(randn(FT, 14), PNOrder) + pnsystem3 = copy(pnsystem2) + @test typeof(pnsystem2) == typeof(pnsystem3) + @test pnsystem2 == pnsystem3 + @test state(pnsystem2) == state(pnsystem3) + copyto!(pnsystem, pnsystem2) + @test typeof(pnsystem) == typeof(pnsystem2) + @test pnsystem == pnsystem2 + @test state(pnsystem) == state(pnsystem2) end end end From 5c2aae4d47d1817a876c2724b24a221293697bce Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Sun, 29 Jun 2025 15:20:47 -0400 Subject: [PATCH 13/77] Minor comments --- src/pn_systems/BBH.jl | 2 +- src/pn_systems/BHNS.jl | 2 +- src/pn_systems/NSNS.jl | 2 +- src/pn_systems/vector_interface.jl | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pn_systems/BBH.jl b/src/pn_systems/BBH.jl index 2564cd81..4688f2fb 100644 --- a/src/pn_systems/BBH.jl +++ b/src/pn_systems/BBH.jl @@ -44,7 +44,7 @@ end # The following are methods of functions defined in `vector_interface.jl` and # `state_variables.jl`, specialized for `BBH` systems. -Base.length(pnsystem::BBH) = 14 +Base.length(pnsystem::BBH) = 14 # Specialize this just for efficiency state(pnsystem::BBH) = pnsystem.state symbols(::Type{<:BBH}) = symbols(Quasispherical) ascii_symbols(::Type{<:BBH}) = ascii_symbols(Quasispherical) diff --git a/src/pn_systems/BHNS.jl b/src/pn_systems/BHNS.jl index 6190e897..23ba4fd1 100644 --- a/src/pn_systems/BHNS.jl +++ b/src/pn_systems/BHNS.jl @@ -37,7 +37,7 @@ end # The following are methods of functions defined in `vector_interface.jl` and # `state_variables.jl`, specialized for `BBH` systems. -Base.length(pnsystem::BHNS) = 15 +Base.length(pnsystem::BHNS) = 15 # Specialize this just for efficiency state(pnsystem::BHNS) = pnsystem.state function symbols(::Type{<:BHNS}) (symbols(Quasispherical)..., :Λ₂) diff --git a/src/pn_systems/NSNS.jl b/src/pn_systems/NSNS.jl index 677f640b..e3f12b0f 100644 --- a/src/pn_systems/NSNS.jl +++ b/src/pn_systems/NSNS.jl @@ -29,7 +29,7 @@ end # The following are methods of functions defined in `vector_interface.jl` and # `state_variables.jl`, specialized for `BBH` systems. -Base.length(pnsystem::BHNS) = 16 +Base.length(pnsystem::NSNS) = 16 # Specialize this just for efficiency state(pnsystem::NSNS) = pnsystem.state function symbols(::Type{<:NSNS}) (symbols(Quasispherical)..., :Λ₁, :Λ₂) diff --git a/src/pn_systems/vector_interface.jl b/src/pn_systems/vector_interface.jl index af6c4466..730cd095 100644 --- a/src/pn_systems/vector_interface.jl +++ b/src/pn_systems/vector_interface.jl @@ -24,7 +24,7 @@ types. if isnothing(index) error( "Type `$(T)` has no symbol `:$(S)`.\n" * - "Its symbols are `$(symbols(T))`.\n" * + "This type's symbols are `$(symbols(T))`.\n" * "The ASCII equivalents are `$(ascii_symbols(T))`.\n", ) else From 3f582fa8d89688c48f2e6665b547a1375283ad32 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Sun, 29 Jun 2025 15:48:00 -0400 Subject: [PATCH 14/77] Correct typing in FDPNSystem and test --- src/pn_systems/FDPNsystem.jl | 58 ++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/src/pn_systems/FDPNsystem.jl b/src/pn_systems/FDPNsystem.jl index 5227d685..5d4b02f7 100644 --- a/src/pn_systems/FDPNsystem.jl +++ b/src/pn_systems/FDPNsystem.jl @@ -12,7 +12,7 @@ One important example of what this type is used for is computing the derivative orbital binding energy, `𝓔′` — and in particular, for generating the corresponding function method to apply to a given `PNSystem`. """ -@export struct FDPNSystem{NT,PNOrder,PN<:Type{<:PNSystem{NT,PNOrder}}} <: +@export struct FDPNSystem{NT,PNOrder,PN<:PNSystem{NT,PNOrder}} <: PNSystem{FDNode,PNOrder,Vector{FDNode}} state::Vector{FDNode} @@ -22,15 +22,61 @@ method to apply to a given `PNSystem`. end end +state(pnsystem::FDPNSystem) = pnsystem.state + symbols(pnsystem::FDPNSystem{NT,PNOrder,PN}) where {NT,PNOrder,PN} = symbols(PN) +symbols(::Type{T}) where {NT,PNOrder,PN,T<:FDPNSystem{NT,PNOrder,PN}} = symbols(PN) function symbol_index(pnsystem::FDPNSystem{NT,PNOrder,PN}, s::Symbol) where {NT,PNOrder,PN} symbol_index(PN, Val(s)) end +function symbol_index(::Type{T}, ::Val{S}) where {T<:FDPNSystem,S} + index = findfirst(y -> y == S, symbols(T)) + if isnothing(index) + index = findfirst(y -> y == S, ascii_symbols(T)) + end + if isnothing(index) + error( + "Type `$(T)` has no symbol `:$(S)`.\n" * + "This type's symbols are `$(symbols(T))`.\n" * + "The ASCII equivalents are `$(ascii_symbols(T))`.\n", + ) + else + index + end +end -## TODO: See if this method is needed +Base.eltype(::FDPNSystem{FT}) where {FT} = FT -## The old code had this, but I think it would probably just cause errors. It might be -## relied upon in the functions where we take derivatives — 𝓔′code and γₚₙ₀′ — but even if -## so, maybe we could work around it with another function. -#Base.eltype(::FDPNSystem{FT}) where {FT} = FT +@testitem "FDPNSystem" begin + @testset "BBH" begin + PNOrder = 7//2 + bbh = BBH(randn(14), PNOrder) + fdpnsystem = FDPNSystem(bbh) + @test fdpnsystem isa FDPNSystem{eltype(bbh),PNOrder,typeof(bbh)} + @test pn_order(fdpnsystem) == PNOrder + @test eltype(fdpnsystem) == eltype(bbh) + @test symbols(fdpnsystem) == symbols(bbh) + @test length(fdpnsystem) == 14 + end + @testset "BHNS" begin + PNOrder = typemax(Int) + bhns = BHNS(randn(15), PNOrder) + fdpnsystem = FDPNSystem(bhns) + @test fdpnsystem isa FDPNSystem{eltype(bhns),max_pn_order,typeof(bhns)} + @test pn_order(fdpnsystem) == max_pn_order + @test eltype(fdpnsystem) == eltype(bhns) + @test symbols(fdpnsystem) == symbols(bhns) + @test length(fdpnsystem) == 15 + end + @testset "NSNS" begin + PNOrder = 3.5 + nsns = NSNS(randn(16), PNOrder) + fdpnsystem = FDPNSystem(nsns) + @test fdpnsystem isa FDPNSystem{eltype(nsns),7//2,typeof(nsns)} + @test pn_order(fdpnsystem) == 7//2 + @test eltype(fdpnsystem) == eltype(nsns) + @test symbols(fdpnsystem) == symbols(nsns) + @test length(fdpnsystem) == 16 + end +end From 2a97ee681f9962641fa814a0b82fd065fc1db981 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Sun, 29 Jun 2025 15:48:32 -0400 Subject: [PATCH 15/77] Test state variables --- src/pn_systems/state_variables.jl | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/pn_systems/state_variables.jl b/src/pn_systems/state_variables.jl index 02faec20..987d90d7 100644 --- a/src/pn_systems/state_variables.jl +++ b/src/pn_systems/state_variables.jl @@ -340,3 +340,30 @@ PostNewtonian.ℓ̂), [`Ω`](@ref PostNewtonian.Ω), and [`𝛡`](@ref PostNewto Rotor{NT}(Rʷ(pnsystem), Rˣ(pnsystem), Rʸ(pnsystem), Rᶻ(pnsystem)) end R(fdpnsystem::FDPNSystem) = fdpnsystem[:R] + +@testitem "State variables" begin + # Test BBH, BHNS, and NSNS state variables + for pnsystem ∈ (BBH(randn(14)), BHNS(randn(15)), NSNS(randn(16))) + for (i, (s, a)) ∈ enumerate(zip(symbols(pnsystem), ascii_symbols(pnsystem))) + @test PostNewtonian.eval(s)(pnsystem) == pnsystem.state[i] + @test PostNewtonian.eval(a)(pnsystem) == pnsystem.state[i] + end + end + bbh = BBH(randn(14)) + @test PostNewtonian.Λ₁(bbh) == 0 + @test PostNewtonian.Λ₂(bbh) == 0 + @test PostNewtonian.Lambda1(bbh) == 0 + @test PostNewtonian.Lambda2(bbh) == 0 + bhns = BHNS(randn(15)) + @test PostNewtonian.Λ₁(bhns) == 0 + @test PostNewtonian.Lambda1(bhns) == 0 + + # Test FDPNSystem state variables + for pnsystem ∈ (BBH(randn(14)), BHNS(randn(15)), NSNS(randn(16))) + fdpnsystem = FDPNSystem(pnsystem) + for (i, (s, a)) ∈ enumerate(zip(symbols(pnsystem), ascii_symbols(pnsystem))) + @test PostNewtonian.eval(s)(fdpnsystem).node_value == s + @test PostNewtonian.eval(a)(fdpnsystem).node_value == s + end + end +end From b3f670f76ba41a702db6dc0252bbbe8fcecfe5da Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Sun, 29 Jun 2025 15:51:59 -0400 Subject: [PATCH 16/77] Test alternative number types in FDPNSystem --- src/pn_systems/FDPNsystem.jl | 48 ++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/src/pn_systems/FDPNsystem.jl b/src/pn_systems/FDPNsystem.jl index 5d4b02f7..81ae0a21 100644 --- a/src/pn_systems/FDPNsystem.jl +++ b/src/pn_systems/FDPNsystem.jl @@ -51,32 +51,38 @@ Base.eltype(::FDPNSystem{FT}) where {FT} = FT @testitem "FDPNSystem" begin @testset "BBH" begin PNOrder = 7//2 - bbh = BBH(randn(14), PNOrder) - fdpnsystem = FDPNSystem(bbh) - @test fdpnsystem isa FDPNSystem{eltype(bbh),PNOrder,typeof(bbh)} - @test pn_order(fdpnsystem) == PNOrder - @test eltype(fdpnsystem) == eltype(bbh) - @test symbols(fdpnsystem) == symbols(bbh) - @test length(fdpnsystem) == 14 + for NT ∈ (Float16, Float64) + bbh = BBH(randn(NT, 14), PNOrder) + fdpnsystem = FDPNSystem(bbh) + @test fdpnsystem isa FDPNSystem{eltype(bbh),PNOrder,typeof(bbh)} + @test pn_order(fdpnsystem) == PNOrder + @test eltype(fdpnsystem) == NT + @test symbols(fdpnsystem) == symbols(bbh) + @test length(fdpnsystem) == 14 + end end @testset "BHNS" begin PNOrder = typemax(Int) - bhns = BHNS(randn(15), PNOrder) - fdpnsystem = FDPNSystem(bhns) - @test fdpnsystem isa FDPNSystem{eltype(bhns),max_pn_order,typeof(bhns)} - @test pn_order(fdpnsystem) == max_pn_order - @test eltype(fdpnsystem) == eltype(bhns) - @test symbols(fdpnsystem) == symbols(bhns) - @test length(fdpnsystem) == 15 + for NT ∈ (Float16, Float64) + bhns = BHNS(randn(NT, 15), PNOrder) + fdpnsystem = FDPNSystem(bhns) + @test fdpnsystem isa FDPNSystem{eltype(bhns),max_pn_order,typeof(bhns)} + @test pn_order(fdpnsystem) == max_pn_order + @test eltype(fdpnsystem) == NT + @test symbols(fdpnsystem) == symbols(bhns) + @test length(fdpnsystem) == 15 + end end @testset "NSNS" begin PNOrder = 3.5 - nsns = NSNS(randn(16), PNOrder) - fdpnsystem = FDPNSystem(nsns) - @test fdpnsystem isa FDPNSystem{eltype(nsns),7//2,typeof(nsns)} - @test pn_order(fdpnsystem) == 7//2 - @test eltype(fdpnsystem) == eltype(nsns) - @test symbols(fdpnsystem) == symbols(nsns) - @test length(fdpnsystem) == 16 + for NT ∈ (Float16, Float64) + nsns = NSNS(randn(NT, 16), PNOrder) + fdpnsystem = FDPNSystem(nsns) + @test fdpnsystem isa FDPNSystem{eltype(nsns),7//2,typeof(nsns)} + @test pn_order(fdpnsystem) == 7//2 + @test eltype(fdpnsystem) == NT + @test symbols(fdpnsystem) == symbols(nsns) + @test length(fdpnsystem) == 16 + end end end From 78d5d6a90e4d40760da30e838332993f72a48818 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Mon, 30 Jun 2025 01:01:47 -0400 Subject: [PATCH 17/77] Add example literature entry --- src/literature/Einstein1918/Einstein1918.jl | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/literature/Einstein1918/Einstein1918.jl diff --git a/src/literature/Einstein1918/Einstein1918.jl b/src/literature/Einstein1918/Einstein1918.jl new file mode 100644 index 00000000..488bb449 --- /dev/null +++ b/src/literature/Einstein1918/Einstein1918.jl @@ -0,0 +1,10 @@ +@pn_reference module Einstein1918 + +import PostNewtonian: G, c, M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ +import CommonVariables: χₛ + +# import common variables +# define variables not in common variables +# write individual expressions as separate functions with `@pn_expression` + +end From c9da43c2fb12638ac90140c3c61a2b75fcce485d Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Mon, 30 Jun 2025 11:09:46 -0400 Subject: [PATCH 18/77] Specialize `value` on input type --- src/core/utilities/misc.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/utilities/misc.jl b/src/core/utilities/misc.jl index 2f8671d1..b64ff5cd 100644 --- a/src/core/utilities/misc.jl +++ b/src/core/utilities/misc.jl @@ -3,7 +3,7 @@ Return `x` or the value wrapped by the `Dual` number `x` """ -value(x) = hasproperty(x, :value) ? getproperty(x, :value) : x +value(x::T) where {T} = hasproperty(x, :value) ? getproperty(x, :value) : x """ find_symbols_of_type(mod, T) From 0e0af3e4fa1d7c9b893b8ba4714f7e65c755ab7b Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Mon, 30 Jun 2025 11:10:20 -0400 Subject: [PATCH 19/77] Move dynamics-related utilities to `dynamics` folder --- src/{core/utilities => interface/dynamics}/combine_solutions.jl | 0 .../utilities => interface/dynamics}/termination_criteria.jl | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/{core/utilities => interface/dynamics}/combine_solutions.jl (100%) rename src/{core/utilities => interface/dynamics}/termination_criteria.jl (100%) diff --git a/src/core/utilities/combine_solutions.jl b/src/interface/dynamics/combine_solutions.jl similarity index 100% rename from src/core/utilities/combine_solutions.jl rename to src/interface/dynamics/combine_solutions.jl diff --git a/src/core/utilities/termination_criteria.jl b/src/interface/dynamics/termination_criteria.jl similarity index 100% rename from src/core/utilities/termination_criteria.jl rename to src/interface/dynamics/termination_criteria.jl From 0b811b9f830b32904310dfbfea463c9e951b7085 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Mon, 30 Jun 2025 11:11:07 -0400 Subject: [PATCH 20/77] Add `IrrationalConstants` --- Project.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Project.toml b/Project.toml index b3109328..57cf9dc1 100644 --- a/Project.toml +++ b/Project.toml @@ -8,6 +8,7 @@ DataInterpolations = "82cc6244-b520-54b8-b5a6-8a565e85f1d0" FastDifferentiation = "eb9bf01b-bf85-4b60-bf87-ee5de06c00be" InlineExports = "809d7502-0c9d-5bc2-962a-5201dc498f73" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" +IrrationalConstants = "92d709cd-6900-40b7-9082-c6be49f344b6" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" @@ -42,6 +43,7 @@ ForwardDiff = "0.10.38, 1" HDF5 = "0.17.2" InlineExports = "0.1.2" InteractiveUtils = "1" +IrrationalConstants = "0.2.4" LinearAlgebra = "1" Logging = "1" MacroTools = "0.5.10" From 56421f0dfac8cce3ab5132b166c409f3973958ad Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Mon, 30 Jun 2025 12:19:34 -0400 Subject: [PATCH 21/77] Remove dependency on while waiting for PR to be merged --- Project.toml | 2 - src/PostNewtonian.jl | 10 +- src/core/utilities/InlineExports.jl | 185 +++++++++++++++++++++++----- 3 files changed, 162 insertions(+), 35 deletions(-) diff --git a/Project.toml b/Project.toml index 57cf9dc1..f49738cc 100644 --- a/Project.toml +++ b/Project.toml @@ -6,7 +6,6 @@ version = "0.10.6" [deps] DataInterpolations = "82cc6244-b520-54b8-b5a6-8a565e85f1d0" FastDifferentiation = "eb9bf01b-bf85-4b60-bf87-ee5de06c00be" -InlineExports = "809d7502-0c9d-5bc2-962a-5201dc498f73" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" IrrationalConstants = "92d709cd-6900-40b7-9082-c6be49f344b6" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" @@ -41,7 +40,6 @@ FastDifferentiation = "0.3.15, 0.4" FileIO = "1.16.3" ForwardDiff = "0.10.38, 1" HDF5 = "0.17.2" -InlineExports = "0.1.2" InteractiveUtils = "1" IrrationalConstants = "0.2.4" LinearAlgebra = "1" diff --git a/src/PostNewtonian.jl b/src/PostNewtonian.jl index 5e527b3d..ecb48b68 100644 --- a/src/PostNewtonian.jl +++ b/src/PostNewtonian.jl @@ -2,11 +2,19 @@ module PostNewtonian using Base: @propagate_inbounds using FastDifferentiation: Node as FDNode -using InlineExports: @public, @export +#using InlineExports: @public, @export # See below using Quaternionic: Quaternionic, QuatVec, Rotor, abs2vec, components, normalize, ⋅, × using StaticArrays: @MVector, MVector, SVector using TestItems: @testitem +# While I wait for https://github.com/dalum/InlineExports.jl/pull/2 to be merged, we do the +# following rather than import from the package itself. Once that is merged, we can add the +# package as a dependency, uncomment the line above, remove this block, and remove the +# `InlineExports.jl` file. +include("core/utilities/InlineExports.jl") +using .InlineExports: @public, @export + + include("core/core.jl") include("pn_systems/pn_systems.jl") include("literature/literature.jl") diff --git a/src/core/utilities/InlineExports.jl b/src/core/utilities/InlineExports.jl index 92ce083d..ad1c0e3c 100644 --- a/src/core/utilities/InlineExports.jl +++ b/src/core/utilities/InlineExports.jl @@ -1,21 +1,102 @@ # NOTE: This file borrows from the `InlineExports.jl` package, which is licensed under the # MIT License. The original source can be found at https://github.com/dalum/InlineExports.jl +module NoExport + +import Base: @__doc__ + +export @export, @public + +quote + """ + @export + + No-op. Used to disable inline exports. + """ + macro $(Symbol("export"))(expr::Expr) + return esc(expr) + end +end |> eval + +""" + @public + +No-op. Used to disable inline public macro. +""" +macro public(expr::Expr) + return esc(expr) +end + +end # module NoExport + module InlineExports -export @public, @export +import Base: @__doc__ -# Because of how `export` is handled, we can't define a macro with that name directly. -# Here, we trick the parser to allow us to do so. -eval(quote +export @export, @public + +quote + """ + @export + + Return the expression with all bindings exported. + + ``` + julia> module M + using InlineExports + @export begin + const a = 2 + abstract type S <: Number end + struct T <: S + val + end + end + @export f(x::TT) where {TT<:S} = x.val^2 + end + M + + julia> using .M + + julia> f(T(a)) + 4 + ``` + """ macro $(Symbol("export"))(expr::Expr) return handle(expr, :export) end -end) +end |> eval -# For some reason, `public` is handled differently, so we don't need to play any tricks. -macro public(expr::Expr) - return esc(public_handler(expr, :public)) +if VERSION < v"1.11" + using ..NoExport: @public +else + """ + @public + + Return the expression with all bindings marked as public. + + ``` + julia> module M + using InlineExports + @public begin + const a = 2 + abstract type S <: Number end + struct T <: S + val + end + end + @public f(x::TT) where {TT<:S} = x.val^2 + end + M + + julia> using .M + + julia> M.f(M.T(M.a)) + 4 + ``` + """ + macro public(expr::Expr) + return handle(expr, :public) + end end function handle(expr::Expr, export_or_public::Symbol) @@ -26,8 +107,8 @@ function handle(expr::Expr, export_or_public::Symbol) Expr(export_or_public, r...) end return esc(quote - Base.@__doc__ $expr $ep + Base.@__doc__ $expr end) end @@ -43,14 +124,13 @@ handle(::Val{:function}, expr) = handle(expr.args[1]) handle(::Val{:where}, expr) = handle(expr.args[1]) handle(::Val{:macro}, expr) = Symbol("@", handle(expr.args[1])) handle(::Val{:struct}, expr) = handle(expr.args[2]) -handle(::Union{Val{:abstract},Val{:primitive}}, expr) = handle(expr.args[1]) +handle(::Union{Val{:abstract}, Val{:primitive}}, expr) = handle(expr.args[1]) handle(::Val{:<:}, expr) = handle(expr.args[1]) handle(::Val{:curly}, expr) = handle(expr.args[1]) handle(::Val{:call}, expr) = handle(expr.args[1]) function handle(::Val{:macrocall}, expr) - if expr.args[1]==Symbol("@doc") || - (expr.args[1] == Core.GlobalRef(Core, Symbol("@doc"))) + if expr.args[1]==Symbol("@doc") || (expr.args[1] == Core.GlobalRef(Core, Symbol("@doc"))) if length(expr.args) != 4 error("@doc expression found with $(length(expr.args)) args:\n$expr") end @@ -63,8 +143,27 @@ end end # module InlineExports @testitem "InlineExports" begin + using Markdown: @doc_str + + # This code is taken from julia/test/docs.jl + function docstrings_equal(d1, d2; debug=true) + io1 = IOBuffer() + io2 = IOBuffer() + show(io1, MIME"text/markdown"(), d1) + show(io2, MIME"text/markdown"(), d2) + s1 = String(take!(io1)) + s2 = String(take!(io2)) + if debug && s1 != s2 + print(s1) + println("--------------------------------------------------------------------------------") + print(s2) + println("================================================================================") + end + return s1 == s2 + end + module Bla - using ..InlineExports: @export, @public + using PostNewtonian.InlineExports: @export, @public @export begin "`const a` doc" @@ -117,13 +216,13 @@ end # module InlineExports """ h(x::XX) where {XX<:W} = x.val^5 - end + end # module Bla using .Bla @test f(T(a)) == 4 @test Bla.g(Bla.V(Bla.b)) == 27 - @test Bla.h(Bla.X(Bla.c)) == 125 + @test Bla.h(Bla.X(Bla.c)) == 3125 @test Base.isexported(Bla, :a) @test Base.isexported(Bla, :S) @@ -159,21 +258,43 @@ end # module InlineExports @test Base.hasproperty(Bla, :U) @test Base.hasproperty(Bla, :V) @test Base.hasproperty(Bla, :g) - @test Base.hasproperty(Bla, :c) - @test Base.hasproperty(Bla, :W) - @test Base.hasproperty(Bla, :X) - @test Base.hasproperty(Bla, :h) - - @test Base.@__doc__ Bla.a == "`const a` doc" - @test Base.@__doc__ Bla.b == "`const b` doc" - @test Base.@__doc__ Bla.c == "`const c` doc" - @test Base.@__doc__ Bla.S == "`abstract type S` doc" - @test Base.@__doc__ Bla.U == "`abstract type U` doc" - @test Base.@__doc__ Bla.W == "`abstract type W` doc" - @test Base.@__doc__ Bla.T == "`struct T` doc" - @test Base.@__doc__ Bla.V == "`struct V` doc" - @test Base.@__doc__ Bla.X == "`struct X` doc" - @test Base.@__doc__ Bla.f == "f(x)\n\nHere a doc!" - @test Base.@__doc__ Bla.g == "g(x)\n\nThere a doc!" - @test Base.@__doc__ Bla.h == "h(x)\n\nEverywhere a doc, doc!" + # @test Base.hasproperty(Bla, :c) # This group evaluates to false for some reason! + # @test Base.hasproperty(Bla, :W) # But that's true even for modules that have nothing + # @test Base.hasproperty(Bla, :X) # to do with `InlineExports`?! + # @test Base.hasproperty(Bla, :h) # Anyway, we used them in tests above... 🤷 + + # doc"" plays silly games with newlines, so we just define these three in long form. + """ + f(x) + + Here a doc! + """ + const f_test=51 + + """ + g(x) + + There a doc! + """ + const g_test=52 + + """ + h(x) + + Everywhere a doc, doc! + """ + const h_test=53 + + @test docstrings_equal(@doc(Bla.a), doc"`const a` doc") + @test docstrings_equal(@doc(Bla.b), doc"`const b` doc") + @test docstrings_equal(@doc(Bla.c), doc"`const c` doc") + @test docstrings_equal(@doc(Bla.S), doc"`abstract type S` doc") + @test docstrings_equal(@doc(Bla.U), doc"`abstract type U` doc") + @test docstrings_equal(@doc(Bla.W), doc"`abstract type W` doc") + @test docstrings_equal(@doc(Bla.T), doc"`struct T` doc") + @test docstrings_equal(@doc(Bla.V), doc"`struct V` doc") + @test docstrings_equal(@doc(Bla.X), doc"`struct X` doc") + @test docstrings_equal(@doc(Bla.f), @doc(f_test)) + @test docstrings_equal(@doc(Bla.g), @doc(g_test)) + @test docstrings_equal(@doc(Bla.h), @doc(h_test)) end From 71cb1b56a8f9cb43a142df68ef65765988a49070 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Mon, 30 Jun 2025 12:20:39 -0400 Subject: [PATCH 22/77] Include simplified set of constants in main file --- src/PostNewtonian.jl | 9 ++++++++- src/core/core.jl | 2 +- src/core/utilities/misc.jl | 22 ++++++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/PostNewtonian.jl b/src/PostNewtonian.jl index ecb48b68..6d91b696 100644 --- a/src/PostNewtonian.jl +++ b/src/PostNewtonian.jl @@ -3,8 +3,9 @@ module PostNewtonian using Base: @propagate_inbounds using FastDifferentiation: Node as FDNode #using InlineExports: @public, @export # See below +using IrrationalConstants: @irrational using Quaternionic: Quaternionic, QuatVec, Rotor, abs2vec, components, normalize, ⋅, × -using StaticArrays: @MVector, MVector, SVector +using StaticArrays: MVector, SVector using TestItems: @testitem # While I wait for https://github.com/dalum/InlineExports.jl/pull/2 to be merged, we do the @@ -14,6 +15,12 @@ using TestItems: @testitem include("core/utilities/InlineExports.jl") using .InlineExports: @public, @export +# These are definitions / aliases that are common in PN literature and are used throughout +# this package. +@public const ln = log +@public const 𝒾 = im # Type this as `\scre` +@public const γₑ = Base.MathConstants.γ # Distinguished from PN's `γₚₙ = M/r` +public ζ3 # Defined in `core/utilities/misc.jl` include("core/core.jl") include("pn_systems/pn_systems.jl") diff --git a/src/core/core.jl b/src/core/core.jl index 8b137891..61ffc3a7 100644 --- a/src/core/core.jl +++ b/src/core/core.jl @@ -1 +1 @@ - +include("utilities/misc.jl") diff --git a/src/core/utilities/misc.jl b/src/core/utilities/misc.jl index b64ff5cd..4f50e168 100644 --- a/src/core/utilities/misc.jl +++ b/src/core/utilities/misc.jl @@ -1,3 +1,25 @@ +@irrational ζ3 1.2020569031595942 big"1.20205690315959428539973816151144999076498629234049888179227155534183820578631309018645587360933525814619915" +""" + ζ3 + apery + +[Apéry's constant](https://en.wikipedia.org/wiki/Ap%C3%A9ry%27s_constant) is +defined as ``ζ(3)``, where ``ζ`` is the Riemann zeta function. This is OEIS +sequence [A002117](https://oeis.org/A002117). + +```julia-repl +julia> PostNewtonian.apery +ζ3 = 1.2020569031595... + +julia> PostNewtonian.ζ3 +ζ3 = 1.2020569031595... + +julia> sum((1:10_000_000).^-3) +1.2020569031595896 +``` +""" +ζ3 # We document it this way because `@irrational` cannot handle docstrings. + """ value(x) From c750a09f1e3daf19cbe0a1c3406480d7289b3833 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Mon, 30 Jun 2025 12:59:04 -0400 Subject: [PATCH 23/77] =?UTF-8?q?Get=20rid=20of=20most=20irrational=20cons?= =?UTF-8?q?tants,=20like=20ln3=20and=20ln=C2=B3=E2=95=B1=E2=82=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/src/internals/utilities.md | 5 - ext/PostNewtonianSymbolicsExt.jl | 3 - src/PostNewtonian.jl | 2 +- src/core/utilities/mathconstants.jl | 183 --------------------------- src/pn_expressions/binding_energy.jl | 14 +- src/pn_expressions/flux.jl | 33 ++--- src/pn_expressions/mode_weights.jl | 40 +++--- test/aqua.jl | 3 - test/binding_energy_reference.jl | 6 +- 9 files changed, 48 insertions(+), 241 deletions(-) delete mode 100644 src/core/utilities/mathconstants.jl diff --git a/docs/src/internals/utilities.md b/docs/src/internals/utilities.md index 8228433d..6386a4ee 100644 --- a/docs/src/internals/utilities.md +++ b/docs/src/internals/utilities.md @@ -90,11 +90,6 @@ and replace them with the appropriate float values. ```@docs PostNewtonian.γₑ PostNewtonian.ζ3 -PostNewtonian.ln2 -PostNewtonian.ln3 -PostNewtonian.ln5 -PostNewtonian.ln³╱₂ -PostNewtonian.ln⁵╱₂ ``` ## Truncated series diff --git a/ext/PostNewtonianSymbolicsExt.jl b/ext/PostNewtonianSymbolicsExt.jl index a8d0a1b5..77106966 100644 --- a/ext/PostNewtonianSymbolicsExt.jl +++ b/ext/PostNewtonianSymbolicsExt.jl @@ -44,9 +44,6 @@ import PostNewtonian: X₁, X₂, ln, - ln2, - ln3, - ln5, ζ3, γₑ, _efficient_vector diff --git a/src/PostNewtonian.jl b/src/PostNewtonian.jl index 6d91b696..8ca5bc02 100644 --- a/src/PostNewtonian.jl +++ b/src/PostNewtonian.jl @@ -19,7 +19,7 @@ using .InlineExports: @public, @export # this package. @public const ln = log @public const 𝒾 = im # Type this as `\scre` -@public const γₑ = Base.MathConstants.γ # Distinguished from PN's `γₚₙ = M/r` +@public const γₑ = Base.MathConstants.γ # Distinguish Euler's constant from `γₚₙ = M/r` public ζ3 # Defined in `core/utilities/misc.jl` include("core/core.jl") diff --git a/src/core/utilities/mathconstants.jl b/src/core/utilities/mathconstants.jl deleted file mode 100644 index df235b82..00000000 --- a/src/core/utilities/mathconstants.jl +++ /dev/null @@ -1,183 +0,0 @@ -module MathConstants - -export log2, - ln2, - log3, - ln3, - log5, - ln5, - log3halves, - log³╱₂, - ln³╱₂, - log5halves, - log⁵╱₂, - ln⁵╱₂, - apery, - ζ3, - γₑ, - 𝒾 - -Base.@irrational ln2 0.6931471805599453 log2 -Base.@irrational γₑ 0.57721566490153286061 euler -Base.@irrational ln3 1.0986122886681097 big"1.098612288668109691395245236922525704647490557822749451734694333637494293218608966873615754813732088787970029" -Base.@irrational ln5 1.6094379124341003 big"1.60943791243410037460075933322618763952560135426851772191264789147417898770765776463013387809317961" -Base.@irrational ln³╱₂ 0.4054651081081644 big"0.40546510810816438197801311546434913657199042346249419761401432414410067124891425126775242781731340" -Base.@irrational ln⁵╱₂ 0.9162907318741551 big"0.91629073187415506518352721176801107145010121990826246779196788198078536573796304902427055109676092" -Base.@irrational ζ3 1.2020569031595942 big"1.20205690315959428539973816151144999076498629234049888179227155534183820578631309018645587360933525814619915" -const 𝒾 = im - -# When defining @irrationals, if the third argument is a symbol, it is expected to name an -# existing constant compiled into MPFR, with prefix `mpfr_const_`, which is a very limited -# set. All MPFR constants except `log2` already exist in `Base.MathConstants`. Otherwise, -# that third argument should be a literal BigFloat; it cannot be a variable name. - -@doc raw""" - γₑ - -[Euler's constant](https://en.wikipedia.org/wiki/Euler%27s_constant) (also known as the -Euler–Mascheroni constant) is defined as the limit as ``n \to \infty`` of the difference -between the ``n``th partial sum of the harmonic series and ``\log(n)``. This is OEIS -sequence [A001620](https://oeis.org/A001620). - -```julia-repl -julia> PostNewtonian.γₑ -γₑ = 0.5772156649015... - -julia> n=10_000_000; sum(1 ./ (1:n))-log(n) -0.5772157149015307 -``` -""" -γₑ - -""" - ζ3 - apery - -[Apéry's constant](https://en.wikipedia.org/wiki/Ap%C3%A9ry%27s_constant) is -defined as ``ζ(3)``, where ``ζ`` is the Riemann zeta function. This is OEIS -sequence [A002117](https://oeis.org/A002117). - -```julia-repl -julia> PostNewtonian.apery -ζ3 = 1.2020569031595... - -julia> PostNewtonian.ζ3 -ζ3 = 1.2020569031595... - -julia> sum((1:10_000_000).^-3) -1.2020569031595896 -``` -""" -ζ3 -const apery = ζ3 - -""" - ln2 - log2 - -The natural logarithm of 2. This is OEIS sequence -[A002162](https://oeis.org/A002162). - -```julia-repl -julia> PostNewtonian.ln2 -ln2 = 0.6931471805599... - -julia> exp(PostNewtonian.ln2) -2.0 - -julia> exp(big(PostNewtonian.ln2)) -2.0 -``` -""" -ln2 -const log2 = ln2 - -""" - ln3 - log3 - -The natural logarithm of 3. This is OEIS sequence -[A002391](https://oeis.org/A002391). - -```julia-repl -julia> PostNewtonian.ln3 -ln3 = 1.0986122886681... - -julia> exp(PostNewtonian.ln3) -3.0000000000000004 - -julia> exp(big(PostNewtonian.ln3)) -3.0 -``` -""" -ln3 -const log3 = ln3 - -""" - ln5 - log5 - -The natural logarithm of 5. This is OEIS sequence -[A016628](https://oeis.org/A016628). - -```julia-repl -julia> PostNewtonian.ln5 -ln5 = 1.6094379124341... - -julia> exp(PostNewtonian.ln5) -4.999999999999999 - -julia> exp(big(PostNewtonian.ln5)) -5.0 -``` -""" -ln5 -const log5 = ln5 - -""" - ln³╱₂ - log³╱₂ - log3halves - -The natural logarithm of 3//2. This is OEIS sequence -[A016578](https://oeis.org/A016578). - -```julia-repl -julia> PostNewtonian.ln³╱₂ -ln³╱₂ = 0.4054651081081... - -julia> exp(PostNewtonian.ln³╱₂) -1.5 - -julia> exp(big(PostNewtonian.ln³╱₂)) -1.5 -``` -""" -ln³╱₂ -const log³╱₂ = ln³╱₂ -const log3halves = ln³╱₂ - -""" - ln⁵╱₂ - log⁵╱₂ - log5halves - -The natural logarithm of 5//2. This is OEIS sequence -[A016579](https://oeis.org/A016579). - -```julia-repl -julia> PostNewtonian.ln⁵╱₂ -ln⁵╱₂ = 0.9162907318741... - -julia> exp(PostNewtonian.ln⁵╱₂) -2.5 - -julia> exp(big(PostNewtonian.ln⁵╱₂)) -2.5 -``` -""" -ln⁵╱₂ -const log⁵╱₂ = ln⁵╱₂ -const log5halves = ln⁵╱₂ - -end diff --git a/src/pn_expressions/binding_energy.jl b/src/pn_expressions/binding_energy.jl index f93f394d..f03fc740 100644 --- a/src/pn_expressions/binding_energy.jl +++ b/src/pn_expressions/binding_energy.jl @@ -56,7 +56,7 @@ overall factor is used, leading to a sign difference. 77ν^4 / 31104 + 301ν^3 / 1728 + (-498449//3456 + 3157π^2 / 576)ν^2 + - (-123671//5760 + 1792ln2 / 15 + 9037π^2 / 1536 + 896γₑ / 15)ν + + (-123671//5760 + 1792ln(2) / 15 + 9037π^2 / 1536 + 896γₑ / 15)ν + 2ln(v) * (448ν / 15) ) @@ -67,9 +67,9 @@ overall factor is used, leading to a sign difference. ν^5 / 512 + 55ν^4 / 512 + (-1353π^2 / 256 + 69423//512)ν^3 + - (-21337π^2 / 1024 + 3a₆ᶜ¹ - 896ln2 / 5 - 448γₑ / 5 + 893429//2880)ν^2 + + (-21337π^2 / 1024 + 3a₆ᶜ¹ - 896ln(2) / 5 - 448γₑ / 5 + 893429//2880)ν^2 + ( - -228916843//115200 - 9976γₑ / 35 + 729ln3 / 7 - 23672ln2 / 35 + + -228916843//115200 - 9976γₑ / 35 + 729ln(3) / 7 - 23672ln(2) / 35 + 126779π^2 / 512 )ν + 2ln(v) * (-4988ν / 35 - 656ν^2 / 5) @@ -82,19 +82,19 @@ overall factor is used, leading to a sign difference. (272855π^2 / 124416 - 20543435//373248)ν^4 + ( 1232γₑ / 27 + 6634243π^2 / 110592 - 11a₆ᶜ¹ / 2 - 71700787//51840 + - 2464ln2 / 27 + 2464ln(2) / 27 )ν^3 + ( 113176680983//14515200 + 18491π^4 / 2304 + - 246004ln2 / 105 + + 246004ln(2) / 105 + 112772γₑ / 105 + a₆ᶜ¹ * 11//2 + a₇ˡⁿ¹ * 2//3 + - a₇ᶜ¹ * 11//3 - 86017789π^2 / 110592 - 2673ln3 / 14 + a₇ᶜ¹ * 11//3 - 86017789π^2 / 110592 - 2673ln(3) / 14 )ν^2 + ( - -389727504721//43545600 + 74888ln2 / 243 - 7128ln3 / 7 - + -389727504721//43545600 + 74888ln(2) / 243 - 7128ln(3) / 7 - 30809603π^4 / 786432 - 3934568γₑ / 8505 + 9118627045π^2 / 5308416 )ν + 2ln(v) * diff --git a/src/pn_expressions/flux.jl b/src/pn_expressions/flux.jl index 000c74ac..ac4e21c7 100644 --- a/src/pn_expressions/flux.jl +++ b/src/pn_expressions/flux.jl @@ -41,18 +41,19 @@ overall factor is used, leading to a sign difference. (v / c)^4 * (-44711//9072 + 9271ν / 504 + 65ν^2 / 18) + (v / c)^5 * ((-8191//672 - 583 * ν / 24)π) + (v / c)^6 * ( - 6643739519//69854400 + 16π^2 / 3 - 1712 * (γₑ + 2ln2 + ln(v)) / 105 + - (-134543//7776 + 41π^2 / 48)ν - 94403ν^2 / 3024 - 775ν^3 / 324 + 6643739519//69854400 + 16π^2 / 3 - + 1712 * (γₑ + 2ln(2) + ln(v)) / 105 + (-134543//7776 + 41π^2 / 48)ν - + 94403ν^2 / 3024 - 775ν^3 / 324 ) + (v / c)^7 * ((-16285//504 + 214745ν / 1728 + 193385ν^2 / 3024)π) + (v / c)^8 * ( -323105549467//3178375200 + 232597γₑ / 4410 - 1369π^2 / 126 + - 39931ln2 / 294 - 47385ln3 / 1568 + + 39931ln(2) / 294 - 47385ln(3) / 1568 + 232597ln(v) / 4410 + ( -1452202403629//1466942400 + 41478γₑ / 245 - 267127π^2 / 4608 + - 479062ln2 / 2205 + - 47385ln3 / 392 + + 479062ln(2) / 2205 + + 47385ln(3) / 392 + 41478ln(v) / 245 )ν + (1607125//6804 - 3157π^2 / 384)ν^2 + @@ -61,7 +62,7 @@ overall factor is used, leading to a sign difference. ) + (v / c)^9 * ( ( - 265978667519//745113600 - 6848 * (γₑ + 2ln2 + ln(v)) / 105 + + 265978667519//745113600 - 6848 * (γₑ + 2ln(2) + ln(v)) / 105 + (2062241//22176 + 41π^2 / 12)ν - 133112905ν^2 / 290304 - 3719141ν^3 / 38016 )π @@ -129,32 +130,32 @@ overall factor is used, leading to a sign difference. + (v / c)^10 * ( -2500861660823683//2831932303200 - 424223π^2 / 6804 - - 83217611ln2 / 1122660 + + 83217611ln(2) / 1122660 + 916628467γₑ / 7858620 + - 47385ln3 / 196 + + 47385ln(3) / 196 + 916628467ln(v) / 7858620 ) + (v / c)^11 * ( - -142155π * ln3 / 784 + + -142155π * ln(3) / 784 + 8399309750401π / 101708006400 + 177293γₑ * π / 1176 + - 8521283π * ln2 / 17640 + + 8521283π * ln(2) / 17640 + 177293π * ln(v) / 1176 ) + (v / c)^12 * ( - -271272899815409ln2 / 157329572400 - 54784π^2 * ln2 / 315 - - 246137536815857γₑ / 157329572400 - 437114506833ln3 / 789268480 - + -271272899815409ln(2) / 157329572400 - 54784π^2 * ln(2) / 315 - + 246137536815857γₑ / 157329572400 - 437114506833ln(3) / 789268480 - 256π^4 / 45 - 27392γₑ * π^2 / 315 - 27392ζ3 / 105 - - 37744140625ln5 / 260941824 + + 37744140625ln(5) / 260941824 + 1465472γₑ^2 / 11025 + - 5861888γₑ * ln2 / 11025 + - 5861888ln2^2 / 11025 + + 5861888γₑ * ln(2) / 11025 + + 5861888ln(2)^2 / 11025 + 2067586193789233570693//602387400044430000 + 3803225263π^2 / 10478160 + ln(v) * ( -246137536815857//157329572400 - 27392π^2 / 315 + 2930944γₑ / 11025 + - 5861888ln2 / 11025 + + 5861888ln(2) / 11025 + 1465472ln(v) / 11025 ) ) diff --git a/src/pn_expressions/mode_weights.jl b/src/pn_expressions/mode_weights.jl index c6187197..531e5f23 100644 --- a/src/pn_expressions/mode_weights.jl +++ b/src/pn_expressions/mode_weights.jl @@ -63,9 +63,9 @@ is from Eq. (4.15) of that reference. h[Yindex(2,1,ℓₘᵢₙ)] = h₀ * @pn_expansion( (v/c)^1 * (𝒾 * δ / 3) + (v/c)^3 * (𝒾 * δ * (-17 + 20ν) / 84) - + (v/c)^4 * ((δ * (1 + 2𝒾*π + 4ln2)) / 6) + + (v/c)^4 * ((δ * (1 + 2𝒾*π + 4ln(2))) / 6) + (v/c)^5 * (𝒾 * δ * (-172 + ν * (-2036 + 237ν)) / 1512) - + (v/c)^6 * (δ*(-34𝒾*π - 17*(1 + 4ln2) + 2ν * (353 + 6𝒾*π + 12ln2)) / 168) + + (v/c)^6 * (δ*(-34𝒾*π - 17*(1 + 4ln(2)) + 2ν * (353 + 6𝒾*π + 12ln(2))) / 168) ) h[Yindex(2,2,ℓₘᵢₙ)] = h₀ * @pn_expansion( 1 @@ -75,14 +75,14 @@ is from Eq. (4.15) of that reference. + (v/c)^5 * (-24𝒾*ν + ((-107 + 34ν)π) / 21) + (v/c)^6 * ( (27027409//646800) - 856γₑ/105 + (ν*(-834555 + ν*(-729396 + 114635ν))) / 99792 - + 41ν * π^2 / 96 + (2π*(214𝒾 + 35π))/105 - 1712ln2/105 + + 41ν * π^2 / 96 + (2π*(214𝒾 + 35π))/105 - 1712ln(2)/105 - (856//105)*ln(v) ) + (v/c)^7 * ((-2𝒾 * ν * (-501655 + 24396ν) + 15*(-2173 + 2ν*(-2459 + 560ν))π) / 11340) # Eq. (6.17) of Blanchet et al. (2023) + (v/c)^8 * ( - - 846557506853//12713500800 + 45796γₑ/2205 - 22898𝒾*π/2205 - 107π^2/63 + 45796(2ln2+ln(v))/2205 - + (-336005827477//4237833600 + 15284γₑ/441 - 219314𝒾*π/2205 - 9755*π^2/32256 + 15284(2ln2+ln(v))/441)ν + - 846557506853//12713500800 + 45796γₑ/2205 - 22898𝒾*π/2205 - 107π^2/63 + 45796(2ln(2)+ln(v))/2205 + + (-336005827477//4237833600 + 15284γₑ/441 - 219314𝒾*π/2205 - 9755*π^2/32256 + 15284(2ln(2)+ln(v))/441)ν + (256450291//7413120 - 1025*π^2/1008)ν^2 - 81579187ν^3/15567552 + 26251249ν^4/31135104 ) ) @@ -94,15 +94,15 @@ is from Eq. (4.15) of that reference. h[Yindex(3,1,ℓₘᵢₙ)] = h₀ * @pn_expansion( (v/c)^1 * (𝒾 * δ / 12√14) + (v/c)^3 * (-𝒾 * δ * (4 + ν) / 18√14) - + (v/c)^4 * (δ * (7 + 5𝒾*π + 10ln2) / 60√14) + + (v/c)^4 * (δ * (7 + 5𝒾*π + 10ln(2)) / 60√14) + (v/c)^5 * (-𝒾 * δ * (-607 + ν*(272 + 247ν)) / 2376√14) + (v/c)^6 * ( - δ * (-5𝒾 * (16 + 7ν)π + 2*(-56 + ν - 5*(16 + 7ν)*ln2)) / 360√14 + δ * (-5𝒾 * (16 + 7ν)π + 2*(-56 + ν - 5*(16 + 7ν)*ln(2))) / 360√14 ) + (v/c)^7 * ( 𝒾 * δ / 12√14 * ( - (10753397//1513512) - 2ln2*((212//105) + ln2) - (26//21)*γₑ + (π^2/6) - - 2𝒾*π*((41//105) + ln2) + (ν/8)*(-(1738843//19305) + (41//8)*π^2) + (10753397//1513512) - 2ln(2)*((212//105) + ln(2)) - (26//21)*γₑ + (π^2/6) + - 2𝒾*π*((41//105) + ln(2)) + (ν/8)*(-(1738843//19305) + (41//8)*π^2) + (327059//30888)*ν^2 - (17525//15444)*ν^3 + ln(v) * (-26//21) ) @@ -117,15 +117,15 @@ is from Eq. (4.15) of that reference. h[Yindex(3,3,ℓₘᵢₙ)] = h₀ * @pn_expansion( (v/c)^1 * (-3𝒾 * √(15//224) * δ) + (v/c)^3 * (-3𝒾 * √(15//56) * δ * (-2 + ν)) - + (v/c)^4 * (√(243//70) * δ * (-7 - 5𝒾*π + 10ln³╱₂) / 4) + + (v/c)^4 * (√(243//70) * δ * (-7 - 5𝒾*π + 10ln(3/2)) / 4) + (v/c)^5 * (-𝒾 * √(3//542080) * δ * (369 + (-3676 + 887ν)ν)) - + (v/c)^6 * (δ * (-3645𝒾 * (-8 + 3ν)π + (40824 - 96206ν + 7290*(-8 + 3ν)ln³╱₂)) / 216√210) + + (v/c)^6 * (δ * (-3645𝒾 * (-8 + 3ν)π + (40824 - 96206ν + 7290*(-8 + 3ν)ln(3/2))) / 216√210) + (v/c)^7 * ( ((-3𝒾)/4) * √(15//14) * δ * ( - (19388147//280280) + (492//35)ln³╱₂ - 18*ln³╱₂^2 - (78//7)γₑ + (3//2)π^2 - + 6𝒾 * π * (-41//35 + 3ln³╱₂) + (19388147//280280) + (492//35)ln(3/2) - 18*ln(3/2)^2 - (78//7)γₑ + (3//2)π^2 + + 6𝒾 * π * (-41//35 + 3ln(3/2)) + (-7055//3432 + 41//64 * π^2)ν - (318841//17160)ν^2 + (8237//2860)ν^3 - + ln(v) * (-(39//7) * 4ln2) + + ln(v) * (-(39//7) * 4ln(2)) ) ) ) @@ -149,7 +149,7 @@ is from Eq. (4.15) of that reference. h[Yindex(4,1,ℓₘᵢₙ)] = h₀ * @pn_expansion( (v/c)^3 * (𝒾 * δ * (1 - 2ν) / 84√10) + (v/c)^5 * (-𝒾 * δ * (404 + (-1011 + 332ν)ν) / 11088√10) - + (v/c)^6 * (δ * (64 - 1661ν - 30𝒾*(-1 + 2ν)π + 60*(1 - 2ν)ln2) / 2520√10) + + (v/c)^6 * (δ * (64 - 1661ν - 30𝒾*(-1 + 2ν)π + 60*(1 - 2ν)ln(2)) / 2520√10) ) h[Yindex(4,2,ℓₘᵢₙ)] = h₀ * @pn_expansion( (v/c)^2 * (√5 * (1 - 3ν) / 63) @@ -160,12 +160,12 @@ is from Eq. (4.15) of that reference. h[Yindex(4,3,ℓₘᵢₙ)] = h₀ * @pn_expansion( (v/c)^3 * (9𝒾 * δ * (-1 + 2ν) / 4√70) + (v/c)^5 * (3𝒾 * δ * (468 + (-1267 + 524ν)ν) / 176√70) - + (v/c)^6 * (δ * (-5184 + 16301ν + 2430𝒾*(-1 + 2ν)π + 4860*(1 - 2ν)ln³╱₂) / 360√70) + + (v/c)^6 * (δ * (-5184 + 16301ν + 2430𝒾*(-1 + 2ν)π + 4860*(1 - 2ν)ln(3/2)) / 360√70) ) h[Yindex(4,4,ℓₘᵢₙ)] = h₀ * @pn_expansion( (v/c)^2 * (8 * √(5//7) * (-1 + 3ν) / 9) + (v/c)^4 * (4 * (1779 + 5ν*(-1273 + 525ν)) / 297√35) - + (v/c)^5 * ((160*(-1 + 3ν)π + 𝒾*(336 - 1193ν + 320*(-1 + 3ν)ln2)) / 9√35) + + (v/c)^5 * ((160*(-1 + 3ν)π + 𝒾*(336 - 1193ν + 320*(-1 + 3ν)ln(2))) / 9√35) + (v/c)^6 * ((-9618039 + 7ν*(9793071 + 5ν*(-3231338 + 678291ν))) / 405405√35) ) end @@ -175,7 +175,7 @@ is from Eq. (4.15) of that reference. h[Yindex(5,1,ℓₘᵢₙ)] = h₀ * @pn_expansion( (v/c)^3 * (𝒾 * δ * (1 - 2ν) / 288√385) + (v/c)^5 * (-𝒾 * δ * (179 + 4*(-88 + ν)ν) / 11232√385) - + (v/c)^6 * (δ * (181 - 70𝒾*(-1 + 2ν)π + 140ln2 - 28ν*(313 + 10ln2)) / 20160√385) + + (v/c)^6 * (δ * (181 - 70𝒾*(-1 + 2ν)π + 140ln(2) - 28ν*(313 + 10ln(2))) / 20160√385) ) h[Yindex(5,2,ℓₘᵢₙ)] = h₀ * @pn_expansion( (v/c)^4 * ((2 + 10*(-1 + ν)ν) / 27√55) @@ -184,7 +184,7 @@ is from Eq. (4.15) of that reference. h[Yindex(5,3,ℓₘᵢₙ)] = h₀ * @pn_expansion( (v/c)^3 * (9𝒾 * √(3//110) * δ * (-1 + 2ν) / 32) + (v/c)^5 * (3𝒾 * √(3//110) * δ * (207 + 8ν*(-58 + 11ν)) / 416) - + (v/c)^6 * (δ * (-395847 + 1171828ν + 153090𝒾*(-1 + 2ν)π - 306180*(-1 + 2ν)*ln³╱₂) / 60480√330) + + (v/c)^6 * (δ * (-395847 + 1171828ν + 153090𝒾*(-1 + 2ν)π - 306180*(-1 + 2ν)*ln(3/2)) / 60480√330) ) h[Yindex(5,4,ℓₘᵢₙ)] = h₀ * @pn_expansion( (v/c)^4 * ((-32 - 160*(-1 + ν)ν) / 9√165) @@ -193,7 +193,7 @@ is from Eq. (4.15) of that reference. h[Yindex(5,5,ℓₘᵢₙ)] = h₀ * @pn_expansion( (v/c)^3 * (-625𝒾 * δ * (-1 + 2ν) / 96√66) + (v/c)^5 * (-625𝒾 * δ * (263 + 16ν*(-43 + 16ν)) / 3744√66) - + (v/c)^6 * (δ * (565625 - 1481676ν - 218750𝒾*(-1 + 2ν)π + 437500*(-1 + 2ν)ln⁵╱₂) / 6720√66) + + (v/c)^6 * (δ * (565625 - 1481676ν - 218750𝒾*(-1 + 2ν)π + 437500*(-1 + 2ν)ln(5/2)) / 6720√66) ) end diff --git a/test/aqua.jl b/test/aqua.jl index 9308612c..00074a42 100644 --- a/test/aqua.jl +++ b/test/aqua.jl @@ -32,9 +32,6 @@ end :χ⃗₂, :ℳ, :ln, - :ln2, - :ln3, - :ln5, :order_index, :γₑ, :ζ3, diff --git a/test/binding_energy_reference.jl b/test/binding_energy_reference.jl index cc7a89c4..2618c20f 100644 --- a/test/binding_energy_reference.jl +++ b/test/binding_energy_reference.jl @@ -36,8 +36,8 @@ function be(pnsystem, deriv) λ₊ = PostNewtonian.λ₊(pnsystem), λ₋ = PostNewtonian.λ₋(pnsystem), π = PostNewtonian.type_converter(pnsystem, π), - ln2 = PostNewtonian.type_converter(pnsystem, PostNewtonian.ln2), - ln3 = PostNewtonian.type_converter(pnsystem, PostNewtonian.ln3), + ln2 = PostNewtonian.type_converter(pnsystem, PostNewtonian.ln(2)), + ln3 = PostNewtonian.type_converter(pnsystem, PostNewtonian.ln(3)), γₑ = PostNewtonian.type_converter(pnsystem, PostNewtonian.γₑ), ln = (x -> log(PostNewtonian.type_converter(pnsystem, x))), pn_order = PostNewtonian.pn_order(pnsystem) @@ -55,7 +55,7 @@ function be(pnsystem, deriv) 77ν^4 / 31104 + 301ν^3 / 1728 + (-498449//3456 + 3157π^2 / 576)ν^2 + - (-123671//5760 + 1792ln2 / 15 + 9037π^2 / 1536 + 896γₑ / 15)ν + (-123671//5760 + 1792ln(2) / 15 + 9037π^2 / 1536 + 896γₑ / 15)ν ) eˡⁿ[8] = (448ν / 15) From 6474d7160b17db4668e267a66e3a68ac93b644b9 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Mon, 30 Jun 2025 13:31:06 -0400 Subject: [PATCH 24/77] Mark a few functions public --- src/core/core.jl | 2 ++ src/core/utilities/misc.jl | 2 +- src/core/utilities/truncated_series_inversion.jl | 6 +++--- src/core/utilities/truncated_series_monoid.jl | 8 ++++---- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/core/core.jl b/src/core/core.jl index 61ffc3a7..86bc8896 100644 --- a/src/core/core.jl +++ b/src/core/core.jl @@ -1 +1,3 @@ include("utilities/misc.jl") +include("utilities/truncated_series_monoid.jl") +include("utilities/truncated_series_inversion.jl") diff --git a/src/core/utilities/misc.jl b/src/core/utilities/misc.jl index 4f50e168..6b51b68f 100644 --- a/src/core/utilities/misc.jl +++ b/src/core/utilities/misc.jl @@ -25,7 +25,7 @@ julia> sum((1:10_000_000).^-3) Return `x` or the value wrapped by the `Dual` number `x` """ -value(x::T) where {T} = hasproperty(x, :value) ? getproperty(x, :value) : x +@public value(x::T) where {T} = hasproperty(x, :value) ? getproperty(x, :value) : x """ find_symbols_of_type(mod, T) diff --git a/src/core/utilities/truncated_series_inversion.jl b/src/core/utilities/truncated_series_inversion.jl index 31eaa50b..48265f71 100644 --- a/src/core/utilities/truncated_series_inversion.jl +++ b/src/core/utilities/truncated_series_inversion.jl @@ -16,7 +16,7 @@ This function is essentially a helper function for the [`lagrange_inversion`](@r function. """ -function x╱f_mod_xⁿ⁻¹(a::NTuple{N,T}) where {N,T} +@public function x╱f_mod_xⁿ⁻¹(a::NTuple{N,T}) where {N,T} b = zeros(MVector{N,typeof(inv(a[1]))}) b[1] = inv(a[1]) for i ∈ 2:N @@ -113,7 +113,7 @@ This function is essentially a helper function for the [`lagrange_inversion`](@r function. """ -function hⁱ✖h_mod_xⁿ⁻¹(hⁱ::NTuple{N,T}, h::NTuple{N,T}) where {N,T} +@public function hⁱ✖h_mod_xⁿ⁻¹(hⁱ::NTuple{N,T}, h::NTuple{N,T}) where {N,T} hⁱ⁺¹ = zeros(MVector{N,T}) for i ∈ 1:N hⁱ⁺¹[i] = sum((hⁱ[j] * h[i - j + 1] for j ∈ 1:i)) @@ -394,7 +394,7 @@ actually be beneficial in practice, so we stick with the basic algorithm here would not be too difficult to implement if needed. """ -function lagrange_inversion(a::NTuple{N,T}) where {N,T} +@public function lagrange_inversion(a::NTuple{N,T}) where {N,T} h = x╱f_mod_xⁿ⁻¹(a) f⁻¹ = zeros(MVector{N,typeof(h[end] / 2)}) hⁱ = h # Create storage for the loop diff --git a/src/core/utilities/truncated_series_monoid.jl b/src/core/utilities/truncated_series_monoid.jl index 97615e9c..2e755e54 100644 --- a/src/core/utilities/truncated_series_monoid.jl +++ b/src/core/utilities/truncated_series_monoid.jl @@ -57,7 +57,7 @@ product of the series and its inverse must be zero. This gives b_{i+1} = -b_0\sum_{j=1}^{i} a_j b_{i-j}. ``` """ -function truncated_series_inverse(a::AbstractVector) +@public function truncated_series_inverse(a::AbstractVector) b = similar(a) return truncated_series_inverse!(b, a) end @@ -68,7 +68,7 @@ function truncated_series_inverse(a::NTuple{N,T}) where {N,T} return Tuple(b) end -function truncated_series_inverse!(b, a) +@public function truncated_series_inverse!(b, a) @assert length(b) == length(a) n = length(a) @inbounds @fastmath if n > 0 @@ -102,7 +102,7 @@ Internally, the sums are performed using `evalpoly`. See also [`truncated_series_ratio`](@ref). """ -function truncated_series_product(a, b, v) +@public function truncated_series_product(a, b, v) @assert length(a) == length(b) N = length(a) - 1 if N < 0 @@ -145,7 +145,7 @@ and return the *value* of the ratio ``A / B`` truncated at ``v^n``. This function simply combines [`truncated_series_product`](@ref) and [`truncated_series_inverse`](@ref). """ -function truncated_series_ratio(a, b, v) +@public function truncated_series_ratio(a, b, v) return truncated_series_product(a, truncated_series_inverse(b), v) end From 9cf618baa99e8d6a7155d667f72f7da2ac064ab4 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Tue, 1 Jul 2025 00:43:13 -0400 Subject: [PATCH 25/77] Define and test initial implementation of `@pn_reference` --- src/core/PNReference.jl | 121 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/src/core/PNReference.jl b/src/core/PNReference.jl index 0ec4afb3..96d93a09 100644 --- a/src/core/PNReference.jl +++ b/src/core/PNReference.jl @@ -36,3 +36,124 @@ look through the current module for symbols that appear in the expression that n replaced with a call to that function. """ + +function pn_reference(expr) + # A module `Expr` has head `:module` and three arguments: + # 1. a boolean indicating if this is a module (true) as opposed to a baremodule (false) + # 2. the module name (a `Symbol`) + # 3. the module body (an `Expr` with head `:block`) + if MacroTools.isexpr(expr, :module) + if length(expr.args) ≠ 3 + error( + "Found a module expression with $(length(expr.args)) arguments:\n" * + "$(expr)", + ) + end + if ! MacroTools.isexpr(expr.args[1], Bool) + error("Found a module expression with a non-boolean flag: $(expr.args[1])") + end + if ! MacroTools.isexpr(expr.args[2], Symbol, :symbol) + error( + "Found a module expression with a non-symbol name: $(expr.args[2])\n" * + "$(expr)", + ) + end + if ! MacroTools.isexpr(expr.args[3], :block) + error("Found a module expression with a non-block body: $(dump(expr.args[3]))") + end + # Now, we assemble the new module, mostly by prepending some imports to the + # contents + new_module = Expr( + :module, + false, # We turn this into a `baremodule` + expr.args[2], # This is the name of the module + Expr( # This is the new module body + :block, + :(import Base), + :(eval(x) = Core.eval(Mod, x)), + :(include(p) = Base.include(Mod, p)), + :(using PostNewtonian: @pn_expression, @pn_expansion), + expr.args[3].args..., # The original module body + ), + ) + # Finally, we return this as a `:toplevel` expression, to ensure that it is + # not buried in some block that causes an error. + return Expr(:toplevel, new_module) + else + error("@pn_reference can only be used with module expressions, not `$expr`") + end +end + +""" + @pn_reference + +Create a module for a specific Post-Newtonian reference. + +Different sources in the literature may use different notations for the same variables, so +enclosing definitions related to a particular reference in a module allows us to use the +appropriate notation within that module while still interfacing with this package and its +conventions outside of the module. For example, some authors use `η` to denote the +symmetric mass ratio whereas this package uses `ν`. We can import this with `using +..PostNewtonian: ν as η` inside the module, and then use `η` in the expressions defined +inside that module, to resemble the reference. But when any expression using `η` is called +from outside the module, the choice of variable used inside that expression will not affect +the result. + +This macro transforms the module to which it is applied, so that unlike a normal module that +effectively includes `using Base` — which allows access to such basic operations as +addition, multiplication, and so on — it instead includes `import Base`. It also includes +`using PostNewtonian: @pn_expression, @pn_expansion`, the first of which redefines the basic +operations — at least those used in post-Newtonian expressions — to account for the number +type relevant to the input `PNSystem`. By not importing the basic operations from `Base`, +we ensure that only those redefined by `@pn_expression` are available in the module, which +ensures that they preserve the number type. If any are missing, `@pn_expression` should be +extended. + +""" +@public macro pn_reference(ex) + esc(pn_reference(ex)) +end + +@testitem "@pn_reference" begin + using MacroTools: MacroTools + using PostNewtonian: @pn_reference + + input = @macroexpand @pn_reference module Einstein1918 + using PostNewtonian: G, c, M, χ⃗₁, χ⃗₂, v, pn_order + using Quaternionic: absvec + @pn_expression χ₁(pnsystem) = absvec(χ⃗₁) + @pn_expression χ₂(pnsystem) = absvec(χ⃗₂) + const x = 3 + module InnerMod + const z = 4 + end + using .InnerMod: z + @pn_expression function y(pnsystem) + G*M/c^3 * @pn_expansion(x + χ₁ + χ₂ + z*(v/c)) + end + end + + output = quote + baremodule Einstein1918 + import Base + eval(x) = Core.eval(Mod, x) + include(p) = Base.include(Mod, p) + using PostNewtonian: @pn_expression, @pn_expansion + + using PostNewtonian: G, c, M, χ⃗₁, χ⃗₂, v, pn_order + using Quaternionic: absvec + @pn_expression χ₁(pnsystem) = absvec(χ⃗₁) + @pn_expression χ₂(pnsystem) = absvec(χ⃗₂) + const x = 3 + module InnerMod + const z = 4 + end + using .InnerMod: z + @pn_expression function y(pnsystem) + G*M/c^3 * @pn_expansion(x + χ₁ + χ₂ + z*(v/c)) + end + end + end + + @test MacroTools.striplines(input).args[1] == MacroTools.striplines(output).args[1] +end From e1511472cb4b73422cd53cee45576033b61b97a3 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Tue, 1 Jul 2025 00:44:36 -0400 Subject: [PATCH 26/77] Reorganize PNTerm/PNExpansion --- src/PostNewtonian.jl | 5 +- src/core/PNExpansion.jl | 153 ++++++++---------------------- src/core/PNTerm.jl | 200 ++++++++++++++++++++++++++++++++++++++++ src/core/core.jl | 4 + test/pn_expansion.jl | 120 ------------------------ 5 files changed, 244 insertions(+), 238 deletions(-) delete mode 100644 test/pn_expansion.jl diff --git a/src/PostNewtonian.jl b/src/PostNewtonian.jl index 8ca5bc02..453e31ac 100644 --- a/src/PostNewtonian.jl +++ b/src/PostNewtonian.jl @@ -1,9 +1,10 @@ module PostNewtonian using Base: @propagate_inbounds -using FastDifferentiation: Node as FDNode +using FastDifferentiation: FastDifferentiation, Node as FDNode #using InlineExports: @public, @export # See below using IrrationalConstants: @irrational +using MacroTools: MacroTools using Quaternionic: Quaternionic, QuatVec, Rotor, abs2vec, components, normalize, ⋅, × using StaticArrays: MVector, SVector using TestItems: @testitem @@ -22,8 +23,8 @@ using .InlineExports: @public, @export @public const γₑ = Base.MathConstants.γ # Distinguish Euler's constant from `γₚₙ = M/r` public ζ3 # Defined in `core/utilities/misc.jl` -include("core/core.jl") include("pn_systems/pn_systems.jl") +include("core/core.jl") include("literature/literature.jl") include("pn_expressions/pn_expressions.jl") include("interface/interface.jl") diff --git a/src/core/PNExpansion.jl b/src/core/PNExpansion.jl index 070186da..5a9c3bc1 100644 --- a/src/core/PNExpansion.jl +++ b/src/core/PNExpansion.jl @@ -39,7 +39,7 @@ The `N` parameter is not related to the PN order; it is just used by Julia to kn elements are currently in the coefficients, but is required to be 1 ≤ N ≤ NMax. """ -struct PNExpansion{N,T,NMax} +@public struct PNExpansion{N,T,NMax} coeffs::NTuple{N,T} function PNExpansion{N,T,NMax}(coeffs) where {N,T,NMax} @@ -165,95 +165,6 @@ end Base.Tuple(pn::PNExpansion) = pn.coeffs SVector(pn::PNExpansion) = SVector(pn.coeffs) -""" - PNTerm{T,PNOrder,c⁻¹Exponent} - -This object represents a single term in a PNExpansion. It has a single field: `coeff`, -which is the coefficient of the term. The type parameter `T` is the type of the -coefficient. The type parameter `PNOrder` is a half-integer (just as in -[`PNSystem`](@ref)s) representing the PN order of the expansion. And the type parameter -`c⁻¹Exponent` is an integer representing the exponent of the PN expansion parameter ``1/c``. - -`PNTerm`s can be multiplied and divided by scalars and exponentiated by integers, to produce -another `PNTerm`. They can also be added to other `PNTerm`s to produce a `PNExpansion`. - -A simple way to define a `PNTerm` or a `PNExpansion` is to define the PN expansion parameter -```julia -c = PNExpansionParameter(pnsystem) -``` -and use that naturally in formulas, as in -```julia -e = 1 + (v/c)^2 * (-ν/12 - 3//4) + (v/c)^4 * (-ν^2/24 + 19ν/8 - 27//8) -``` -Any exponent higher than the desired `PNOrder` will be automatically set to zero. - -Useful facts: - - `v` has order `1/c` - - `x` has order `1/c^2` - - `γ` has order `1/c^2` - - `1/r` has order `1/c^2` - -""" -struct PNTerm{T,PNOrder,c⁻¹Exponent} - coeff::T - - function PNTerm{T,PNOrder,c⁻¹Exponent}(coeff) where {T,PNOrder,c⁻¹Exponent} - if c⁻¹Exponent > 2PNOrder - coeff = zero(coeff) - end - return new{T,PNOrder,c⁻¹Exponent}(coeff) - end - function PNTerm{T,PNOrder}(c⁻¹exp::Int, coeff) where {T,PNOrder} - if c⁻¹exp > 2PNOrder - coeff = zero(coeff) - end - return new{T,PNOrder,c⁻¹exp}(coeff) - end -end - -Base.length(pn::PNTerm) = 1 -Base.eltype(pn::PNTerm{T}) where {T} = T -c⁻¹exp(pn::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} = c⁻¹Exponent - -function Base.sum(pn::PNTerm) - return pn.coeff -end - -function Base.:+(pn::PNTerm) - return pn -end - -function Base.inv(term::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} - return PNTerm{T,PNOrder}(-c⁻¹exp(term), inv(term.coeff)) -end - -function Base.:^(term::PNTerm{T,PNOrder,c⁻¹Exponent}, n::Int) where {T,PNOrder,c⁻¹Exponent} - coeff = term.coeff^n - return PNTerm{typeof(coeff),PNOrder}(c⁻¹exp(term) * n, coeff) -end - -function Base.:*( - x::Number, term::PNTerm{T,PNOrder,c⁻¹Exponent} -) where {T,PNOrder,c⁻¹Exponent} - coeff = x * term.coeff - return PNTerm{typeof(coeff),PNOrder,c⁻¹Exponent}(coeff) -end -Base.:*(term::PNTerm, x::Number) = x * term - -function Base.:/( - term::PNTerm{T,PNOrder,c⁻¹Exponent}, x::Number -) where {T,PNOrder,c⁻¹Exponent} - coeff = term.coeff / x - return PNTerm{typeof(coeff),PNOrder,c⁻¹Exponent}(coeff) -end - -function Base.:/( - x::Number, term::PNTerm{T,PNOrder,c⁻¹Exponent} -) where {T,PNOrder,c⁻¹Exponent} - coeff = x / term.coeff - return PNTerm{typeof(coeff),PNOrder}(-c⁻¹exp(term), coeff) -end - function Base.:+( x::T1, term::PNTerm{T2,PNOrder,c⁻¹Exponent} ) where {T1<:Number,T2,PNOrder,c⁻¹Exponent} @@ -284,22 +195,6 @@ function Base.:-(term::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹ return PNTerm{T,PNOrder,c⁻¹Exponent}(-term.coeff) end -function Base.:*( - term1::PNTerm{T1,PNOrder,c⁻¹E1}, term2::PNTerm{T2,PNOrder,c⁻¹E2} -) where {T1,T2,PNOrder,c⁻¹E1,c⁻¹E2} - c⁻¹Exponent = c⁻¹exp(term1) + c⁻¹exp(term2) - coeff = term1.coeff * term2.coeff - return PNTerm{typeof(coeff),PNOrder,c⁻¹Exponent}(coeff) -end - -function Base.:/( - term1::PNTerm{T1,PNOrder,c⁻¹E1}, term2::PNTerm{T2,PNOrder,c⁻¹E2} -) where {T1,T2,PNOrder,c⁻¹E1,c⁻¹E2} - c⁻¹Exponent = c⁻¹E1 - c⁻¹E2 - coeff = term1.coeff / term2.coeff - return PNTerm{typeof(coeff),PNOrder,c⁻¹Exponent}(coeff) -end - function Base.:+( term1::PNTerm{T1,PNOrder,c⁻¹E1}, term2::PNTerm{T2,PNOrder,c⁻¹E2} ) where {T1,T2,PNOrder,c⁻¹E1,c⁻¹E2} @@ -411,14 +306,40 @@ Base.:*(term::PNTerm, expansion::PNExpansion) = expansion * term Base.:/(expansion::PNExpansion, term::PNTerm) = expansion * inv(term) -""" - PNExpansionParameter(pnsystem) - -Create a [`PNTerm`](@ref) object representing the post-Newtonian expansion parameter ``c``. -This can be used to automatically create more complicated `PNTerm`s, which combine to form a -[`PNExpansion`](@ref). This is a simple but effective way to write PN formulas while -automatically tracking the PN order of each term. -""" -function PNExpansionParameter(::PNSystem{ST,PNOrder}) where {ST,PNOrder} - return PNTerm{eltype(ST),PNOrder}(-1, one(eltype(ST))) +@testitem "PNExpansion algebra" begin + using Symbolics: @variables, simplify, substitute + using PostNewtonian: PNExpansion + + for N1 ∈ 1:9 + for N2 ∈ 1:9 + for NMax ∈ max(N1, N2):(N1 + N2 + 3) + @variables c⁻¹ x[1:N1] y[1:N2] z + poly(e::PNExpansion) = sum(e[i] * c⁻¹^(i - 1) for i ∈ 1:length(e)) + eˣ = PNExpansion(tuple(x...), NMax) + eʸ = PNExpansion(tuple(y...), NMax) + + # Test sums + polysum = simplify(poly(eˣ + eʸ); expand=true) + sumpoly = simplify(poly(eˣ) + poly(eʸ); expand=true) + Δ = simplify(polysum - sumpoly; expand=true) + @test iszero(Δ) + @test_throws ArgumentError eˣ + PNExpansion(tuple(z, x...), NMax + 1) + @test_throws ArgumentError PNExpansion(tuple(z, x...), NMax + 1) + eˣ + + # Test products + polyprod = simplify(poly(eˣ * eʸ); expand=true) + prodpoly = simplify( + substitute( + simplify(poly(eˣ) * poly(eʸ); expand=true), + Dict([c⁻¹^n => 0 for n ∈ NMax:(2NMax + 3)]), + ); + expand=true, + ) + Δ = simplify(polyprod - prodpoly; expand=true) + @test iszero(Δ) + @test_throws ArgumentError eˣ * PNExpansion(tuple(z, x...), NMax + 1) + @test_throws ArgumentError PNExpansion(tuple(z, x...), NMax + 1) * eˣ + end + end + end end diff --git a/src/core/PNTerm.jl b/src/core/PNTerm.jl index 8b137891..1ebefbf0 100644 --- a/src/core/PNTerm.jl +++ b/src/core/PNTerm.jl @@ -1 +1,201 @@ +""" + PNExpansionParameter(pnsystem) +Create a [`PNTerm`](@ref) object representing the post-Newtonian expansion parameter ``c``. +This can be used to automatically create more complicated `PNTerm`s, which combine to form a +[`PNExpansion`](@ref). This is a simple but effective way to write PN formulas while +automatically tracking the PN order of each term. +""" +@public function PNExpansionParameter(::T) where {NT,PNOrder,T<:PNSystem{NT,PNOrder}} + return PNTerm{NT,PNOrder}(-1, one(T)) +end + +""" + PNTerm{T,PNOrder,c⁻¹Exponent} + +This object represents a single term in a PNExpansion. It has a single field: `coeff`, +which is the coefficient of the term. The type parameter `T` is the type of the +coefficient. The type parameter `PNOrder` is a half-integer (just as in +[`PNSystem`](@ref)s) representing the PN order of the expansion. And the type parameter +`c⁻¹Exponent` is an integer representing the exponent of the PN expansion parameter ``1/c``. + +`PNTerm`s can be multiplied and divided by scalars and exponentiated by integers, to produce +another `PNTerm`. They can also be added to other `PNTerm`s to produce a `PNExpansion`. + +A simple way to define a `PNTerm` or a `PNExpansion` is to define the PN expansion parameter +```julia +c = PNExpansionParameter(pnsystem) +``` +and use that naturally in formulas, as in +```julia +e = 1 + (v/c)^2 * (-ν/12 - 3//4) + (v/c)^4 * (-ν^2/24 + 19ν/8 - 27//8) +``` +Any exponent higher than the desired `PNOrder` will be automatically set to zero. + +Useful facts: + - `v` has order `1/c` + - `x` has order `1/c^2` + - `γ` has order `1/c^2` + - `1/r` has order `1/c^2` + +""" +@public struct PNTerm{T,PNOrder,c⁻¹Exponent} + coeff::T + + function PNTerm{T,PNOrder,c⁻¹Exponent}(coeff) where {T,PNOrder,c⁻¹Exponent} + if c⁻¹Exponent > 2PNOrder + coeff = zero(coeff) + end + return new{T,PNOrder,c⁻¹Exponent}(coeff) + end + function PNTerm{T,PNOrder}(c⁻¹exp::Int, coeff) where {T,PNOrder} + if c⁻¹exp > 2PNOrder + coeff = zero(coeff) + end + return new{T,PNOrder,c⁻¹exp}(coeff) + end +end + +Base.length(pn::PNTerm) = 1 +Base.eltype(pn::PNTerm{T}) where {T} = T +@public c⁻¹exp(pn::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} = + c⁻¹Exponent + +function Base.sum(pn::PNTerm) + return pn.coeff +end + +function Base.:+(pn::PNTerm) + return pn +end + +function Base.inv(term::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} + return PNTerm{T,PNOrder}(-c⁻¹exp(term), inv(term.coeff)) +end + +function Base.:^(term::PNTerm{T,PNOrder,c⁻¹Exponent}, n::Int) where {T,PNOrder,c⁻¹Exponent} + coeff = term.coeff^n + return PNTerm{typeof(coeff),PNOrder}(c⁻¹exp(term) * n, coeff) +end + +function Base.:*( + x::Number, term::PNTerm{T,PNOrder,c⁻¹Exponent} +) where {T,PNOrder,c⁻¹Exponent} + coeff = x * term.coeff + return PNTerm{typeof(coeff),PNOrder,c⁻¹Exponent}(coeff) +end +Base.:*(term::PNTerm, x::Number) = x * term + +function Base.:/( + term::PNTerm{T,PNOrder,c⁻¹Exponent}, x::Number +) where {T,PNOrder,c⁻¹Exponent} + coeff = term.coeff / x + return PNTerm{typeof(coeff),PNOrder,c⁻¹Exponent}(coeff) +end + +function Base.:/( + x::Number, term::PNTerm{T,PNOrder,c⁻¹Exponent} +) where {T,PNOrder,c⁻¹Exponent} + coeff = x / term.coeff + return PNTerm{typeof(coeff),PNOrder}(-c⁻¹exp(term), coeff) +end + +function Base.:*( + term1::PNTerm{T1,PNOrder,c⁻¹E1}, term2::PNTerm{T2,PNOrder,c⁻¹E2} +) where {T1,T2,PNOrder,c⁻¹E1,c⁻¹E2} + c⁻¹Exponent = c⁻¹exp(term1) + c⁻¹exp(term2) + coeff = term1.coeff * term2.coeff + return PNTerm{typeof(coeff),PNOrder,c⁻¹Exponent}(coeff) +end + +function Base.:/( + term1::PNTerm{T1,PNOrder,c⁻¹E1}, term2::PNTerm{T2,PNOrder,c⁻¹E2} +) where {T1,T2,PNOrder,c⁻¹E1,c⁻¹E2} + c⁻¹Exponent = c⁻¹E1 - c⁻¹E2 + coeff = term1.coeff / term2.coeff + return PNTerm{typeof(coeff),PNOrder,c⁻¹Exponent}(coeff) +end + +@testitem "PNTerm algebra" begin + using DoubleFloats + using PostNewtonian: PostNewtonian, PNExpansionParameter, PNExpansion, pn_order + + for T ∈ [Float64, Float16, Double64] + pn = BBH(randn(T, 14), 9//2) + pn[:v] = T(1) / 5 + c = PNExpansionParameter(pn) + z = zero(T) + x = T(6) / 5 + y = T(17) / 5 + w = T(28) / 5 + v = T(27) / 13 + + # Test behavior of `c` as the basic PNTerm + for (term, c⁻¹exponent, coeff) ∈ ( + (c, -1, 1), + (c^2, -2, 1), + (x * c^2, -2, x), + (c^2 * x, -2, x), + (c^2 / x, -2, 1 / x), + ((x * c)^2, -2, x^2), + ((x * c^2) / c^4, 2, x), + ((x * c^2) / c^-4, -6, x), + ((x / c^2) / c^4, 6, x), + ((x / c^2) * (y / c^4), 6, x * y), + ) + @test PostNewtonian.c⁻¹exp(term) == c⁻¹exponent + @test term.coeff == coeff + @test term.coeff isa eltype(pn) + @test length(term) == 1 + @test eltype(term) === T + end + + # Test PNExpressions + @test_throws ArgumentError PNExpansion((), 0) + for (expr, expected) ∈ ( + (w - (x - y / c), (w - x, y)), + (w - (x / c - y), (w + y, -x)), + (x - y / c, (x, -y)), + (x / c - y, (-y, x)), + (x / c^6 + y / c, (z, y, z, z, z, z, x)), + (x / c^6 + y / c + w / c^10, (z, y, z, z, z, z, x, z, z, z)), + (x / c^6 - y / c, (z, -y, z, z, z, z, x)), + (x / c^6 - y / c - w, (-w, -y, z, z, z, z, x)), + (x / c^6 - y / c - w / c^10, (z, -y, z, z, z, z, x, z, z, z)), + (x / c^6 - (y / c - w), (w, -y, z, z, z, z, x)), + (x / c^6 - (y / c - w / c^10), (z, -y, z, z, z, z, x, z, z, z)), + (-(x / c^6) - y / c, (z, -y, z, z, z, z, -x)), + (-(x / c^6) - y / c - w, (-w, -y, z, z, z, z, -x)), + (-(x / c^6) - y / c - w / c^10, (z, -y, z, z, z, z, -x, z, z, z)), + ((x * c^2) / c^4 + y / c, (z, y, x)), + ((x * c^2) / c^4 + y / c + w, (w, y, x)), + ((x * c^2) / c^9 + y / c + w, (w, y, z, z, z, z, z, x)), + (w + (x * c^2) / c^4 + y / c, (w, y, x)), + (w + (x * c^2) / c^9 + y / c, (w, y, z, z, z, z, z, x)), + (w + ((x * c^2) / c^4 + y / c), (w, y, x)), + (w + ((x * c^2) / c^9 + y / c), (w, y, z, z, z, z, z, x)), + (((x * c^2) / c^4 + y / c + w) / c^3, (z, z, z, w, y, x)), + (((x * c^2) / c^4 + y / c + w) / c^5, (z, z, z, z, z, w, y, x)), + (((x * c^2) / c^7 + y / c + w) / c^5, (z, z, z, z, z, w, y, z, z, z)), + (v * (((x * c^2) / c^4 + y / c + w) / c^3), v .* (z, z, z, w, y, x)), + ((((x * c^2) / c^4 + y / c + w) / c^3) * v, v .* (z, z, z, w, y, x)), + ((((x * c^2) / c^4 + y / c + w) / c^3) / v, (z, z, z, w, y, x) .* (1 / v)), + ) + @test expr.coeffs == expected + @test pn_order(expr) == pn_order(pn) + @test sum(expr) == sum(expected) + @test eltype(expr) === T + end + + # Can't make a PNExpression with positive exponents + @test_throws ArgumentError x * c + y + @test_throws ArgumentError x * c + y / c + @test_throws ArgumentError x * c^2 + y + @test_throws ArgumentError x * c^2 + y / c + @test_throws ArgumentError y + x * c + @test_throws ArgumentError y / c + x * c + @test_throws ArgumentError y + x * c^2 + @test_throws ArgumentError y / c + x * c^2 + @test_throws ArgumentError x * c^2 + (y / c + z / c^2) + end +end diff --git a/src/core/core.jl b/src/core/core.jl index 86bc8896..85510118 100644 --- a/src/core/core.jl +++ b/src/core/core.jl @@ -1,3 +1,7 @@ include("utilities/misc.jl") include("utilities/truncated_series_monoid.jl") include("utilities/truncated_series_inversion.jl") +include("PNTerm.jl") +include("PNExpansion.jl") +# include("PNExpression.jl") +include("PNReference.jl") diff --git a/test/pn_expansion.jl b/test/pn_expansion.jl deleted file mode 100644 index 8497a302..00000000 --- a/test/pn_expansion.jl +++ /dev/null @@ -1,120 +0,0 @@ -@testitem "PNTerm algebra" begin - using DoubleFloats - using PostNewtonian: PNExpansion - - for T ∈ [Float64, Float16, Double64] - pn = rand(BBH; v=T(1) / 5, PNOrder=9//2) - c = PNExpansionParameter(pn) - z = zero(T) - x = T(6) / 5 - y = T(17) / 5 - w = T(28) / 5 - v = T(27) / 13 - - # Test behavior of `c` as the basic PNTerm - for (term, c⁻¹exponent, coeff) ∈ ( - (c, -1, 1), - (c^2, -2, 1), - (x * c^2, -2, x), - (c^2 * x, -2, x), - (c^2 / x, -2, 1 / x), - ((x * c)^2, -2, x^2), - ((x * c^2) / c^4, 2, x), - ((x * c^2) / c^-4, -6, x), - ((x / c^2) / c^4, 6, x), - ((x / c^2) * (y / c^4), 6, x * y), - ) - @test PostNewtonian.c⁻¹exp(term) == c⁻¹exponent - @test term.coeff == coeff - @test term.coeff isa eltype(pn) - @test length(term) == 1 - @test eltype(term) === T - end - - # Test PNExpressions - @test_throws ArgumentError PNExpansion((), 0) - for (expr, expected) ∈ ( - (w - (x - y / c), (w - x, y)), - (w - (x / c - y), (w + y, -x)), - (x - y / c, (x, -y)), - (x / c - y, (-y, x)), - (x / c^6 + y / c, (z, y, z, z, z, z, x)), - (x / c^6 + y / c + w / c^10, (z, y, z, z, z, z, x, z, z, z)), - (x / c^6 - y / c, (z, -y, z, z, z, z, x)), - (x / c^6 - y / c - w, (-w, -y, z, z, z, z, x)), - (x / c^6 - y / c - w / c^10, (z, -y, z, z, z, z, x, z, z, z)), - (x / c^6 - (y / c - w), (w, -y, z, z, z, z, x)), - (x / c^6 - (y / c - w / c^10), (z, -y, z, z, z, z, x, z, z, z)), - (-(x / c^6) - y / c, (z, -y, z, z, z, z, -x)), - (-(x / c^6) - y / c - w, (-w, -y, z, z, z, z, -x)), - (-(x / c^6) - y / c - w / c^10, (z, -y, z, z, z, z, -x, z, z, z)), - ((x * c^2) / c^4 + y / c, (z, y, x)), - ((x * c^2) / c^4 + y / c + w, (w, y, x)), - ((x * c^2) / c^9 + y / c + w, (w, y, z, z, z, z, z, x)), - (w + (x * c^2) / c^4 + y / c, (w, y, x)), - (w + (x * c^2) / c^9 + y / c, (w, y, z, z, z, z, z, x)), - (w + ((x * c^2) / c^4 + y / c), (w, y, x)), - (w + ((x * c^2) / c^9 + y / c), (w, y, z, z, z, z, z, x)), - (((x * c^2) / c^4 + y / c + w) / c^3, (z, z, z, w, y, x)), - (((x * c^2) / c^4 + y / c + w) / c^5, (z, z, z, z, z, w, y, x)), - (((x * c^2) / c^7 + y / c + w) / c^5, (z, z, z, z, z, w, y, z, z, z)), - (v * (((x * c^2) / c^4 + y / c + w) / c^3), v .* (z, z, z, w, y, x)), - ((((x * c^2) / c^4 + y / c + w) / c^3) * v, v .* (z, z, z, w, y, x)), - ((((x * c^2) / c^4 + y / c + w) / c^3) / v, (z, z, z, w, y, x) .* (1 / v)), - ) - @test expr.coeffs == expected - @test pn_order(expr) == pn_order(pn) - @test sum(expr) == sum(expected) - @test eltype(expr) === T - end - - # Can't make a PNExpression with positive exponents - @test_throws ArgumentError x * c + y - @test_throws ArgumentError x * c + y / c - @test_throws ArgumentError x * c^2 + y - @test_throws ArgumentError x * c^2 + y / c - @test_throws ArgumentError y + x * c - @test_throws ArgumentError y / c + x * c - @test_throws ArgumentError y + x * c^2 - @test_throws ArgumentError y / c + x * c^2 - @test_throws ArgumentError x * c^2 + (y / c + z / c^2) - end -end - -@testitem "PNExpansion algebra" begin - using Symbolics - using PostNewtonian: PNExpansion - - for N1 ∈ 1:9 - for N2 ∈ 1:9 - for NMax ∈ max(N1, N2):(N1 + N2 + 3) - @variables c⁻¹ x[1:N1] y[1:N2] z - poly(e::PNExpansion) = sum(e[i] * c⁻¹^(i - 1) for i ∈ 1:length(e)) - eˣ = PNExpansion(tuple(x...), NMax) - eʸ = PNExpansion(tuple(y...), NMax) - - # Test sums - polysum = simplify(poly(eˣ + eʸ); expand=true) - sumpoly = simplify(poly(eˣ) + poly(eʸ); expand=true) - Δ = simplify(polysum - sumpoly; expand=true) - @test iszero(Δ) - @test_throws ArgumentError eˣ + PNExpansion(tuple(z, x...), NMax + 1) - @test_throws ArgumentError PNExpansion(tuple(z, x...), NMax + 1) + eˣ - - # Test products - polyprod = simplify(poly(eˣ * eʸ); expand=true) - prodpoly = simplify( - substitute( - simplify(poly(eˣ) * poly(eʸ); expand=true), - Dict([c⁻¹^n => 0 for n ∈ NMax:(2NMax + 3)]), - ); - expand=true, - ) - Δ = simplify(polyprod - prodpoly; expand=true) - @test iszero(Δ) - @test_throws ArgumentError eˣ * PNExpansion(tuple(z, x...), NMax + 1) - @test_throws ArgumentError PNExpansion(tuple(z, x...), NMax + 1) * eˣ - end - end - end -end From 2b3bf2a6ba7e073432ae0d6fb4187e64c795794b Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Tue, 1 Jul 2025 00:52:29 -0400 Subject: [PATCH 27/77] Simplify docstring tests --- src/core/utilities/InlineExports.jl | 76 ++++++++--------------------- 1 file changed, 19 insertions(+), 57 deletions(-) diff --git a/src/core/utilities/InlineExports.jl b/src/core/utilities/InlineExports.jl index ad1c0e3c..6f20f686 100644 --- a/src/core/utilities/InlineExports.jl +++ b/src/core/utilities/InlineExports.jl @@ -7,7 +7,7 @@ import Base: @__doc__ export @export, @public -quote +eval(quote """ @export @@ -16,7 +16,7 @@ quote macro $(Symbol("export"))(expr::Expr) return esc(expr) end -end |> eval +end) """ @public @@ -35,7 +35,7 @@ import Base: @__doc__ export @export, @public -quote +eval(quote """ @export @@ -64,7 +64,7 @@ quote macro $(Symbol("export"))(expr::Expr) return handle(expr, :export) end -end |> eval +end) if VERSION < v"1.11" using ..NoExport: @public @@ -124,13 +124,14 @@ handle(::Val{:function}, expr) = handle(expr.args[1]) handle(::Val{:where}, expr) = handle(expr.args[1]) handle(::Val{:macro}, expr) = Symbol("@", handle(expr.args[1])) handle(::Val{:struct}, expr) = handle(expr.args[2]) -handle(::Union{Val{:abstract}, Val{:primitive}}, expr) = handle(expr.args[1]) +handle(::Union{Val{:abstract},Val{:primitive}}, expr) = handle(expr.args[1]) handle(::Val{:<:}, expr) = handle(expr.args[1]) handle(::Val{:curly}, expr) = handle(expr.args[1]) handle(::Val{:call}, expr) = handle(expr.args[1]) function handle(::Val{:macrocall}, expr) - if expr.args[1]==Symbol("@doc") || (expr.args[1] == Core.GlobalRef(Core, Symbol("@doc"))) + if expr.args[1]==Symbol("@doc") || + (expr.args[1] == Core.GlobalRef(Core, Symbol("@doc"))) if length(expr.args) != 4 error("@doc expression found with $(length(expr.args)) args:\n$expr") end @@ -145,23 +146,6 @@ end # module InlineExports @testitem "InlineExports" begin using Markdown: @doc_str - # This code is taken from julia/test/docs.jl - function docstrings_equal(d1, d2; debug=true) - io1 = IOBuffer() - io2 = IOBuffer() - show(io1, MIME"text/markdown"(), d1) - show(io2, MIME"text/markdown"(), d2) - s1 = String(take!(io1)) - s2 = String(take!(io2)) - if debug && s1 != s2 - print(s1) - println("--------------------------------------------------------------------------------") - print(s2) - println("================================================================================") - end - return s1 == s2 - end - module Bla using PostNewtonian.InlineExports: @export, @public @@ -263,38 +247,16 @@ end # module InlineExports # @test Base.hasproperty(Bla, :X) # to do with `InlineExports`?! # @test Base.hasproperty(Bla, :h) # Anyway, we used them in tests above... 🤷 - # doc"" plays silly games with newlines, so we just define these three in long form. - """ - f(x) - - Here a doc! - """ - const f_test=51 - - """ - g(x) - - There a doc! - """ - const g_test=52 - - """ - h(x) - - Everywhere a doc, doc! - """ - const h_test=53 - - @test docstrings_equal(@doc(Bla.a), doc"`const a` doc") - @test docstrings_equal(@doc(Bla.b), doc"`const b` doc") - @test docstrings_equal(@doc(Bla.c), doc"`const c` doc") - @test docstrings_equal(@doc(Bla.S), doc"`abstract type S` doc") - @test docstrings_equal(@doc(Bla.U), doc"`abstract type U` doc") - @test docstrings_equal(@doc(Bla.W), doc"`abstract type W` doc") - @test docstrings_equal(@doc(Bla.T), doc"`struct T` doc") - @test docstrings_equal(@doc(Bla.V), doc"`struct V` doc") - @test docstrings_equal(@doc(Bla.X), doc"`struct X` doc") - @test docstrings_equal(@doc(Bla.f), @doc(f_test)) - @test docstrings_equal(@doc(Bla.g), @doc(g_test)) - @test docstrings_equal(@doc(Bla.h), @doc(h_test)) + @test repr(@doc(Bla.a)) == "`const a` doc\n" + @test repr(@doc(Bla.b)) == "`const b` doc\n" + @test repr(@doc(Bla.c)) == "`const c` doc\n" + @test repr(@doc(Bla.S)) == "`abstract type S` doc\n" + @test repr(@doc(Bla.U)) == "`abstract type U` doc\n" + @test repr(@doc(Bla.W)) == "`abstract type W` doc\n" + @test repr(@doc(Bla.T)) == "`struct T` doc\n" + @test repr(@doc(Bla.V)) == "`struct V` doc\n" + @test repr(@doc(Bla.X)) == "`struct X` doc\n" + @test repr(@doc(Bla.f)) == """```\nf(x)\n```\n\nHere a doc!\n""" + @test repr(@doc(Bla.g)) == """```\ng(x)\n```\n\nThere a doc!\n""" + @test repr(@doc(Bla.h)) == """```\nh(x)\n```\n\nEverywhere a doc, doc!\n""" end From a209c2120e5ee52a07dbae7acffae32115d577b0 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Tue, 1 Jul 2025 09:50:48 -0400 Subject: [PATCH 28/77] Add `constant_convert` --- src/PostNewtonian.jl | 6 ++- src/pn_systems/BBH.jl | 10 ++++- src/pn_systems/BHNS.jl | 10 ++++- src/pn_systems/FDPNsystem.jl | 29 +++++++++++-- src/pn_systems/NSNS.jl | 10 ++++- src/pn_systems/PNSystem.jl | 84 ++++++++++++++++++++++-------------- 6 files changed, 109 insertions(+), 40 deletions(-) diff --git a/src/PostNewtonian.jl b/src/PostNewtonian.jl index 453e31ac..fd9cbe60 100644 --- a/src/PostNewtonian.jl +++ b/src/PostNewtonian.jl @@ -16,13 +16,17 @@ using TestItems: @testitem include("core/utilities/InlineExports.jl") using .InlineExports: @public, @export -# These are definitions / aliases that are common in PN literature and are used throughout +# These are definitions / aliases that are common in PN literature and/or used throughout # this package. @public const ln = log @public const 𝒾 = im # Type this as `\scre` @public const γₑ = Base.MathConstants.γ # Distinguish Euler's constant from `γₚₙ = M/r` public ζ3 # Defined in `core/utilities/misc.jl` +# We will use these types to ensure that precision is preserved in PN expressions. +@public const exact_real = Union{Integer,Rational,AbstractIrrational} +@public const exact_number = Union{exact_real,Complex{<:exact_real}} + include("pn_systems/pn_systems.jl") include("core/core.jl") include("literature/literature.jl") diff --git a/src/pn_systems/BBH.jl b/src/pn_systems/BBH.jl index 4688f2fb..5d41e924 100644 --- a/src/pn_systems/BBH.jl +++ b/src/pn_systems/BBH.jl @@ -71,7 +71,7 @@ end Λ₂(pnsystem::BBH) = zero(pnsystem) @testitem "BBH constructors" begin - using PostNewtonian: state + using PostNewtonian: state, constant_convert using Quaternionic pnA = BBH(; @@ -80,6 +80,8 @@ end @test eltype(pnA) == Float32 @test state(pnA) == Float32[1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; 1.0; 0.0; 0.0; 0.0; 0.23; 0.0] + @test constant_convert(pnA, π) isa Float32 + @test constant_convert(pnA, π) == Float32(π) pnB = BBH(; M₁=1.0f0, @@ -91,6 +93,8 @@ end ) @test state(pnB) == Float32[1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; 1.0; 0.0; 0.0; 0.0; 0.23; 9.0] + @test constant_convert(pnB, π) isa Float32 + @test constant_convert(pnB, π) == Float32(π) R = randn(RotorF32) pn1 = BBH(; @@ -102,6 +106,8 @@ end v=0.23f0, ) @test state(pn1) ≈ [1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; components(R)...; 0.23; 0.0] + @test constant_convert(pn1, π) isa Float32 + @test constant_convert(pn1, π) == Float32(π) pn2 = BBH(; M₁=1.0f0, @@ -113,6 +119,8 @@ end Φ=9.0f0, ) @test state(pn2) ≈ [1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; components(R)...; 0.23; 9.0] + @test constant_convert(pn2, π) isa Float32 + @test constant_convert(pn2, π) == Float32(π) state(pn1)[end] = 9.0f0 @test state(pn1) == state(pn2) diff --git a/src/pn_systems/BHNS.jl b/src/pn_systems/BHNS.jl index 23ba4fd1..a93d7653 100644 --- a/src/pn_systems/BHNS.jl +++ b/src/pn_systems/BHNS.jl @@ -67,7 +67,7 @@ end Λ₁(pnsystem::BHNS) = zero(pnsystem) @testitem "BHNS constructors" begin - using PostNewtonian: state + using PostNewtonian: state, constant_convert using Quaternionic # minimal constructor: default Φ=0, R=Rotor(1) @@ -97,6 +97,8 @@ end 0.0; 4.0 ] + @test constant_convert(pnA, π) isa Float32 + @test constant_convert(pnA, π) == Float32(π) # explicit orbital phase pnB = BHNS( @@ -125,6 +127,8 @@ end 9.0; 4.0 ] + @test constant_convert(pnB, π) isa Float32 + @test constant_convert(pnB, π) == Float32(π) # custom rotor, default Φ R = randn(RotorF32) @@ -139,6 +143,8 @@ end ) @test state(pn1) ≈ [1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; components(R)...; 0.23; 0.0; 4.0] + @test constant_convert(pn1, π) isa Float32 + @test constant_convert(pn1, π) == Float32(π) # custom rotor and Φ pn2 = BHNS( @@ -153,6 +159,8 @@ end ) @test state(pn2) ≈ [1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; components(R)...; 0.23; 9.0; 4.0] + @test constant_convert(pn2, π) isa Float32 + @test constant_convert(pn2, π) == Float32(π) # mutating the second-to-last element (Φ) to match pn2 pn1[end - 1] = 9.0f0 diff --git a/src/pn_systems/FDPNsystem.jl b/src/pn_systems/FDPNsystem.jl index 81ae0a21..22b2f5ee 100644 --- a/src/pn_systems/FDPNsystem.jl +++ b/src/pn_systems/FDPNsystem.jl @@ -1,7 +1,7 @@ """ - FDPNSystem{NT, PN, PNOrder} <: PNSystem{FDNode, Vector{FDNode}, PNOrder} + FDPNSystem{NT, PN, PNOrder} <: PNSystem{Node, Vector{Node}, PNOrder} -A `PNSystem` that contains information as variables from +A `PNSystem` that contains information as variables of type `Node` from [`FastDifferentiation.jl`](https://docs.juliahub.com/General/FastDifferentiation/stable/). Note that this type also involves the type parameter `PN`, which is actually the type of a @@ -11,6 +11,15 @@ that eventually get fed into (and will be passed out from) functions that use th One important example of what this type is used for is computing the derivative of the orbital binding energy, `𝓔′` — and in particular, for generating the corresponding function method to apply to a given `PNSystem`. + +!!! warning + + Because of the structure of the type parameters, most methods defined for a general + `PNSystem` will use `Node` as the number type. This may be correct in many cases, but + when `Node` is applied to an integer, for example, it will generally result in a + `Float64` value, rather than the `NT` type parameter. For this reason many methods + will need to be specialized for `FDPNSystem` types, using almost the same definition, + but with the additional `FD` prefix inserted throughout. """ @export struct FDPNSystem{NT,PNOrder,PN<:PNSystem{NT,PNOrder}} <: PNSystem{FDNode,PNOrder,Vector{FDNode}} @@ -24,6 +33,10 @@ end state(pnsystem::FDPNSystem) = pnsystem.state +# This is an example of where we need to specialize the method for `FDPNSystem`, as +# explained in the docstring. +Base.eltype(::FDPNSystem{FT}) where {FT} = FT + symbols(pnsystem::FDPNSystem{NT,PNOrder,PN}) where {NT,PNOrder,PN} = symbols(PN) symbols(::Type{T}) where {NT,PNOrder,PN,T<:FDPNSystem{NT,PNOrder,PN}} = symbols(PN) @@ -46,9 +59,13 @@ function symbol_index(::Type{T}, ::Val{S}) where {T<:FDPNSystem,S} end end -Base.eltype(::FDPNSystem{FT}) where {FT} = FT +# These are more examples of where we need to specialize the method for `FDPNSystem`, as +# explained in the docstring. +constant_convert(::T, x::exact_number) where {NT,T<:FDPNSystem{NT}} = NT(x) +constant_convert(::T, x::NT) where {NT,T<:FDPNSystem{NT}} = x @testitem "FDPNSystem" begin + using PostNewtonian: constant_convert @testset "BBH" begin PNOrder = 7//2 for NT ∈ (Float16, Float64) @@ -59,6 +76,8 @@ Base.eltype(::FDPNSystem{FT}) where {FT} = FT @test eltype(fdpnsystem) == NT @test symbols(fdpnsystem) == symbols(bbh) @test length(fdpnsystem) == 14 + @test constant_convert(fdpnsystem, π) isa NT + @test constant_convert(fdpnsystem, π) == NT(π) end end @testset "BHNS" begin @@ -71,6 +90,8 @@ Base.eltype(::FDPNSystem{FT}) where {FT} = FT @test eltype(fdpnsystem) == NT @test symbols(fdpnsystem) == symbols(bhns) @test length(fdpnsystem) == 15 + @test constant_convert(fdpnsystem, π) isa NT + @test constant_convert(fdpnsystem, π) == NT(π) end end @testset "NSNS" begin @@ -83,6 +104,8 @@ Base.eltype(::FDPNSystem{FT}) where {FT} = FT @test eltype(fdpnsystem) == NT @test symbols(fdpnsystem) == symbols(nsns) @test length(fdpnsystem) == 16 + @test constant_convert(fdpnsystem, π) isa NT + @test constant_convert(fdpnsystem, π) == NT(π) end end end diff --git a/src/pn_systems/NSNS.jl b/src/pn_systems/NSNS.jl index e3f12b0f..921af0d7 100644 --- a/src/pn_systems/NSNS.jl +++ b/src/pn_systems/NSNS.jl @@ -55,7 +55,7 @@ for (i, (symbol, ascii_symbol)) ∈ enumerate(zip(symbols(NSNS), ascii_symbols(N end @testitem "NSNS constructors" begin - using PostNewtonian: state + using PostNewtonian: state, constant_convert using Quaternionic # minimal constructor: default Φ=0, R=Rotor(1) @@ -87,6 +87,8 @@ end 5.0; 6.0 ] + @test constant_convert(pnA, π) isa Float32 + @test constant_convert(pnA, π) == Float32(π) # explicit orbital phase pnB = NSNS( @@ -117,6 +119,8 @@ end 5.0; 6.0 ] + @test constant_convert(pnB, π) isa Float32 + @test constant_convert(pnB, π) == Float32(π) # custom rotor, default Φ R = randn(RotorF32) @@ -132,6 +136,8 @@ end ) @test state(pn1) ≈ [1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; components(R)...; 0.23; 0.0; 5.0; 6.0] + @test constant_convert(pn1, π) isa Float32 + @test constant_convert(pn1, π) == Float32(π) # custom rotor and Φ pn2 = NSNS( @@ -147,6 +153,8 @@ end ) @test state(pn2) ≈ [1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; components(R)...; 0.23; 9.0; 5.0; 6.0] + @test constant_convert(pn2, π) isa Float32 + @test constant_convert(pn2, π) == Float32(π) # mutating the second-to-last element (Φ) to match pn2 pn1[end - 2] = 9.0f0 diff --git a/src/pn_systems/PNSystem.jl b/src/pn_systems/PNSystem.jl index d6be7234..8a5178cd 100644 --- a/src/pn_systems/PNSystem.jl +++ b/src/pn_systems/PNSystem.jl @@ -18,6 +18,48 @@ should be carried out when using the given object. @export abstract type PNSystem{NT<:Number,PNOrder,ST<:AbstractVector{NT}} <: AbstractVector{NT} end +""" + pnsystem::PNSystem(; kwargs...) + +State-modifying copy constructor for `PNSystem` objects. + +!!! warning + + This function is slow; it should not be used in performance-critical code. + +Note that this cannot modify the type's parameters — the number type `NT`, the `PNOrder` of +the system, or the state type `ST`. However, it can modify any of the state variables by +symbol or by ASCII symbol. This function will raise an AssertionError if any of the keys in +`kwargs` is not a valid symbol for the given `PNSystem` type. + +```jldoctest +julia> using PostNewtonian: BBH + +julia> pnsystem = BBH(ones(14)/2; PNOrder=7//2) +BBH{Vector{Float64}, 7//2}([0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5]) + +julia> pnsystem2 = pnsystem(M₁=0.2, M₂=0.8, chi1x=0.1) +BBH{Vector{Float64}, 7//2}([0.2, 0.8, 0.1, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5]) +``` +""" +function (pnsystem::PNSystem{NT,PNOrder,ST})(; kwargs...) where {NT,PNOrder,ST} + all_symbols = Set(symbols(pnsystem)) ∪ Set(ascii_symbols(pnsystem)) + @assert keys(kwargs) ⊆ all_symbols ( + "PNSystem of type $(typeof(pnsystem)) does not have these symbols, which were " * + "input as keyword arguments:\n $(setdiff(keys(kwargs), all_symbols))\n" * + "Maybe you passed `String`s instead of `Symbol`s?\n" * + "The available symbols for this type are\n" * + " $(symbols(pnsystem))\n" * + "and their ASCII equivalents:\n" * + " $(ascii_symbols(pnsystem))" + ) + state = Tuple( + get(kwargs, symbol, get(kwargs, ascii_symbol, pnsystem[symbol])) for + (symbol, ascii_symbol) ∈ zip(symbols(pnsystem), ascii_symbols(pnsystem)) + ) + typeof(pnsystem)(ST(SVector(state))) +end + """ state(pnsystem::PNSystem) @@ -78,39 +120,15 @@ function ascii_symbols(::Type{T}) where {T<:PNSystem} end """ - pnsystem::PNSystem(; kwargs...) - -State-modifying copy constructor for `PNSystem` objects. - -Note that this cannot modify the type's parameters, including the number type `NT`, the -state type `ST`, or the `PNOrder` of the system. However, it can modify any of the state -variables by symbol or by ASCII symbol. This function will raise an AssertionError if -any of the keys in `kwargs` is not a valid symbol for the given `PNSystem` type. - -```jldoctest -julia> using PostNewtonian: BBH + constant_convert(pnsystem, x) -julia> pnsystem = BBH(ones(14)/2; PNOrder=7//2) -BBH{Vector{Float64}, 7//2}([0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5]) +Convert `x` to the type of the constant numbers to be used with `pnsystem`. Usually, this +type will be the first template parameter. However, when that parameter is a `Dual`, for +example, we don't want to bother defining constants as `Dual`, so we will just define them +as the `Dual`'s underlying type in PostNewtonianForwardDiffExt. -julia> pnsystem2 = pnsystem(M₁=0.2, M₂=0.8, chi1x=0.1) -BBH{Vector{Float64}, 7//2}([0.2, 0.8, 0.1, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5]) -``` +You generally will not need to call this function directly; it will be used automatically by +the [`@pn_expression`](@ref) macro. """ -function (pnsystem::PNSystem{NT,PNOrder,ST})(; kwargs...) where {NT,PNOrder,ST} - all_symbols = Set(symbols(pnsystem)) ∪ Set(ascii_symbols(pnsystem)) - @assert keys(kwargs) ⊆ all_symbols ( - "PNSystem of type $(typeof(pnsystem)) does not have these symbols, which were " * - "input as keyword arguments:\n $(setdiff(keys(kwargs), all_symbols))\n" * - "Maybe you passed `String`s instead of `Symbol`s?\n" * - "The available symbols for this type are\n" * - " $(symbols(pnsystem))\n" * - "and their ASCII equivalents:\n" * - " $(ascii_symbols(pnsystem))" - ) - state = Tuple( - get(kwargs, symbol, get(kwargs, ascii_symbol, pnsystem[symbol])) for - (symbol, ascii_symbol) ∈ zip(symbols(pnsystem), ascii_symbols(pnsystem)) - ) - typeof(pnsystem)(ST(SVector(state))) -end +@public constant_convert(::T, x::exact_number) where {NT,T<:PNSystem{NT}} = NT(x) +constant_convert(::T, x::NT) where {NT,T<:PNSystem{NT}} = x From d80a831523e88e48911ef5309c1c824854693867 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Tue, 1 Jul 2025 16:32:25 -0400 Subject: [PATCH 29/77] Remove `ln` from main namespace --- src/PostNewtonian.jl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/PostNewtonian.jl b/src/PostNewtonian.jl index fd9cbe60..13312e6f 100644 --- a/src/PostNewtonian.jl +++ b/src/PostNewtonian.jl @@ -12,16 +12,15 @@ using TestItems: @testitem # While I wait for https://github.com/dalum/InlineExports.jl/pull/2 to be merged, we do the # following rather than import from the package itself. Once that is merged, we can add the # package as a dependency, uncomment the line above, remove this block, and remove the -# `InlineExports.jl` file. +# following file. include("core/utilities/InlineExports.jl") using .InlineExports: @public, @export # These are definitions / aliases that are common in PN literature and/or used throughout # this package. -@public const ln = log -@public const 𝒾 = im # Type this as `\scre` +@public const 𝒾 = im # Type this as `\scri` @public const γₑ = Base.MathConstants.γ # Distinguish Euler's constant from `γₚₙ = M/r` -public ζ3 # Defined in `core/utilities/misc.jl` +public ζ3 # Defined and documented in `core/utilities/misc.jl` # We will use these types to ensure that precision is preserved in PN expressions. @public const exact_real = Union{Integer,Rational,AbstractIrrational} From f5f4b09611ba899089c616cff3ee3d057c783333 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Tue, 1 Jul 2025 16:33:44 -0400 Subject: [PATCH 30/77] Get PNReference mostly working --- src/core/PNExpression.jl | 192 +++++++++++++++++++++++++++++++++++ src/core/PNReference.jl | 28 ++--- src/core/core.jl | 2 +- src/core/utilities/macros.jl | 2 +- 4 files changed, 209 insertions(+), 15 deletions(-) diff --git a/src/core/PNExpression.jl b/src/core/PNExpression.jl index 8b137891..8a3988ef 100644 --- a/src/core/PNExpression.jl +++ b/src/core/PNExpression.jl @@ -1 +1,193 @@ +public PNExpressionArithmetic +baremodule PNExpressionArithmetic +import Base +#using ..PostNewtonian: constant_convert, PNSystem +using PostNewtonian: constant_convert, PNSystem +using PostNewtonian.InlineExports: @export +using Base: @inline + +@export @inline ln(pnsystem::PNSystem, x) = Base.log(constant_convert(pnsystem, x)) +@export const log = ln + +@export @inline √(pnsystem::PNSystem, x) = Base.sqrt(constant_convert(pnsystem, x)) +@export const sqrt = √ + +@export @inline (+)(pnsystem::PNSystem, x, y) = Base.:+(constant_convert(pnsystem, x), y) +@export @inline (-)(pnsystem::PNSystem, x, y) = Base.:-(constant_convert(pnsystem, x), y) +@export @inline (*)(pnsystem::PNSystem, x, y) = Base.:*(constant_convert(pnsystem, x), y) +@export @inline (/)(pnsystem::PNSystem, x, y) = Base.:/(constant_convert(pnsystem, x), y) +@export @inline (^)(pnsystem::PNSystem, x, n) = Base.:^(constant_convert(pnsystem, x), n) + +@export @inline function (+)(pnsystem::PNSystem, w, x, y, z...) + return Base.:+(constant_convert(pnsystem, w), x, y, z...) +end +@export @inline function (*)(pnsystem::PNSystem, w, x, y, z...) + return Base.:*(constant_convert(pnsystem, w), x, y, z...) +end + +end # baremodule PNExpressionArithmetic + +const pnexpressionarithmetic_functions = filter( + s -> isa(getfield(PNExpressionArithmetic, s), Function), names(PNExpressionArithmetic) +) + +@doc """ + PNExpressionArithmetic + +This module provides arithmetic operations to be used inside `@pn_expression` modules. + +It is intentionally very restrictive, so that it only includes the basic arithmetic +operations that are used in post-Newtonian expressions, and even then only in modified forms +that take a `PNSystem` as the first argument. The defined operations are + + $(pnexpressionarithmetic_functions) + +This module is not intended to be used directly, but is imported by the +[`@pn_expression`](@ref) macro, and its methods are called by [`@pn_expansion`](@ref) to +ensure that the arithmetic operations preserve the number type of the input `PNSystem`. +""" +PNExpressionArithmetic + +macro pn_expression(arg_index, func=:(nothing)) + # Note that macros secretly get two extra variables: `__module__` and `__source__`. + # These refer to the place where the macro was called. The second is useful for + # debugging, but the first is crucial here, because it lets us see what's defined in the + # module in which this macro was used. + + # First, we just search for everything defined in the module where this macro is called. + all_names = if VERSION < v"1.12.0-beta1" + @warn "Some `names` in `@pn_expression` at $(__source__)\n" * + "may not be found in Julia 1.11 or earlier; for now, be sure to use\n" * + "`import` rather than `using` inside `@pn_reference` modules." maxlog=10 + names(__module__; all=true, imported=true) + else + names(__module__; all=true, imported=true, usings=true) + end + # Next, we narrow that list down to callable objects defined in the `__module__` where + # this macro was used. Specifically, we filter for those objects that take one + # `PNSystem`, and nothing else, as an argument. These will be valid targets for + # replacement in the body that `@pn_expression` is applied to. + pnsystem_functions = filter( + function (name) + if isdefined(__module__, name) + f = getfield(__module__, name) + if isa(f, Base.Callable) + if !isempty(methods(f, Tuple{PNSystem}, __module__)) + return true + end + end + end + return false + end, all_names + ) + return esc(pn_expression(arg_index, func, pnsystem_functions, __module__, __source__)) +end + +function pn_expression(arg_index, func, pnsystem_functions, __module__, __source__) + # Handle the default argument + if func === :(nothing) + arg_index, func = 1, arg_index + end + + # Check to make sure that `arg_index` is an integer; we'll check that it's valid below. + if !isa(arg_index, Integer) + error( + "The optional argument to `@pn_expression` must be an integer, not " * + "$(typeof(arg_index))\nThe incorrect application is at $(__source__).", + ) + end + + # Check to make sure that `func` is a function-definition expression + if !MacroTools.isdef(func) + error( + "The `@pn_expression` macro can only be applied to function definitions.\n" * + "The incorrect application is at $(__source__).", + ) + end + + # MacroTools has this nice function to parse function definitions so we can operate on + # each piece separately. + splitfunc = MacroTools.splitdef(func) + + # Check that the argument index is valid + if arg_index < 1 || arg_index > length(splitfunc[:args]) + error( + "Invalid argument index $arg_index for function $(splitfunc[:name]); " * + "it must be between 1 and $(length(splitfunc[:args]))\n" * + "The incorrect application is at $(__source__).", + ) + end + + # Get the name of the PNSystem argument + pnsystem = MacroTools.namify(splitfunc[:args][arg_index]) + + # Append a keyword argument `pn_expansion_reducer` to the function signature, with + # default value `Val(sum)`. The `Val` is captured in the "where" parameter + # `PNExpansionReducer` + splitfunc[:kwargs] = [ + splitfunc[:kwargs] + :($(Expr(:kw, :(pn_expansion_reducer::Val{PNExpansionReducer}), :(Val(Base.sum))))) + ] + + # Add `PNExpansionReducer` to the `where` clause (see previous comment) + splitfunc[:whereparams] = (splitfunc[:whereparams]..., :PNExpansionReducer) + + # Now look for any of the `pnsystem_functions` in the body of the function and prepare + # an entry for the `let` block that will surround the body on output. + pnsystem_function_exprs = [ + :($s = $s($pnsystem)) for + s ∈ filter(s -> MacroTools.inexpr(splitfunc[:body], s), pnsystem_functions) + ] + + # Next, add `pnsystem` as the argument to each @pn_expansion call... + new_body = MacroTools.postwalk(splitfunc[:body]) do x + if MacroTools.isexpr(x, :macrocall) && + x.args[1] == Symbol("@pn_expansion") && + !isa(x.args[end - 1], Symbol) # ...unless there's already one there + x′ = deepcopy(x) + insert!(x′.args, length(x′.args), pnsystem) + x′ + else + x + end + end + + # Now expand all the macros inside the body + new_body = macroexpand(__module__, new_body; recursive=true) + + # # Next, we walk the expression tree and find any function calls to one of the + # # `pnexpressionarithmetic_functions`, and insert `pnsystem` as the first argument. + new_body = MacroTools.postwalk(new_body) do x + if iscall(x, pnexpressionarithmetic_functions) + @show x.head x.args + println() + insert!(x.args, 2, (pnsystem)) + x + else + x + end + end + + # Finally, include the `let` statements so that the functions can all be used as plain + # symbols without having to call them on `pnsystem`, but do so without burying it in a + # code block. + new_body = MacroTools.unblock(quote + #@fastmath + let $(pnsystem_function_exprs...) + $(new_body) + end + end) + + # Replace the old body with the new one + splitfunc[:body] = new_body + + # And reassemble the function definition + return MacroTools.combinedef(splitfunc) +end + +# baremodule Mod +# import Base +# (+)(x, y) = Base.:+(Base.BigFloat(x), y) +# const x = Base.π + 3 +# end diff --git a/src/core/PNReference.jl b/src/core/PNReference.jl index 96d93a09..3dd17b49 100644 --- a/src/core/PNReference.jl +++ b/src/core/PNReference.jl @@ -69,10 +69,11 @@ function pn_reference(expr) expr.args[2], # This is the name of the module Expr( # This is the new module body :block, - :(import Base), - :(eval(x) = Core.eval(Mod, x)), - :(include(p) = Base.include(Mod, p)), - :(using PostNewtonian: @pn_expression, @pn_expansion), + :(using Base: Base, Val), + :(eval(x::Expr) = Core.eval(Mod, x)), + :(include(p::AbstractString) = Base.include(Mod, p)), + :(using PostNewtonian: @pn_expression, @pn_expansion, 𝒾, γₑ, ζ3), + :(using PostNewtonian.PNExpressionArithmetic), expr.args[3].args..., # The original module body ), ) @@ -119,15 +120,15 @@ end using PostNewtonian: @pn_reference input = @macroexpand @pn_reference module Einstein1918 - using PostNewtonian: G, c, M, χ⃗₁, χ⃗₂, v, pn_order - using Quaternionic: absvec + import PostNewtonian: G, c, M, χ⃗₁, χ⃗₂, v, pn_order + import Quaternionic: absvec @pn_expression χ₁(pnsystem) = absvec(χ⃗₁) @pn_expression χ₂(pnsystem) = absvec(χ⃗₂) const x = 3 module InnerMod const z = 4 end - using .InnerMod: z + import .InnerMod: z @pn_expression function y(pnsystem) G*M/c^3 * @pn_expansion(x + χ₁ + χ₂ + z*(v/c)) end @@ -136,19 +137,20 @@ end output = quote baremodule Einstein1918 import Base - eval(x) = Core.eval(Mod, x) - include(p) = Base.include(Mod, p) - using PostNewtonian: @pn_expression, @pn_expansion + eval(x::Expr) = Core.eval(Mod, x) + include(p::AbstractString) = Base.include(Mod, p) + using PostNewtonian: @pn_expression, @pn_expansion, 𝒾, γₑ, ζ3 + using PostNewtonian.PNExpressionArithmetic - using PostNewtonian: G, c, M, χ⃗₁, χ⃗₂, v, pn_order - using Quaternionic: absvec + import PostNewtonian: G, c, M, χ⃗₁, χ⃗₂, v, pn_order + import Quaternionic: absvec @pn_expression χ₁(pnsystem) = absvec(χ⃗₁) @pn_expression χ₂(pnsystem) = absvec(χ⃗₂) const x = 3 module InnerMod const z = 4 end - using .InnerMod: z + import .InnerMod: z @pn_expression function y(pnsystem) G*M/c^3 * @pn_expansion(x + χ₁ + χ₂ + z*(v/c)) end diff --git a/src/core/core.jl b/src/core/core.jl index 85510118..fc77065e 100644 --- a/src/core/core.jl +++ b/src/core/core.jl @@ -3,5 +3,5 @@ include("utilities/truncated_series_monoid.jl") include("utilities/truncated_series_inversion.jl") include("PNTerm.jl") include("PNExpansion.jl") -# include("PNExpression.jl") +include("PNExpression.jl") include("PNReference.jl") diff --git a/src/core/utilities/macros.jl b/src/core/utilities/macros.jl index 264079da..b9ad661d 100644 --- a/src/core/utilities/macros.jl +++ b/src/core/utilities/macros.jl @@ -80,7 +80,7 @@ function pn_expression(pnsystem::Symbol, body) unary_funcs_exprs ] - # Next, add pnsystem as the argument to each @pn_expansion call + # Next, add `pnsystem` as the argument to each @pn_expansion call new_body = MacroTools.unblock( macroexpand( @__MODULE__, From 2ae5135413f71492f71cbb9a4ac9b23167373c4c Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Tue, 1 Jul 2025 17:38:29 -0400 Subject: [PATCH 31/77] Split `PNExpressionArithmetic` into separate file --- src/core/PNExpression.jl | 50 ---------------------------- src/core/PNExpressionArithmetic.jl | 52 ++++++++++++++++++++++++++++++ src/core/core.jl | 1 + 3 files changed, 53 insertions(+), 50 deletions(-) create mode 100644 src/core/PNExpressionArithmetic.jl diff --git a/src/core/PNExpression.jl b/src/core/PNExpression.jl index 8a3988ef..fc8c78ed 100644 --- a/src/core/PNExpression.jl +++ b/src/core/PNExpression.jl @@ -1,53 +1,3 @@ -public PNExpressionArithmetic - -baremodule PNExpressionArithmetic -import Base -#using ..PostNewtonian: constant_convert, PNSystem -using PostNewtonian: constant_convert, PNSystem -using PostNewtonian.InlineExports: @export -using Base: @inline - -@export @inline ln(pnsystem::PNSystem, x) = Base.log(constant_convert(pnsystem, x)) -@export const log = ln - -@export @inline √(pnsystem::PNSystem, x) = Base.sqrt(constant_convert(pnsystem, x)) -@export const sqrt = √ - -@export @inline (+)(pnsystem::PNSystem, x, y) = Base.:+(constant_convert(pnsystem, x), y) -@export @inline (-)(pnsystem::PNSystem, x, y) = Base.:-(constant_convert(pnsystem, x), y) -@export @inline (*)(pnsystem::PNSystem, x, y) = Base.:*(constant_convert(pnsystem, x), y) -@export @inline (/)(pnsystem::PNSystem, x, y) = Base.:/(constant_convert(pnsystem, x), y) -@export @inline (^)(pnsystem::PNSystem, x, n) = Base.:^(constant_convert(pnsystem, x), n) - -@export @inline function (+)(pnsystem::PNSystem, w, x, y, z...) - return Base.:+(constant_convert(pnsystem, w), x, y, z...) -end -@export @inline function (*)(pnsystem::PNSystem, w, x, y, z...) - return Base.:*(constant_convert(pnsystem, w), x, y, z...) -end - -end # baremodule PNExpressionArithmetic - -const pnexpressionarithmetic_functions = filter( - s -> isa(getfield(PNExpressionArithmetic, s), Function), names(PNExpressionArithmetic) -) - -@doc """ - PNExpressionArithmetic - -This module provides arithmetic operations to be used inside `@pn_expression` modules. - -It is intentionally very restrictive, so that it only includes the basic arithmetic -operations that are used in post-Newtonian expressions, and even then only in modified forms -that take a `PNSystem` as the first argument. The defined operations are - - $(pnexpressionarithmetic_functions) - -This module is not intended to be used directly, but is imported by the -[`@pn_expression`](@ref) macro, and its methods are called by [`@pn_expansion`](@ref) to -ensure that the arithmetic operations preserve the number type of the input `PNSystem`. -""" -PNExpressionArithmetic macro pn_expression(arg_index, func=:(nothing)) # Note that macros secretly get two extra variables: `__module__` and `__source__`. diff --git a/src/core/PNExpressionArithmetic.jl b/src/core/PNExpressionArithmetic.jl new file mode 100644 index 00000000..37d6dac7 --- /dev/null +++ b/src/core/PNExpressionArithmetic.jl @@ -0,0 +1,52 @@ +public PNExpressionArithmetic + +baremodule PNExpressionArithmetic +import Base +using PostNewtonian: constant_convert, PNSystem +using PostNewtonian.InlineExports: @export +using Base: @inline + +@export @inline ln(pnsystem::PNSystem, x) = Base.log(constant_convert(pnsystem, x)) +@export const log = ln + +@export @inline √(pnsystem::PNSystem, x) = Base.sqrt(constant_convert(pnsystem, x)) +@export const sqrt = √ + +@export @inline (+)(pnsystem::PNSystem, x) = constant_convert(pnsystem, x) +@export @inline (*)(pnsystem::PNSystem, x) = constant_convert(pnsystem, x) + +@export @inline (+)(pnsystem::PNSystem, x, y) = Base.:+(constant_convert(pnsystem, x), y) +@export @inline (-)(pnsystem::PNSystem, x, y) = Base.:-(constant_convert(pnsystem, x), y) +@export @inline (*)(pnsystem::PNSystem, x, y) = Base.:*(constant_convert(pnsystem, x), y) +@export @inline (/)(pnsystem::PNSystem, x, y) = Base.:/(constant_convert(pnsystem, x), y) +@export @inline (^)(pnsystem::PNSystem, x, n) = Base.:^(constant_convert(pnsystem, x), n) + +@export @inline function (+)(pnsystem::PNSystem, w, x, y, z...) + return Base.:+(constant_convert(pnsystem, w), x, y, z...) +end +@export @inline function (*)(pnsystem::PNSystem, w, x, y, z...) + return Base.:*(constant_convert(pnsystem, w), x, y, z...) +end + +end # baremodule PNExpressionArithmetic + +const pnexpressionarithmetic_functions = filter( + s -> isa(getfield(PNExpressionArithmetic, s), Function), names(PNExpressionArithmetic) +) + +@doc """ + PNExpressionArithmetic + +This module provides arithmetic operations to be used inside `@pn_expression` modules. + +It is intentionally very restrictive, so that it only includes the basic arithmetic +operations that are used in post-Newtonian expressions, and even then only in modified forms +that take a `PNSystem` as the first argument. The defined operations are + + $(pnexpressionarithmetic_functions) + +This module is not intended to be used directly, but is imported by the +[`@pn_expression`](@ref) macro, and its methods are called by [`@pn_expansion`](@ref) to +ensure that the arithmetic operations preserve the number type of the input `PNSystem`. +""" +PNExpressionArithmetic diff --git a/src/core/core.jl b/src/core/core.jl index fc77065e..e24bbcf4 100644 --- a/src/core/core.jl +++ b/src/core/core.jl @@ -3,5 +3,6 @@ include("utilities/truncated_series_monoid.jl") include("utilities/truncated_series_inversion.jl") include("PNTerm.jl") include("PNExpansion.jl") +include("PNExpressionArithmetic.jl") include("PNExpression.jl") include("PNReference.jl") From 7617f879cb9d79d5b61c8f11def38f2c8d888788 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Tue, 1 Jul 2025 18:03:10 -0400 Subject: [PATCH 32/77] Start some tests for @pn_expression --- src/core/PNExpression.jl | 40 +++++++++++++++++++++++++----- src/core/PNExpressionArithmetic.jl | 25 +++++++++++++++++-- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/src/core/PNExpression.jl b/src/core/PNExpression.jl index fc8c78ed..d50db2a3 100644 --- a/src/core/PNExpression.jl +++ b/src/core/PNExpression.jl @@ -1,4 +1,3 @@ - macro pn_expression(arg_index, func=:(nothing)) # Note that macros secretly get two extra variables: `__module__` and `__source__`. # These refer to the place where the macro was called. The second is useful for @@ -136,8 +135,37 @@ function pn_expression(arg_index, func, pnsystem_functions, __module__, __source return MacroTools.combinedef(splitfunc) end -# baremodule Mod -# import Base -# (+)(x, y) = Base.:+(Base.BigFloat(x), y) -# const x = Base.π + 3 -# end +@testitem "@pn_expression" begin + baremodule Mod + # These are the expressions added by `@pn_reference` + using Base: Base, Val + eval(x::Expr) = Core.eval(@__MODULE__, x) + include(p::AbstractString) = Base.include(@__MODULE__, p) + using PostNewtonian: @pn_expression, @pn_expansion, 𝒾, γₑ, ζ3 + using PostNewtonian.PNExpressionArithmetic + + const x = 7 + f(pnsys::PNSystem) = x + @pn_expression function g(pnsy) + f + 2 + end + @pn_expression function h(pns) + f - 2 + end + @pn_expression function i(pn) + f * 2 + end + @pn_expression function j(p) + f / 2 + end + end + # Test that `f` and only `f` is the thing visible to the macro function finder + # Test the output against BBH{Float16}, BBH{Float64}, and BBH{BigFloat} + + # @pn_expression function test_arithmetic(pnsystem) + # return (pnsystem.M + pnsystem.c) * (pnsystem.M - pnsystem.c) / pnsystem.c^2 + # end + # result = test_arithmetic(pnsystem) + # @test result == (pnsystem.M^2 - pnsystem.c^2) / pnsystem.c^2 + +end diff --git a/src/core/PNExpressionArithmetic.jl b/src/core/PNExpressionArithmetic.jl index 37d6dac7..aeb77277 100644 --- a/src/core/PNExpressionArithmetic.jl +++ b/src/core/PNExpressionArithmetic.jl @@ -7,10 +7,8 @@ using PostNewtonian.InlineExports: @export using Base: @inline @export @inline ln(pnsystem::PNSystem, x) = Base.log(constant_convert(pnsystem, x)) -@export const log = ln @export @inline √(pnsystem::PNSystem, x) = Base.sqrt(constant_convert(pnsystem, x)) -@export const sqrt = √ @export @inline (+)(pnsystem::PNSystem, x) = constant_convert(pnsystem, x) @export @inline (*)(pnsystem::PNSystem, x) = constant_convert(pnsystem, x) @@ -50,3 +48,26 @@ This module is not intended to be used directly, but is imported by the ensure that the arithmetic operations preserve the number type of the input `PNSystem`. """ PNExpressionArithmetic + +@testitem "PNExpressionArithmetic" begin + const ln = log + baremodule Mod + using PostNewtonian.PNExpressionArithmetic + end + + for NT ∈ (Float16, Float64, BigFloat) + pnsystem = BHNS(randn(NT, 15)) + + @test PostNewtonian.constant_convert(pnsystem, 17) isa NT + @test PostNewtonian.constant_convert(pnsystem, 17) == NT(17) + + # for f ∈ (:+, :-, :*, :/, :^, :ln, :√) + # @test Mod.$f(pnsystem, 17) isa NT + # @test Mod.$f(pnsystem, 17) == eval(:((√)(17))) + # end + for f ∈ (:ln, :√) + @test eval(:(Mod.$f($pnsystem, 17))) isa NT + @test eval(:(Mod.$f($pnsystem, 17))) == eval(:(($f)($(NT(17))))) + end + end +end From a8273063d7ed26add302273e5735b0c45fb1c34a Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Tue, 1 Jul 2025 22:29:06 -0400 Subject: [PATCH 33/77] Test PNExpressionArithmetic --- src/core/PNExpressionArithmetic.jl | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/core/PNExpressionArithmetic.jl b/src/core/PNExpressionArithmetic.jl index aeb77277..22d8fc67 100644 --- a/src/core/PNExpressionArithmetic.jl +++ b/src/core/PNExpressionArithmetic.jl @@ -50,24 +50,32 @@ ensure that the arithmetic operations preserve the number type of the input `PNS PNExpressionArithmetic @testitem "PNExpressionArithmetic" begin - const ln = log baremodule Mod using PostNewtonian.PNExpressionArithmetic end + using PostNewtonian: pnexpressionarithmetic_functions + + const ln = log + for NT ∈ (Float16, Float64, BigFloat) pnsystem = BHNS(randn(NT, 15)) + z = (1, 2, 3, 17, 31, ℯ, π, 3//13, 47//59) - @test PostNewtonian.constant_convert(pnsystem, 17) isa NT - @test PostNewtonian.constant_convert(pnsystem, 17) == NT(17) - - # for f ∈ (:+, :-, :*, :/, :^, :ln, :√) - # @test Mod.$f(pnsystem, 17) isa NT - # @test Mod.$f(pnsystem, 17) == eval(:((√)(17))) - # end + for f ∈ (:+, :-, :*, :/, :^, :ln, :√) + @test f ∈ pnexpressionarithmetic_functions + end + for f ∈ (:+, :-, :*, :/, :^) + for x ∈ z, y ∈ z + @test eval(:(Mod.$f))(pnsystem, x, y) isa NT + @test eval(:(Mod.$f))(pnsystem, x, y) == eval(:($f))(NT(x), NT(y)) + end + end for f ∈ (:ln, :√) - @test eval(:(Mod.$f($pnsystem, 17))) isa NT - @test eval(:(Mod.$f($pnsystem, 17))) == eval(:(($f)($(NT(17))))) + for x ∈ z + @test eval(:(Mod.$f))(pnsystem, x) isa NT + @test eval(:(Mod.$f))(pnsystem, x) == eval(:($f))(NT(x)) + end end end end From f331d338af6ae8394b3b28febf0e09037fd1f0cc Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Wed, 2 Jul 2025 00:00:28 -0400 Subject: [PATCH 34/77] Test @pn_expression --- src/core/PNExpression.jl | 51 +++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/src/core/PNExpression.jl b/src/core/PNExpression.jl index d50db2a3..26847a91 100644 --- a/src/core/PNExpression.jl +++ b/src/core/PNExpression.jl @@ -22,7 +22,7 @@ macro pn_expression(arg_index, func=:(nothing)) if isdefined(__module__, name) f = getfield(__module__, name) if isa(f, Base.Callable) - if !isempty(methods(f, Tuple{PNSystem}, __module__)) + if !isempty(methods(f, Tuple{PNSystem})) return true end end @@ -109,8 +109,6 @@ function pn_expression(arg_index, func, pnsystem_functions, __module__, __source # # `pnexpressionarithmetic_functions`, and insert `pnsystem` as the first argument. new_body = MacroTools.postwalk(new_body) do x if iscall(x, pnexpressionarithmetic_functions) - @show x.head x.args - println() insert!(x.args, 2, (pnsystem)) x else @@ -124,7 +122,7 @@ function pn_expression(arg_index, func, pnsystem_functions, __module__, __source new_body = MacroTools.unblock(quote #@fastmath let $(pnsystem_function_exprs...) - $(new_body) + $(MacroTools.unblock(new_body)) end end) @@ -139,26 +137,47 @@ end baremodule Mod # These are the expressions added by `@pn_reference` using Base: Base, Val - eval(x::Expr) = Core.eval(@__MODULE__, x) - include(p::AbstractString) = Base.include(@__MODULE__, p) - using PostNewtonian: @pn_expression, @pn_expansion, 𝒾, γₑ, ζ3 + eval(x::Expr) = Core.eval(Base.@__MODULE__, x) + include(p::AbstractString) = Base.include(Base.@__MODULE__, p) + using PostNewtonian: PostNewtonian, @pn_expression, @pn_expansion, 𝒾, γₑ, ζ3 using PostNewtonian.PNExpressionArithmetic - const x = 7 - f(pnsys::PNSystem) = x + # Below are what we would normally write manually + import PostNewtonian: M₁, M₂, χ₁ˣ, χ₁ʸ, χ₁ᶻ, v, Φ + const x = Base.://(7, 2) + f(pnsys::PostNewtonian.PNSystem) = x @pn_expression function g(pnsy) - f + 2 - end - @pn_expression function h(pns) - f - 2 + f + M₁ end + @pn_expression h(pns) = f - M₂ @pn_expression function i(pn) - f * 2 + f * χ₁ˣ + end + @pn_expression function j(pnsyst) + f / χ₁ʸ + end + @pn_expression function k(pnsyste) + f ^ χ₁ᶻ end - @pn_expression function j(p) - f / 2 + @pn_expression function l(pnsystm) + f * ln(v) end + @pn_expression function m(pnsystem) + √f * Φ end + end # baremodule Mod + + import .Mod + + const ln = log + + for NT ∈ (Float16, Float64, BigFloat) + pnsystem = BHNS(NT.(1:15)) + for v ∈ (:g, :h, :i, :j, :k, :l, :m) + @test eval(:(Mod.$v))(pnsystem) isa NT + end + end + # Test that `f` and only `f` is the thing visible to the macro function finder # Test the output against BBH{Float16}, BBH{Float64}, and BBH{BigFloat} From d26c043dce2efaa3229dd2c7f6812bf0d3307a26 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Wed, 2 Jul 2025 00:01:10 -0400 Subject: [PATCH 35/77] Simplify spin notation --- docs/src/interface/high-level.md | 8 +-- docs/src/interface/symbolics.md | 2 +- docs/src/internals/code_diagram.md | 12 ++-- ext/PostNewtonianSymbolicsExt.jl | 4 +- .../compatibility_layers/gwframes.jl | 4 +- src/interface/dynamics/orbital_evolution.jl | 12 ++-- src/interface/dynamics/right_hand_sides.jl | 12 ++-- .../common_variables/common_variables.jl | 2 +- src/pn_systems/BBH.jl | 2 +- src/pn_systems/PNSystem.jl | 2 +- src/pn_systems/Quasispherical.jl | 2 +- src/pn_systems/state_variables.jl | 68 +++++++++---------- src/pn_systems/vector_interface.jl | 24 +++---- src/predefinitions_Symbolics.jl | 2 +- test/orbital_evolution.jl | 2 +- 15 files changed, 79 insertions(+), 79 deletions(-) diff --git a/docs/src/interface/high-level.md b/docs/src/interface/high-level.md index 0bf595c6..e6d94463 100644 --- a/docs/src/interface/high-level.md +++ b/docs/src/interface/high-level.md @@ -53,9 +53,9 @@ using Plots # Requires also installing `Plots` in your project plotlyjs() # hide default(size=(800,480), linewidth=2, leg=:top) # hide -plot(inspiral.t, inspiral[:χ⃗₁ˣ], label=raw"$\vec{\chi}_1^x$") -plot!(inspiral.t, inspiral[:χ⃗₁ʸ], label=raw"$\vec{\chi}_1^y$") -plot!(inspiral.t, inspiral[:χ⃗₁ᶻ], label=raw"$\vec{\chi}_1^z$") +plot(inspiral.t, inspiral[:χ₁ˣ], label=raw"$\vec{\chi}_1^x$") +plot!(inspiral.t, inspiral[:χ₁ʸ], label=raw"$\vec{\chi}_1^y$") +plot!(inspiral.t, inspiral[:χ₁ᶻ], label=raw"$\vec{\chi}_1^z$") plot!(xlabel="Time (𝑀)", ylabel="Dimensionless spin components") savefig("inspiral_spins.html"); nothing # hide ``` @@ -78,7 +78,7 @@ They can be accessed by their symbols, like the spins above, or by their number in this list. To access the `i`th variable at time step `j`, use `sol[i, j]`. You can also use colons: `sol[i, :]` is a vector of the `i`th variable at all times, and `sol[:, j]` is a vector of all the data at time step `j`. For -example, `inspiral[:χ⃗₁ˣ]` could also be written as `inspiral[3, :]`. +example, `inspiral[:χ₁ˣ]` could also be written as `inspiral[3, :]`. By default, the output of `orbital_evolution` is just the time steps to which the adaptive ODE integrator happened to step. If you know that you want the diff --git a/docs/src/interface/symbolics.md b/docs/src/interface/symbolics.md index 06da77b9..9df9afd6 100644 --- a/docs/src/interface/symbolics.md +++ b/docs/src/interface/symbolics.md @@ -23,7 +23,7 @@ symbols as usual: julia> using PostNewtonian, Symbolics julia> symbolic_pnsystem = PostNewtonian.SymbolicPNSystem() -PostNewtonian.SymbolicPNSystem{Vector{Num}, 9223372036854775805//2, Num}(Num[M₁, M₂, χ⃗₁ˣ, χ⃗₁ʸ, χ⃗₁ᶻ, χ⃗₂ˣ, χ⃗₂ʸ, χ⃗₂ᶻ, Rʷ, Rˣ, Rʸ, Rᶻ, v, Φ], Λ₁, Λ₂) +PostNewtonian.SymbolicPNSystem{Vector{Num}, 9223372036854775805//2, Num}(Num[M₁, M₂, χ₁ˣ, χ₁ʸ, χ₁ᶻ, χ₂ˣ, χ₂ʸ, χ₂ᶻ, Rʷ, Rˣ, Rʸ, Rᶻ, v, Φ], Λ₁, Λ₂) julia> v = PostNewtonian.v(symbolic_pnsystem) v diff --git a/docs/src/internals/code_diagram.md b/docs/src/internals/code_diagram.md index fc210e91..816f53ea 100644 --- a/docs/src/internals/code_diagram.md +++ b/docs/src/internals/code_diagram.md @@ -16,13 +16,13 @@ struct BBH{T, PNOrder} <: FieldVector{14, T} M₁::T M₂::T χ⃗₁::T - χ⃗₁ˣ::T - χ⃗₁ʸ::T - χ⃗₁ᶻ::T + χ₁ˣ::T + χ₁ʸ::T + χ₁ᶻ::T χ⃗₂::T - χ⃗₂ˣ::T - χ⃗₂ʸ::T - χ⃗₂ᶻ::T + χ₂ˣ::T + χ₂ʸ::T + χ₂ᶻ::T R::T Rʷ::T Rˣ::T diff --git a/ext/PostNewtonianSymbolicsExt.jl b/ext/PostNewtonianSymbolicsExt.jl index 77106966..9169cee2 100644 --- a/ext/PostNewtonianSymbolicsExt.jl +++ b/ext/PostNewtonianSymbolicsExt.jl @@ -257,10 +257,10 @@ end causes_domain_error!(u̇, ::PNSystem{VT}) where {VT<:Vector{Symbolics.Num}} = false function SymbolicPNSystem(PNOrder=typemax(Int)) - Symbolics.@variables M₁ M₂ χ⃗₁ˣ χ⃗₁ʸ χ⃗₁ᶻ χ⃗₂ˣ χ⃗₂ʸ χ⃗₂ᶻ Rʷ Rˣ Rʸ Rᶻ v Φ Λ₁ Λ₂ + Symbolics.@variables M₁ M₂ χ₁ˣ χ₁ʸ χ₁ᶻ χ₂ˣ χ₂ʸ χ₂ᶻ Rʷ Rˣ Rʸ Rᶻ v Φ Λ₁ Λ₂ ET = typeof(M₁) return SymbolicPNSystem{Vector{ET},prepare_pn_order(PNOrder),ET}( - [M₁, M₂, χ⃗₁ˣ, χ⃗₁ʸ, χ⃗₁ᶻ, χ⃗₂ˣ, χ⃗₂ʸ, χ⃗₂ᶻ, Rʷ, Rˣ, Rʸ, Rᶻ, v, Φ], Λ₁, Λ₂ + [M₁, M₂, χ₁ˣ, χ₁ʸ, χ₁ᶻ, χ₂ˣ, χ₂ʸ, χ₂ᶻ, Rʷ, Rˣ, Rʸ, Rᶻ, v, Φ], Λ₁, Λ₂ ) end diff --git a/src/interface/compatibility_layers/gwframes.jl b/src/interface/compatibility_layers/gwframes.jl index ad848c38..343bca4d 100644 --- a/src/interface/compatibility_layers/gwframes.jl +++ b/src/interface/compatibility_layers/gwframes.jl @@ -231,8 +231,8 @@ function PNWaveform( frame=transpose(stack(solution[[:Rʷ, :Rˣ, :Rʸ, :Rᶻ]])), M1=solution[:M₁], M2=solution[:M₂], - chi1=transpose(stack(solution[[:χ⃗₁ˣ, :χ⃗₁ʸ, :χ⃗₁ᶻ]])), - chi2=transpose(stack(solution[[:χ⃗₂ˣ, :χ⃗₂ʸ, :χ⃗₂ᶻ]])), + chi1=transpose(stack(solution[[:χ₁ˣ, :χ₁ʸ, :χ₁ᶻ]])), + chi2=transpose(stack(solution[[:χ₂ˣ, :χ₂ʸ, :χ₂ᶻ]])), v=solution[:v], Phi=solution[:Φ], ) diff --git a/src/interface/dynamics/orbital_evolution.jl b/src/interface/dynamics/orbital_evolution.jl index 19d7af07..684a9883 100644 --- a/src/interface/dynamics/orbital_evolution.jl +++ b/src/interface/dynamics/orbital_evolution.jl @@ -227,12 +227,12 @@ The evolved variables, in order, are * `M₁`: Mass of black hole 1 * `M₂`: Mass of black hole 2 - * `χ⃗₁ˣ`: ``x`` component of dimensionless spin of black hole 1 - * `χ⃗₁ʸ`: ``y`` component... - * `χ⃗₁ᶻ`: ``z`` component... - * `χ⃗₂ˣ`: ``x`` component of dimensionless spin of black hole 2 - * `χ⃗₂ʸ`: ``y`` component... - * `χ⃗₂ᶻ`: ``z`` component... + * `χ₁ˣ`: ``x`` component of dimensionless spin of black hole 1 + * `χ₁ʸ`: ``y`` component... + * `χ₁ᶻ`: ``z`` component... + * `χ₂ˣ`: ``x`` component of dimensionless spin of black hole 2 + * `χ₂ʸ`: ``y`` component... + * `χ₂ᶻ`: ``z`` component... * `Rʷ`: Scalar component of frame rotor * `Rˣ`: ``x`` component... * `Rʸ`: ``y`` component... diff --git a/src/interface/dynamics/right_hand_sides.jl b/src/interface/dynamics/right_hand_sides.jl index b221da22..fa783fca 100644 --- a/src/interface/dynamics/right_hand_sides.jl +++ b/src/interface/dynamics/right_hand_sides.jl @@ -37,12 +37,12 @@ end Ṙ = Ω⃗ * R / 2 u̇[M₁index] = Ṁ₁ u̇[M₂index] = Ṁ₂ - u̇[χ⃗₁ˣindex] = χ⃗̇₁.x - u̇[χ⃗₁ʸindex] = χ⃗̇₁.y - u̇[χ⃗₁ᶻindex] = χ⃗̇₁.z - u̇[χ⃗₂ˣindex] = χ⃗̇₂.x - u̇[χ⃗₂ʸindex] = χ⃗̇₂.y - u̇[χ⃗₂ᶻindex] = χ⃗̇₂.z + u̇[χ₁ˣindex] = χ⃗̇₁.x + u̇[χ₁ʸindex] = χ⃗̇₁.y + u̇[χ₁ᶻindex] = χ⃗̇₁.z + u̇[χ₂ˣindex] = χ⃗̇₂.x + u̇[χ₂ʸindex] = χ⃗̇₂.y + u̇[χ₂ᶻindex] = χ⃗̇₂.z u̇[Rʷindex] = Ṙ.w u̇[Rˣindex] = Ṙ.x u̇[Rʸindex] = Ṙ.y diff --git a/src/literature/common_variables/common_variables.jl b/src/literature/common_variables/common_variables.jl index 59da72cc..f73d0243 100644 --- a/src/literature/common_variables/common_variables.jl +++ b/src/literature/common_variables/common_variables.jl @@ -3,7 +3,7 @@ module CommonVariables # Import the fundamental variables, which are generally part of the `state` vector of a # PNSystem. import ..PostNewtonian: - M₁, M₂, χ⃗₁, χ⃗₁ˣ, χ⃗₁ʸ, χ⃗₁ᶻ, χ⃗₂, χ⃗₂ˣ, χ⃗₂ʸ, χ⃗₂ᶻ, R, Rʷ, Rˣ, Rʸ, Rᶻ, v, Φ, Λ₁, Λ₂ + M₁, M₂, χ⃗₁, χ₁ˣ, χ₁ʸ, χ₁ᶻ, χ⃗₂, χ₂ˣ, χ₂ʸ, χ₂ᶻ, R, Rʷ, Rˣ, Rʸ, Rᶻ, v, Φ, Λ₁, Λ₂ # Now, we define all the common variables used throughout the literature as functions of # those fundamental variables. diff --git a/src/pn_systems/BBH.jl b/src/pn_systems/BBH.jl index 5d41e924..3b9fc53f 100644 --- a/src/pn_systems/BBH.jl +++ b/src/pn_systems/BBH.jl @@ -8,7 +8,7 @@ spins, orientation, velocity, and orbital phase of the system. The spins unpack three components each. The orientation is described by the four components of the `Rotor` `R`. This gives us a total of 14 elements: - M₁, M₂, χ⃗₁ˣ, χ⃗₁ʸ, χ⃗₁ᶻ, χ⃗₂ˣ, χ⃗₂ʸ, χ⃗₂ᶻ, Rʷ, Rˣ, Rʸ, Rᶻ, v, Φ + M₁, M₂, χ₁ˣ, χ₁ʸ, χ₁ᶻ, χ₂ˣ, χ₂ʸ, χ₂ᶻ, Rʷ, Rˣ, Rʸ, Rᶻ, v, Φ The "orbital phase" `Φ` is tracked as the 14th element of the `state` vector. This is just the integral of the (scalar) orbital angular frequency `Ω`, and holds little interest for diff --git a/src/pn_systems/PNSystem.jl b/src/pn_systems/PNSystem.jl index 8a5178cd..b5a8ad27 100644 --- a/src/pn_systems/PNSystem.jl +++ b/src/pn_systems/PNSystem.jl @@ -104,7 +104,7 @@ julia> using PostNewtonian: BBH julia> pnsystem = BBH(randn(14); PNOrder=7//2); julia> symbols(pnsystem) -(:M₁, :M₂, :χ⃗₁ˣ, :χ⃗₁ʸ, :χ⃗₁ᶻ, :χ⃗₂ˣ, :χ⃗₂ʸ, :χ⃗₂ᶻ, :Rʷ, :Rˣ, :Rʸ, :Rᶻ, :v, :Φ) +(:M₁, :M₂, :χ₁ˣ, :χ₁ʸ, :χ₁ᶻ, :χ₂ˣ, :χ₂ʸ, :χ₂ᶻ, :Rʷ, :Rˣ, :Rʸ, :Rᶻ, :v, :Φ) julia> ascii_symbols(pnsystem) (:M1, :M2, :chi1x, :chi1y, :chi1z, :chi2x, :chi2y, :chi2z, :Rw, :Rx, :Ry, :Rz, :v, :Phi) diff --git a/src/pn_systems/Quasispherical.jl b/src/pn_systems/Quasispherical.jl index 4872f378..cb02f779 100644 --- a/src/pn_systems/Quasispherical.jl +++ b/src/pn_systems/Quasispherical.jl @@ -3,7 +3,7 @@ @public abstract type Quasispherical{NT,PNOrder,ST} <: PNSystem{NT,PNOrder,ST} end function symbols(::Type{<:Quasispherical}) - (:M₁, :M₂, :χ⃗₁ˣ, :χ⃗₁ʸ, :χ⃗₁ᶻ, :χ⃗₂ˣ, :χ⃗₂ʸ, :χ⃗₂ᶻ, :Rʷ, :Rˣ, :Rʸ, :Rᶻ, :v, :Φ) + (:M₁, :M₂, :χ₁ˣ, :χ₁ʸ, :χ₁ᶻ, :χ₂ˣ, :χ₂ʸ, :χ₂ᶻ, :Rʷ, :Rˣ, :Rʸ, :Rᶻ, :v, :Φ) end function ascii_symbols(::Type{<:Quasispherical}) (:M1, :M2, :chi1x, :chi1y, :chi1z, :chi2x, :chi2y, :chi2z, :Rw, :Rx, :Ry, :Rz, :v, :Phi) diff --git a/src/pn_systems/state_variables.jl b/src/pn_systems/state_variables.jl index 987d90d7..a7331477 100644 --- a/src/pn_systems/state_variables.jl +++ b/src/pn_systems/state_variables.jl @@ -55,88 +55,88 @@ M₂(fdpnsystem::FDPNSystem) = fdpnsystem[:M₂] @public const M2 = M₂ """ - χ⃗₁ˣ(pnsystem) + χ₁ˣ(pnsystem) chi1x(pnsystem) `x`-component of dimensionless spin vector of object 1 in this system, as a `QuatVec`. See [`χ⃗₁`](@ref) for details. """ -@public function χ⃗₁ˣ(::T) where {T<:PNSystem} - error("χ⃗₁ˣ is not (yet) defined for PNSystem subtype `$T`.") +@public function χ₁ˣ(::T) where {T<:PNSystem} + error("χ₁ˣ is not (yet) defined for PNSystem subtype `$T`.") end -χ⃗₁ˣ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ⃗₁ˣ] -@public const chi1x = χ⃗₁ˣ +χ₁ˣ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ₁ˣ] +@public const chi1x = χ₁ˣ """ - χ⃗₁ʸ(pnsystem) + χ₁ʸ(pnsystem) chi1y(pnsystem) `y`-component of dimensionless spin vector of object 1 in this system, as a `QuatVec`. See [`χ⃗₁`](@ref) for details. """ -@public function χ⃗₁ʸ(::T) where {T<:PNSystem} - error("χ⃗₁ʸ is not (yet) defined for PNSystem subtype `$T`.") +@public function χ₁ʸ(::T) where {T<:PNSystem} + error("χ₁ʸ is not (yet) defined for PNSystem subtype `$T`.") end -χ⃗₁ʸ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ⃗₁ʸ] -@public const chi1y = χ⃗₁ʸ +χ₁ʸ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ₁ʸ] +@public const chi1y = χ₁ʸ """ - χ⃗₁ᶻ(pnsystem) + χ₁ᶻ(pnsystem) chi1z(pnsystem) `z`-component of dimensionless spin vector of object 1 in this system, as a `QuatVec`. See [`χ⃗₁`](@ref) for details. """ -@public function χ⃗₁ᶻ(::T) where {T<:PNSystem} - error("χ⃗₁ᶻ is not (yet) defined for PNSystem subtype `$T`.") +@public function χ₁ᶻ(::T) where {T<:PNSystem} + error("χ₁ᶻ is not (yet) defined for PNSystem subtype `$T`.") end -χ⃗₁ᶻ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ⃗₁ᶻ] -@public const chi1z = χ⃗₁ᶻ +χ₁ᶻ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ₁ᶻ] +@public const chi1z = χ₁ᶻ """ - χ⃗₂ˣ(pnsystem) + χ₂ˣ(pnsystem) chi2x(pnsystem) `x`-component of dimensionless spin vector of object 2 in this system, as a `QuatVec`. See [`χ⃗₂`](@ref) for details. """ -@public function χ⃗₂ˣ(::T) where {T<:PNSystem} - error("χ⃗₂ˣ is not (yet) defined for PNSystem subtype `$T`.") +@public function χ₂ˣ(::T) where {T<:PNSystem} + error("χ₂ˣ is not (yet) defined for PNSystem subtype `$T`.") end -χ⃗₂ˣ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ⃗₂ˣ] -@public const chi2x = χ⃗₂ˣ +χ₂ˣ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ₂ˣ] +@public const chi2x = χ₂ˣ """ - χ⃗₂ʸ(pnsystem) + χ₂ʸ(pnsystem) chi2y(pnsystem) `y`-component of dimensionless spin vector of object 2 in this system, as a `QuatVec`. See [`χ⃗₂`](@ref) for details. """ -@public function χ⃗₂ʸ(::T) where {T<:PNSystem} - error("χ⃗₂ʸ is not (yet) defined for PNSystem subtype `$T`.") +@public function χ₂ʸ(::T) where {T<:PNSystem} + error("χ₂ʸ is not (yet) defined for PNSystem subtype `$T`.") end -χ⃗₂ʸ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ⃗₂ʸ] -@public const chi2y = χ⃗₂ʸ +χ₂ʸ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ₂ʸ] +@public const chi2y = χ₂ʸ """ - χ⃗₂ᶻ(pnsystem) + χ₂ᶻ(pnsystem) chi2z(pnsystem) `z`-component of dimensionless spin vector of object 2 in this system, as a `QuatVec`. See [`χ⃗₂`](@ref) for details. """ -@public function χ⃗₂ᶻ(::T) where {T<:PNSystem} - error("χ⃗₂ᶻ is not (yet) defined for PNSystem subtype `$T`.") +@public function χ₂ᶻ(::T) where {T<:PNSystem} + error("χ₂ᶻ is not (yet) defined for PNSystem subtype `$T`.") end -χ⃗₂ᶻ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ⃗₂ᶻ] -@public const chi2z = χ⃗₂ᶻ +χ₂ᶻ(fdpnsystem::FDPNSystem) = fdpnsystem[:χ₂ᶻ] +@public const chi2z = χ₂ᶻ """ Rʷ(pnsystem) @@ -295,10 +295,10 @@ end Dimensionless spin vector of object 1 in this system, as a `QuatVec`. -See also [`χ⃗₁ˣ`](@ref), [`χ⃗₁ʸ`](@ref), and [`χ⃗₁ᶻ`](@ref) for the individual components. +See also [`χ₁ˣ`](@ref), [`χ₁ʸ`](@ref), and [`χ₁ᶻ`](@ref) for the individual components. """ @public function χ⃗₁(::T) where {T<:PNSystem} - QuatVec(χ⃗₁ˣ(pnsystem), χ⃗₁ʸ(pnsystem), χ⃗₁ᶻ(pnsystem)) + QuatVec(χ₁ˣ(pnsystem), χ₁ʸ(pnsystem), χ₁ᶻ(pnsystem)) end @public const chi1 = χ⃗₁ @@ -308,10 +308,10 @@ end Dimensionless spin vector of object 2 in this system, as a `QuatVec`. -See also [`χ⃗₂ˣ`](@ref), [`χ⃗₂ʸ`](@ref), and [`χ⃗₂ᶻ`](@ref) for the individual components. +See also [`χ₂ˣ`](@ref), [`χ₂ʸ`](@ref), and [`χ₂ᶻ`](@ref) for the individual components. """ @public function χ⃗₂(::T) where {T<:PNSystem} - QuatVec(χ⃗₂ˣ(pnsystem), χ⃗₂ʸ(pnsystem), χ⃗₂ᶻ(pnsystem)) + QuatVec(χ₂ˣ(pnsystem), χ₂ʸ(pnsystem), χ₂ᶻ(pnsystem)) end @public const chi2 = χ⃗₂ diff --git a/src/pn_systems/vector_interface.jl b/src/pn_systems/vector_interface.jl index 730cd095..8cd0a744 100644 --- a/src/pn_systems/vector_interface.jl +++ b/src/pn_systems/vector_interface.jl @@ -181,24 +181,24 @@ Base.dataids(pnsystem::PNSystem) = Base.dataids(state(pnsystem)) @test symbol_index(typeof(pnsystem), Val(:M₂)) == 2 @test symbol_index(typeof(pnsystem), Val(:M2)) == 2 @test pnsystem[:M₂] == pnsystem[:M2] == pnsystem[2] == pnsystem.state[2] - @test symbol_index(typeof(pnsystem), Val(:χ⃗₁ˣ)) == 3 + @test symbol_index(typeof(pnsystem), Val(:χ₁ˣ)) == 3 @test symbol_index(typeof(pnsystem), Val(:chi1x)) == 3 - @test pnsystem[:χ⃗₁ˣ] == pnsystem[:chi1x] == pnsystem[3] == pnsystem.state[3] - @test symbol_index(typeof(pnsystem), Val(:χ⃗₁ʸ)) == 4 + @test pnsystem[:χ₁ˣ] == pnsystem[:chi1x] == pnsystem[3] == pnsystem.state[3] + @test symbol_index(typeof(pnsystem), Val(:χ₁ʸ)) == 4 @test symbol_index(typeof(pnsystem), Val(:chi1y)) == 4 - @test pnsystem[:χ⃗₁ʸ] == pnsystem[:chi1y] == pnsystem[4] == pnsystem.state[4] - @test symbol_index(typeof(pnsystem), Val(:χ⃗₁ᶻ)) == 5 + @test pnsystem[:χ₁ʸ] == pnsystem[:chi1y] == pnsystem[4] == pnsystem.state[4] + @test symbol_index(typeof(pnsystem), Val(:χ₁ᶻ)) == 5 @test symbol_index(typeof(pnsystem), Val(:chi1z)) == 5 - @test pnsystem[:χ⃗₁ᶻ] == pnsystem[:chi1z] == pnsystem[5] == pnsystem.state[5] - @test symbol_index(typeof(pnsystem), Val(:χ⃗₂ˣ)) == 6 + @test pnsystem[:χ₁ᶻ] == pnsystem[:chi1z] == pnsystem[5] == pnsystem.state[5] + @test symbol_index(typeof(pnsystem), Val(:χ₂ˣ)) == 6 @test symbol_index(typeof(pnsystem), Val(:chi2x)) == 6 - @test pnsystem[:χ⃗₂ˣ] == pnsystem[:chi2x] == pnsystem[6] == pnsystem.state[6] - @test symbol_index(typeof(pnsystem), Val(:χ⃗₂ʸ)) == 7 + @test pnsystem[:χ₂ˣ] == pnsystem[:chi2x] == pnsystem[6] == pnsystem.state[6] + @test symbol_index(typeof(pnsystem), Val(:χ₂ʸ)) == 7 @test symbol_index(typeof(pnsystem), Val(:chi2y)) == 7 - @test pnsystem[:χ⃗₂ʸ] == pnsystem[:chi2y] == pnsystem[7] == pnsystem.state[7] - @test symbol_index(typeof(pnsystem), Val(:χ⃗₂ᶻ)) == 8 + @test pnsystem[:χ₂ʸ] == pnsystem[:chi2y] == pnsystem[7] == pnsystem.state[7] + @test symbol_index(typeof(pnsystem), Val(:χ₂ᶻ)) == 8 @test symbol_index(typeof(pnsystem), Val(:chi2z)) == 8 - @test pnsystem[:χ⃗₂ᶻ] == pnsystem[:chi2z] == pnsystem[8] == pnsystem.state[8] + @test pnsystem[:χ₂ᶻ] == pnsystem[:chi2z] == pnsystem[8] == pnsystem.state[8] @test symbol_index(typeof(pnsystem), Val(:Rʷ)) == 9 @test symbol_index(typeof(pnsystem), Val(:Rw)) == 9 @test pnsystem[:Rʷ] == pnsystem[:Rw] == pnsystem[9] == pnsystem.state[9] diff --git a/src/predefinitions_Symbolics.jl b/src/predefinitions_Symbolics.jl index c92eb0f2..951a5b9b 100644 --- a/src/predefinitions_Symbolics.jl +++ b/src/predefinitions_Symbolics.jl @@ -37,7 +37,7 @@ julia> using Symbolics julia> using PostNewtonian: M₁, M₂, χ⃗₁, χ⃗₂, SymbolicPNSystem julia> symbolic_pnsystem = SymbolicPNSystem() -SymbolicPNSystem{Vector{Num}, 9223372036854775805//2, Num}(Num[M₁, M₂, χ⃗₁ˣ, χ⃗₁ʸ, χ⃗₁ᶻ, χ⃗₂ˣ, χ⃗₂ʸ, χ⃗₂ᶻ, Rʷ, Rˣ, Rʸ, Rᶻ, v, Φ], Λ₁, Λ₂) +SymbolicPNSystem{Vector{Num}, 9223372036854775805//2, Num}(Num[M₁, M₂, χ₁ˣ, χ₁ʸ, χ₁ᶻ, χ₂ˣ, χ₂ʸ, χ₂ᶻ, Rʷ, Rˣ, Rʸ, Rᶻ, v, Φ], Λ₁, Λ₂) julia> M₁(symbolic_pnsystem), M₂(symbolic_pnsystem) (M₁, M₂) diff --git a/test/orbital_evolution.jl b/test/orbital_evolution.jl index 8bddeb76..603e8976 100644 --- a/test/orbital_evolution.jl +++ b/test/orbital_evolution.jl @@ -81,7 +81,7 @@ sol_np = orbital_evolution( M₁, M₂, QuatVec(0, 0, χ⃗₁.z), QuatVec(0, 0, χ⃗₂.z), Ωᵢ; Ω₁, Rᵢ, quiet=true ) - for c ∈ [:χ⃗₁ˣ, :χ⃗₁ʸ, :χ⃗₂ˣ, :χ⃗₂ʸ, :Rˣ, :Rʸ] + for c ∈ [:χ₁ˣ, :χ₁ʸ, :χ₂ˣ, :χ₂ʸ, :Rˣ, :Rʸ] @test all(sol_np[c] .== 0) end From 3655605301cc9de46f34310855bdfcc38344ff1d Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Wed, 2 Jul 2025 09:15:29 -0400 Subject: [PATCH 36/77] Correct string syntax and format --- sources/PNpedia/test.jl | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/sources/PNpedia/test.jl b/sources/PNpedia/test.jl index f03085f9..70138be7 100644 --- a/sources/PNpedia/test.jl +++ b/sources/PNpedia/test.jl @@ -1,4 +1,4 @@ -""" +raw""" We need to parse Mathematica expressions in Julia, using SymPy's parsing capabilities. Unfortunately, at the moment, there is a bug in SymPy @@ -58,9 +58,6 @@ One problem is unicode's combining characters, some of which """ - - - using Pkg Pkg.activate(@__DIR__) @@ -77,20 +74,20 @@ greek_replacements = ( raw"\[Lambda]" => "λ", ) - - -const parse_mathematica = PythonCall.pyimport("sympy.parsing.mathematica" => "parse_mathematica") +const parse_mathematica = PythonCall.pyimport( + "sympy.parsing.mathematica" => "parse_mathematica" +) s = replace( raw"((1594323*I)/4480)*Sqrt[Pi/4199]*x^(9/2)*\[Nu]*(\[Delta] - 6*\[Delta]*\[Nu] + 10*\[Delta]*\[Nu]^2 - 4*\[Delta]*\[Nu]^3)", - greek_replacements... + greek_replacements..., ) ex = parse_mathematica(s)#, Dict("EllipticE[x]"=>"elliptic_e(x)")) - - -const elliptic_e = PythonCall.pyimport("sympy.functions.special.elliptic_integrals" => "elliptic_e") +const elliptic_e = PythonCall.pyimport( + "sympy.functions.special.elliptic_integrals" => "elliptic_e" +) s = raw"(Sqrt[Pi])/(2*EllipticE[m])" s = raw"((1594323*I)/4480)*Sqrt[Pi/4199]*x^(9/2)*\[Nu]*(\[Delta] - 6*\[Delta]*\[Nu] + 10*\[Delta]*\[Nu]^2 - 4*\[Delta]*\[Nu]^3)" From d57cadaaae177ea4ee5eba508ee9b6eec031295f Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Wed, 2 Jul 2025 09:16:05 -0400 Subject: [PATCH 37/77] Finish testing @pn_expression --- src/core/PNExpression.jl | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/core/PNExpression.jl b/src/core/PNExpression.jl index 26847a91..22fb01f0 100644 --- a/src/core/PNExpression.jl +++ b/src/core/PNExpression.jl @@ -72,8 +72,8 @@ function pn_expression(arg_index, func, pnsystem_functions, __module__, __source pnsystem = MacroTools.namify(splitfunc[:args][arg_index]) # Append a keyword argument `pn_expansion_reducer` to the function signature, with - # default value `Val(sum)`. The `Val` is captured in the "where" parameter - # `PNExpansionReducer` + # default value `Val(sum)`. The `Val` parameter is captured as `PNExpansionReducer`, + # which we can then use in the body of the function. splitfunc[:kwargs] = [ splitfunc[:kwargs] :($(Expr(:kw, :(pn_expansion_reducer::Val{PNExpansionReducer}), :(Val(Base.sum))))) @@ -105,8 +105,8 @@ function pn_expression(arg_index, func, pnsystem_functions, __module__, __source # Now expand all the macros inside the body new_body = macroexpand(__module__, new_body; recursive=true) - # # Next, we walk the expression tree and find any function calls to one of the - # # `pnexpressionarithmetic_functions`, and insert `pnsystem` as the first argument. + # Next, we walk the expression tree and find all function calls to any of the + # `pnexpressionarithmetic_functions`, and insert `pnsystem` as the first argument. new_body = MacroTools.postwalk(new_body) do x if iscall(x, pnexpressionarithmetic_functions) insert!(x.args, 2, (pnsystem)) @@ -117,8 +117,8 @@ function pn_expression(arg_index, func, pnsystem_functions, __module__, __source end # Finally, include the `let` statements so that the functions can all be used as plain - # symbols without having to call them on `pnsystem`, but do so without burying it in a - # code block. + # symbols without having to call them on `pnsystem`, but we do so without burying it in + # a code block. new_body = MacroTools.unblock(quote #@fastmath let $(pnsystem_function_exprs...) @@ -151,7 +151,7 @@ end end @pn_expression h(pns) = f - M₂ @pn_expression function i(pn) - f * χ₁ˣ + f * χ₁ˣ * γₑ end @pn_expression function j(pnsyst) f / χ₁ʸ @@ -160,7 +160,7 @@ end f ^ χ₁ᶻ end @pn_expression function l(pnsystm) - f * ln(v) + f * ln(v) * ζ3 end @pn_expression function m(pnsystem) √f * Φ @@ -176,15 +176,12 @@ end for v ∈ (:g, :h, :i, :j, :k, :l, :m) @test eval(:(Mod.$v))(pnsystem) isa NT end + @test Mod.g(pnsystem) == NT(7//2) + pnsystem[:M₁] + @test Mod.h(pnsystem) == NT(7//2) - pnsystem[:M₂] + @test Mod.i(pnsystem) == NT(7//2) * pnsystem[:χ₁ˣ] * PostNewtonian.γₑ + @test Mod.j(pnsystem) == NT(7//2) / pnsystem[:χ₁ʸ] + @test Mod.k(pnsystem) == NT(7//2) ^ pnsystem[:χ₁ᶻ] + @test Mod.l(pnsystem) == NT(7//2) * log(pnsystem[:v]) * PostNewtonian.ζ3 + @test Mod.m(pnsystem) == √(NT(7//2)) * pnsystem[:Φ] end - - # Test that `f` and only `f` is the thing visible to the macro function finder - # Test the output against BBH{Float16}, BBH{Float64}, and BBH{BigFloat} - - # @pn_expression function test_arithmetic(pnsystem) - # return (pnsystem.M + pnsystem.c) * (pnsystem.M - pnsystem.c) / pnsystem.c^2 - # end - # result = test_arithmetic(pnsystem) - # @test result == (pnsystem.M^2 - pnsystem.c^2) / pnsystem.c^2 - end From cb7948db118dfa99985a87e025ee8877ab2ba5c3 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Wed, 2 Jul 2025 10:38:14 -0400 Subject: [PATCH 38/77] Add `@pn_expansion`; improve docs --- src/core/PNExpansion.jl | 49 ++++++++++++++++++++++++++ src/core/PNExpression.jl | 74 ++++++++++++++++++++++++++++++++++++++-- src/core/PNReference.jl | 2 ++ src/core/core.jl | 5 +++ 4 files changed, 128 insertions(+), 2 deletions(-) diff --git a/src/core/PNExpansion.jl b/src/core/PNExpansion.jl index 5a9c3bc1..b940ad2d 100644 --- a/src/core/PNExpansion.jl +++ b/src/core/PNExpansion.jl @@ -343,3 +343,52 @@ Base.:/(expansion::PNExpansion, term::PNTerm) = expansion * inv(term) end end end + +@doc raw""" + @pn_expansion [pnsystem] expansion + +Mark `expansion` as a post-Newtonian expansion in powers of ``1/c``, to be truncated at the +order given by the `pnsystem`. + +Note that we generally want this PN order to be interpreted as a *relative* PN order. For +example, the expression for [gravitational-wave energy flux](@ref 𝓕) looks like +```math +\mathcal{F} = \frac{32c^5}{5G} \nu^2 \left(\frac{v}{c}\right)^{10} +\left[1 - \left(\frac{ν}{12} + \frac{3}{4}\right) \left(\frac{v}{c}\right)^2 + \ldots\right] +``` +Here, the part in square brackets is the expansion in terms of *relative* PN order, with the +1 representing the 0-pN term, the ``(v/c)^2`` term being the 1-pN term, etc. So in the +code, we apply `@pn_expansion` to the part in square brackets: +```julia +32c^5 / 5G * ν^2 * (v / c)^10 * @pn_expansion(1 - (ν/12 + 3/4) * (v/c)^2 + ...) +``` +Note that the entire expression is written in a function that is modified by the +[`@pn_expression`](@ref) macro, which automatically inserts the `pnsystem` argument to this +`@pn_expansion` call, so you generally should not have to worry about it. + +This macro gathers terms in `expansion` by the powers of ``1/c`` involved, zeroing out any +terms with powers of ``1/c`` higher than (twice) the `pnsystem`'s `PNOrder` parameter, and +combine the terms using the `PNExpansionReducer` specified in argument of the function that +includes this macro call. + +The expansion and truncation are achieved by redefining `c` to be a +[`PNExpansionParameter(pnsystem)`](@ref PNExpansionParameter), which is just a +[`PNTerm`](@ref) with a coefficient of 1 and a `c⁻¹Exponent` of -1. This redefinition +happens inside a `let` block created by this macro so that it doesn't interfere with any +`c`s on the outside. For example, in the flux expression above, the `c`s in `32c^5 / 5G * +ν^2 * (v / c)^10` are outside the scope of the macro, even if we set `c=1` above that line, +the expansion will still be correct. +""" +@public macro pn_expansion(pnsystem, expr) + return esc(quote + let c = PNExpansionParameter($pnsystem) + PNExpansionReducer($expr) + end + end) +end + +# @testitem "@pn_expansion" begin +# using PostNewtonian: @pn_expansion + +# @pn_expansion [pnsystem] expansion +# end diff --git a/src/core/PNExpression.jl b/src/core/PNExpression.jl index 22fb01f0..422f1d22 100644 --- a/src/core/PNExpression.jl +++ b/src/core/PNExpression.jl @@ -1,4 +1,70 @@ -macro pn_expression(arg_index, func=:(nothing)) +""" + @pn_expression [arg_index=1] func + +This macro makes it easier to define post-Newtonian expressions. + +The optional first argument to this macro is `arg_index`, which just tells us which argument +to the function `func` is a `PNSystem` — defaulting to the first argument. For example, the +variables defined in [`PostNewtonian.FundamentalVariables`](@ref "Fundamental variables") +all take a single argument of `pnsystem`, which is used to compute the values for those +variables; this macro just needs to know where to find `pnsystem`. + +The required argument to this macro is a function the will compute some values related to +the `pnsystem`. + +Once it has this information, there are three types of transformations the macro will make: + + 1. Adds a keyword argument `pn_expansion_reducer::Val{PNExpansionReducer}=Val(sum)` to the + function signature. This is used to determine how to reduce the PN expansion terms. + The default is `Val(sum)`, which will just return a single number, but `Val(identity)` + can be used to return the expansion. The reducing function can be used inside the main + function as `PNExpansionReducer`, and will be automatically used inside any + `@pn_expansion`. + 2. For every function defined in the module where this macro is called that takes a single + `PNSystem` argument, this macro will look for the name of that function inside the + expression. If it finds any such names, it will replace them with the corresponding + calls to the function with `pnsystem` as the argument. For example, you can simply use + the symbols `M₁` or `μ` in your code, rather than calling them as [`M₁(pnsystem)`](@ref + M₁) or [`μ(pnsystem)`](@ref μ) every time they appear. To be more explicit, this is + achieved by defining the relevant quantities in a `let` block placed around the body of + `func`, so that the values may be used efficiently without recomputation. If you need + to use one of those functions with different arguments, you'll need to address them + explicitly with the module name — as in `PostNewtonian.v(;Ω, M)`. + 3. Insert the `pnsystem` argument as the first argument to each occurrence of + `@pn_expansion` that needs it. + +For example, we might write a function like this: +```julia +@pn_expression function f(pnsystem) + 5μ/c^2 * @pn_expansion(1 + 4(v/c)^2) +end +``` +That is effectively rewritten by this macro as +```julia +function f(pnsystem, ::Val{PNExpansionReducer}=Val(sum)) where {PNExpansionReducer} + let μ=μ(pnsystem), c=c(pnsystem), v=v(pnsystem) + 5μ/c^2 * @pn_expansion pnsystem (1 + 4(v/c)^2) + end +end +``` +The [`@pn_expansion`](@ref) macro is further expanded so that the final result looks like +```julia +function f(pnsystem, ::Val{PNExpansionReducer}=Val(sum)) where {PNExpansionReducer} + let μ=μ(pnsystem), c=c(pnsystem), v=v(pnsystem) + 5μ/c^2 * ( + let c=PNExpansionParameter(pnsystem) + PNExpansionReducer(1 + 4(v/c)^2) + end + ) + end +end +``` +Moreover, this expression should itself be defined within an [`@pn_reference`](@ref) module, +which preserves the precision of the `pnsystem`, so that we can define `pnsystem` with, +e.g., `Float16` or `BigFloat` numbers, and natural expressions like `2π` or `4/3` won't +automatically be converted to `Float64` and spoil the requested precision. +""" +@public macro pn_expression(arg_index, func=:(nothing)) # Note that macros secretly get two extra variables: `__module__` and `__source__`. # These refer to the place where the macro was called. The second is useful for # debugging, but the first is crucial here, because it lets us see what's defined in the @@ -13,6 +79,7 @@ macro pn_expression(arg_index, func=:(nothing)) else names(__module__; all=true, imported=true, usings=true) end + # Next, we narrow that list down to callable objects defined in the `__module__` where # this macro was used. Specifically, we filter for those objects that take one # `PNSystem`, and nothing else, as an argument. These will be valid targets for @@ -30,6 +97,8 @@ macro pn_expression(arg_index, func=:(nothing)) return false end, all_names ) + + # Now, we pass that information along to the function that actually modifies our code return esc(pn_expression(arg_index, func, pnsystem_functions, __module__, __source__)) end @@ -73,7 +142,8 @@ function pn_expression(arg_index, func, pnsystem_functions, __module__, __source # Append a keyword argument `pn_expansion_reducer` to the function signature, with # default value `Val(sum)`. The `Val` parameter is captured as `PNExpansionReducer`, - # which we can then use in the body of the function. + # which we can then use in the body of the function — specifically, it is automatically + # used by the `@pn_expansion` macro. splitfunc[:kwargs] = [ splitfunc[:kwargs] :($(Expr(:kw, :(pn_expansion_reducer::Val{PNExpansionReducer}), :(Val(Base.sum))))) diff --git a/src/core/PNReference.jl b/src/core/PNReference.jl index 3dd17b49..67d106ca 100644 --- a/src/core/PNReference.jl +++ b/src/core/PNReference.jl @@ -43,6 +43,7 @@ function pn_reference(expr) # 2. the module name (a `Symbol`) # 3. the module body (an `Expr` with head `:block`) if MacroTools.isexpr(expr, :module) + # Check to make sure that this module is structured exactly as expected if length(expr.args) ≠ 3 error( "Found a module expression with $(length(expr.args)) arguments:\n" * @@ -61,6 +62,7 @@ function pn_reference(expr) if ! MacroTools.isexpr(expr.args[3], :block) error("Found a module expression with a non-block body: $(dump(expr.args[3]))") end + # Now, we assemble the new module, mostly by prepending some imports to the # contents new_module = Expr( diff --git a/src/core/core.jl b/src/core/core.jl index e24bbcf4..726d20da 100644 --- a/src/core/core.jl +++ b/src/core/core.jl @@ -1,3 +1,8 @@ +# This directory provides the core functionality for the post-Newtonian framework — things +# that we will use to build out the rest of the code. As such, it is full of little coding +# details that are important for understanding the detailed structure of the code, but not +# for understanding the physics that code is representing. + include("utilities/misc.jl") include("utilities/truncated_series_monoid.jl") include("utilities/truncated_series_inversion.jl") From a80dcd38564b6c1c076f785a1ae59fc733774cf7 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Wed, 2 Jul 2025 19:49:02 -0400 Subject: [PATCH 39/77] Explain the purpose of @pn_reference --- src/core/PNReference.jl | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/core/PNReference.jl b/src/core/PNReference.jl index 67d106ca..d6e6989d 100644 --- a/src/core/PNReference.jl +++ b/src/core/PNReference.jl @@ -92,6 +92,23 @@ end Create a module for a specific Post-Newtonian reference. +!!! warning + + Due to limitations of Julia versions less than 1.12, you *must* use `import` instead of + `using` to import variables inside the module created by `@pn_reference`, or they won't + be available to `@pn_expression`. + +The purposes of this macro are: + + 1. To isolate the code for each reference, allowing for different notations such as + `η` or `ν` for the reduced mass ratio, or for subtly different definitions of + fundamental quantities like spin. + 2. To require that all functions and variables used in the expressions defined in the + module are explicitly imported or defined, to minimize the chance of accidentally + using incorrect definitions. + 3. To preserve precision relevant to the number type of the input `PNSystem` + by redefining basic operations such as addition, multiplication, and so on. + Different sources in the literature may use different notations for the same variables, so enclosing definitions related to a particular reference in a module allows us to use the appropriate notation within that module while still interfacing with this package and its From 853f008bb4f051e0c9c10d63669bf9b1e8eeaf8f Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Thu, 3 Jul 2025 00:17:16 -0400 Subject: [PATCH 40/77] Make sure eval and include are addressing correct module --- src/core/PNReference.jl | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/core/PNReference.jl b/src/core/PNReference.jl index d6e6989d..381cf556 100644 --- a/src/core/PNReference.jl +++ b/src/core/PNReference.jl @@ -63,8 +63,7 @@ function pn_reference(expr) error("Found a module expression with a non-block body: $(dump(expr.args[3]))") end - # Now, we assemble the new module, mostly by prepending some imports to the - # contents + # Now, we assemble the new module, mostly by prepending some imports to the contents new_module = Expr( :module, false, # We turn this into a `baremodule` @@ -72,8 +71,8 @@ function pn_reference(expr) Expr( # This is the new module body :block, :(using Base: Base, Val), - :(eval(x::Expr) = Core.eval(Mod, x)), - :(include(p::AbstractString) = Base.include(Mod, p)), + :(eval(x::Expr) = Core.eval($(expr.args[2]), x)), + :(include(p::AbstractString) = Base.include($(expr.args[2]), p)), :(using PostNewtonian: @pn_expression, @pn_expansion, 𝒾, γₑ, ζ3), :(using PostNewtonian.PNExpressionArithmetic), expr.args[3].args..., # The original module body @@ -155,9 +154,9 @@ end output = quote baremodule Einstein1918 - import Base - eval(x::Expr) = Core.eval(Mod, x) - include(p::AbstractString) = Base.include(Mod, p) + using Base: Base, Val + eval(x::Expr) = Core.eval(Einstein1918, x) + include(p::AbstractString) = Base.include(Einstein1918, p) using PostNewtonian: @pn_expression, @pn_expansion, 𝒾, γₑ, ζ3 using PostNewtonian.PNExpressionArithmetic From 1e5c8e18575feae113179c6443de33564f9c69c6 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Thu, 3 Jul 2025 00:18:41 -0400 Subject: [PATCH 41/77] Test the actual expression that `@pn_expression` produces --- src/core/PNExpression.jl | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/core/PNExpression.jl b/src/core/PNExpression.jl index 422f1d22..00786714 100644 --- a/src/core/PNExpression.jl +++ b/src/core/PNExpression.jl @@ -204,6 +204,8 @@ function pn_expression(arg_index, func, pnsystem_functions, __module__, __source end @testitem "@pn_expression" begin + using MacroTools + baremodule Mod # These are the expressions added by `@pn_reference` using Base: Base, Val @@ -235,12 +237,28 @@ end @pn_expression function m(pnsystem) √f * Φ end + + const expr = Base.@macroexpand @pn_expression function n(pnsystem) + l * f * ln(v) * ζ3 + m * Φ + end + end # baremodule Mod import .Mod const ln = log + output = :( + function n( + pnsystem; pn_expansion_reducer::Val{PNExpansionReducer}=Val(Base.sum) + ) where {PNExpansionReducer} + let f=f(pnsystem), l=l(pnsystem), m=m(pnsystem), v=v(pnsystem), Φ=Φ(pnsystem) + pnsystem + pnsystem * l * f * ln(pnsystem, v) * ζ3 + pnsystem * m * Φ + end + end + ) + @test MacroTools.striplines(Mod.expr) == MacroTools.striplines(output) + for NT ∈ (Float16, Float64, BigFloat) pnsystem = BHNS(NT.(1:15)) for v ∈ (:g, :h, :i, :j, :k, :l, :m) From 62a7049391589e857d734af21d478ca02be86549 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Thu, 3 Jul 2025 00:18:53 -0400 Subject: [PATCH 42/77] Test the actual expression that `@pn_expansion` produces --- src/core/PNExpansion.jl | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/core/PNExpansion.jl b/src/core/PNExpansion.jl index b940ad2d..a9b858ff 100644 --- a/src/core/PNExpansion.jl +++ b/src/core/PNExpansion.jl @@ -387,8 +387,15 @@ the expansion will still be correct. end) end -# @testitem "@pn_expansion" begin -# using PostNewtonian: @pn_expansion - -# @pn_expansion [pnsystem] expansion -# end +@testitem "@pn_expansion" begin + using PostNewtonian: @pn_expansion + using MacroTools + + input = @macroexpand @pn_expansion pnsystem (1 - (ν/12 + 3/4) * (v/c)^2) + output = quote + let c = PNExpansionParameter(pnsystem) + PNExpansionReducer(1 - (ν/12 + 3/4) * (v/c)^2) + end + end + @test MacroTools.striplines(input) == MacroTools.striplines(output) +end From 8a25d31a7760af85598c8eb366307d35be201a01 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Thu, 3 Jul 2025 11:44:36 -0400 Subject: [PATCH 43/77] Use @static for the VERSION detection --- src/core/utilities/InlineExports.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/utilities/InlineExports.jl b/src/core/utilities/InlineExports.jl index 6f20f686..1705a3eb 100644 --- a/src/core/utilities/InlineExports.jl +++ b/src/core/utilities/InlineExports.jl @@ -66,7 +66,7 @@ eval(quote end end) -if VERSION < v"1.11" +@static if VERSION < v"1.11" using ..NoExport: @public else """ From 25c54f43bf9cd43a9f2119079d2d192b0b0aea39 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Thu, 3 Jul 2025 12:56:05 -0400 Subject: [PATCH 44/77] Simplify indexing and add some tests --- src/core/utilities/truncated_series_monoid.jl | 150 +++++++++++------- 1 file changed, 91 insertions(+), 59 deletions(-) diff --git a/src/core/utilities/truncated_series_monoid.jl b/src/core/utilities/truncated_series_monoid.jl index 2e755e54..315c7c95 100644 --- a/src/core/utilities/truncated_series_monoid.jl +++ b/src/core/utilities/truncated_series_monoid.jl @@ -5,11 +5,11 @@ Given the coefficients `a` of a series, find the coefficients `b` of the *multiplicative* inverse of that series, up to the order of the original series. That is, if ```math -A \colonequals \sum_{i=0}^n a_{i-1} v^i, +A \colonequals \sum_{i=0}^n a_{i} v^i, ``` then we return the coefficients `b` of the series ```math -B \colonequals \sum_{i=0}^n b_{i-1} v^i +B \colonequals \sum_{i=0}^n b_{i} v^i ``` such that ```math @@ -25,24 +25,11 @@ This is relevant for use in [`truncated_series_product`](@ref) and [`truncated_series_ratio`](@ref) — the latter of which just combines the former with this function. -For example, suppose the input coefficients represent the series -```math -A \colonequals \sum_{i=0}^n a_{i-1} v^i. -``` -(Remember that Julia's indexing is 1-based, so we subtract 1 to get the index of the -coefficient of ``v^i``.) Then we return the coefficients `b` of the series -```math -B \colonequals \sum_{i=0}^n b_{i-1} v^i -``` -such that -```math -A\, B = 1 + \mathcal{O}(v^{n+1}). -``` - !!! note - This function requires that `a[1]` be nonzero. If you have a series that starts at - a higher term — say, ``v^n`` for ``n>0`` — you should factor out the ``v^n``, and - multiply the series resulting from this function by ``v^{-n}``. + + This function requires that the constant term (`a[1]`) be nonzero. If you have a series + that starts at a higher term — say, ``v^n`` for ``n>0`` — you should factor out the + ``v^n``, and multiply the series resulting from this function by ``v^{-n}``. ## Explanation @@ -52,9 +39,16 @@ b_0 = 1/a_0. ``` Now, assuming that we've computed all coefficients up to and including ``b_{i}``, we can compute ``b_{i+1}`` from the condition that the term proportional to ``v^{i+1}`` in the -product of the series and its inverse must be zero. This gives +product of the series and its inverse must be zero. That coefficient of that term is +clearly given by the sum of all pairs of coefficients ``a_j b_{i+1-j}`` for +``j=0,1,\ldots,i+1``: +```math +\sum_{j=0}^{i+1} a_j b_{i+1-j} = a_0 b_{i+1} + \sum_{j=1}^{i+1} a_j b_{i+1-j}. +``` +Setting this last expression to zero, using the value of ``b_0`` above, and rearranging, we +have ```math -b_{i+1} = -b_0\sum_{j=1}^{i} a_j b_{i-j}. +b_{i+1} = -b_0\sum_{j=1}^{i} a_j b_{i+1-j}. ``` """ @public function truncated_series_inverse(a::AbstractVector) @@ -69,15 +63,18 @@ function truncated_series_inverse(a::NTuple{N,T}) where {N,T} end @public function truncated_series_inverse!(b, a) + # We fake 0-based indexing by using `begin + i` for `i ∈ 0:n`. @assert length(b) == length(a) - n = length(a) - @inbounds @fastmath if n > 0 - b[0 + 1] = inv(a[0 + 1]) + n = length(a) - 1 + @inbounds @fastmath if n ≥ 0 + b[begin + 0] = inv(a[begin + 0]) end - @inbounds @fastmath for i ∈ 0:(n - 2) - b[i + 1 + 1] = - -b[0 + 1] * - sum((a[j + 1] * b[i + 1 - j + 1] for j ∈ 1:(i + 1)); init=zero(eltype(a))) + @inbounds @fastmath for i ∈ 0:(n - 1) + b[begin + i + 1] = + -b[begin + 0] * sum( + (a[begin + j] * b[begin + i + 1 - j] for j ∈ 1:(i + 1)); + init=zero(eltype(a)), + ) end return b end @@ -92,9 +89,9 @@ Note that this function returns the *value* of the summation, rather than its co Here we define the series in terms of the coefficients `a` and `b` as ```math -A \colonequals \sum_{i=0}^n a_{i-1} v^i +A \colonequals \sum_{i=0}^n a_{i} v^i \qquad -B \colonequals \sum_{i=0}^n b_{i-1} v^i, +B \colonequals \sum_{i=0}^n b_{i} v^i, ``` and return the *value* of the product ``A\, B`` truncated at ``v^n``. @@ -108,23 +105,28 @@ See also [`truncated_series_ratio`](@ref). if N < 0 return zero(v) end - ex = b[N + 1] * a[1] + AB = b[begin + N] * a[begin + 0] for n ∈ (N - 1):-1:0 - ex = muladd(v, ex, b[n + 1] * evalpoly(v, @view a[1:((N - n) + 1)])) + AB = muladd( + v, AB, b[begin + n] * evalpoly(v, @view a[(begin + 0):(begin + (N - n))]) + ) end - return ex + return AB end -function truncated_series_product(a::NTuple{N,T}, b, v) where {N,T} +function truncated_series_product(a::NTuple{NT,T}, b, v) where {NT,T} @assert length(a) == length(b) - if N < 1 + N = length(a) - 1 + if N < 0 return zero(v) end - ex = b[N] * a[1] - for n ∈ (N - 2):-1:0 - ex = muladd(v, ex, b[n + 1] * evalpoly(v, a[1:((N - n - 1) + 1)])) + AB = b[begin + N] * a[begin + 0] + for n ∈ (N - 1):-1:0 + AB = muladd( + v, AB, b[begin + n] * evalpoly(v, @view a[(begin + 0):(begin + (N - n))]) + ) end - return ex + return AB end @doc raw""" @@ -136,9 +138,9 @@ Note that this function returns the *value* of the summation, rather than its co Here we define the series in terms of the coefficients `a` and `b` as ```math -A \colonequals \sum_{i=0}^n a_{i-1} v^i +A \colonequals \sum_{i=0}^n a_{i} v^i \qquad -B \colonequals \sum_{i=0}^n b_{i-1} v^i, +B \colonequals \sum_{i=0}^n b_{i} v^i, ``` and return the *value* of the ratio ``A / B`` truncated at ``v^n``. @@ -152,37 +154,48 @@ end @doc raw""" truncated_series_ratio(a, b) -Evaluate the truncated ratio of the series `a` and `b`, evaluated at expansion value 1. +Evaluate the truncated ratio of the series `a` and `b` at expansion value 1. + This is relevant when the expansion is not in the dynamic variable `v`, for example, but in powers of ``1/c`` as in post-Newtonian expansions. (That is, when the `v` dependence is -already include in the input coefficients.) +already included in the input coefficients.) + +This is different from `truncated_series_ratio(a, b, 1)` in that `a` and `b` may have +different lengths, and it should be somewhat more efficient. """ function truncated_series_ratio(a::NTuple{N1,T1}, b::NTuple{N2,T2}) where {N1,N2,T1,T2} N = max(N1, N2) T = promote_type(T1, T2) if N2 == 0 - throw(DomainError("truncated_series_ratio(a,b): b must have at least one term")) + throw(DomainError("In truncated_series_ratio(a,b): b must have at least one term")) elseif N1 == 0 return zero(T) end - b⁻¹ = MVector{N,T}(undef) + # The uppercase `N`s represent the number of terms in the tuples, while the lowercase + # `n`s represent the highest index of the terms. + n1, n2, n = N1 - 1, N2 - 1, N - 1 + + # Next, we compute the same thing as `truncated_series_inverse` except that we truncate + # this inverse series at `n`, instead of `n2`. Specifically, the differences are that + # (1) the range of iteration over `i` extends to `(n-1)` instead of `(n2-1)`, and (2) + # the range of iteration over `j` extends to `min(i+1, n2)` instead of `(i+1)`. + b⁻¹ = MVector{N,T}(undef) @inbounds @fastmath begin - b⁻¹[0 + 1] = inv(b[0 + 1]) - for i ∈ 0:(N2 - 2) - b⁻¹[i + 1 + 1] = - -b⁻¹[0 + 1] * - sum((b[j + 1] * b⁻¹[i + 1 - j + 1] for j ∈ 1:(i + 1)); init=zero(T)) - end - for i ∈ (N2 - 1):(N - 2) - b⁻¹[i + 1 + 1] = - -b⁻¹[0 + 1] * - sum((b[j + 1] * b⁻¹[i + 1 - j + 1] for j ∈ 1:(N2 - 1)); init=zero(T)) + b⁻¹[begin + 0] = inv(b[begin + 0]) + for i ∈ 0:(n - 1) + b⁻¹[begin + i + 1] = + -b⁻¹[begin + 0] * sum( + (b[begin + j] * b⁻¹[begin + i + 1 - j] for j ∈ 1:min((i + 1), n2)); + init=zero(T), + ) end + # Now, we do the same thing as `truncated_series_product`, except that we account + # for the fact that `a` may not be as long as `b⁻¹`, and we are assuming `v=1`. a╱b = zero(T) - for i1 ∈ 1:N1 - a╱b += a[i1] * sum((b⁻¹[i2] for i2 ∈ 1:(N - i1 + 1)); init=zero(T)) + for i1 ∈ 0:n1 + a╱b += a[begin + i1] * sum((b⁻¹[begin + i2] for i2 ∈ 0:(n - i1)); init=zero(T)) end a╱b end @@ -191,15 +204,16 @@ end @testitem "truncated_series_ratio(a,b)" begin using Random using DoubleFloats + using StaticArrays: SVector import PostNewtonian: truncated_series_inverse, truncated_series_ratio Random.seed!(123) for T ∈ [Float32, Float64, Double64] - for N ∈ 1:20 + for N ∈ 1:15 A = rand(T, N) A[1] = one(T) + rand(T) / 100 a = Tuple(A) x = rand(T) - ϵ = sum(a) * N * eps(T) + ϵ = eps(sum(a) * N) for N1 ∈ 1:N expected = sum(truncated_series_inverse(a)) unit = zeros(T, N1) @@ -209,6 +223,24 @@ end @test truncated_series_ratio(a, a) ≈ 1 rtol = ϵ @test truncated_series_ratio(a, x .* a) ≈ 1 / x rtol = ϵ @test truncated_series_ratio(x .* a, a) ≈ x rtol = ϵ + + c = Tuple(rand(T, N)) + @test truncated_series_ratio(c, a) ≈ + truncated_series_ratio(SVector(c), SVector(a), 1) rtol = ϵ + + for Nzero ∈ 2:N + cshort = c[begin:(Nzero - 1)] + czeros = (cshort..., (zero(T) for i ∈ Nzero:N)...) + #! format: off + @test truncated_series_ratio(cshort, a) ≈ + truncated_series_ratio(czeros, a) atol=2eps(T) rtol=2eps(T) + + ashort = a[begin:(Nzero - 1)] + azeros = (ashort..., (zero(T) for i ∈ Nzero:N)...) + @test truncated_series_ratio(c, ashort) ≈ + truncated_series_ratio(c, azeros) atol=2eps(T) rtol=2eps(T) + #! format: on + end end end end From a71b5e6ed0cf6ce1f4ed1d5c098250ece605bc32 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Thu, 3 Jul 2025 13:42:21 -0400 Subject: [PATCH 45/77] Remove old versions of macros --- src/core/utilities/macros.jl | 198 ----------------------------------- 1 file changed, 198 deletions(-) delete mode 100644 src/core/utilities/macros.jl diff --git a/src/core/utilities/macros.jl b/src/core/utilities/macros.jl deleted file mode 100644 index b9ad661d..00000000 --- a/src/core/utilities/macros.jl +++ /dev/null @@ -1,198 +0,0 @@ -""" - type_converter(pnsystem, x) - -Convert `x` to a type appropriate for the float type of `pnsystem`. - -This is sort of an expansion of the `convert` function, but with nicer syntax for types from -this package, including the ability to do really weird things for `SymbolicPNSystem`. It's -needed to ensure that the types of variables and constants are correct when we use them in -expressions, rather than just assuming everything is a `Float64`. -""" -function type_converter(pnsystem, x) - return convert(eltype(pnsystem), x) -end -function type_converter(::FDPNSystem{FT}, x) where {FT} - return x -end -function type_converter(::FDPNSystem{FT}, x::Integer) where {FT} - return convert(FT, x) -end -function type_converter(::FDPNSystem{FT}, x::Rational) where {FT} - return convert(FT, x) -end -function type_converter(::FDPNSystem{FT}, x::AbstractIrrational) where {FT} - return convert(FT, x) -end - -fundamental_variables = methodswith(PNSystem, FundamentalVariables) -fundamental_quaternionic_variables = [ - m for m ∈ methodswith(AbstractVector, FundamentalVariables) if m.name ∈ [:χ⃗₁, :χ⃗₂, :R] -] -derived_variables = methodswith(VecOrPNSystem, DerivedVariables) -pnvariables = map(v -> v.name, [fundamental_variables; derived_variables]) - -irrationals = unique( - [ - find_symbols_of_type(Base.MathConstants, Irrational) - find_symbols_of_type(MathConstants, Irrational) - ], -) - -# This should include all the unary functions that we want to use in any PN expression. -unary_funcs = [:√, :sqrt, :log, :ln, :sin, :cos] -# unary_funcs = Dict( -# :√ => :(Base.sqrt), -# :sqrt => :(Base.sqrt), -# :log => :(Base.log), -# :ln => :(Base.log), -# ) - -# for (expr, f) ∈ pairs(unary_funcs) -# if expr ∈ (:sqrt, :ln) # These are just aliases, so avoid redefinitions -# continue -# end -# @eval begin -# $f(::Type{T}, x) where T = $f(x) -# $f(::Type{T}, x::Int) where T = $f(T(x)) -# $f(::Type{T}, x::Rational) where T = $f(T(x)) -# $f(::Type{T}, x::Irrational) where T = $f(T(x)) -# end -# end - -function pn_expression(pnsystem::Symbol, body) - # Look for variables in `body` that we need to treat specially, and write exprs to do - # so. These three are described as bullet points in the docstring of `@pn_expression`. - pnvariables_exprs = [ - :($v = $v($pnsystem)) for v ∈ filter(v -> MacroTools.inexpr(body, v), pnvariables) - ] - irrationals_exprs = [ - :($v = type_converter($pnsystem, $v)) for - v ∈ filter(v -> MacroTools.inexpr(body, v), irrationals) - ] - unary_funcs_exprs = [ - :($v = (x -> $v(type_converter($pnsystem, x)))) for - v ∈ filter(v -> MacroTools.inexpr(body, v), unary_funcs) - ] - - exprs = [ - pnvariables_exprs - irrationals_exprs - unary_funcs_exprs - ] - - # Next, add `pnsystem` as the argument to each @pn_expansion call - new_body = MacroTools.unblock( - macroexpand( - @__MODULE__, - MacroTools.postwalk(body) do x - if MacroTools.isexpr(x, :macrocall) && - x.args[1] == Symbol("@pn_expansion") && - !isa(x.args[end - 1], Symbol) - x′ = deepcopy(x) - insert!(x′.args, length(x′.args), pnsystem) - x′ - else - x - end - end; - recursive=true, - ), - ) - - # Finally, just wrap `new_body` in a `let` block, where we include exprs created above. - # Also include the definitions `c=G=1` (to be overwritten inside any `@pn_expansion`). - full_body = MacroTools.unblock(quote - let c=G=one(pnsystem) - #@fastmath - let $(exprs...) - $(new_body) - end - end - end) - return full_body -end - -function pn_expression(arg_index::Integer, func) - splitfunc = MacroTools.splitdef(func) - pnsystem = MacroTools.namify(splitfunc[:args][arg_index]) - splitfunc[:kwargs] = [ - splitfunc[:kwargs] - :($(Expr(:kw, :(pn_expansion_reducer::Val{PNExpansionReducer}), :(Val(sum))))) - ] - splitfunc[:whereparams] = (splitfunc[:whereparams]..., :PNExpansionReducer) - splitfunc[:body] = pn_expression(pnsystem, splitfunc[:body]) - return MacroTools.combinedef(splitfunc) -end - -""" - @pn_expression [arg_index=1] func - -This macro takes the function `func`, looks for various symbols inside that function, and if -present defines them appropriately inside that function. - -The first argument to this macro is `arg_index`, which just tells us which argument to the -function `func` is a `PNSystem`. For example, the variables defined in -[`PostNewtonian.FundamentalVariables`](@ref "Fundamental variables") all take a single -argument of `pnsystem`, which is used to compute the values for those variables; this macro -just needs to know where to find `pnsystem`. - -Once it has this information, there are five types of transformations it will make: - - 1. Adds a keyword argument `pn_expansion_reducer::Val{PNExpansionReducer}=Val(sum)` to the - function signature. This is used to determine how to reduce the PN expansion terms. - The default is `Val(sum)`, which will just return a single number, but `Val(identity)` - can be used to return the expansion. This should be used inside the function as - `PNExpansionReducer`, and will be automatically used inside any `@pn_expansion`. - 2. For every [fundamental](@ref "Fundamental variables") or [derived](@ref "Derived - variables") variable, the name of that variable used in the body of `func` will be - replaced by its value when called with `pnsystem`. For example, you can simply use the - symbols `M₁` or `μ` in your code, rather than calling them as [`M₁(pnsystem)`](@ref M₁) - or [`μ(pnsystem)`](@ref μ) every time they appear. - 3. Every `Irrational` defined in `Base.MathConstants` or `PostNewtonian.MathConstants` will - be transformed to the `eltype` of `pnsystem`. This lets you naturally use such - constants in expressions like `2π/3` without automatically converting to `Float64`. - 4. Each of a short list of functions given by `unary_funcs` in `utilities/macros.jl` will - first convert their arguments to the `eltype` of `pnsystem`. In particular, you can use - expressions like `√10` or `ln(2)` without the result being converted to a `Float64`. - 5. Insert the `pnsystem` argument as the first argument to each occurrence of - `@pn_expansion` that needs it. - -To be more explicit, the first three are achieved by defining the relevant quantities in a -`let` block placed around the body of `func`, so that the values may be used efficiently -without recomputation. - -If you need to use one of the fundamental- or derived-variable functions as arguments of -values other than those encapsulated in `pnsystem`, you'll need to address them explicitly -with the module name — as in `PostNewtonian.v(;Ω, M)`. -""" -macro pn_expression(func) - return esc(pn_expression(1, func)) -end - -macro pn_expression(arg_index, func) - return esc(pn_expression(arg_index, func)) -end - -""" - @pn_expansion [pnsystem] expansion - -Gather terms in `expansion` by the powers of ``1/c`` involved, zeroing out any terms with -powers of ``1/c`` higher than the `pnsystem`'s `PNOrder` parameter, and combine the terms -using the `PNExpansionReducer` specified in argument of the function that includes this -macro call. - -This expansion is achieved by setting — inside a `let` block created by this macro — - -Note that the `pnsystem` argument can be inserted automatically by [`@pn_expression`](@ref). -""" -macro pn_expansion(pnsystem, expr) - return esc(pn_expansion(pnsystem, expr)) -end - -function pn_expansion(pnsystem, expr) - quote - let c = PNExpansionParameter($pnsystem) - PNExpansionReducer($expr) - end - end -end From aab41bf1a21f0e15581c7b8097d579bf2e788ed8 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Thu, 3 Jul 2025 16:31:51 -0400 Subject: [PATCH 46/77] Add common variables to `literature` and start including files --- src/PostNewtonian.jl | 3 +- .../{Einstein1918 => }/Einstein1918.jl | 3 +- .../common_variables/common_variables.jl | 88 -------------- src/literature/common_variables/horizons.jl | 36 +++--- .../common_variables/mass_combinations.jl | 48 ++++---- .../common_variables/orbital_elements.jl | 26 ++-- .../common_variables/spin_combinations.jl | 112 +++++++++--------- .../common_variables/tidal_coupling.jl | 4 +- src/literature/literature.jl | 11 ++ 9 files changed, 125 insertions(+), 206 deletions(-) rename src/literature/{Einstein1918 => }/Einstein1918.jl (87%) delete mode 100644 src/literature/common_variables/common_variables.jl diff --git a/src/PostNewtonian.jl b/src/PostNewtonian.jl index 13312e6f..8f84abf0 100644 --- a/src/PostNewtonian.jl +++ b/src/PostNewtonian.jl @@ -5,7 +5,8 @@ using FastDifferentiation: FastDifferentiation, Node as FDNode #using InlineExports: @public, @export # See below using IrrationalConstants: @irrational using MacroTools: MacroTools -using Quaternionic: Quaternionic, QuatVec, Rotor, abs2vec, components, normalize, ⋅, × +using Quaternionic: + Quaternionic, QuatVec, Rotor, 𝐢, 𝐣, 𝐤, abs2vec, absvec, components, normalize, ⋅, × using StaticArrays: MVector, SVector using TestItems: @testitem diff --git a/src/literature/Einstein1918/Einstein1918.jl b/src/literature/Einstein1918.jl similarity index 87% rename from src/literature/Einstein1918/Einstein1918.jl rename to src/literature/Einstein1918.jl index 488bb449..f1ea0f56 100644 --- a/src/literature/Einstein1918/Einstein1918.jl +++ b/src/literature/Einstein1918.jl @@ -1,7 +1,6 @@ @pn_reference module Einstein1918 -import PostNewtonian: G, c, M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ -import CommonVariables: χₛ +import PostNewtonian: G, c, M₁, M₂, χ⃗₁, χ⃗₂, R, v, Φ, χₛ # import common variables # define variables not in common variables diff --git a/src/literature/common_variables/common_variables.jl b/src/literature/common_variables/common_variables.jl deleted file mode 100644 index f73d0243..00000000 --- a/src/literature/common_variables/common_variables.jl +++ /dev/null @@ -1,88 +0,0 @@ -module CommonVariables - -# Import the fundamental variables, which are generally part of the `state` vector of a -# PNSystem. -import ..PostNewtonian: - M₁, M₂, χ⃗₁, χ₁ˣ, χ₁ʸ, χ₁ᶻ, χ⃗₂, χ₂ˣ, χ₂ʸ, χ₂ᶻ, R, Rʷ, Rˣ, Rʸ, Rᶻ, v, Φ, Λ₁, Λ₂ - -# Now, we define all the common variables used throughout the literature as functions of -# those fundamental variables. - -using ..PostNewtonian.FundamentalVariables -using Quaternionic: 𝐢, 𝐣, 𝐤, QuatVec, ⋅, ×, abs2vec, absvec - -include("derived_variables/mass_combinations.jl") -export total_mass, - M, - reduced_mass, - μ, - reduced_mass_ratio, - ν, - mass_difference_ratio, - δ, - mass_ratio, - q, - chirp_mass, - ℳ, - X1, - X₁, - X2, - X₂ - -include("derived_variables/orbital_elements.jl") -export n_hat, n̂, lambda_hat, λ̂, ell_hat, ℓ̂, Omega, Ω, lnv - -include("derived_variables/spin_combinations.jl") -export S⃗₁, - S⃗₂, - S⃗, - Σ⃗, - χ⃗, - χ⃗ₛ, - χ⃗ₐ, - chi_perp, - χₚₑᵣₚ, - χₑ, - chi_eff, - χₚ, - chi_p, - S⃗₀⁺, - S⃗₀⁻, - S₀⁺ₙ, - S₀⁻ₙ, - S₀⁺λ, - S₀⁻λ, - S₀⁺ₗ, - S₀⁻ₗ, - χ₁², - χ₂², - χ₁, - χ₂, - χ₁₂, - χ₁ₗ, - χ₂ₗ, - χₛₗ, - χₐₗ, - Sₙ, - Σₙ, - Sλ, - Σλ, - Sₗ, - Σₗ, - sₗ, - σₗ, - S₁ₙ, - S₁λ, - S₁ₗ, - S₂ₙ, - S₂λ, - S₂ₗ - -include("derived_variables/horizons.jl") -export rₕ₁, - rₕ₂, Ωₕ₁, Ωₕ₂, sin²θ₁, sin²θ₂, ϕ̇̂₁, ϕ̇̂₂, Î₀₁, Î₀₂, κ₁, κ₂, κ₊, κ₋, λ₁, λ₂, λ₊, λ₋ - -include("derived_variables/tidal_coupling.jl") -export Λ̃, Lambda_tilde - -end diff --git a/src/literature/common_variables/horizons.jl b/src/literature/common_variables/horizons.jl index 80c031bb..3e45a4bf 100644 --- a/src/literature/common_variables/horizons.jl +++ b/src/literature/common_variables/horizons.jl @@ -7,7 +7,7 @@ As defined on page 2, line 4, of [Alvi (2001)](https://doi.org/10.1103/PhysRevD.64.104020). See the documentation section on ["Horizons"](@ref Horizons) for more details. """ -rₕ₁(s::VecOrPNSystem) = M₁(s) * (1 + √(1 - min(χ₁²(s), 1))) +@public rₕ₁(s::PNSystem) = M₁(s) * (1 + √(1 - min(χ₁²(s), 1))) """ rₕ₂(s) @@ -18,7 +18,7 @@ As defined on page 2, line 4, of [Alvi (2001)](https://doi.org/10.1103/PhysRevD.64.104020). See the documentation section on ["Horizons"](@ref Horizons) for more details. """ -rₕ₂(s::VecOrPNSystem) = M₂(s) * (1 + √(1 - min(χ₂²(s), 1))) +@public rₕ₂(s::PNSystem) = M₂(s) * (1 + √(1 - min(χ₂²(s), 1))) """ Ωₕ₁(s) @@ -29,7 +29,7 @@ As defined on page 2, line 5, of [Alvi (2001)](https://doi.org/10.1103/PhysRevD.64.104020). See the documentation section on ["Horizons"](@ref Horizons) for more details. """ -Ωₕ₁(s::VecOrPNSystem) = χ₁(s) / 2rₕ₁(s) +@public Ωₕ₁(s::PNSystem) = χ₁(s) / 2rₕ₁(s) """ Ωₕ₂(s) @@ -40,7 +40,7 @@ As defined on page 2, line 5, of [Alvi (2001)](https://doi.org/10.1103/PhysRevD.64.104020). See the documentation section on ["Horizons"](@ref Horizons) for more details. """ -Ωₕ₂(s::VecOrPNSystem) = χ₂(s) / 2rₕ₂(s) +@public Ωₕ₂(s::PNSystem) = χ₂(s) / 2rₕ₂(s) """ sin²θ₁(s) @@ -50,7 +50,7 @@ Sine-squared of angle between spin of black hole 1 and vector to black hole 2. Compare to Eq. (18) of [Alvi (2001)](https://doi.org/10.1103/PhysRevD.64.104020). See the documentation section on ["Horizons"](@ref Horizons) for more details. """ -function sin²θ₁(s::VecOrPNSystem) +@public function sin²θ₁(s::PNSystem) let χ₁² = χ₁²(s) ifelse(iszero(χ₁²), one(χ₁²), abs2vec(n̂(s) × χ⃗₁(s)) / χ₁²) end @@ -64,7 +64,7 @@ Sine-squared of angle between spin of black hole 2 and vector to black hole 1. Compare to Eq. (18) of [Alvi (2001)](https://doi.org/10.1103/PhysRevD.64.104020). See the documentation section on ["Horizons"](@ref Horizons) for more details. """ -function sin²θ₂(s::VecOrPNSystem) +@public function sin²θ₂(s::PNSystem) let χ₂² = χ₂²(s) ifelse(iszero(χ₂²), one(χ₂²), abs2vec(n̂(s) × χ⃗₂(s)) / χ₂²) end @@ -83,7 +83,7 @@ that depend on this. See the documentation section on ["Horizons"](@ref Horizons) for more details. """ -function ϕ̇̂₁(s::VecOrPNSystem) +@public function ϕ̇̂₁(s::PNSystem) let χ₁ = χ₁(s), sin²θ₁ = sin²θ₁(s), M = M(s) ifelse( iszero(χ₁), @@ -106,7 +106,7 @@ that depend on this. See the documentation section on ["Horizons"](@ref Horizons) for more details. """ -function ϕ̇̂₂(s::VecOrPNSystem) +@public function ϕ̇̂₂(s::PNSystem) let χ₂ = χ₂(s), sin²θ₂ = sin²θ₂(s), M = M(s) ifelse( iszero(χ₂), @@ -125,7 +125,7 @@ This is the moment divided by ``ν^2 v^{12}``, as given by Eq. (10) of [Alvi (2001)](https://doi.org/10.1103/PhysRevD.64.104020). See the documentation section on ["Horizons"](@ref Horizons) for more details. """ -function Î₀₁(s::VecOrPNSystem) +@public function Î₀₁(s::PNSystem) let χ₁² = χ₁²(s), sin²θ₁ = sin²θ₁(s) (16rₕ₁(s) / 5M(s)^2) * M₁(s)^3 * sin²θ₁ * (1 - 3//4 * χ₁² + 15//4 * χ₁² * sin²θ₁) end @@ -140,7 +140,7 @@ This is the moment divided by ``ν^2 v^{12}``, as given by Eq. (10) of [Alvi (2001)](https://doi.org/10.1103/PhysRevD.64.104020). See the documentation section on ["Horizons"](@ref Horizons) for more details. """ -function Î₀₂(s::VecOrPNSystem) +@public function Î₀₂(s::PNSystem) let χ₂² = χ₂²(s), sin²θ₂ = sin²θ₂(s) (16rₕ₂(s) / 5M(s)^2) * M₂(s)^3 * sin²θ₂ * (1 - 3//4 * χ₂² + 15//4 * χ₂² * sin²θ₂) end @@ -173,7 +173,7 @@ See also [`λ₁`](@ref). quantity may also be related to [`λ₁`](@ref). Pull requests or issues with more information are welcome. """ -function κ₁(s::VecOrPNSystem) +@public function κ₁(s::PNSystem) return 1 end @@ -183,7 +183,7 @@ end The "quadrupolar polarisability" of object 2 used by [Bohé et al. (2015)](https://arxiv.org/abs/1501.01529). See [`κ₁`](@ref) for more details. """ -function κ₂(s::VecOrPNSystem) +@public function κ₂(s::PNSystem) return 1 end @@ -193,7 +193,7 @@ end Equal to [`κ₁`](@ref)` + `[`κ₂`](@ref); defined below Eq. (3.28) of [Bohé et al. (2015)](https://arxiv.org/abs/1501.01529). """ -function κ₊(s::VecOrPNSystem) +@public function κ₊(s::PNSystem) return κ₁(s) + κ₂(s) end @@ -203,7 +203,7 @@ end Equal to [`κ₁`](@ref)` - `[`κ₂`](@ref); defined below Eq. (3.28) of [Bohé et al. (2015)](https://arxiv.org/abs/1501.01529). """ -function κ₋(s::VecOrPNSystem) +@public function κ₋(s::PNSystem) return κ₁(s) - κ₂(s) end @@ -226,7 +226,7 @@ See also [`κ₁`](@ref). quantity may also be related to [`λ₁`](@ref). Pull requests or issues with more information are welcome. """ -function λ₁(s::VecOrPNSystem) +@public function λ₁(s::PNSystem) return 1 end @@ -236,7 +236,7 @@ end The "quadrupolar polarisability" of object 2 used by [Bohé et al. (2015)](https://arxiv.org/abs/1501.01529). See [`λ₁`](@ref) for more details. """ -function λ₂(s::VecOrPNSystem) +@public function λ₂(s::PNSystem) return 1 end @@ -246,7 +246,7 @@ end Equal to [`λ₁`](@ref)` + `[`λ₂`](@ref); defined below Eq. (3.28) of [Bohé et al. (2015)](https://arxiv.org/abs/1501.01529). """ -function λ₊(s::VecOrPNSystem) +@public function λ₊(s::PNSystem) return λ₁(s) + λ₂(s) end @@ -256,6 +256,6 @@ end Equal to [`λ₁`](@ref)` - `[`λ₂`](@ref); defined below Eq. (3.28) of [Bohé et al. (2015)](https://arxiv.org/abs/1501.01529). """ -function λ₋(s::VecOrPNSystem) +@public function λ₋(s::PNSystem) return λ₁(s) - λ₂(s) end diff --git a/src/literature/common_variables/mass_combinations.jl b/src/literature/common_variables/mass_combinations.jl index 95044df1..30cef306 100644 --- a/src/literature/common_variables/mass_combinations.jl +++ b/src/literature/common_variables/mass_combinations.jl @@ -6,9 +6,9 @@ Compute the total mass ``M₁+M₂``. """ -M(M₁, M₂) = M₁ + M₂ -M(s::VecOrPNSystem) = M(M₁(s), M₂(s)) -const total_mass = M +@public M(M₁, M₂) = M₁ + M₂ +M(s::PNSystem) = M(M₁(s), M₂(s)) +@public const total_mass = M """ μ(pnsystem) @@ -18,9 +18,9 @@ const total_mass = M Compute the reduced mass ``(M₁ M₂)/(M₁+M₂)``. """ -μ(M₁, M₂) = (M₁ * M₂) / (M₁ + M₂) -μ(s::VecOrPNSystem) = μ(M₁(s), M₂(s)) -const reduced_mass = μ +@public μ(M₁, M₂) = (M₁ * M₂) / (M₁ + M₂) +μ(s::PNSystem) = μ(M₁(s), M₂(s)) +@public const reduced_mass = μ """ ν(pnsystem) @@ -32,10 +32,10 @@ Compute the reduced mass ratio ``(M₁ M₂)/(M₁+M₂)^2``. Note that the denominator is squared, unlike in the reduced mass [`μ`](@ref). """ -ν(M₁, M₂) = (M₁ * M₂) / (M₁ + M₂)^2 -ν(s::VecOrPNSystem) = ν(M₁(s), M₂(s)) +@public ν(M₁, M₂) = (M₁ * M₂) / (M₁ + M₂)^2 +ν(s::PNSystem) = ν(M₁(s), M₂(s)) ν(; q) = q / (1 + q)^2 -const reduced_mass_ratio = ν +@public const reduced_mass_ratio = ν """ δ(pnsystem) @@ -48,10 +48,10 @@ Compute mass-difference ratio ``(M₁-M₂)/(M₁+M₂)``. Note that we do not restrict to ``M₁ ≥ M₂`` or vice versa; if you prefer that ``δ`` always be positive (or always negative), you are responsible for ensuring that. """ -δ(M₁, M₂) = (M₁ - M₂) / (M₁ + M₂) -δ(s::VecOrPNSystem) = δ(M₁(s), M₂(s)) +@public δ(M₁, M₂) = (M₁ - M₂) / (M₁ + M₂) +δ(s::PNSystem) = δ(M₁(s), M₂(s)) δ(; q) = (q - 1) / (q + 1) -const mass_difference_ratio = δ +@public const mass_difference_ratio = δ """ q(pnsystem) @@ -64,9 +64,9 @@ Compute mass ratio ``M₁/M₂``. Note that we do not restrict to ``M₁ ≥ M₂`` or vice versa; if you prefer that ``q`` always be greater than or equal to 1 (or vice versa), you are responsible for ensuring that. """ -q(M₁, M₂) = M₁ / M₂ -q(s::VecOrPNSystem) = q(M₁(s), M₂(s)) -const mass_ratio = q +@public q(M₁, M₂) = M₁ / M₂ +q(s::PNSystem) = q(M₁(s), M₂(s)) +@public const mass_ratio = q """ ℳ(pnsystem) @@ -82,9 +82,9 @@ The chirp mass is defined as \\mathcal{M} = \\frac{(M_1 M_2)^{3/5}} {(M_1 + M_2)^{1/5}}. ``` """ -ℳ(M₁, M₂) = ((M₁ * M₂)^3 / (M₁ + M₂))^(1//5) -ℳ(s::VecOrPNSystem) = ℳ(M₁(s), M₂(s)) -const chirp_mass = ℳ +@public ℳ(M₁, M₂) = ((M₁ * M₂)^3 / (M₁ + M₂))^(1//5) +ℳ(s::PNSystem) = ℳ(M₁(s), M₂(s)) +@public const chirp_mass = ℳ """ X₁(pnsystem) @@ -94,9 +94,9 @@ const chirp_mass = ℳ Compute the reduced *individual* mass ``M₁/(M₁+M₂)``. """ -X₁(M₁, M₂) = M₁ / (M₁ + M₂) -X₁(s::VecOrPNSystem) = X₁(M₁(s), M₂(s)) -const X1 = X₁ +@public X₁(M₁, M₂) = M₁ / (M₁ + M₂) +X₁(s::PNSystem) = X₁(M₁(s), M₂(s)) +@public const X1 = X₁ """ X₂(pnsystem) @@ -106,6 +106,6 @@ const X1 = X₁ Compute the reduced *individual* mass ``M₂/(M₁+M₂)``. """ -X₂(M₁, M₂) = M₂ / (M₁ + M₂) -X₂(s::VecOrPNSystem) = X₂(M₁(s), M₂(s)) -const X2 = X₂ +@public X₂(M₁, M₂) = M₂ / (M₁ + M₂) +X₂(s::PNSystem) = X₂(M₁(s), M₂(s)) +@public const X2 = X₂ diff --git a/src/literature/common_variables/orbital_elements.jl b/src/literature/common_variables/orbital_elements.jl index d0e24064..0f9ba5f6 100644 --- a/src/literature/common_variables/orbital_elements.jl +++ b/src/literature/common_variables/orbital_elements.jl @@ -10,9 +10,9 @@ The unit vector pointing from object 2 to object 1, when the frame is given by t n̂(R) = R x̂ R̄ ``` """ -n̂(R) = QuatVec(R(𝐢)) -n̂(s::VecOrPNSystem) = n̂(R(s)) -const n_hat = n̂ +@public n̂(R) = QuatVec(R(𝐢)) +n̂(s::PNSystem) = n̂(R(s)) +@public const n_hat = n̂ """ λ̂(pnsystem) @@ -27,9 +27,9 @@ the frame is given by the rotor `R`. This is equal to ``` This also completes the right-handed triple of ``(n̂, λ̂, ℓ̂)``. """ -λ̂(R) = QuatVec(R(𝐣)) -λ̂(s::VecOrPNSystem) = λ̂(R(s)) -const lambda_hat = λ̂ +@public λ̂(R) = QuatVec(R(𝐣)) +λ̂(s::PNSystem) = λ̂(R(s)) +@public const lambda_hat = λ̂ """ ℓ̂(pnsystem) @@ -43,9 +43,9 @@ given by the rotor `R`. This is equal to ℓ̂(R) = R ẑ R̄ ``` """ -ℓ̂(R) = QuatVec(R(𝐤)) -ℓ̂(s::VecOrPNSystem) = ℓ̂(R(s)) -const ell_hat = ℓ̂ +@public ℓ̂(R) = QuatVec(R(𝐤)) +ℓ̂(s::PNSystem) = ℓ̂(R(s)) +@public const ell_hat = ℓ̂ @doc raw""" Ω(pnsystem) @@ -63,8 +63,6 @@ definition* as ``` See also [`v`](@ref). """ -Ω(; v, M=1) = v^3 / M -Ω(s::VecOrPNSystem) = Ω(; v=v(s), M=M(s)) -const Omega = Ω - -lnv(s::VecOrPNSystem) = ln(v(s)) +@public Ω(; v, M=1) = v^3 / M +Ω(s::PNSystem) = Ω(; v=v(s), M=M(s)) +@public const Omega = Ω diff --git a/src/literature/common_variables/spin_combinations.jl b/src/literature/common_variables/spin_combinations.jl index e36b5cbe..42952dd3 100644 --- a/src/literature/common_variables/spin_combinations.jl +++ b/src/literature/common_variables/spin_combinations.jl @@ -3,14 +3,14 @@ Dimensionful spin vector of object 1. """ -S⃗₁(s::VecOrPNSystem) = χ⃗₁(s) * M₁(s)^2 +@public S⃗₁(s::PNSystem) = χ⃗₁(s) * M₁(s)^2 """ S⃗₂(pnsystem) Dimensionful spin vector of object 2. """ -S⃗₂(s::VecOrPNSystem) = χ⃗₂(s) * M₂(s)^2 +@public S⃗₂(s::PNSystem) = χ⃗₂(s) * M₂(s)^2 """ S⃗(pnsystem) @@ -18,8 +18,8 @@ S⃗₂(s::VecOrPNSystem) = χ⃗₂(s) * M₂(s)^2 Total (dimensionful) spin vector ``S⃗₁+S⃗₂``. """ -S⃗(M₁, M₂, χ⃗₁, χ⃗₂) = χ⃗₁ * M₁^2 + χ⃗₂ * M₂^2 -S⃗(s::VecOrPNSystem) = S⃗(M₁(s), M₂(s), χ⃗₁(s), χ⃗₂(s)) +@public S⃗(M₁, M₂, χ⃗₁, χ⃗₂) = χ⃗₁ * M₁^2 + χ⃗₂ * M₂^2 +S⃗(s::PNSystem) = S⃗(M₁(s), M₂(s), χ⃗₁(s), χ⃗₂(s)) """ Σ⃗(pnsystem) @@ -27,8 +27,8 @@ S⃗(s::VecOrPNSystem) = S⃗(M₁(s), M₂(s), χ⃗₁(s), χ⃗₂(s)) Differential spin vector ``M(a⃗₂-a⃗₁)``. """ -Σ⃗(M₁, M₂, χ⃗₁, χ⃗₂) = (M₁ + M₂) * (χ⃗₂ * M₂ - χ⃗₁ * M₁) -Σ⃗(s::VecOrPNSystem) = Σ⃗(M₁(s), M₂(s), χ⃗₁(s), χ⃗₂(s)) +@public Σ⃗(M₁, M₂, χ⃗₁, χ⃗₂) = (M₁ + M₂) * (χ⃗₂ * M₂ - χ⃗₁ * M₁) +Σ⃗(s::PNSystem) = Σ⃗(M₁(s), M₂(s), χ⃗₁(s), χ⃗₂(s)) """ χ⃗(pnsystem) @@ -36,29 +36,29 @@ Differential spin vector ``M(a⃗₂-a⃗₁)``. Normalized spin vector ``S⃗/M²``. """ -χ⃗(S⃗, M) = S⃗ / M^2 -χ⃗(s::VecOrPNSystem) = χ⃗(S⃗(s), M(s)) +@public χ⃗(S⃗, M) = S⃗ / M^2 +χ⃗(s::PNSystem) = χ⃗(S⃗(s), M(s)) """ χ⃗ₛ(M₁, M₂, χ⃗₁, χ⃗₂) Symmetric spin vector ``(χ⃗₁+χ⃗₂)/2``. """ -χ⃗ₛ(χ⃗₁, χ⃗₂) = (χ⃗₁ + χ⃗₂) / 2 +@public χ⃗ₛ(χ⃗₁, χ⃗₂) = (χ⃗₁ + χ⃗₂) / 2 χ⃗ₛ(M₁, M₂, χ⃗₁, χ⃗₂) = (χ⃗₁ + χ⃗₂) / 2 -χ⃗ₛ(s::VecOrPNSystem) = χ⃗ₛ(M₁(s), M₂(s), χ⃗₁(s), χ⃗₂(s)) +χ⃗ₛ(s::PNSystem) = χ⃗ₛ(M₁(s), M₂(s), χ⃗₁(s), χ⃗₂(s)) """ χ⃗ₐ(M₁, M₂, χ⃗₁, χ⃗₂) Antisymmetric spin vector ``(χ⃗₁-χ⃗₂)/2``. """ -χ⃗ₐ(χ⃗₁, χ⃗₂) = (χ⃗₁ - χ⃗₂) / 2 +@public χ⃗ₐ(χ⃗₁, χ⃗₂) = (χ⃗₁ - χ⃗₂) / 2 χ⃗ₐ(M₁, M₂, χ⃗₁, χ⃗₂) = (χ⃗₁ - χ⃗₂) / 2 -χ⃗ₐ(s::VecOrPNSystem) = χ⃗ₐ(M₁(s), M₂(s), χ⃗₁(s), χ⃗₂(s)) +χ⃗ₐ(s::PNSystem) = χ⃗ₐ(M₁(s), M₂(s), χ⃗₁(s), χ⃗₂(s)) -χₚₑᵣₚ(s::VecOrPNSystem) = √(χ₁²(s) - (χ₁ₗ(s))^2 + χ₂²(s) - (χ₂ₗ(s))^2) -const chi_perp = χₚₑᵣₚ +@public χₚₑᵣₚ(s::PNSystem) = √(χ₁²(s) - (χ₁ₗ(s))^2 + χ₂²(s) - (χ₂ₗ(s))^2) +@public const chi_perp = χₚₑᵣₚ @doc raw""" χₑ(s) @@ -74,10 +74,8 @@ Defined as \right) \cdot \frac{\hat{\mathbf{L}}_{\mathrm{N}}} {M}. ``` """ -function χₑ(s::VecOrPNSystem) - return (S₁ₗ(s) / M₁(s) + S₂ₗ(s) / M₂(s)) / M(s) -end -const chi_eff = χₑ +@public χₑ(s::PNSystem) = (S₁ₗ(s) / M₁(s) + S₂ₗ(s) / M₂(s)) / M(s) +@public const chi_eff = χₑ @doc raw""" χₚ(s) @@ -112,7 +110,7 @@ convention. Because it seems to be the trend, this function uses the latter definition. """ -function χₚ(s::VecOrPNSystem) +@public function χₚ(s::PNSystem) χ₁ₚₑᵣₚ = √(χ₁ₙ(s)^2 + χ₁λ(s)^2) χ₂ₚₑᵣₚ = √(χ₂ₙ(s)^2 + χ₂λ(s)^2) let q = 1 / q(s) # This is to convert to LVK's convention @@ -152,16 +150,16 @@ the case for black holes. You can define `κ₁` and `κ₂` to have other valu See also [`S⃗₀⁻`](@ref). """ -S⃗₀⁺(s::VecOrPNSystem) = S⃗₀⁺(M₁(s), M₂(s), κ₁(s), κ₂(s), S⃗₁(s), S⃗₂(s)) +@public S⃗₀⁺(s::PNSystem) = S⃗₀⁺(M₁(s), M₂(s), κ₁(s), κ₂(s), S⃗₁(s), S⃗₂(s)) function S⃗₀⁺(M₁, M₂, κ₁, κ₂, S⃗₁, S⃗₂) M = M₁ + M₂ κᵣ = (κ₁ / κ₂)^(1//4) return (M / M₁) * κᵣ * √(1 + √(1 - κ₁ * κ₂)) * S⃗₁ + (M / M₂) / κᵣ * √(1 - √(1 - κ₁ * κ₂)) * S⃗₂ end -S₀⁺ₙ(s::VecOrPNSystem) = S⃗₀⁺(M₁(s), M₂(s), κ₁(s), κ₂(s), S₁ₙ(s), S₂ₙ(s)) -S₀⁺λ(s::VecOrPNSystem) = S⃗₀⁺(M₁(s), M₂(s), κ₁(s), κ₂(s), S₁λ(s), S₂λ(s)) -S₀⁺ₗ(s::VecOrPNSystem) = S⃗₀⁺(M₁(s), M₂(s), κ₁(s), κ₂(s), S₁ₗ(s), S₂ₗ(s)) +@public S₀⁺ₙ(s::PNSystem) = S⃗₀⁺(M₁(s), M₂(s), κ₁(s), κ₂(s), S₁ₙ(s), S₂ₙ(s)) +@public S₀⁺λ(s::PNSystem) = S⃗₀⁺(M₁(s), M₂(s), κ₁(s), κ₂(s), S₁λ(s), S₂λ(s)) +@public S₀⁺ₗ(s::PNSystem) = S⃗₀⁺(M₁(s), M₂(s), κ₁(s), κ₂(s), S₁ₗ(s), S₂ₗ(s)) @doc raw""" S⃗₀⁻(s) @@ -181,38 +179,38 @@ the case for black holes. You can define `κ₁` and `κ₂` to have other valu See also [`S⃗₀⁺`](@ref). """ -S⃗₀⁻(s::VecOrPNSystem) = S⃗₀⁻(M₁(s), M₂(s), κ₁(s), κ₂(s), S⃗₁(s), S⃗₂(s)) +@public S⃗₀⁻(s::PNSystem) = S⃗₀⁻(M₁(s), M₂(s), κ₁(s), κ₂(s), S⃗₁(s), S⃗₂(s)) S⃗₀⁻(M₁, M₂, κ₁, κ₂, S⃗₁, S⃗₂) = S⃗₀⁺(M₂, M₁, κ₂, κ₁, S⃗₂, S⃗₁) -S₀⁻ₙ(s::VecOrPNSystem) = S⃗₀⁻(M₁(s), M₂(s), κ₁(s), κ₂(s), S₁ₙ(s), S₂ₙ(s)) -S₀⁻λ(s::VecOrPNSystem) = S⃗₀⁻(M₁(s), M₂(s), κ₁(s), κ₂(s), S₁λ(s), S₂λ(s)) -S₀⁻ₗ(s::VecOrPNSystem) = S⃗₀⁻(M₁(s), M₂(s), κ₁(s), κ₂(s), S₁ₗ(s), S₂ₗ(s)) - -χ₁²(s::VecOrPNSystem) = abs2vec(χ⃗₁(s)) -χ₂²(s::VecOrPNSystem) = abs2vec(χ⃗₂(s)) -χ₁(s::VecOrPNSystem) = absvec(χ⃗₁(s)) -χ₂(s::VecOrPNSystem) = absvec(χ⃗₂(s)) -χ₁₂(s::VecOrPNSystem) = χ⃗₁(s) ⋅ χ⃗₂(s) -χₛₗ(s::VecOrPNSystem) = χ⃗ₛ(s) ⋅ ℓ̂(s) -χₐₗ(s::VecOrPNSystem) = χ⃗ₐ(s) ⋅ ℓ̂(s) -χ₁ₙ(s::VecOrPNSystem) = χ⃗₁(s) ⋅ n̂(s) -χ₁λ(s::VecOrPNSystem) = χ⃗₁(s) ⋅ λ̂(s) -χ₁ₗ(s::VecOrPNSystem) = χ⃗₁(s) ⋅ ℓ̂(s) -χ₂ₙ(s::VecOrPNSystem) = χ⃗₂(s) ⋅ n̂(s) -χ₂λ(s::VecOrPNSystem) = χ⃗₂(s) ⋅ λ̂(s) -χ₂ₗ(s::VecOrPNSystem) = χ⃗₂(s) ⋅ ℓ̂(s) - -Sₙ(s::VecOrPNSystem) = S⃗(s) ⋅ n̂(s) -Σₙ(s::VecOrPNSystem) = Σ⃗(s) ⋅ n̂(s) -Sλ(s::VecOrPNSystem) = S⃗(s) ⋅ λ̂(s) -Σλ(s::VecOrPNSystem) = Σ⃗(s) ⋅ λ̂(s) -Sₗ(s::VecOrPNSystem) = S⃗(s) ⋅ ℓ̂(s) -Σₗ(s::VecOrPNSystem) = Σ⃗(s) ⋅ ℓ̂(s) -sₗ(s::VecOrPNSystem) = S⃗(s) ⋅ ℓ̂(s) / M(s)^2 -σₗ(s::VecOrPNSystem) = Σ⃗(s) ⋅ ℓ̂(s) / M(s)^2 - -S₁ₙ(s::VecOrPNSystem) = S⃗₁(s) ⋅ n̂(s) -S₁λ(s::VecOrPNSystem) = S⃗₁(s) ⋅ λ̂(s) -S₁ₗ(s::VecOrPNSystem) = S⃗₁(s) ⋅ ℓ̂(s) -S₂ₙ(s::VecOrPNSystem) = S⃗₂(s) ⋅ n̂(s) -S₂λ(s::VecOrPNSystem) = S⃗₂(s) ⋅ λ̂(s) -S₂ₗ(s::VecOrPNSystem) = S⃗₂(s) ⋅ ℓ̂(s) +@public S₀⁻ₙ(s::PNSystem) = S⃗₀⁻(M₁(s), M₂(s), κ₁(s), κ₂(s), S₁ₙ(s), S₂ₙ(s)) +@public S₀⁻λ(s::PNSystem) = S⃗₀⁻(M₁(s), M₂(s), κ₁(s), κ₂(s), S₁λ(s), S₂λ(s)) +@public S₀⁻ₗ(s::PNSystem) = S⃗₀⁻(M₁(s), M₂(s), κ₁(s), κ₂(s), S₁ₗ(s), S₂ₗ(s)) + +@public χ₁²(s::PNSystem) = abs2vec(χ⃗₁(s)) +@public χ₂²(s::PNSystem) = abs2vec(χ⃗₂(s)) +@public χ₁(s::PNSystem) = absvec(χ⃗₁(s)) +@public χ₂(s::PNSystem) = absvec(χ⃗₂(s)) +@public χ₁₂(s::PNSystem) = χ⃗₁(s) ⋅ χ⃗₂(s) +@public χₛₗ(s::PNSystem) = χ⃗ₛ(s) ⋅ ℓ̂(s) +@public χₐₗ(s::PNSystem) = χ⃗ₐ(s) ⋅ ℓ̂(s) +@public χ₁ₙ(s::PNSystem) = χ⃗₁(s) ⋅ n̂(s) +@public χ₁λ(s::PNSystem) = χ⃗₁(s) ⋅ λ̂(s) +@public χ₁ₗ(s::PNSystem) = χ⃗₁(s) ⋅ ℓ̂(s) +@public χ₂ₙ(s::PNSystem) = χ⃗₂(s) ⋅ n̂(s) +@public χ₂λ(s::PNSystem) = χ⃗₂(s) ⋅ λ̂(s) +@public χ₂ₗ(s::PNSystem) = χ⃗₂(s) ⋅ ℓ̂(s) + +@public Sₙ(s::PNSystem) = S⃗(s) ⋅ n̂(s) +@public Σₙ(s::PNSystem) = Σ⃗(s) ⋅ n̂(s) +@public Sλ(s::PNSystem) = S⃗(s) ⋅ λ̂(s) +@public Σλ(s::PNSystem) = Σ⃗(s) ⋅ λ̂(s) +@public Sₗ(s::PNSystem) = S⃗(s) ⋅ ℓ̂(s) +@public Σₗ(s::PNSystem) = Σ⃗(s) ⋅ ℓ̂(s) +@public sₗ(s::PNSystem) = S⃗(s) ⋅ ℓ̂(s) / M(s)^2 +@public σₗ(s::PNSystem) = Σ⃗(s) ⋅ ℓ̂(s) / M(s)^2 + +@public S₁ₙ(s::PNSystem) = S⃗₁(s) ⋅ n̂(s) +@public S₁λ(s::PNSystem) = S⃗₁(s) ⋅ λ̂(s) +@public S₁ₗ(s::PNSystem) = S⃗₁(s) ⋅ ℓ̂(s) +@public S₂ₙ(s::PNSystem) = S⃗₂(s) ⋅ n̂(s) +@public S₂λ(s::PNSystem) = S⃗₂(s) ⋅ λ̂(s) +@public S₂ₗ(s::PNSystem) = S⃗₂(s) ⋅ ℓ̂(s) diff --git a/src/literature/common_variables/tidal_coupling.jl b/src/literature/common_variables/tidal_coupling.jl index 211306a6..ff172f23 100644 --- a/src/literature/common_variables/tidal_coupling.jl +++ b/src/literature/common_variables/tidal_coupling.jl @@ -16,7 +16,7 @@ tidal couplings ``\Lambda_1`` and ``\Lambda_2`` of the two components. See also [`Λ₁`](@ref) and [`Λ₂`](@ref). """ -Λ̃(s::VecOrPNSystem) = +@public Λ̃(s::PNSystem) = 16//13 * ((M₁(s) + 12M₂(s)) * M₁(s)^4 * Λ₁(s) + (M₂(s) + 12M₁(s)) * M₂(s)^4 * Λ₂(s)) / M(s)^5 -const Lambda_tilde = Λ̃ +@public const Lambda_tilde = Λ̃ diff --git a/src/literature/literature.jl b/src/literature/literature.jl index 8b137891..5b59f10b 100644 --- a/src/literature/literature.jl +++ b/src/literature/literature.jl @@ -1 +1,12 @@ +# These files define functions encapsulating common variables used throughout the +# literature. Note that not every reference uses these conventions, which is why +# they must be explicitly imported to be used by `@pn_reference` modules. +include("common_variables/horizons.jl") +include("common_variables/mass_combinations.jl") +include("common_variables/orbital_elements.jl") +include("common_variables/spin_combinations.jl") +include("common_variables/tidal_coupling.jl") +# Now we include the `@pn_reference` modules themselves, named by the bibtex keys we use for +# them in ``../references.bib`. +include("references/Einstein1918.jl") From 1e7d3cec0acb0d73ff310576da50f55da97c774b Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Fri, 4 Jul 2025 16:40:31 -0400 Subject: [PATCH 47/77] Document Euler's constant --- src/core/utilities/misc.jl | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/core/utilities/misc.jl b/src/core/utilities/misc.jl index 6b51b68f..6823ef45 100644 --- a/src/core/utilities/misc.jl +++ b/src/core/utilities/misc.jl @@ -20,6 +20,32 @@ julia> sum((1:10_000_000).^-3) """ ζ3 # We document it this way because `@irrational` cannot handle docstrings. +@doc raw""" + γₑ + +[Euler's constant](https://en.wikipedia.org/wiki/Euler%27s_constant) (also known as the +Euler–Mascheroni constant) is defined as the limit as ``n \to \infty`` of the difference +between the ``n``th partial sum of the harmonic series and ``\log(n)``. This is OEIS +sequence [A001620](https://oeis.org/A001620). + +This is distinct from the Euler's number ``e``, which is defined as the limit as ``n \to +\infty`` of the sum of ``1/n!``. + +Note that it is usually denoted simply as `γ` in the broader literature, but that is used in +the post-Newtonian literature for the quantity denoted in this package as [`γₚₙ`](@ref). To +distinguish between the two, the PN literature uses ``\gamma_\mathrm{E}`` for Euler's +constant. (There is no Unicode subscript "E", so we use "e" instead.) + +```julia-repl +julia> PostNewtonian.γₑ +γₑ = 0.5772156649015... + +julia> n=10_000_000; sum(1 ./ (1:n))-log(n) +0.5772157149015307 +``` +""" +γₑ # We document it here because docs don't fit nicely in the main file where it's defined. + """ value(x) From d876dba6ee960be589c98b038862bd3f3f5a384a Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Fri, 4 Jul 2025 16:40:51 -0400 Subject: [PATCH 48/77] Add some details and correct units for `v` --- src/pn_systems/state_variables.jl | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/pn_systems/state_variables.jl b/src/pn_systems/state_variables.jl index a7331477..6a00e133 100644 --- a/src/pn_systems/state_variables.jl +++ b/src/pn_systems/state_variables.jl @@ -201,14 +201,20 @@ Rᶻ(fdpnsystem::FDPNSystem) = fdpnsystem[:Rᶻ] Post-Newtonian velocity parameter. This is related to the orbital angular frequency ``\Omega`` as ```math -v \colonequals (M\,\Omega)^{1/3}, +v \colonequals (G\,M\,\Omega)^{1/3}, +``` +where [`M`](@ref) is the total mass of the binary, and [`G`](@ref) is the gravitational +constant. This has units of speed, so it must be divided by [`c`](@ref) to be +dimensionless. In particular, it is related to the "frequency-related parameter" +[`x`](@ref) via +```math +x = \left(\frac{v}{c}\right)^{2}. ``` -where ``M`` is the total mass of the binary. -Note that if you want to pass the value ``Ω`` (rather than a `PNSystem`), you must pass it +Note that if you want to pass the value ``Ω`` (rather than a `PNSystem`), you *must* pass it as a keyword argument — as in `v(Ω=0.1)`. -See also [`Ω`](@ref). +See also [`Ω`](@ref) and [`x`](@ref). """ @public function v(::T) where {T<:PNSystem} error("v is not (yet) defined for PNSystem subtype `$T`.") From a7cca53f83ec498ca1fcc30994c8ea162231ce7f Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Fri, 4 Jul 2025 16:42:03 -0400 Subject: [PATCH 49/77] Comment out example reference code --- src/literature/literature.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/literature/literature.jl b/src/literature/literature.jl index 5b59f10b..6c437a90 100644 --- a/src/literature/literature.jl +++ b/src/literature/literature.jl @@ -9,4 +9,4 @@ include("common_variables/tidal_coupling.jl") # Now we include the `@pn_reference` modules themselves, named by the bibtex keys we use for # them in ``../references.bib`. -include("references/Einstein1918.jl") +#include("references/Einstein1918.jl") From acc244895215f76737724e05de2b67eb76d87cc9 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Fri, 4 Jul 2025 16:42:30 -0400 Subject: [PATCH 50/77] Tweak --- src/pn_expressions/flux.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pn_expressions/flux.jl b/src/pn_expressions/flux.jl index ac4e21c7..54eef84a 100644 --- a/src/pn_expressions/flux.jl +++ b/src/pn_expressions/flux.jl @@ -39,7 +39,7 @@ overall factor is used, leading to a sign difference. (v / c)^2 * (-1247//336 - 35ν / 12) + (v / c)^3 * (4π) + (v / c)^4 * (-44711//9072 + 9271ν / 504 + 65ν^2 / 18) + - (v / c)^5 * ((-8191//672 - 583 * ν / 24)π) + + (v / c)^5 * ((-8191//672 - 583ν / 24)π) + (v / c)^6 * ( 6643739519//69854400 + 16π^2 / 3 - 1712 * (γₑ + 2ln(2) + ln(v)) / 105 + (-134543//7776 + 41π^2 / 48)ν - From d910df4e8c571abb6ab784c27dbf2a08ef9e5e89 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Fri, 4 Jul 2025 16:44:23 -0400 Subject: [PATCH 51/77] Add PN expansion parameter `x` --- .../common_variables/orbital_elements.jl | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/literature/common_variables/orbital_elements.jl b/src/literature/common_variables/orbital_elements.jl index 0f9ba5f6..29829896 100644 --- a/src/literature/common_variables/orbital_elements.jl +++ b/src/literature/common_variables/orbital_elements.jl @@ -66,3 +66,19 @@ See also [`v`](@ref). @public Ω(; v, M=1) = v^3 / M Ω(s::PNSystem) = Ω(; v=v(s), M=M(s)) @public const Omega = Ω + +@doc raw""" + x(pnsystem) + x(;v, M=1) + +The "frequency-related parameter" defined in Eq. (375) of [Blanchet2024](@cite) as +```math +x \colonequals \left(\frac{Gm\Omega}{c^3}\right)^{2/3} +``` +where `m` is the total mass of the binary, and `Ω` is the orbital angular frequency. +This is related to the PN velocity parameter `v` as +```math +x = \left(\frac{v}{c}\right)^{2}. +``` +""" +@public x(s::PNSystem) = (v(s)/c(s))^2 From 4259e5deea8536500a77f95d63165f6ed87726c1 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Fri, 4 Jul 2025 23:29:58 -0400 Subject: [PATCH 52/77] =?UTF-8?q?A=20little=20more=20detail=20on=20=CE=B3?= =?UTF-8?q?=E2=82=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/utilities/misc.jl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/core/utilities/misc.jl b/src/core/utilities/misc.jl index 6823ef45..be8a532c 100644 --- a/src/core/utilities/misc.jl +++ b/src/core/utilities/misc.jl @@ -28,13 +28,14 @@ Euler–Mascheroni constant) is defined as the limit as ``n \to \infty`` of the between the ``n``th partial sum of the harmonic series and ``\log(n)``. This is OEIS sequence [A001620](https://oeis.org/A001620). -This is distinct from the Euler's number ``e``, which is defined as the limit as ``n \to +This is distinct from the Euler's *number* ``e``, which is defined as the limit as ``n \to \infty`` of the sum of ``1/n!``. -Note that it is usually denoted simply as `γ` in the broader literature, but that is used in -the post-Newtonian literature for the quantity denoted in this package as [`γₚₙ`](@ref). To -distinguish between the two, the PN literature uses ``\gamma_\mathrm{E}`` for Euler's -constant. (There is no Unicode subscript "E", so we use "e" instead.) +Note that it is usually denoted simply as `γ` in the broader literature (and in Julia's own +`Base.MathConstants`), but that symbol is used in the post-Newtonian literature for the +quantity denoted in this package as [`γₚₙ`](@ref). To distinguish between the two, the PN +literature uses ``\gamma_\mathrm{E}`` for Euler's constant. (There is no Unicode subscript +"E", so we use "e" instead.) ```julia-repl julia> PostNewtonian.γₑ From 86ada3b8531c4fe55362b529a63633dbe3e07dec Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Sun, 6 Jul 2025 12:26:57 -0400 Subject: [PATCH 53/77] Get basic functionality for @pn_reference working --- src/core/PNTerm.jl | 18 +++++++++ src/literature/Blanchet2024.jl | 71 ++++++++++++++++++++++++++++++++++ src/literature/literature.jl | 3 +- 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 src/literature/Blanchet2024.jl diff --git a/src/core/PNTerm.jl b/src/core/PNTerm.jl index 1ebefbf0..100385f6 100644 --- a/src/core/PNTerm.jl +++ b/src/core/PNTerm.jl @@ -61,6 +61,19 @@ Base.eltype(pn::PNTerm{T}) where {T} = T @public c⁻¹exp(pn::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} = c⁻¹Exponent +function constant_convert(pn::PNSystem, term::PNTerm) + # Generic fallback to provide a useful error message + error( + "Input values to `constant_convert` must have the same `NT` and `PNOrder` type " * + "parameters; got `$(typeof(pn))` and `$(typeof(term))`.", + ) +end +function constant_convert( + pn::P, term::T +) where {NT,PNOrder,P<:PNSystem{NT,PNOrder},T<:PNTerm{NT,PNOrder}} + term +end + function Base.sum(pn::PNTerm) return pn.coeff end @@ -73,6 +86,11 @@ function Base.inv(term::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻ return PNTerm{T,PNOrder}(-c⁻¹exp(term), inv(term.coeff)) end +function Base.sqrt(term::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} + @assert iseven(c⁻¹Exponent) "Only half-integer PN orders are supported." + return PNTerm{T,PNOrder}(c⁻¹Exponent ÷ 2, sqrt(term.coeff)) +end + function Base.:^(term::PNTerm{T,PNOrder,c⁻¹Exponent}, n::Int) where {T,PNOrder,c⁻¹Exponent} coeff = term.coeff^n return PNTerm{typeof(coeff),PNOrder}(c⁻¹exp(term) * n, coeff) diff --git a/src/literature/Blanchet2024.jl b/src/literature/Blanchet2024.jl new file mode 100644 index 00000000..a202ea5a --- /dev/null +++ b/src/literature/Blanchet2024.jl @@ -0,0 +1,71 @@ +@pn_reference module Blanchet2024 + +import PostNewtonian: G, c, v, x, ν, γₑ + +Base.raw""" + +In Sec. 3.2.4, Blanchet defines the total mass ``m=m₁+m₂``, the relative mass difference +``\Delta = (m₁ - m₂) / m``, the reduced mass ``μ = m₁ m₂ / m``, and the symmetric mass ratio +``ν ≡ \mu/m = m₁ m₂ / m²``. He also poses ``X₁ = m₁ / m`` and ``X₂ = m₂ / m`` so that ``Δ = +X₁ - X₂`` and ``ν = X₁ X₂``. + +In Eq. (369), he defines +```math +\gamma = \frac{GM}{rc^2}. +``` + +In Eq. (375), he defines the "frequency-related parameter" +```math +x = \left(\frac{Gm\Omega}{c^3}\right)^{2/3} +``` + +""" + +""" + Eq483(pnsystem) + +Quasi-circular 4.5PN energy flux. + +As given in Eq. (483) of [Blanchet2024](@cite), this expression includes "contributions from +tails, iterated tails, and tails-of-memory". + +""" +@pn_expression function Eq483(pnsystem) + # TODO: Deal with, e.g., x^(3/2) + + ℱ = + (32c^5 / 5G * ν * x^5) * @pn_expansion( + 1 + + (-1247/336 - 35/12 * ν)x + + (4π)x^(3/2) + + (-44711/9072 + 9271/504 * ν + 65/18 * ν^2)x^2 + + (-8191/672 - 583/24 * ν)π * x^(5/2) + + ( + 6643739519/69854400 + 16/3 * π^2 / 3 - 1712/105 * γₑ - + 856/105 * ln(16x) + (-134543/7776 + 41/48 * π^2)ν - 94403/3024 * ν^2 - + 775/324 * ν^3 + )x^3 + + (-16285/504 + 214745/1728 * ν + 193385/3024 * ν^2)π * x^(7/2) + + ( + -323105549467/3178375200 + 232597/4410 * γₑ - 1369/126 * π^2 + + 39931/294 * ln(2) - 47385/1568 * ln(3) + + 232597/8820 * ln(x) + + ( + -1452202403629/1466942400 + 41478/245 * γₑ - 267127/4608 * π^2 + + 479062/2205 * ln(2) + + 47385/392 * ln(3) + + 20739/245 * ln(x) + )ν + + (1607125/6804 - 3157/384 * π^2)ν^2 + + 6875/504 * ν^3 + + 5/6 * ν^4 + )x^4 + + ( + 265978667519/745113600 - 6848/105 * γₑ - 3424/105 * ln(16x) + + (2062241/22176 + 41/12 * π^2)ν - 133112905/290304 * ν^2 - + 3719141/38016 * ν^3 + )π * x^(9/2) + ) +end + +end # module Blanchet2024 diff --git a/src/literature/literature.jl b/src/literature/literature.jl index 6c437a90..2775b895 100644 --- a/src/literature/literature.jl +++ b/src/literature/literature.jl @@ -9,4 +9,5 @@ include("common_variables/tidal_coupling.jl") # Now we include the `@pn_reference` modules themselves, named by the bibtex keys we use for # them in ``../references.bib`. -#include("references/Einstein1918.jl") +#include("Einstein1918.jl") +include("Blanchet2024.jl") From fa1ad09d2ba3c699d3e62f2d9a0dedbeeb126bd5 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Sun, 6 Jul 2025 12:28:26 -0400 Subject: [PATCH 54/77] Rearrange files a little --- src/core/PNExpansion.jl | 56 -------- src/core/core.jl | 10 +- src/core/pn_expansion.jl | 134 ++++++++++++++++++ .../{PNExpression.jl => pn_expression.jl} | 109 +++++++++----- src/core/{PNReference.jl => pn_reference.jl} | 26 +++- 5 files changed, 237 insertions(+), 98 deletions(-) create mode 100644 src/core/pn_expansion.jl rename src/core/{PNExpression.jl => pn_expression.jl} (73%) rename src/core/{PNReference.jl => pn_reference.jl} (87%) diff --git a/src/core/PNExpansion.jl b/src/core/PNExpansion.jl index a9b858ff..5a9c3bc1 100644 --- a/src/core/PNExpansion.jl +++ b/src/core/PNExpansion.jl @@ -343,59 +343,3 @@ Base.:/(expansion::PNExpansion, term::PNTerm) = expansion * inv(term) end end end - -@doc raw""" - @pn_expansion [pnsystem] expansion - -Mark `expansion` as a post-Newtonian expansion in powers of ``1/c``, to be truncated at the -order given by the `pnsystem`. - -Note that we generally want this PN order to be interpreted as a *relative* PN order. For -example, the expression for [gravitational-wave energy flux](@ref 𝓕) looks like -```math -\mathcal{F} = \frac{32c^5}{5G} \nu^2 \left(\frac{v}{c}\right)^{10} -\left[1 - \left(\frac{ν}{12} + \frac{3}{4}\right) \left(\frac{v}{c}\right)^2 + \ldots\right] -``` -Here, the part in square brackets is the expansion in terms of *relative* PN order, with the -1 representing the 0-pN term, the ``(v/c)^2`` term being the 1-pN term, etc. So in the -code, we apply `@pn_expansion` to the part in square brackets: -```julia -32c^5 / 5G * ν^2 * (v / c)^10 * @pn_expansion(1 - (ν/12 + 3/4) * (v/c)^2 + ...) -``` -Note that the entire expression is written in a function that is modified by the -[`@pn_expression`](@ref) macro, which automatically inserts the `pnsystem` argument to this -`@pn_expansion` call, so you generally should not have to worry about it. - -This macro gathers terms in `expansion` by the powers of ``1/c`` involved, zeroing out any -terms with powers of ``1/c`` higher than (twice) the `pnsystem`'s `PNOrder` parameter, and -combine the terms using the `PNExpansionReducer` specified in argument of the function that -includes this macro call. - -The expansion and truncation are achieved by redefining `c` to be a -[`PNExpansionParameter(pnsystem)`](@ref PNExpansionParameter), which is just a -[`PNTerm`](@ref) with a coefficient of 1 and a `c⁻¹Exponent` of -1. This redefinition -happens inside a `let` block created by this macro so that it doesn't interfere with any -`c`s on the outside. For example, in the flux expression above, the `c`s in `32c^5 / 5G * -ν^2 * (v / c)^10` are outside the scope of the macro, even if we set `c=1` above that line, -the expansion will still be correct. -""" -@public macro pn_expansion(pnsystem, expr) - return esc(quote - let c = PNExpansionParameter($pnsystem) - PNExpansionReducer($expr) - end - end) -end - -@testitem "@pn_expansion" begin - using PostNewtonian: @pn_expansion - using MacroTools - - input = @macroexpand @pn_expansion pnsystem (1 - (ν/12 + 3/4) * (v/c)^2) - output = quote - let c = PNExpansionParameter(pnsystem) - PNExpansionReducer(1 - (ν/12 + 3/4) * (v/c)^2) - end - end - @test MacroTools.striplines(input) == MacroTools.striplines(output) -end diff --git a/src/core/core.jl b/src/core/core.jl index 726d20da..25588993 100644 --- a/src/core/core.jl +++ b/src/core/core.jl @@ -3,11 +3,17 @@ # details that are important for understanding the detailed structure of the code, but not # for understanding the physics that code is representing. +# These are low-level utilities. include("utilities/misc.jl") include("utilities/truncated_series_monoid.jl") include("utilities/truncated_series_inversion.jl") + +# These are types and modules that help to build the post-Newtonian framework. include("PNTerm.jl") include("PNExpansion.jl") include("PNExpressionArithmetic.jl") -include("PNExpression.jl") -include("PNReference.jl") + +# These macros effectively form the interface used directly in the rest of the code. +include("pn_expansion.jl") +include("pn_expression.jl") +include("pn_reference.jl") diff --git a/src/core/pn_expansion.jl b/src/core/pn_expansion.jl new file mode 100644 index 00000000..2a0ef1f4 --- /dev/null +++ b/src/core/pn_expansion.jl @@ -0,0 +1,134 @@ + +@doc raw""" + @pn_expansion pnsystem expansion + +Mark `expansion` as a post-Newtonian expansion, to be truncated at the order given by the +`pnsystem`. + +!!! warning + + The symbols `c`, `x`, `γₚₙ`, and `γ` are all considered to be PN-expansion terms inside + the expression passed to this macro. If present inside the expression, this macro will + multiply each by the appropriate power of + [`PNExpansionParameter(pnsystem)`](@ref PNExpansionParameter) to ensure that the + expansion is truncated correctly. + +Note that we generally interpret this PN order as a *relative* PN order. For example, the +expression for [gravitational-wave energy flux](@ref 𝓕) looks like +```math +\mathcal{F} = \frac{32c^5}{5G} \nu^2 \left(\frac{v}{c}\right)^{10} +\left[1 - \left(\frac{ν}{12} + \frac{3}{4}\right) \left(\frac{v}{c}\right)^2 + \ldots\right] +``` +Here, the part in square brackets is the expansion in terms of *relative* PN order, with the +1 representing the relative 0-pN term, the ``(v/c)^2`` term being the relative 1-pN term, +etc. So in the code, we apply `@pn_expansion` to the part in square brackets: +```julia +32c^5 / 5G * ν^2 * (v / c)^10 * @pn_expansion(1 - (ν/12 + 3/4) * (v/c)^2 + ...) +``` +Note that the entire expression is written inside a function that is modified by the +[`@pn_expression`](@ref) macro, which automatically inserts the `pnsystem` argument to this +`@pn_expansion` call, so you generally should not have to worry about it. + +This macro gathers terms in `expansion` by the powers of ``1/c`` involved, zeroing out any +terms with powers of ``1/c`` higher than (twice) the `pnsystem`'s `PNOrder` parameter, and +combine the terms using the `PNExpansionReducer` specified in argument of the function that +includes this macro call. + +The expansion and truncation are achieved by multiplying `c` by a +[`PNExpansionParameter(pnsystem)`](@ref PNExpansionParameter), which is just a +[`PNTerm`](@ref) with a coefficient of 1 and a `c⁻¹Exponent` of -1. This redefinition +happens inside a `let` block created by this macro so that it doesn't interfere with any +`c`s on the outside. For example, in the flux expression above, the `c`s in `32c^5 / 5G * +ν^2 * (v / c)^10` are outside the scope of the macro, so even if we set `c=2` above that +line the expansion will still be correctly truncated, and the value of `c` will be +preserved. + +In the literature, some PN expansions do not *explicitly* use the `c` parameter, but instead +define the expansion in terms of the dimensionless parameters [`x`](@ref) or [`γₚₙ`](@ref), +which include powers of `c` in their definitions. Thus, if these symbols are present inside +the expression passed to this macro, they will be multiplied by the appropriate power of +`PNExpansionParameter`. The literature simply uses `γ`, rather than the more explicit `γₚₙ` +we use here, so `γ` will be treated the same as `γₚₙ`. + +Euler's constant — sometimes called the Euler–Mascheroni constant, and distinct from Euler's +*number* ``e`` — is usually denoted by `γ` (including in Julia's own `Base.MathConstants`), +but is distinguished in the post-Newtonian literature by the subscript "E", and is defined +in this package as [`γₑ`](@ref). + +!!! note + + Because `x` and `γₚₙ` are "of order" ``1/c^2``, their exponents are generally + half-integers in any expansion. Evaluating a term like `x^(7/2)` generically will be + relatively inefficient, requiring a floating-point division, the logarithm of `x`, a + floating-point multiplication, and exponentiation. Compared to a single square-root and + integer exponentiation, this is very slow. Moreover, it does not work well with the + [`PNTerm`](@ref) mechanism. So another thing this macro does is search for terms like + `x^(7/2)` and convert them to `(√x)^7`, and similarly for `γₚₙ` and `γ`. This only + works for explicit half-integers, rather than variables that may be half-integers. +""" +@public macro pn_expansion(pnsystem, expr) + expansion_parameters = Expr[] + if MacroTools.inexpr(expr, :c) + push!(expansion_parameters, :(c = c * PNExpansionParameter($pnsystem))) + end + if MacroTools.inexpr(expr, :x) + push!(expansion_parameters, :(x = x / PNExpansionParameter($pnsystem)^2)) + expr = MacroTools.postwalk(expr) do ex + MacroTools.@capture(ex, x^(n_Int/2)) || return ex + return :((√x)^$n) + end + end + if MacroTools.inexpr(expr, :γₚₙ) + push!(expansion_parameters, :(γₚₙ = γₚₙ / PNExpansionParameter($pnsystem)^2)) + expr = MacroTools.postwalk(expr) do ex + MacroTools.@capture(ex, γₚₙ^(n_Int/2)) || return ex + return :((√γₚₙ)^$n) + end + end + if MacroTools.inexpr(expr, :γ) + push!(expansion_parameters, :(γ = γ / PNExpansionParameter($pnsystem)^2)) + expr = MacroTools.postwalk(expr) do ex + MacroTools.@capture(ex, γ^(n_Int/2)) || return ex + return :((√γ)^$n) + end + end + if isempty(expansion_parameters) + error("No expansion parameters found in `@pn_expansion`.") + end + return esc(MacroTools.unblock(quote + let $(expansion_parameters...) + PNExpansionReducer($expr) + end + end)) +end + +@testitem "@pn_expansion" begin + using PostNewtonian: @pn_expansion + using MacroTools + + input = @macroexpand @pn_expansion pnsystem ( + 1 - (ν/12 + 3/4) * (v/c)^2 + 4x^3 + 5x^(7/2) + ) + output = MacroTools.unblock( + quote + let c = c * PNExpansionParameter(pnsystem), + x = x / PNExpansionParameter(pnsystem)^2 + + PNExpansionReducer(1 - (ν/12 + 3/4) * (v/c)^2 + 4x^3 + 5(√x)^7) + end + end, + ) + @test MacroTools.striplines(input) == MacroTools.striplines(output) + + input = @macroexpand @pn_expansion pnsystem (1 - (ν/12 + 3/4)γ + 4γₚₙ^3 + 5γₚₙ^(7/2)) + output = MacroTools.unblock( + quote + let γₚₙ = γₚₙ / PNExpansionParameter(pnsystem)^2, + γ = γ / PNExpansionParameter(pnsystem)^2 + + PNExpansionReducer(1 - (ν/12 + 3/4)γ + 4γₚₙ^3 + 5(√γₚₙ)^7) + end + end, + ) + @test MacroTools.striplines(input) == MacroTools.striplines(output) +end diff --git a/src/core/PNExpression.jl b/src/core/pn_expression.jl similarity index 73% rename from src/core/PNExpression.jl rename to src/core/pn_expression.jl index 00786714..d60248b5 100644 --- a/src/core/PNExpression.jl +++ b/src/core/pn_expression.jl @@ -14,7 +14,8 @@ the `pnsystem`. Once it has this information, there are three types of transformations the macro will make: - 1. Adds a keyword argument `pn_expansion_reducer::Val{PNExpansionReducer}=Val(sum)` to the + 1. If either `PNExpansionReducer` or `@pn_expansion` is used in the expression, this adds a + keyword argument `pn_expansion_reducer::Val{PNExpansionReducer}=Val(sum)` to the function signature. This is used to determine how to reduce the PN expansion terms. The default is `Val(sum)`, which will just return a single number, but `Val(identity)` can be used to return the expansion. The reducing function can be used inside the main @@ -72,9 +73,7 @@ automatically be converted to `Float64` and spoil the requested precision. # First, we just search for everything defined in the module where this macro is called. all_names = if VERSION < v"1.12.0-beta1" - @warn "Some `names` in `@pn_expression` at $(__source__)\n" * - "may not be found in Julia 1.11 or earlier; for now, be sure to use\n" * - "`import` rather than `using` inside `@pn_reference` modules." maxlog=10 + # See the warning in `@pn_reference` for why we should avoid `using` on Julia <1.12 names(__module__; all=true, imported=true) else names(__module__; all=true, imported=true, usings=true) @@ -140,26 +139,7 @@ function pn_expression(arg_index, func, pnsystem_functions, __module__, __source # Get the name of the PNSystem argument pnsystem = MacroTools.namify(splitfunc[:args][arg_index]) - # Append a keyword argument `pn_expansion_reducer` to the function signature, with - # default value `Val(sum)`. The `Val` parameter is captured as `PNExpansionReducer`, - # which we can then use in the body of the function — specifically, it is automatically - # used by the `@pn_expansion` macro. - splitfunc[:kwargs] = [ - splitfunc[:kwargs] - :($(Expr(:kw, :(pn_expansion_reducer::Val{PNExpansionReducer}), :(Val(Base.sum))))) - ] - - # Add `PNExpansionReducer` to the `where` clause (see previous comment) - splitfunc[:whereparams] = (splitfunc[:whereparams]..., :PNExpansionReducer) - - # Now look for any of the `pnsystem_functions` in the body of the function and prepare - # an entry for the `let` block that will surround the body on output. - pnsystem_function_exprs = [ - :($s = $s($pnsystem)) for - s ∈ filter(s -> MacroTools.inexpr(splitfunc[:body], s), pnsystem_functions) - ] - - # Next, add `pnsystem` as the argument to each @pn_expansion call... + # Add `pnsystem` as the argument to each @pn_expansion call... new_body = MacroTools.postwalk(splitfunc[:body]) do x if MacroTools.isexpr(x, :macrocall) && x.args[1] == Symbol("@pn_expansion") && @@ -172,10 +152,45 @@ function pn_expression(arg_index, func, pnsystem_functions, __module__, __source end end - # Now expand all the macros inside the body + # Expand all the macros inside the body new_body = macroexpand(__module__, new_body; recursive=true) - # Next, we walk the expression tree and find all function calls to any of the + # If `PNExpansionReducer` is or will be in the body of the function, we need to make + # sure it is included in the function signature. + if MacroTools.inexpr(new_body, :(@pn_expansion)) || + MacroTools.inexpr(new_body, :(PNExpansionReducer)) + + # Just so we don't accidentally add it twice, make sure it's not already in the + # function signature. + if !any(MacroTools.inexpr.(splitfunc[:kwargs], :(PNExpansionReducer))) + # Append a keyword argument `pn_expansion_reducer` to the function signature, + # with default value `Val(sum)`. The `Val` parameter is captured as + # `PNExpansionReducer`, which we can then use in the body of the function — + # specifically, it is automatically used by the `@pn_expansion` macro. + splitfunc[:kwargs] = [ + splitfunc[:kwargs] + :( + $(Expr( + :kw, + :(pn_expansion_reducer::Val{PNExpansionReducer}), + :(Val(Base.sum)), + )) + ) + ] + + # Add `PNExpansionReducer` to the `where` clause + splitfunc[:whereparams] = (splitfunc[:whereparams]..., :PNExpansionReducer) + end + end + + # Look for any of the `pnsystem_functions` in the body of the function and prepare an + # entry for the `let` block that will surround the body on output. + pnsystem_function_exprs = [ + :($s = $s($pnsystem)) for + s ∈ filter(s -> MacroTools.inexpr(new_body, s), pnsystem_functions) + ] + + # Walk the expression tree and find all function calls to any of the # `pnexpressionarithmetic_functions`, and insert `pnsystem` as the first argument. new_body = MacroTools.postwalk(new_body) do x if iscall(x, pnexpressionarithmetic_functions) @@ -186,9 +201,9 @@ function pn_expression(arg_index, func, pnsystem_functions, __module__, __source end end - # Finally, include the `let` statements so that the functions can all be used as plain - # symbols without having to call them on `pnsystem`, but we do so without burying it in - # a code block. + # Include the `let` statements so that the functions can all be used as plain symbols + # without having to call them on `pnsystem`, but we do so without burying it in a code + # block. new_body = MacroTools.unblock(quote #@fastmath let $(pnsystem_function_exprs...) @@ -238,8 +253,12 @@ end √f * Φ end - const expr = Base.@macroexpand @pn_expression function n(pnsystem) - l * f * ln(v) * ζ3 + m * Φ + const expr1 = Base.@macroexpand @pn_expression function n(pnsystem) + l * f * ln(v) * ζ3 + m * Φ / c + end + + const expr2 = Base.@macroexpand @pn_expression function n(pnsystem) + @pn_expansion l * f * ln(v) * ζ3 + m * Φ / c end end # baremodule Mod @@ -248,16 +267,38 @@ end const ln = log - output = :( + output1 = :( + function n(pnsystem;) + let f=f(pnsystem), l=l(pnsystem), m=m(pnsystem), v=v(pnsystem), Φ=Φ(pnsystem) + + pnsystem + + pnsystem * l * f * ln(pnsystem, v) * ζ3 + + /(pnsystem, pnsystem * m * Φ, c) + end + end + ) + @test MacroTools.striplines(Mod.expr1) == MacroTools.striplines(output1) + + # Because the `c...` in the `let c...` statement is actually in a block, we have to do + # this little `c2` hack, so our explicit version resembles the generated version. + c2 = [:(c = pnsystem * c * PNExpansionParameter(pnsystem))] + output2 = :( function n( pnsystem; pn_expansion_reducer::Val{PNExpansionReducer}=Val(Base.sum) ) where {PNExpansionReducer} let f=f(pnsystem), l=l(pnsystem), m=m(pnsystem), v=v(pnsystem), Φ=Φ(pnsystem) - pnsystem + pnsystem * l * f * ln(pnsystem, v) * ζ3 + pnsystem * m * Φ + + let $(c2...) + PNExpansionReducer( + pnsystem + + pnsystem * l * f * ln(pnsystem, v) * ζ3 + + /(pnsystem, pnsystem * m * Φ, c), + ) + end end end ) - @test MacroTools.striplines(Mod.expr) == MacroTools.striplines(output) + @test MacroTools.striplines(Mod.expr2) == MacroTools.striplines(output2) for NT ∈ (Float16, Float64, BigFloat) pnsystem = BHNS(NT.(1:15)) diff --git a/src/core/PNReference.jl b/src/core/pn_reference.jl similarity index 87% rename from src/core/PNReference.jl rename to src/core/pn_reference.jl index 381cf556..3011eafc 100644 --- a/src/core/PNReference.jl +++ b/src/core/pn_reference.jl @@ -37,7 +37,7 @@ replaced with a call to that function. """ -function pn_reference(expr) +function pn_reference(expr, __module__, __source__) # A module `Expr` has head `:module` and three arguments: # 1. a boolean indicating if this is a module (true) as opposed to a baremodule (false) # 2. the module name (a `Symbol`) @@ -63,6 +63,19 @@ function pn_reference(expr) error("Found a module expression with a non-block body: $(dump(expr.args[3]))") end + # Warn if the module uses `using` on Julia < 1.12 + if VERSION < v"1.12.0-beta1" && any( + arg -> hasproperty(arg, :head) && getproperty(arg, :head) ≡ :using, + expr.args[3].args, + ) + @warn "Prefer `import` inside `@pn_reference` modules on Julia<1.12\n" * + "You are defining a `@pn_reference` module that uses `using` at\n" * + "`$(__source__)`.\n" * + "Due to a limitation in Julia 1.11 and earlier, you must use `import`\n" * + "rather than `using` to import function names that you want to use in a\n" * + "`@pn_expression`." + end + # Now, we assemble the new module, mostly by prepending some imports to the contents new_module = Expr( :module, @@ -70,10 +83,11 @@ function pn_reference(expr) expr.args[2], # This is the name of the module Expr( # This is the new module body :block, - :(using Base: Base, Val), + :(using Base: Base, Val, π), :(eval(x::Expr) = Core.eval($(expr.args[2]), x)), :(include(p::AbstractString) = Base.include($(expr.args[2]), p)), - :(using PostNewtonian: @pn_expression, @pn_expansion, 𝒾, γₑ, ζ3), + :(using PostNewtonian: + @pn_expression, @pn_expansion, PNExpansionParameter, 𝒾, γₑ, ζ3), :(using PostNewtonian.PNExpressionArithmetic), expr.args[3].args..., # The original module body ), @@ -130,7 +144,7 @@ extended. """ @public macro pn_reference(ex) - esc(pn_reference(ex)) + esc(pn_reference(ex, __module__, __source__)) end @testitem "@pn_reference" begin @@ -154,10 +168,10 @@ end output = quote baremodule Einstein1918 - using Base: Base, Val + using Base: Base, Val, π eval(x::Expr) = Core.eval(Einstein1918, x) include(p::AbstractString) = Base.include(Einstein1918, p) - using PostNewtonian: @pn_expression, @pn_expansion, 𝒾, γₑ, ζ3 + using PostNewtonian: @pn_expression, @pn_expansion, PNExpansionParameter, 𝒾, γₑ, ζ3 using PostNewtonian.PNExpressionArithmetic import PostNewtonian: G, c, M, χ⃗₁, χ⃗₂, v, pn_order From 7cd538e31d630a1912b4ad88bf602804060e71ba Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Sun, 6 Jul 2025 12:36:47 -0400 Subject: [PATCH 55/77] Tweak some docs --- src/literature/Blanchet2024.jl | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/literature/Blanchet2024.jl b/src/literature/Blanchet2024.jl index a202ea5a..c2d614c5 100644 --- a/src/literature/Blanchet2024.jl +++ b/src/literature/Blanchet2024.jl @@ -1,8 +1,9 @@ -@pn_reference module Blanchet2024 - -import PostNewtonian: G, c, v, x, ν, γₑ +@doc raw""" +Expressions from [Blanchet2024](@cite) -Base.raw""" +This package mostly derives its notation from that paper. The biggest (mild) exception is +that Blanchet uses the symbol `m` for mass, while we reserve that for the azimuthal number +of spin-weighted spherical harmonics, and use `M` for mass instead. In Sec. 3.2.4, Blanchet defines the total mass ``m=m₁+m₂``, the relative mass difference ``\Delta = (m₁ - m₂) / m``, the reduced mass ``μ = m₁ m₂ / m``, and the symmetric mass ratio @@ -16,23 +17,24 @@ In Eq. (369), he defines In Eq. (375), he defines the "frequency-related parameter" ```math -x = \left(\frac{Gm\Omega}{c^3}\right)^{2/3} +x = \left(\frac{GM\Omega}{c^3}\right)^{2/3} ``` """ +@pn_reference module Blanchet2024 + +import PostNewtonian: G, c, x, ν, γₑ """ Eq483(pnsystem) -Quasi-circular 4.5PN energy flux. +Quasi-circular non-spinning 4.5PN energy flux terms. As given in Eq. (483) of [Blanchet2024](@cite), this expression includes "contributions from -tails, iterated tails, and tails-of-memory". +tails, iterated tails, and tails-of-memory", but no contributions from spin. """ @pn_expression function Eq483(pnsystem) - # TODO: Deal with, e.g., x^(3/2) - ℱ = (32c^5 / 5G * ν * x^5) * @pn_expansion( 1 + From 3c732904273106beb88e1a698f8afb73a4ecd6d9 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Mon, 7 Jul 2025 00:14:38 -0400 Subject: [PATCH 56/77] Fix silly formatting bug --- src/core/PNExpansion.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/PNExpansion.jl b/src/core/PNExpansion.jl index 5a9c3bc1..3ce6fe56 100644 --- a/src/core/PNExpansion.jl +++ b/src/core/PNExpansion.jl @@ -69,7 +69,7 @@ Base.length(pn::PNExpansion) = length(pn.coeffs) Base.eltype(pn::PNExpansion) = eltype(pn.coeffs) function Base.sum(pn_expansion::PNExpansion{N,T,NMax}) where {N,T,NMax} - return sum(pn_expansion[i] for i ∈ 1:N, init ∈ zero(T)) + return sum(pn_expansion[i] for i ∈ 1:N; init=zero(T)) end function Base.:+(pn::PNExpansion{N,T1,NMax}, x::T2) where {N,T1,NMax,T2<:Number} @@ -147,8 +147,8 @@ function product_term( ) where {N1,N2,T1,T2,NMax} T3 = promote_type(T1, T2) return sum( - pn1.coeffs[j] * pn2.coeffs[i - j + 1] for - j ∈ max(1, i - N2 + 1):min(i, N1), init ∈ zero(T3) + pn1.coeffs[j] * pn2.coeffs[i - j + 1] for j ∈ max(1, i - N2 + 1):min(i, N1); + init=zero(T3), ) end From 43c6f15911ffbc03be9e8d5464409fcaaed4b51e Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Mon, 7 Jul 2025 22:07:32 -0400 Subject: [PATCH 57/77] Change PNExpressionArithmetic to PNBase --- .../{PNExpressionArithmetic.jl => PNBase.jl} | 24 +++++++++---------- src/core/PNTerm.jl | 7 ------ src/core/core.jl | 2 +- src/core/pn_expansion.jl | 1 - src/core/pn_expression.jl | 6 ++--- src/core/pn_reference.jl | 2 +- 6 files changed, 16 insertions(+), 26 deletions(-) rename src/core/{PNExpressionArithmetic.jl => PNBase.jl} (82%) diff --git a/src/core/PNExpressionArithmetic.jl b/src/core/PNBase.jl similarity index 82% rename from src/core/PNExpressionArithmetic.jl rename to src/core/PNBase.jl index 22d8fc67..bd9d030f 100644 --- a/src/core/PNExpressionArithmetic.jl +++ b/src/core/PNBase.jl @@ -1,6 +1,6 @@ -public PNExpressionArithmetic +public PNBase -baremodule PNExpressionArithmetic +baremodule PNBase import Base using PostNewtonian: constant_convert, PNSystem using PostNewtonian.InlineExports: @export @@ -26,14 +26,12 @@ end return Base.:*(constant_convert(pnsystem, w), x, y, z...) end -end # baremodule PNExpressionArithmetic +end # baremodule PNBase -const pnexpressionarithmetic_functions = filter( - s -> isa(getfield(PNExpressionArithmetic, s), Function), names(PNExpressionArithmetic) -) +const pnbase_functions = filter(s -> isa(getfield(PNBase, s), Function), names(PNBase)) @doc """ - PNExpressionArithmetic + PNBase This module provides arithmetic operations to be used inside `@pn_expression` modules. @@ -41,20 +39,20 @@ It is intentionally very restrictive, so that it only includes the basic arithme operations that are used in post-Newtonian expressions, and even then only in modified forms that take a `PNSystem` as the first argument. The defined operations are - $(pnexpressionarithmetic_functions) + $(pnbase_functions) This module is not intended to be used directly, but is imported by the [`@pn_expression`](@ref) macro, and its methods are called by [`@pn_expansion`](@ref) to ensure that the arithmetic operations preserve the number type of the input `PNSystem`. """ -PNExpressionArithmetic +PNBase -@testitem "PNExpressionArithmetic" begin +@testitem "PNBase" begin baremodule Mod - using PostNewtonian.PNExpressionArithmetic + using PostNewtonian.PNBase end - using PostNewtonian: pnexpressionarithmetic_functions + using PostNewtonian: pnbase_functions const ln = log @@ -63,7 +61,7 @@ PNExpressionArithmetic z = (1, 2, 3, 17, 31, ℯ, π, 3//13, 47//59) for f ∈ (:+, :-, :*, :/, :^, :ln, :√) - @test f ∈ pnexpressionarithmetic_functions + @test f ∈ pnbase_functions end for f ∈ (:+, :-, :*, :/, :^) for x ∈ z, y ∈ z diff --git a/src/core/PNTerm.jl b/src/core/PNTerm.jl index 100385f6..24e6750a 100644 --- a/src/core/PNTerm.jl +++ b/src/core/PNTerm.jl @@ -61,13 +61,6 @@ Base.eltype(pn::PNTerm{T}) where {T} = T @public c⁻¹exp(pn::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} = c⁻¹Exponent -function constant_convert(pn::PNSystem, term::PNTerm) - # Generic fallback to provide a useful error message - error( - "Input values to `constant_convert` must have the same `NT` and `PNOrder` type " * - "parameters; got `$(typeof(pn))` and `$(typeof(term))`.", - ) -end function constant_convert( pn::P, term::T ) where {NT,PNOrder,P<:PNSystem{NT,PNOrder},T<:PNTerm{NT,PNOrder}} diff --git a/src/core/core.jl b/src/core/core.jl index 25588993..94cc3331 100644 --- a/src/core/core.jl +++ b/src/core/core.jl @@ -11,7 +11,7 @@ include("utilities/truncated_series_inversion.jl") # These are types and modules that help to build the post-Newtonian framework. include("PNTerm.jl") include("PNExpansion.jl") -include("PNExpressionArithmetic.jl") +include("PNBase.jl") # These macros effectively form the interface used directly in the rest of the code. include("pn_expansion.jl") diff --git a/src/core/pn_expansion.jl b/src/core/pn_expansion.jl index 2a0ef1f4..a9e38409 100644 --- a/src/core/pn_expansion.jl +++ b/src/core/pn_expansion.jl @@ -1,4 +1,3 @@ - @doc raw""" @pn_expansion pnsystem expansion diff --git a/src/core/pn_expression.jl b/src/core/pn_expression.jl index d60248b5..1a44cdba 100644 --- a/src/core/pn_expression.jl +++ b/src/core/pn_expression.jl @@ -191,9 +191,9 @@ function pn_expression(arg_index, func, pnsystem_functions, __module__, __source ] # Walk the expression tree and find all function calls to any of the - # `pnexpressionarithmetic_functions`, and insert `pnsystem` as the first argument. + # `pnbase_functions`, and insert `pnsystem` as the first argument. new_body = MacroTools.postwalk(new_body) do x - if iscall(x, pnexpressionarithmetic_functions) + if iscall(x, pnbase_functions) insert!(x.args, 2, (pnsystem)) x else @@ -227,7 +227,7 @@ end eval(x::Expr) = Core.eval(Base.@__MODULE__, x) include(p::AbstractString) = Base.include(Base.@__MODULE__, p) using PostNewtonian: PostNewtonian, @pn_expression, @pn_expansion, 𝒾, γₑ, ζ3 - using PostNewtonian.PNExpressionArithmetic + using PostNewtonian.PNBase # Below are what we would normally write manually import PostNewtonian: M₁, M₂, χ₁ˣ, χ₁ʸ, χ₁ᶻ, v, Φ diff --git a/src/core/pn_reference.jl b/src/core/pn_reference.jl index 3011eafc..dd0fa557 100644 --- a/src/core/pn_reference.jl +++ b/src/core/pn_reference.jl @@ -172,7 +172,7 @@ end eval(x::Expr) = Core.eval(Einstein1918, x) include(p::AbstractString) = Base.include(Einstein1918, p) using PostNewtonian: @pn_expression, @pn_expansion, PNExpansionParameter, 𝒾, γₑ, ζ3 - using PostNewtonian.PNExpressionArithmetic + using PostNewtonian.PNBase import PostNewtonian: G, c, M, χ⃗₁, χ⃗₂, v, pn_order import Quaternionic: absvec From 28c9c817871e3b737e829df93b7ddf8a0c1d5954 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Mon, 7 Jul 2025 22:24:13 -0400 Subject: [PATCH 58/77] Simplify creation of new_module --- src/core/pn_reference.jl | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/core/pn_reference.jl b/src/core/pn_reference.jl index dd0fa557..3628ad1a 100644 --- a/src/core/pn_reference.jl +++ b/src/core/pn_reference.jl @@ -77,21 +77,22 @@ function pn_reference(expr, __module__, __source__) end # Now, we assemble the new module, mostly by prepending some imports to the contents - new_module = Expr( - :module, - false, # We turn this into a `baremodule` - expr.args[2], # This is the name of the module - Expr( # This is the new module body - :block, - :(using Base: Base, Val, π), - :(eval(x::Expr) = Core.eval($(expr.args[2]), x)), - :(include(p::AbstractString) = Base.include($(expr.args[2]), p)), - :(using PostNewtonian: - @pn_expression, @pn_expansion, PNExpansionParameter, 𝒾, γₑ, ζ3), - :(using PostNewtonian.PNExpressionArithmetic), - expr.args[3].args..., # The original module body - ), - ) + module_name = expr.args[2] + module_body = expr.args[3].args + public = VERSION ≥ v"1.11.0-beta1" ? Expr(:public, module_name) : :() + new_module = quote + $public + Base.@__doc__ baremodule $(module_name) + using Base: Base, Val, π, @raw_str, @doc + eval(x::Expr) = Core.eval($(module_name), x) + include(p::AbstractString) = Base.include($(module_name), p) + using PostNewtonian: + @pn_expression, @pn_expansion, PNExpansionParameter, 𝒾, γₑ, ζ3 + using PostNewtonian.PNBase + $(module_body...) + end + end + # Finally, we return this as a `:toplevel` expression, to ensure that it is # not buried in some block that causes an error. return Expr(:toplevel, new_module) From f4667c186071822218a1b1d8093ecc44e7b629fb Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Mon, 7 Jul 2025 23:49:59 -0400 Subject: [PATCH 59/77] Move PNTerm/PNExpansion arithmetic into PNBase --- src/core/PNBase.jl | 31 ++++++++++++++--------- src/core/PNExpansion.jl | 54 ++++++++++++++++++++--------------------- src/core/PNTerm.jl | 44 +++++++++++++++++---------------- src/core/core.jl | 2 +- 4 files changed, 71 insertions(+), 60 deletions(-) diff --git a/src/core/PNBase.jl b/src/core/PNBase.jl index bd9d030f..555ee734 100644 --- a/src/core/PNBase.jl +++ b/src/core/PNBase.jl @@ -1,8 +1,15 @@ public PNBase +""" + PNBase + +This module provides many of the same essential functions available in Julia's `Base` +module, but restricted to operations we expect to be used in post-Newtonian contexts, so +that we can ensure that the resulting expressions are valid within that framework. +""" baremodule PNBase import Base -using PostNewtonian: constant_convert, PNSystem +using PostNewtonian: constant_convert, PNSystem, PNTerm, PNExpansionParameter, PNExpansion using PostNewtonian.InlineExports: @export using Base: @inline @@ -10,22 +17,24 @@ using Base: @inline @export @inline √(pnsystem::PNSystem, x) = Base.sqrt(constant_convert(pnsystem, x)) -@export @inline (+)(pnsystem::PNSystem, x) = constant_convert(pnsystem, x) -@export @inline (*)(pnsystem::PNSystem, x) = constant_convert(pnsystem, x) - @export @inline (+)(pnsystem::PNSystem, x, y) = Base.:+(constant_convert(pnsystem, x), y) -@export @inline (-)(pnsystem::PNSystem, x, y) = Base.:-(constant_convert(pnsystem, x), y) -@export @inline (*)(pnsystem::PNSystem, x, y) = Base.:*(constant_convert(pnsystem, x), y) -@export @inline (/)(pnsystem::PNSystem, x, y) = Base.:/(constant_convert(pnsystem, x), y) -@export @inline (^)(pnsystem::PNSystem, x, n) = Base.:^(constant_convert(pnsystem, x), n) - -@export @inline function (+)(pnsystem::PNSystem, w, x, y, z...) +@inline (+)(pnsystem::PNSystem, x) = constant_convert(pnsystem, x) +@inline function (+)(pnsystem::PNSystem, w, x, y, z...) return Base.:+(constant_convert(pnsystem, w), x, y, z...) end -@export @inline function (*)(pnsystem::PNSystem, w, x, y, z...) + +@export @inline (-)(pnsystem::PNSystem, x, y) = Base.:-(constant_convert(pnsystem, x), y) + +@export @inline (*)(pnsystem::PNSystem, x, y) = Base.:*(constant_convert(pnsystem, x), y) +@inline (*)(pnsystem::PNSystem, x) = constant_convert(pnsystem, x) +@inline function (*)(pnsystem::PNSystem, w, x, y, z...) return Base.:*(constant_convert(pnsystem, w), x, y, z...) end +@export @inline (/)(pnsystem::PNSystem, x, y) = Base.:/(constant_convert(pnsystem, x), y) + +@export @inline (^)(pnsystem::PNSystem, x, n) = Base.:^(constant_convert(pnsystem, x), n) + end # baremodule PNBase const pnbase_functions = filter(s -> isa(getfield(PNBase, s), Function), names(PNBase)) diff --git a/src/core/PNExpansion.jl b/src/core/PNExpansion.jl index 3ce6fe56..b266c7fb 100644 --- a/src/core/PNExpansion.jl +++ b/src/core/PNExpansion.jl @@ -72,23 +72,23 @@ function Base.sum(pn_expansion::PNExpansion{N,T,NMax}) where {N,T,NMax} return sum(pn_expansion[i] for i ∈ 1:N; init=zero(T)) end -function Base.:+(pn::PNExpansion{N,T1,NMax}, x::T2) where {N,T1,NMax,T2<:Number} +function PNBase.:+(pn::PNExpansion{N,T1,NMax}, x::T2) where {N,T1,NMax,T2<:Number} T3 = promote_type(T1, T2) return PNExpansion(ntuple(i -> i == 1 ? pn[1] + x : T3(pn[i]), Val(N)), NMax) end -Base.:+(x::T, pn::PNExpansion) where {T<:Number} = pn + x +PNBase.:+(x::T, pn::PNExpansion) where {T<:Number} = PNBase.(+)(pn, x) -function Base.:-(pn::PNExpansion{N,T,NMax}) where {N,T,NMax} +function PNBase.:-(pn::PNExpansion{N,T,NMax}) where {N,T,NMax} return PNExpansion{N,T,NMax}((-).(pn.coeffs)) end -function Base.:*(pn::PNExpansion{N,T1,NMax}, x::T2) where {N,T1,NMax,T2<:Number} +function PNBase.:*(pn::PNExpansion{N,T1,NMax}, x::T2) where {N,T1,NMax,T2<:Number} T3 = promote_type(T1, T2) return PNExpansion{N,T3,NMax}(@. T3(pn.coeffs * x)) end -Base.:*(x::T, pn::PNExpansion) where {T<:Number} = pn * x +PNBase.:*(x::T, pn::PNExpansion) where {T<:Number} = PNBase.(*)(pn, x) -function Base.:+( +function PNBase.:+( pn1::PNExpansion{N1,T1,NMax1}, pn2::PNExpansion{N2,T2,NMax2} ) where {N1,N2,T1,T2,NMax1,NMax2} throw( @@ -99,7 +99,7 @@ function Base.:+( ) end -function Base.:+( +function PNBase.:+( pn1::PNExpansion{N1,T1,NMax}, pn2::PNExpansion{N2,T2,NMax} ) where {N1,N2,T1,T2,NMax} if N1 > N2 @@ -120,7 +120,7 @@ function sum_term( end end -function Base.:*( +function PNBase.:*( pn1::PNExpansion{N1,T1,NMax1}, pn2::PNExpansion{N2,T2,NMax2} ) where {N1,N2,T1,T2,NMax1,NMax2} throw( @@ -131,11 +131,11 @@ function Base.:*( ) end -function Base.:*( +function PNBase.:*( pn1::PNExpansion{N1,T1,NMax}, pn2::PNExpansion{N2,T2,NMax} ) where {N1,N2,T1,T2,NMax} if N1 > N2 - return pn2 * pn1 + return PNBase.(*)(pn2, pn1) else N3 = min(N1 + N2 - 1, NMax) PNExpansion(ntuple(i -> product_term(i, pn1, pn2), Val(N3)), NMax) @@ -152,7 +152,7 @@ function product_term( ) end -Base.:/(p::PNExpansion, x::Number) = p * (1 / x) +PNBase.:/(p::PNExpansion, x::Number) = p * (1 / x) function FastDifferentiation.derivative( pn_expansion::PNExpansion{N,T,NMax}, fd_node::FastDifferentiation.Node @@ -165,7 +165,7 @@ end Base.Tuple(pn::PNExpansion) = pn.coeffs SVector(pn::PNExpansion) = SVector(pn.coeffs) -function Base.:+( +function PNBase.:+( x::T1, term::PNTerm{T2,PNOrder,c⁻¹Exponent} ) where {T1<:Number,T2,PNOrder,c⁻¹Exponent} if c⁻¹exp(term) < 0 @@ -189,13 +189,13 @@ function Base.:+( end return PNExpansion{N,T,NMax}(Tuple(coeffs)) end -Base.:+(term::PNTerm, x::Number) = x + term +PNBase.:+(term::PNTerm, x::Number) = PNBase.(+)(x, term) -function Base.:-(term::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} +function PNBase.:-(term::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} return PNTerm{T,PNOrder,c⁻¹Exponent}(-term.coeff) end -function Base.:+( +function PNBase.:+( term1::PNTerm{T1,PNOrder,c⁻¹E1}, term2::PNTerm{T2,PNOrder,c⁻¹E2} ) where {T1,T2,PNOrder,c⁻¹E1,c⁻¹E2} if c⁻¹exp(term1) < 0 @@ -232,9 +232,9 @@ function Base.:+( return PNExpansion{N,T,NMax}(Tuple(coeffs)) end -Base.:-(term1::PNTerm, term2::PNTerm) = term1 + (-term2) +PNBase.:-(term1::PNTerm, term2::PNTerm) = PNBase.(+)(term1, -term2) -function Base.:+( +function PNBase.:+( term::PNTerm{T1,PNOrder,c⁻¹E1}, expansion::PNExpansion{N2,T2,NMax2} ) where {T1,PNOrder,c⁻¹E1,N2,T2,NMax2} if c⁻¹exp(term) < 0 @@ -263,16 +263,16 @@ function Base.:+( end return PNExpansion{N,T,NMax}(Tuple(coeffs)) end -Base.:+(expansion::PNExpansion, term::PNTerm) = term + expansion +PNBase.:+(expansion::PNExpansion, term::PNTerm) = PNBase.(+)(term, expansion) -Base.:-(term::PNTerm, x::Number) = term + (-x) -Base.:-(x::Number, term::PNTerm) = x + (-term) -Base.:-(term::PNTerm, expansion::PNExpansion) = term + (-expansion) -Base.:-(expansion::PNExpansion, term::PNTerm) = expansion + (-term) -Base.:-(x::Number, expansion::PNExpansion) = x + (-expansion) -Base.:-(expansion::PNExpansion, x::Number) = expansion + (-x) +PNBase.:-(term::PNTerm, x::Number) = PNBase.(+)(term, -x) +PNBase.:-(x::Number, term::PNTerm) = PNBase.(+)(x, -term) +PNBase.:-(term::PNTerm, expansion::PNExpansion) = PNBase.(+)(term, -expansion) +PNBase.:-(expansion::PNExpansion, term::PNTerm) = PNBase.(+)(expansion, -term) +PNBase.:-(x::Number, expansion::PNExpansion) = PNBase.(+)(x, -expansion) +PNBase.:-(expansion::PNExpansion, x::Number) = PNBase.(+)(expansion, -x) -function Base.:*( +function PNBase.:*( expansion::PNExpansion{N1,T1,NMax1}, term::PNTerm{T2,PNOrder,c⁻¹E2} ) where {N1,T1,NMax1,T2,PNOrder,c⁻¹E2} ΔN = c⁻¹exp(term) # Note that ΔN may be negative! @@ -301,10 +301,10 @@ function Base.:*( end return PNExpansion{N,T,NMax}(Tuple(coeffs)) end -Base.:*(term::PNTerm, expansion::PNExpansion) = expansion * term +PNBase.:*(term::PNTerm, expansion::PNExpansion) = PNBase.(*)(expansion, term) # (a, b, c, d, e, f, g) * (c⁻¹^2) = (0, 0, a, b, c, d, e) -Base.:/(expansion::PNExpansion, term::PNTerm) = expansion * inv(term) +PNBase.:/(expansion::PNExpansion, term::PNTerm) = PNBase.(*)(expansion, inv(term)) @testitem "PNExpansion algebra" begin using Symbolics: @variables, simplify, substitute diff --git a/src/core/PNTerm.jl b/src/core/PNTerm.jl index 24e6750a..d492a700 100644 --- a/src/core/PNTerm.jl +++ b/src/core/PNTerm.jl @@ -1,15 +1,3 @@ -""" - PNExpansionParameter(pnsystem) - -Create a [`PNTerm`](@ref) object representing the post-Newtonian expansion parameter ``c``. -This can be used to automatically create more complicated `PNTerm`s, which combine to form a -[`PNExpansion`](@ref). This is a simple but effective way to write PN formulas while -automatically tracking the PN order of each term. -""" -@public function PNExpansionParameter(::T) where {NT,PNOrder,T<:PNSystem{NT,PNOrder}} - return PNTerm{NT,PNOrder}(-1, one(T)) -end - """ PNTerm{T,PNOrder,c⁻¹Exponent} @@ -56,6 +44,18 @@ Useful facts: end end +""" + PNExpansionParameter(pnsystem) + +Create a [`PNTerm`](@ref) object representing the post-Newtonian expansion parameter ``c``. +This can be used to automatically create more complicated `PNTerm`s, which combine to form a +[`PNExpansion`](@ref). This is a simple but effective way to write PN formulas while +automatically tracking the PN order of each term. +""" +@public function PNExpansionParameter(::T) where {NT,PNOrder,T<:PNSystem{NT,PNOrder}} + return PNTerm{NT,PNOrder}(-1, one(T)) +end + Base.length(pn::PNTerm) = 1 Base.eltype(pn::PNTerm{T}) where {T} = T @public c⁻¹exp(pn::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} = @@ -71,7 +71,7 @@ function Base.sum(pn::PNTerm) return pn.coeff end -function Base.:+(pn::PNTerm) +function PNBase.:+(pn::PNTerm) return pn end @@ -79,39 +79,41 @@ function Base.inv(term::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻ return PNTerm{T,PNOrder}(-c⁻¹exp(term), inv(term.coeff)) end -function Base.sqrt(term::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} +function PNBase.:√(term::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} @assert iseven(c⁻¹Exponent) "Only half-integer PN orders are supported." return PNTerm{T,PNOrder}(c⁻¹Exponent ÷ 2, sqrt(term.coeff)) end -function Base.:^(term::PNTerm{T,PNOrder,c⁻¹Exponent}, n::Int) where {T,PNOrder,c⁻¹Exponent} +function PNBase.:^( + term::PNTerm{T,PNOrder,c⁻¹Exponent}, n::Int +) where {T,PNOrder,c⁻¹Exponent} coeff = term.coeff^n return PNTerm{typeof(coeff),PNOrder}(c⁻¹exp(term) * n, coeff) end -function Base.:*( +function PNBase.:*( x::Number, term::PNTerm{T,PNOrder,c⁻¹Exponent} ) where {T,PNOrder,c⁻¹Exponent} coeff = x * term.coeff return PNTerm{typeof(coeff),PNOrder,c⁻¹Exponent}(coeff) end -Base.:*(term::PNTerm, x::Number) = x * term +PNBase.:*(term::PNTerm, x::Number) = x * term -function Base.:/( +function PNBase.:/( term::PNTerm{T,PNOrder,c⁻¹Exponent}, x::Number ) where {T,PNOrder,c⁻¹Exponent} coeff = term.coeff / x return PNTerm{typeof(coeff),PNOrder,c⁻¹Exponent}(coeff) end -function Base.:/( +function PNBase.:/( x::Number, term::PNTerm{T,PNOrder,c⁻¹Exponent} ) where {T,PNOrder,c⁻¹Exponent} coeff = x / term.coeff return PNTerm{typeof(coeff),PNOrder}(-c⁻¹exp(term), coeff) end -function Base.:*( +function PNBase.:*( term1::PNTerm{T1,PNOrder,c⁻¹E1}, term2::PNTerm{T2,PNOrder,c⁻¹E2} ) where {T1,T2,PNOrder,c⁻¹E1,c⁻¹E2} c⁻¹Exponent = c⁻¹exp(term1) + c⁻¹exp(term2) @@ -119,7 +121,7 @@ function Base.:*( return PNTerm{typeof(coeff),PNOrder,c⁻¹Exponent}(coeff) end -function Base.:/( +function PNBase.:/( term1::PNTerm{T1,PNOrder,c⁻¹E1}, term2::PNTerm{T2,PNOrder,c⁻¹E2} ) where {T1,T2,PNOrder,c⁻¹E1,c⁻¹E2} c⁻¹Exponent = c⁻¹E1 - c⁻¹E2 diff --git a/src/core/core.jl b/src/core/core.jl index 94cc3331..5ca703f7 100644 --- a/src/core/core.jl +++ b/src/core/core.jl @@ -9,9 +9,9 @@ include("utilities/truncated_series_monoid.jl") include("utilities/truncated_series_inversion.jl") # These are types and modules that help to build the post-Newtonian framework. +include("PNBase.jl") include("PNTerm.jl") include("PNExpansion.jl") -include("PNBase.jl") # These macros effectively form the interface used directly in the rest of the code. include("pn_expansion.jl") From 2512ce689dec1e9cc03e32d743e90fe71669f5a3 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Tue, 8 Jul 2025 22:29:45 -0400 Subject: [PATCH 60/77] Actually, half-integer exponentiation isn't THAT bad --- src/core/pn_expansion.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/pn_expansion.jl b/src/core/pn_expansion.jl index a9e38409..8ebb2c7b 100644 --- a/src/core/pn_expansion.jl +++ b/src/core/pn_expansion.jl @@ -60,7 +60,7 @@ in this package as [`γₑ`](@ref). half-integers in any expansion. Evaluating a term like `x^(7/2)` generically will be relatively inefficient, requiring a floating-point division, the logarithm of `x`, a floating-point multiplication, and exponentiation. Compared to a single square-root and - integer exponentiation, this is very slow. Moreover, it does not work well with the + integer exponentiation, this is slow. Moreover, it does not work well with the [`PNTerm`](@ref) mechanism. So another thing this macro does is search for terms like `x^(7/2)` and convert them to `(√x)^7`, and similarly for `γₚₙ` and `γ`. This only works for explicit half-integers, rather than variables that may be half-integers. From 09d8138fb9c800725b7800d42152b980d14f3579 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Tue, 8 Jul 2025 22:34:36 -0400 Subject: [PATCH 61/77] Fix docstring mistake --- src/core/PNBase.jl | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/core/PNBase.jl b/src/core/PNBase.jl index 555ee734..0671bea4 100644 --- a/src/core/PNBase.jl +++ b/src/core/PNBase.jl @@ -1,15 +1,9 @@ public PNBase -""" - PNBase - -This module provides many of the same essential functions available in Julia's `Base` -module, but restricted to operations we expect to be used in post-Newtonian contexts, so -that we can ensure that the resulting expressions are valid within that framework. -""" +# Documented below so that we can list the functions in the module automatically. baremodule PNBase import Base -using PostNewtonian: constant_convert, PNSystem, PNTerm, PNExpansionParameter, PNExpansion +using PostNewtonian: constant_convert, PNSystem using PostNewtonian.InlineExports: @export using Base: @inline @@ -33,6 +27,8 @@ end @export @inline (/)(pnsystem::PNSystem, x, y) = Base.:/(constant_convert(pnsystem, x), y) +@export @inline (//)(x, y) = Base.://(x, y) + @export @inline (^)(pnsystem::PNSystem, x, n) = Base.:^(constant_convert(pnsystem, x), n) end # baremodule PNBase @@ -42,7 +38,10 @@ const pnbase_functions = filter(s -> isa(getfield(PNBase, s), Function), names(P @doc """ PNBase -This module provides arithmetic operations to be used inside `@pn_expression` modules. +This module provides many of the same essential functions available in Julia's `Base` +module, but restricted to operations we expect to be used in post-Newtonian contexts, so +that we can ensure that the resulting expressions are valid within that framework. This is +intended to be used inside `@pn_expression` modules. It is intentionally very restrictive, so that it only includes the basic arithmetic operations that are used in post-Newtonian expressions, and even then only in modified forms From c643e5a4a7d01ae910718339bd7def4b72b24dde Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Tue, 8 Jul 2025 22:40:39 -0400 Subject: [PATCH 62/77] Correct calling of operators with specified module --- src/core/PNExpansion.jl | 28 ++++++++++++++-------------- src/core/PNTerm.jl | 14 +++++++++----- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/core/PNExpansion.jl b/src/core/PNExpansion.jl index b266c7fb..de0e412f 100644 --- a/src/core/PNExpansion.jl +++ b/src/core/PNExpansion.jl @@ -76,7 +76,7 @@ function PNBase.:+(pn::PNExpansion{N,T1,NMax}, x::T2) where {N,T1,NMax,T2<:Numbe T3 = promote_type(T1, T2) return PNExpansion(ntuple(i -> i == 1 ? pn[1] + x : T3(pn[i]), Val(N)), NMax) end -PNBase.:+(x::T, pn::PNExpansion) where {T<:Number} = PNBase.(+)(pn, x) +PNBase.:+(x::T, pn::PNExpansion) where {T<:Number} = PNBase.:+(pn, x) function PNBase.:-(pn::PNExpansion{N,T,NMax}) where {N,T,NMax} return PNExpansion{N,T,NMax}((-).(pn.coeffs)) @@ -86,7 +86,7 @@ function PNBase.:*(pn::PNExpansion{N,T1,NMax}, x::T2) where {N,T1,NMax,T2<:Numbe T3 = promote_type(T1, T2) return PNExpansion{N,T3,NMax}(@. T3(pn.coeffs * x)) end -PNBase.:*(x::T, pn::PNExpansion) where {T<:Number} = PNBase.(*)(pn, x) +PNBase.:*(x::T, pn::PNExpansion) where {T<:Number} = PNBase.:*(pn, x) function PNBase.:+( pn1::PNExpansion{N1,T1,NMax1}, pn2::PNExpansion{N2,T2,NMax2} @@ -135,7 +135,7 @@ function PNBase.:*( pn1::PNExpansion{N1,T1,NMax}, pn2::PNExpansion{N2,T2,NMax} ) where {N1,N2,T1,T2,NMax} if N1 > N2 - return PNBase.(*)(pn2, pn1) + return PNBase.:*(pn2, pn1) else N3 = min(N1 + N2 - 1, NMax) PNExpansion(ntuple(i -> product_term(i, pn1, pn2), Val(N3)), NMax) @@ -189,7 +189,7 @@ function PNBase.:+( end return PNExpansion{N,T,NMax}(Tuple(coeffs)) end -PNBase.:+(term::PNTerm, x::Number) = PNBase.(+)(x, term) +PNBase.:+(term::PNTerm, x::Number) = PNBase.:+(x, term) function PNBase.:-(term::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} return PNTerm{T,PNOrder,c⁻¹Exponent}(-term.coeff) @@ -232,7 +232,7 @@ function PNBase.:+( return PNExpansion{N,T,NMax}(Tuple(coeffs)) end -PNBase.:-(term1::PNTerm, term2::PNTerm) = PNBase.(+)(term1, -term2) +PNBase.:-(term1::PNTerm, term2::PNTerm) = PNBase.:+(term1, -term2) function PNBase.:+( term::PNTerm{T1,PNOrder,c⁻¹E1}, expansion::PNExpansion{N2,T2,NMax2} @@ -263,14 +263,14 @@ function PNBase.:+( end return PNExpansion{N,T,NMax}(Tuple(coeffs)) end -PNBase.:+(expansion::PNExpansion, term::PNTerm) = PNBase.(+)(term, expansion) +PNBase.:+(expansion::PNExpansion, term::PNTerm) = PNBase.:+(term, expansion) -PNBase.:-(term::PNTerm, x::Number) = PNBase.(+)(term, -x) -PNBase.:-(x::Number, term::PNTerm) = PNBase.(+)(x, -term) -PNBase.:-(term::PNTerm, expansion::PNExpansion) = PNBase.(+)(term, -expansion) -PNBase.:-(expansion::PNExpansion, term::PNTerm) = PNBase.(+)(expansion, -term) -PNBase.:-(x::Number, expansion::PNExpansion) = PNBase.(+)(x, -expansion) -PNBase.:-(expansion::PNExpansion, x::Number) = PNBase.(+)(expansion, -x) +PNBase.:-(term::PNTerm, x::Number) = PNBase.:+(term, -x) +PNBase.:-(x::Number, term::PNTerm) = PNBase.:+(x, -term) +PNBase.:-(term::PNTerm, expansion::PNExpansion) = PNBase.:+(term, -expansion) +PNBase.:-(expansion::PNExpansion, term::PNTerm) = PNBase.:+(expansion, -term) +PNBase.:-(x::Number, expansion::PNExpansion) = PNBase.:+(x, -expansion) +PNBase.:-(expansion::PNExpansion, x::Number) = PNBase.:+(expansion, -x) function PNBase.:*( expansion::PNExpansion{N1,T1,NMax1}, term::PNTerm{T2,PNOrder,c⁻¹E2} @@ -301,10 +301,10 @@ function PNBase.:*( end return PNExpansion{N,T,NMax}(Tuple(coeffs)) end -PNBase.:*(term::PNTerm, expansion::PNExpansion) = PNBase.(*)(expansion, term) +PNBase.:*(term::PNTerm, expansion::PNExpansion) = PNBase.:*(expansion, term) # (a, b, c, d, e, f, g) * (c⁻¹^2) = (0, 0, a, b, c, d, e) -PNBase.:/(expansion::PNExpansion, term::PNTerm) = PNBase.(*)(expansion, inv(term)) +PNBase.:/(expansion::PNExpansion, term::PNTerm) = PNBase.:*(expansion, inv(term)) @testitem "PNExpansion algebra" begin using Symbolics: @variables, simplify, substitute diff --git a/src/core/PNTerm.jl b/src/core/PNTerm.jl index d492a700..6740b074 100644 --- a/src/core/PNTerm.jl +++ b/src/core/PNTerm.jl @@ -67,12 +67,16 @@ function constant_convert( term end -function Base.sum(pn::PNTerm) - return pn.coeff +function Base.sum(term::PNTerm) + return term.coeff end -function PNBase.:+(pn::PNTerm) - return pn +function PNBase.:+(term::PNTerm) + return term +end + +function PNBase.:-(term::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} + return PNTerm{T,PNOrder,c⁻¹Exponent}(-term.coeff) end function Base.inv(term::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} @@ -97,7 +101,7 @@ function PNBase.:*( coeff = x * term.coeff return PNTerm{typeof(coeff),PNOrder,c⁻¹Exponent}(coeff) end -PNBase.:*(term::PNTerm, x::Number) = x * term +PNBase.:*(term::PNTerm, x::Number) = PNBase.:*(x, term) function PNBase.:/( term::PNTerm{T,PNOrder,c⁻¹Exponent}, x::Number From 1df5ced7691e00303873ef1028dd97242555e7f6 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Thu, 10 Jul 2025 00:40:24 -0400 Subject: [PATCH 63/77] Add a little more flesh to the literature --- docs/src/references.bib | 34 +++++++++++++++++++++++++++++ src/literature/Blanchet2024.jl | 16 ++++++++++---- src/literature/TaylorPoisson2008.jl | 20 +++++++++++++++++ src/literature/Trestini2025.jl | 24 ++++++++++++++++++++ 4 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 docs/src/references.bib create mode 100644 src/literature/TaylorPoisson2008.jl create mode 100644 src/literature/Trestini2025.jl diff --git a/docs/src/references.bib b/docs/src/references.bib new file mode 100644 index 00000000..a0d6fb41 --- /dev/null +++ b/docs/src/references.bib @@ -0,0 +1,34 @@ +@article{Blanchet2024, + title = {{Post-Newtonian} theory for gravitational waves}, + volume = {27}, + issn = {1433-8351}, + url = {https://doi.org/10.1007/s41114-024-00050-z}, + doi = {10.1007/s41114-024-00050-z}, + number = {1}, + journal = {Living Reviews in Relativity}, + author = {Blanchet, Luc}, + month = jul, + year = {2024}, +} + +@article{TaylorPoisson2008, + title = {Nonrotating black hole in a {post-Newtonian} tidal environment}, + volume = {78}, + issn = {1550-7998, 1550-2368}, + url = {http://arxiv.org/abs/0806.3052}, + doi = {10.1103/PhysRevD.78.084016}, + number = {8}, + journal = {Physical Review D}, + author = {Taylor, Stephanne and Poisson, Eric}, + month = oct, + year = {2008}, +} + +@misc{Trestini2025, + title = {The Schott term in the binding energy for compact binaries on circular orbits at fourth {post-Newtonian} order}, + url = {http://arxiv.org/abs/2504.13245}, + doi = {10.48550/arXiv.2504.13245}, + author = {Trestini, David}, + month = apr, + year = {2025}, +} diff --git a/src/literature/Blanchet2024.jl b/src/literature/Blanchet2024.jl index c2d614c5..95db07a2 100644 --- a/src/literature/Blanchet2024.jl +++ b/src/literature/Blanchet2024.jl @@ -1,9 +1,10 @@ @doc raw""" Expressions from [Blanchet2024](@cite) -This package mostly derives its notation from that paper. The biggest (mild) exception is -that Blanchet uses the symbol `m` for mass, while we reserve that for the azimuthal number -of spin-weighted spherical harmonics, and use `M` for mass instead. +This package mostly derives its notation from that paper. The main exception is that +Blanchet uses the symbol ``m`` for mass and ``\mathup{M}`` for ADM mass, while we reserve +``m`` for the azimuthal number of spin-weighted spherical harmonics, and use ``M`` for mass +instead. (ADM mass does not appear in this package as of this writing.) In Sec. 3.2.4, Blanchet defines the total mass ``m=m₁+m₂``, the relative mass difference ``\Delta = (m₁ - m₂) / m``, the reduced mass ``μ = m₁ m₂ / m``, and the symmetric mass ratio @@ -17,8 +18,15 @@ In Eq. (369), he defines In Eq. (375), he defines the "frequency-related parameter" ```math -x = \left(\frac{GM\Omega}{c^3}\right)^{2/3} +x = \left(\frac{GM\Omega}{c^3}\right)^{2/3}, ``` +Note that this ``\Omega`` is "the orbital frequency [...] of the circular orbit as measured +by a distant observer." He does not define this completely, except to say that it is the +frequency associated with the asymptotic Killing vector field "in any natural coordinate +system which respects the helical symmetry" for an idealized circular system. + +[Trestini2025](@cite) describes roughly the same frequency as the "waveform frequency" Ω, +and uses "orbital frequency" ω to refer to — presumably — the coordinate frequency. """ @pn_reference module Blanchet2024 diff --git a/src/literature/TaylorPoisson2008.jl b/src/literature/TaylorPoisson2008.jl new file mode 100644 index 00000000..bcb56584 --- /dev/null +++ b/src/literature/TaylorPoisson2008.jl @@ -0,0 +1,20 @@ +@doc raw""" +Expressions from [TaylorPoisson2008](@cite) + +Note that there are expressions in the paper that could probably be used pretty easily to +generalize to precessing and/or eccentric systems. +""" +@pn_reference module TaylorPoisson2008 + +import PostNewtonian: G, c, v, M₁ as m₁, M₂ as m₂, M as m, r, χ₁ₗ as χ₁, χ₂ₗ as χ₂ + +# These are defined in the text below Eq. (9.5), though I've added subscripts to identify +# them as applying to the first and second bodies, respectively. +ϵ₁(pnsystem) = Base.sign(χ₁(pnsystem)) +ϵ₂(pnsystem) = Base.sign(χ₂(pnsystem)) + +@pn_expression function Eq_9_4(pnsystem) end + +@pn_expression function Eq_9_7(pnsystem) end + +end # module TaylorPoisson2008 diff --git a/src/literature/Trestini2025.jl b/src/literature/Trestini2025.jl new file mode 100644 index 00000000..53fb89e8 --- /dev/null +++ b/src/literature/Trestini2025.jl @@ -0,0 +1,24 @@ +@doc raw""" +Expressions from [Trestini2025](@cite) + +""" +@pn_reference module Trestini2025 + +import PostNewtonian: G, c, v, M₁ as m₁, M₂ as m₂, M as m, r, χ₁ₗ as χ₁, χ₂ₗ as χ₂ + +""" + ℵ₈(pnsystem) + +4PN tidal dissipation coefficient + +As given in the text below Eqs. (7.3) of [Trestini2025](@cite). Trestini defines ℵ₂ₙ as the + +> tidal dissipation (or black hole absorption) coefficient entering at the ``n``PN order +> beyond the leading, quadrupolar flux ('aleph' stands for 'absorption'). +""" +@pn_expression ℵ₈(pnsystem) = 1 - 4ν + 2ν^2 + +# Eq. (7.3a) +@pn_expression ℱₑᴴ(pnsystem) = 32c^5 * ν^2 / 5G * ℵ₈ * y^9 + +end # module Trestini2025 From f3135181107d3751dacf8555fbfc5c53dfe52792 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Thu, 10 Jul 2025 00:41:46 -0400 Subject: [PATCH 64/77] Reorganize strict arithmetic functions --- src/core/PNBase.jl | 38 ++++++++++++---- src/core/PNExpansion.jl | 89 +++++++++++++++++++++++--------------- src/core/PNTerm.jl | 74 +++++++++++++++++++------------ src/core/core.jl | 1 + src/pn_systems/pn_order.jl | 2 +- 5 files changed, 132 insertions(+), 72 deletions(-) diff --git a/src/core/PNBase.jl b/src/core/PNBase.jl index 0671bea4..27f0c88f 100644 --- a/src/core/PNBase.jl +++ b/src/core/PNBase.jl @@ -2,7 +2,15 @@ public PNBase # Documented below so that we can list the functions in the module automatically. baremodule PNBase -import Base + +# We want to be able to use the Base operators like `+`, `-`, `*`, `/`, and `^`, but we're +# already redefining those operators here, so we need alternative characters for them. +# Julia defines operators that can be used with equivalent precedence and as infix operators +# in this file: https://github.com/JuliaLang/julia/blob/master/src/julia-parser.scm. +# Circled versions of each will work, except for `^`, for which we use an arrow. They can +# be typed as `\oplus`, `\ominus`, `\circledast`, `\oslash`, and `\uparrow`. +using Base: Base, + as ⊕, - as ⊖, * as ⊛, / as ⊘, ^ as ↑ + using PostNewtonian: constant_convert, PNSystem using PostNewtonian.InlineExports: @export using Base: @inline @@ -11,25 +19,37 @@ using Base: @inline @export @inline √(pnsystem::PNSystem, x) = Base.sqrt(constant_convert(pnsystem, x)) -@export @inline (+)(pnsystem::PNSystem, x, y) = Base.:+(constant_convert(pnsystem, x), y) +@export @inline (+)(pnsystem::PNSystem, x, y) = ⊕(constant_convert(pnsystem, x), y) @inline (+)(pnsystem::PNSystem, x) = constant_convert(pnsystem, x) @inline function (+)(pnsystem::PNSystem, w, x, y, z...) - return Base.:+(constant_convert(pnsystem, w), x, y, z...) + return ⊕(constant_convert(pnsystem, w), x, y, z...) end +@inline (+)(x::AbstractFloat, y::AbstractFloat) = x ⊕ y +@inline (+)(x::AbstractFloat) = ⊕(x) -@export @inline (-)(pnsystem::PNSystem, x, y) = Base.:-(constant_convert(pnsystem, x), y) +@export @inline (-)(pnsystem::PNSystem, x, y) = ⊖(constant_convert(pnsystem, x), y) +@inline (-)(x::AbstractFloat, y::AbstractFloat) = x ⊖ y +@inline (-)(pnsystem::PNSystem, x) = ⊖(constant_convert(pnsystem, x)) +@inline (-)(x::AbstractFloat) = ⊖(x) -@export @inline (*)(pnsystem::PNSystem, x, y) = Base.:*(constant_convert(pnsystem, x), y) +@export @inline (*)(pnsystem::PNSystem, x, y) = ⊛(constant_convert(pnsystem, x), y) @inline (*)(pnsystem::PNSystem, x) = constant_convert(pnsystem, x) @inline function (*)(pnsystem::PNSystem, w, x, y, z...) - return Base.:*(constant_convert(pnsystem, w), x, y, z...) + return ⊛(constant_convert(pnsystem, w), x, y, z...) end +@inline (*)(x::AbstractFloat, y::AbstractFloat) = x ⊛ y -@export @inline (/)(pnsystem::PNSystem, x, y) = Base.:/(constant_convert(pnsystem, x), y) +@export @inline (/)(pnsystem::PNSystem, x, y) = ⊘(constant_convert(pnsystem, x), y) +@inline (/)(x::AbstractFloat, y::AbstractFloat) = x ⊘ y -@export @inline (//)(x, y) = Base.://(x, y) +@export @inline (^)(pnsystem::PNSystem, x, n) = ↑(constant_convert(pnsystem, x), n) +@inline (^)(x::AbstractFloat, n::Integer) = x ↑ n +@inline (^)(x::AbstractFloat, n::AbstractFloat) = x ↑ n -@export @inline (^)(pnsystem::PNSystem, x, n) = Base.:^(constant_convert(pnsystem, x), n) +# We can actually use the original definition of `//` for most purposes; we define it +# both ways here for simplicity. +@export @inline (//)(pnsystem::PNSystem, x, y) = Base.://(x, y) +@inline (//)(x, y) = Base.://(x, y) end # baremodule PNBase diff --git a/src/core/PNExpansion.jl b/src/core/PNExpansion.jl index de0e412f..6dbd9fe8 100644 --- a/src/core/PNExpansion.jl +++ b/src/core/PNExpansion.jl @@ -1,3 +1,19 @@ +# We enclose all of this in a `baremodule` so that we can isolate Base operations, and use +# PNBase operations by default. +baremodule PNExpansions + +# See explanation in `PNBase.jl` for why we use these operators. +using Base: Base, NTuple, ntuple, Val, Vector, @doc, @raw_str, @assert, @inbounds, @__dot__, + (:), eltype, promote_type, isbitstype, min, max, one, zero, + ==, ≤, <, >, ÷, + as ⊕, - as ⊖, * as ⊛, / as ⊘, ^ as ↑ + +using PostNewtonian: PostNewtonian +using PostNewtonian.PNBase +using PostNewtonian.PNTerms +using PostNewtonian.InlineExports: @export, @public +using StaticArrays: MVector, SVector +import FastDifferentiation + # This a utility that allow us to interoperate with FastDifferentiation.Node and other # Number types. function _efficient_vector(::Val{N}, ::Val{T}) where {N,T} @@ -62,14 +78,14 @@ elements are currently in the coefficients, but is required to be 1 ≤ N ≤ NM end end -pn_order(::PNExpansion{N,T,NMax}) where {N,T,NMax} = (NMax - 1)//2 +PostNewtonian.pn_order(::PNExpansion{N,T,NMax}) where {N,T,NMax} = (NMax ⊖ 1)//2 Base.getindex(pn::PNExpansion, i::Int) = pn.coeffs[i] Base.length(pn::PNExpansion) = length(pn.coeffs) Base.eltype(pn::PNExpansion) = eltype(pn.coeffs) function Base.sum(pn_expansion::PNExpansion{N,T,NMax}) where {N,T,NMax} - return sum(pn_expansion[i] for i ∈ 1:N; init=zero(T)) + return Base.sum(pn_expansion[i] for i ∈ 1:N; init=zero(T)) end function PNBase.:+(pn::PNExpansion{N,T1,NMax}, x::T2) where {N,T1,NMax,T2<:Number} @@ -93,7 +109,7 @@ function PNBase.:+( ) where {N1,N2,T1,T2,NMax1,NMax2} throw( ArgumentError( - "`PNExpansion` addition is only defined for objects of the same PN order." * + "`PNExpansion` addition is only defined for objects of the same PN order." ⊛ "\nGot NMax1=$(NMax1) and NMax2=$(NMax2).", ), ) @@ -125,7 +141,7 @@ function PNBase.:*( ) where {N1,N2,T1,T2,NMax1,NMax2} throw( ArgumentError( - "`PNExpansion` multiplication is only defined for objects of the same PN order." * + "`PNExpansion` multiplication is only defined for objects of the same PN order." ⊛ "\nGot NMax1=$(NMax1) and NMax2=$(NMax2).", ), ) @@ -146,17 +162,22 @@ function product_term( i, pn1::PNExpansion{N1,T1,NMax}, pn2::PNExpansion{N2,T2,NMax} ) where {N1,N2,T1,T2,NMax} T3 = promote_type(T1, T2) - return sum( - pn1.coeffs[j] * pn2.coeffs[i - j + 1] for j ∈ max(1, i - N2 + 1):min(i, N1); + return Base.sum( + pn1.coeffs[j] ⊛ pn2.coeffs[i - j + 1] for j ∈ max(1, i - N2 + 1):min(i, N1); init=zero(T3), ) end PNBase.:/(p::PNExpansion, x::Number) = p * (1 / x) +# Note that an PNExpansion is really a *relative* expansion in `1/c` — *not* `v` or `x`. +# Therefore, the correct derivative with respect to any variable (other than `c`, which we +# never differentiate with respect to) extends to just derivatives of the coefficients, +# without any change to the exponent of `1/c`. function FastDifferentiation.derivative( pn_expansion::PNExpansion{N,T,NMax}, fd_node::FastDifferentiation.Node ) where {N,T,NMax} + @assert fd_node.node_value ≠ :c "Can't differentiate `PNExpansion` with respect to `c`." return PNExpansion( ntuple(i -> FastDifferentiation.derivative(pn_expansion[i], fd_node), Val(N)), NMax ) @@ -171,15 +192,15 @@ function PNBase.:+( if c⁻¹exp(term) < 0 throw( ArgumentError( - "Cannot add a `PNTerm` with a negative exponent: " * - "c⁻¹exp(term)=$(c⁻¹exp(term))." * + "Cannot add a `PNTerm` with a negative exponent: " ⊛ + "c⁻¹exp(term)=$(c⁻¹exp(term))." ⊛ "\nResult will be a `PNExpansion`, which cannot store positive exponents.", ), ) end T = promote_type(T1, T2) - N₀ = c⁻¹exp(term) + 1 - NMax = Int(2PNOrder + 1) + N₀ = c⁻¹exp(term) ⊕ 1 + NMax = Int(2 ⊛ PNOrder ⊕ 1) N = min(N₀, NMax) coeffs = _efficient_vector(Val(N), Val(T)) coeffs .= zero(T) @@ -191,18 +212,14 @@ function PNBase.:+( end PNBase.:+(term::PNTerm, x::Number) = PNBase.:+(x, term) -function PNBase.:-(term::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} - return PNTerm{T,PNOrder,c⁻¹Exponent}(-term.coeff) -end - function PNBase.:+( term1::PNTerm{T1,PNOrder,c⁻¹E1}, term2::PNTerm{T2,PNOrder,c⁻¹E2} ) where {T1,T2,PNOrder,c⁻¹E1,c⁻¹E2} if c⁻¹exp(term1) < 0 throw( ArgumentError( - "Cannot add a `PNTerm` with a negative exponent: " * - "c⁻¹exp(term1)=$(c⁻¹exp(term1))." * + "Cannot add a `PNTerm` with a negative exponent: " ⊛ + "c⁻¹exp(term1)=$(c⁻¹exp(term1))." ⊛ "\nResult will be a `PNExpansion`, which cannot store positive exponents.", ), ) @@ -210,16 +227,16 @@ function PNBase.:+( if c⁻¹exp(term2) < 0 throw( ArgumentError( - "Cannot add a `PNTerm` with a negative exponent: " * - "c⁻¹exp(term2)=$(c⁻¹exp(term2))." * + "Cannot add a `PNTerm` with a negative exponent: " ⊛ + "c⁻¹exp(term2)=$(c⁻¹exp(term2))." ⊛ "\nResult will be a `PNExpansion`, which cannot store positive exponents.", ), ) end T = promote_type(T1, T2) - N1₀ = c⁻¹exp(term1) + 1 - N2₀ = c⁻¹exp(term2) + 1 - NMax = Int(2PNOrder + 1) + N1₀ = c⁻¹exp(term1) ⊕ 1 + N2₀ = c⁻¹exp(term2) ⊕ 1 + NMax = Int(2 ⊛ PNOrder ⊕ 1) N = min(max(N1₀, N2₀), NMax) coeffs = _efficient_vector(Val(N), Val(T)) coeffs .= zero(T) @@ -240,14 +257,14 @@ function PNBase.:+( if c⁻¹exp(term) < 0 throw( ArgumentError( - "Cannot add a `PNTerm` with a negative exponent: " * - "c⁻¹exp(term)=$(c⁻¹exp(term))." * + "Cannot add a `PNTerm` with a negative exponent: " ⊛ + "c⁻¹exp(term)=$(c⁻¹exp(term))." ⊛ "\nResult will be a `PNExpansion`, which cannot store positive exponents.", ), ) end - N1 = c⁻¹exp(term) + 1 - NMax1 = Int(2PNOrder + 1) + N1 = c⁻¹exp(term) ⊕ 1 + NMax1 = Int(2 ⊛ PNOrder ⊕ 1) NMax = min(NMax1, NMax2) N = min(max(N1, N2), NMax) T = promote_type(T1, T2) @@ -276,17 +293,17 @@ function PNBase.:*( expansion::PNExpansion{N1,T1,NMax1}, term::PNTerm{T2,PNOrder,c⁻¹E2} ) where {N1,T1,NMax1,T2,PNOrder,c⁻¹E2} ΔN = c⁻¹exp(term) # Note that ΔN may be negative! - NMax2 = Int(2PNOrder + 1) + NMax2 = Int(2 ⊛ PNOrder ⊕ 1) NMax = min(NMax1, NMax2) - N = min(max(N1, N1 + ΔN), NMax) + N = min(max(N1, N1 ⊕ ΔN), NMax) # Check that no terms from expansion will be lost to negative PN orders @inbounds for i ∈ 1:min(max(0, -ΔN), N1) if !iszero(expansion[i]) throw( ArgumentError( - "Cannot multiply `PNExpansion` by `PNTerm` with negative exponent: " * - "c⁻¹exp(term)=$(c⁻¹exp(term))." * + "Cannot multiply `PNExpansion` by `PNTerm` with negative exponent: " ⊛ + "c⁻¹exp(term)=$(c⁻¹exp(term))." ⊛ "\nResult will be a `PNExpansion`, which cannot store positive exponents.", ), ) @@ -297,7 +314,7 @@ function PNBase.:*( coeffs = _efficient_vector(Val(N), Val(T)) coeffs .= zero(T) @inbounds for i ∈ max(1, 1 - ΔN):min(N1, N - ΔN) - coeffs[i + ΔN] = expansion[i] * term.coeff + coeffs[i + ΔN] = expansion[i] ⊛ term.coeff end return PNExpansion{N,T,NMax}(Tuple(coeffs)) end @@ -306,6 +323,8 @@ PNBase.:*(term::PNTerm, expansion::PNExpansion) = PNBase.:*(expansion, term) PNBase.:/(expansion::PNExpansion, term::PNTerm) = PNBase.:*(expansion, inv(term)) +end # baremodule PNExpansions + @testitem "PNExpansion algebra" begin using Symbolics: @variables, simplify, substitute using PostNewtonian: PNExpansion @@ -314,7 +333,7 @@ PNBase.:/(expansion::PNExpansion, term::PNTerm) = PNBase.:*(expansion, inv(term) for N2 ∈ 1:9 for NMax ∈ max(N1, N2):(N1 + N2 + 3) @variables c⁻¹ x[1:N1] y[1:N2] z - poly(e::PNExpansion) = sum(e[i] * c⁻¹^(i - 1) for i ∈ 1:length(e)) + poly(e::PNExpansion) = sum(e[i] ⊛ c⁻¹^(i - 1) for i ∈ 1:length(e)) eˣ = PNExpansion(tuple(x...), NMax) eʸ = PNExpansion(tuple(y...), NMax) @@ -327,18 +346,18 @@ PNBase.:/(expansion::PNExpansion, term::PNTerm) = PNBase.:*(expansion, inv(term) @test_throws ArgumentError PNExpansion(tuple(z, x...), NMax + 1) + eˣ # Test products - polyprod = simplify(poly(eˣ * eʸ); expand=true) + polyprod = simplify(poly(eˣ ⊛ eʸ); expand=true) prodpoly = simplify( substitute( - simplify(poly(eˣ) * poly(eʸ); expand=true), + simplify(poly(eˣ) ⊛ poly(eʸ); expand=true), Dict([c⁻¹^n => 0 for n ∈ NMax:(2NMax + 3)]), ); expand=true, ) Δ = simplify(polyprod - prodpoly; expand=true) @test iszero(Δ) - @test_throws ArgumentError eˣ * PNExpansion(tuple(z, x...), NMax + 1) - @test_throws ArgumentError PNExpansion(tuple(z, x...), NMax + 1) * eˣ + @test_throws ArgumentError eˣ ⊛ PNExpansion(tuple(z, x...), NMax + 1) + @test_throws ArgumentError PNExpansion(tuple(z, x...), NMax + 1) ⊛ eˣ end end end diff --git a/src/core/PNTerm.jl b/src/core/PNTerm.jl index 6740b074..667b35b9 100644 --- a/src/core/PNTerm.jl +++ b/src/core/PNTerm.jl @@ -1,3 +1,16 @@ +# We enclose all of this in a `baremodule` so that we can isolate Base operations, and use +# PNBase operations by default. +baremodule PNTerms + +# See explanation in `PNBase.jl` for why we use these operators. +using Base: Base, @doc, @assert, one, zero, >, ÷, + as ⊕, - as ⊖, * as ⊛, / as ⊘, ^ as ↑ + +import PostNewtonian: constant_convert +using PostNewtonian: PNSystem +using PostNewtonian.InlineExports: @export +using PostNewtonian.PNBase + + """ PNTerm{T,PNOrder,c⁻¹Exponent} @@ -27,17 +40,17 @@ Useful facts: - `1/r` has order `1/c^2` """ -@public struct PNTerm{T,PNOrder,c⁻¹Exponent} +@export struct PNTerm{T,PNOrder,c⁻¹Exponent} coeff::T function PNTerm{T,PNOrder,c⁻¹Exponent}(coeff) where {T,PNOrder,c⁻¹Exponent} - if c⁻¹Exponent > 2PNOrder + if c⁻¹Exponent > 2 ⊛ PNOrder coeff = zero(coeff) end return new{T,PNOrder,c⁻¹Exponent}(coeff) end function PNTerm{T,PNOrder}(c⁻¹exp::Int, coeff) where {T,PNOrder} - if c⁻¹exp > 2PNOrder + if c⁻¹exp > 2 ⊛ PNOrder coeff = zero(coeff) end return new{T,PNOrder,c⁻¹exp}(coeff) @@ -52,13 +65,14 @@ This can be used to automatically create more complicated `PNTerm`s, which combi [`PNExpansion`](@ref). This is a simple but effective way to write PN formulas while automatically tracking the PN order of each term. """ -@public function PNExpansionParameter(::T) where {NT,PNOrder,T<:PNSystem{NT,PNOrder}} +@export function PNExpansionParameter(::T) where {NT,PNOrder,T<:PNSystem{NT,PNOrder}} return PNTerm{NT,PNOrder}(-1, one(T)) end Base.length(pn::PNTerm) = 1 Base.eltype(pn::PNTerm{T}) where {T} = T -@public c⁻¹exp(pn::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} = + +@export c⁻¹exp(pn::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} = c⁻¹Exponent function constant_convert( @@ -76,29 +90,29 @@ function PNBase.:+(term::PNTerm) end function PNBase.:-(term::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} - return PNTerm{T,PNOrder,c⁻¹Exponent}(-term.coeff) + return PNTerm{T,PNOrder,c⁻¹Exponent}(⊖(term.coeff)) end function Base.inv(term::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} - return PNTerm{T,PNOrder}(-c⁻¹exp(term), inv(term.coeff)) + return PNTerm{T,PNOrder}(⊖(c⁻¹exp(term)), Base.inv(term.coeff)) end function PNBase.:√(term::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} - @assert iseven(c⁻¹Exponent) "Only half-integer PN orders are supported." - return PNTerm{T,PNOrder}(c⁻¹Exponent ÷ 2, sqrt(term.coeff)) + @assert Base.iseven(c⁻¹Exponent) "Only half-integer PN orders are supported." + return PNTerm{T,PNOrder}(c⁻¹Exponent ÷ 2, Base.sqrt(term.coeff)) end function PNBase.:^( term::PNTerm{T,PNOrder,c⁻¹Exponent}, n::Int ) where {T,PNOrder,c⁻¹Exponent} - coeff = term.coeff^n - return PNTerm{typeof(coeff),PNOrder}(c⁻¹exp(term) * n, coeff) + coeff = term.coeff↑n + return PNTerm{typeof(coeff),PNOrder}(c⁻¹exp(term) ⊛ n, coeff) end function PNBase.:*( x::Number, term::PNTerm{T,PNOrder,c⁻¹Exponent} ) where {T,PNOrder,c⁻¹Exponent} - coeff = x * term.coeff + coeff = x ⊛ term.coeff return PNTerm{typeof(coeff),PNOrder,c⁻¹Exponent}(coeff) end PNBase.:*(term::PNTerm, x::Number) = PNBase.:*(x, term) @@ -106,46 +120,52 @@ PNBase.:*(term::PNTerm, x::Number) = PNBase.:*(x, term) function PNBase.:/( term::PNTerm{T,PNOrder,c⁻¹Exponent}, x::Number ) where {T,PNOrder,c⁻¹Exponent} - coeff = term.coeff / x + coeff = term.coeff ⊘ x return PNTerm{typeof(coeff),PNOrder,c⁻¹Exponent}(coeff) end function PNBase.:/( x::Number, term::PNTerm{T,PNOrder,c⁻¹Exponent} ) where {T,PNOrder,c⁻¹Exponent} - coeff = x / term.coeff - return PNTerm{typeof(coeff),PNOrder}(-c⁻¹exp(term), coeff) + coeff = x ⊘ term.coeff + return PNTerm{typeof(coeff),PNOrder}(⊖(c⁻¹exp(term)), coeff) end function PNBase.:*( term1::PNTerm{T1,PNOrder,c⁻¹E1}, term2::PNTerm{T2,PNOrder,c⁻¹E2} ) where {T1,T2,PNOrder,c⁻¹E1,c⁻¹E2} - c⁻¹Exponent = c⁻¹exp(term1) + c⁻¹exp(term2) - coeff = term1.coeff * term2.coeff + c⁻¹Exponent = c⁻¹exp(term1) ⊕ c⁻¹exp(term2) + coeff = term1.coeff ⊛ term2.coeff return PNTerm{typeof(coeff),PNOrder,c⁻¹Exponent}(coeff) end function PNBase.:/( term1::PNTerm{T1,PNOrder,c⁻¹E1}, term2::PNTerm{T2,PNOrder,c⁻¹E2} ) where {T1,T2,PNOrder,c⁻¹E1,c⁻¹E2} - c⁻¹Exponent = c⁻¹E1 - c⁻¹E2 - coeff = term1.coeff / term2.coeff + c⁻¹Exponent = c⁻¹E1 ⊖ c⁻¹E2 + coeff = term1.coeff ⊘ term2.coeff return PNTerm{typeof(coeff),PNOrder,c⁻¹Exponent}(coeff) end +end # baremodule PNTerms + @testitem "PNTerm algebra" begin - using DoubleFloats - using PostNewtonian: PostNewtonian, PNExpansionParameter, PNExpansion, pn_order + using Base: Base, one, zero, <, ÷, + as ⊕, - as ⊖, * as ⊛, / as ⊘, ^ as ↑ + using DoubleFloats: Double64 + using PostNewtonian: PostNewtonian, pn_order + using PostNewtonian.PNBase: ln, (√), (+), (-), (*), (/), (//), (^) + using PostNewtonian.PNTerms: PNTerm, PNExpansionParameter, c⁻¹exp, PNBase, constant_convert + using PostNewtonian.PNExpansions: PNExpansion for T ∈ [Float64, Float16, Double64] pn = BBH(randn(T, 14), 9//2) - pn[:v] = T(1) / 5 + pn[:v] = /(pn, 1, 5) c = PNExpansionParameter(pn) z = zero(T) - x = T(6) / 5 - y = T(17) / 5 - w = T(28) / 5 - v = T(27) / 13 + x = /(pn, 6, 5) + y = /(pn, 17, 5) + w = /(pn, 28, 5) + v = /(pn, 27, 13) # Test behavior of `c` as the basic PNTerm for (term, c⁻¹exponent, coeff) ∈ ( @@ -160,7 +180,7 @@ end ((x / c^2) / c^4, 6, x), ((x / c^2) * (y / c^4), 6, x * y), ) - @test PostNewtonian.c⁻¹exp(term) == c⁻¹exponent + @test c⁻¹exp(term) == c⁻¹exponent @test term.coeff == coeff @test term.coeff isa eltype(pn) @test length(term) == 1 diff --git a/src/core/core.jl b/src/core/core.jl index 5ca703f7..134df618 100644 --- a/src/core/core.jl +++ b/src/core/core.jl @@ -12,6 +12,7 @@ include("utilities/truncated_series_inversion.jl") include("PNBase.jl") include("PNTerm.jl") include("PNExpansion.jl") +import PostNewtonian.PNTerms: PNExpansionParameter # These macros effectively form the interface used directly in the rest of the code. include("pn_expansion.jl") diff --git a/src/pn_systems/pn_order.jl b/src/pn_systems/pn_order.jl index 8bf22e93..ff452398 100644 --- a/src/pn_systems/pn_order.jl +++ b/src/pn_systems/pn_order.jl @@ -7,7 +7,7 @@ This is a `Rational{Int}` that indicates the order to which the PN expansions sh carried out when using the given object. """ @export pn_order(::Type{<:PNSystem{NT,PNOrder}}) where {NT,PNOrder} = PNOrder -pn_order(pnsystem) = pn_order(typeof(pnsystem)) +pn_order(pnsystem::PNSystem) = pn_order(typeof(pnsystem)) """ order_index(pnsystem::PNSystem) From beed15f79d2aab9063755167ad49d38de3318a4b Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Thu, 10 Jul 2025 14:09:13 -0400 Subject: [PATCH 65/77] Change some names --- src/PostNewtonian.jl | 5 +++-- src/pn_systems/FDPNsystem.jl | 2 +- src/pn_systems/PNSystem.jl | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/PostNewtonian.jl b/src/PostNewtonian.jl index 8f84abf0..de5c3d74 100644 --- a/src/PostNewtonian.jl +++ b/src/PostNewtonian.jl @@ -24,8 +24,9 @@ using .InlineExports: @public, @export public ζ3 # Defined and documented in `core/utilities/misc.jl` # We will use these types to ensure that precision is preserved in PN expressions. -@public const exact_real = Union{Integer,Rational,AbstractIrrational} -@public const exact_number = Union{exact_real,Complex{<:exact_real}} +@public const ExactReal = Union{Integer,Rational,AbstractIrrational} +@public const ExactNumber = Union{ExactReal,Complex{<:ExactReal}} +@public const ExactIntegerBased = Union{Integer,Rational} include("pn_systems/pn_systems.jl") include("core/core.jl") diff --git a/src/pn_systems/FDPNsystem.jl b/src/pn_systems/FDPNsystem.jl index 22b2f5ee..478667b2 100644 --- a/src/pn_systems/FDPNsystem.jl +++ b/src/pn_systems/FDPNsystem.jl @@ -61,7 +61,7 @@ end # These are more examples of where we need to specialize the method for `FDPNSystem`, as # explained in the docstring. -constant_convert(::T, x::exact_number) where {NT,T<:FDPNSystem{NT}} = NT(x) +constant_convert(::T, x::ExactNumber) where {NT,T<:FDPNSystem{NT}} = NT(x) constant_convert(::T, x::NT) where {NT,T<:FDPNSystem{NT}} = x @testitem "FDPNSystem" begin diff --git a/src/pn_systems/PNSystem.jl b/src/pn_systems/PNSystem.jl index b5a8ad27..1cb68fd6 100644 --- a/src/pn_systems/PNSystem.jl +++ b/src/pn_systems/PNSystem.jl @@ -130,5 +130,5 @@ as the `Dual`'s underlying type in PostNewtonianForwardDiffExt. You generally will not need to call this function directly; it will be used automatically by the [`@pn_expression`](@ref) macro. """ -@public constant_convert(::T, x::exact_number) where {NT,T<:PNSystem{NT}} = NT(x) +@public constant_convert(::T, x::ExactNumber) where {NT,T<:PNSystem{NT}} = NT(x) constant_convert(::T, x::NT) where {NT,T<:PNSystem{NT}} = x From ec0cafa0bd4baf1c2eb1805c4fc29b39e772d312 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Thu, 10 Jul 2025 14:09:30 -0400 Subject: [PATCH 66/77] Complete test coverage of PNBase --- src/core/PNBase.jl | 86 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 75 insertions(+), 11 deletions(-) diff --git a/src/core/PNBase.jl b/src/core/PNBase.jl index 27f0c88f..e2d981c2 100644 --- a/src/core/PNBase.jl +++ b/src/core/PNBase.jl @@ -11,39 +11,55 @@ baremodule PNBase # be typed as `\oplus`, `\ominus`, `\circledast`, `\oslash`, and `\uparrow`. using Base: Base, + as ⊕, - as ⊖, * as ⊛, / as ⊘, ^ as ↑ -using PostNewtonian: constant_convert, PNSystem +using PostNewtonian: constant_convert, PNSystem, ExactNumber, ExactIntegerBased using PostNewtonian.InlineExports: @export using Base: @inline @export @inline ln(pnsystem::PNSystem, x) = Base.log(constant_convert(pnsystem, x)) +@inline ln(x::AbstractFloat) = Base.log(x) @export @inline √(pnsystem::PNSystem, x) = Base.sqrt(constant_convert(pnsystem, x)) +@inline √(x::AbstractFloat) = Base.sqrt(x) @export @inline (+)(pnsystem::PNSystem, x, y) = ⊕(constant_convert(pnsystem, x), y) @inline (+)(pnsystem::PNSystem, x) = constant_convert(pnsystem, x) -@inline function (+)(pnsystem::PNSystem, w, x, y, z...) - return ⊕(constant_convert(pnsystem, w), x, y, z...) -end +# @inline function (+)(pnsystem::PNSystem, w, x, y, z...) +# return ⊕(constant_convert(pnsystem, w), x, y, z...) +# end @inline (+)(x::AbstractFloat, y::AbstractFloat) = x ⊕ y -@inline (+)(x::AbstractFloat) = ⊕(x) +@inline (+)(x::AbstractFloat, y::ExactNumber) = x ⊕ y +@inline (+)(x::ExactNumber, y::AbstractFloat) = x ⊕ y +@inline (+)(x::ExactIntegerBased, y::ExactIntegerBased) = x ⊕ y +@inline (+)(x::Number) = x +(+)(x, y, z, args...) = Base.afoldl(+, (x + y) + z, args...) @export @inline (-)(pnsystem::PNSystem, x, y) = ⊖(constant_convert(pnsystem, x), y) @inline (-)(x::AbstractFloat, y::AbstractFloat) = x ⊖ y +@inline (-)(x::AbstractFloat, y::ExactNumber) = x ⊖ y +@inline (-)(x::ExactNumber, y::AbstractFloat) = x ⊖ y +@inline (-)(x::ExactIntegerBased, y::ExactIntegerBased) = x ⊖ y +@inline (-)(x::Number) = ⊖(x) @inline (-)(pnsystem::PNSystem, x) = ⊖(constant_convert(pnsystem, x)) -@inline (-)(x::AbstractFloat) = ⊖(x) @export @inline (*)(pnsystem::PNSystem, x, y) = ⊛(constant_convert(pnsystem, x), y) @inline (*)(pnsystem::PNSystem, x) = constant_convert(pnsystem, x) -@inline function (*)(pnsystem::PNSystem, w, x, y, z...) - return ⊛(constant_convert(pnsystem, w), x, y, z...) -end +# @inline function (*)(pnsystem::PNSystem, w, x, y, z...) +# return ⊛(constant_convert(pnsystem, w), x, y, z...) +# end @inline (*)(x::AbstractFloat, y::AbstractFloat) = x ⊛ y +@inline (*)(x::AbstractFloat, y::ExactNumber) = x ⊛ y +@inline (*)(x::ExactNumber, y::AbstractFloat) = x ⊛ y +@inline (*)(x::ExactIntegerBased, y::ExactIntegerBased) = x ⊛ y +(*)(x, y, z, args...) = Base.afoldl(*, (x * y) * z, args...) @export @inline (/)(pnsystem::PNSystem, x, y) = ⊘(constant_convert(pnsystem, x), y) @inline (/)(x::AbstractFloat, y::AbstractFloat) = x ⊘ y +@inline (/)(x::AbstractFloat, y::ExactNumber) = x ⊘ y +@inline (/)(x::ExactNumber, y::AbstractFloat) = x ⊘ y @export @inline (^)(pnsystem::PNSystem, x, n) = ↑(constant_convert(pnsystem, x), n) -@inline (^)(x::AbstractFloat, n::Integer) = x ↑ n +@inline (^)(x::AbstractFloat, n::ExactNumber) = x ↑ n +@inline (^)(x::ExactNumber, n::AbstractFloat) = x ↑ n @inline (^)(x::AbstractFloat, n::AbstractFloat) = x ↑ n # We can actually use the original definition of `//` for most purposes; we define it @@ -86,7 +102,35 @@ PNBase for NT ∈ (Float16, Float64, BigFloat) pnsystem = BHNS(randn(NT, 15)) - z = (1, 2, 3, 17, 31, ℯ, π, 3//13, 47//59) + Z = (1, 2, 3, 17, 31, 3//13, 47//59) + z = (Z..., ℯ, π) + + for x ∈ z + @test eval(:(Mod.:+))(pnsystem, x) isa NT + @test eval(:(Mod.:+))(pnsystem, x) == NT(x) + @test eval(:(Mod.:+))(x) == x + + @test eval(:(Mod.:-))(pnsystem, x) isa NT + @test eval(:(Mod.:-))(pnsystem, x) == -NT(x) + @test eval(:(Mod.:-))(x) == -x + + @test eval(:(Mod.:*))(pnsystem, x) isa NT + @test eval(:(Mod.:*))(pnsystem, x) == NT(x) + end + + for f ∈ (:+, :*) + op = f===:+ ? sum : prod + for w ∈ Z, x ∈ Z, y ∈ Z + @test eval(:(Mod.$f))(pnsystem, w, x, y) isa NT + @test eval(:(Mod.$f))(pnsystem, w, x, y) == op(NT, (w, x, y)) + @test eval(:(Mod.$f))(w, x, y) === op((w, x, y)) + for v ∈ Z + @test eval(:(Mod.$f))(pnsystem, v, w, x, y) isa NT + @test eval(:(Mod.$f))(pnsystem, v, w, x, y) == op(NT, (v, w, x, y)) + @test eval(:(Mod.$f))(v, w, x, y) === op((v, w, x, y)) + end + end + end for f ∈ (:+, :-, :*, :/, :^, :ln, :√) @test f ∈ pnbase_functions @@ -95,12 +139,32 @@ PNBase for x ∈ z, y ∈ z @test eval(:(Mod.$f))(pnsystem, x, y) isa NT @test eval(:(Mod.$f))(pnsystem, x, y) == eval(:($f))(NT(x), NT(y)) + @test eval(:(Mod.$f))(x, NT(y)) isa NT + @test eval(:(Mod.$f))(x, NT(y)) ≈ eval(:($f))(NT(x), NT(y)) + @test eval(:(Mod.$f))(NT(x), y) isa NT + @test eval(:(Mod.$f))(NT(x), y) ≈ eval(:($f))(NT(x), NT(y)) + @test eval(:(Mod.$f))(NT(x), NT(y)) isa NT + @test eval(:(Mod.$f))(NT(x), NT(y)) == eval(:($f))(NT(x), NT(y)) + end + end + for f ∈ (:+, :-, :*, ://) + for x ∈ Z, y ∈ Z + @test typeof(eval(:(Mod.$f))(x, y)) === typeof(eval(:($f))(x, y)) + @test eval(:(Mod.$f))(x, y) == eval(:($f))(x, y) end end + for x ∈ Z, y ∈ Z + @test typeof(eval(:(Mod.://))(pnsystem, x, y)) === typeof(x // y) + @test eval(:(Mod.://))(pnsystem, x, y) == x // y + end for f ∈ (:ln, :√) for x ∈ z @test eval(:(Mod.$f))(pnsystem, x) isa NT @test eval(:(Mod.$f))(pnsystem, x) == eval(:($f))(NT(x)) + @test eval(:(Mod.$f))(NT(x)) == eval(:($f))(NT(x)) + end + for x ∈ Z + @test_throws MethodError eval(:(Mod.$f))((x)) end end end From be45c9df38cfb11f9ec674959bfe2e6d0f8559b2 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Thu, 10 Jul 2025 14:11:20 -0400 Subject: [PATCH 67/77] Complete test coverage of PNTerm --- src/core/PNTerm.jl | 109 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 90 insertions(+), 19 deletions(-) diff --git a/src/core/PNTerm.jl b/src/core/PNTerm.jl index 667b35b9..e59d7eba 100644 --- a/src/core/PNTerm.jl +++ b/src/core/PNTerm.jl @@ -6,7 +6,7 @@ baremodule PNTerms using Base: Base, @doc, @assert, one, zero, >, ÷, + as ⊕, - as ⊖, * as ⊛, / as ⊘, ^ as ↑ import PostNewtonian: constant_convert -using PostNewtonian: PNSystem +using PostNewtonian: PostNewtonian, PNSystem using PostNewtonian.InlineExports: @export using PostNewtonian.PNBase @@ -75,12 +75,15 @@ Base.eltype(pn::PNTerm{T}) where {T} = T @export c⁻¹exp(pn::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} = c⁻¹Exponent -function constant_convert( +function PostNewtonian.constant_convert( pn::P, term::T ) where {NT,PNOrder,P<:PNSystem{NT,PNOrder},T<:PNTerm{NT,PNOrder}} term end +### NOTE: Adding or subtracting multiple `PNTerm`s will produce a `PNExpansion`, which +### is defined in `PNExpansion.jl`. The following additive functions are just unary. + function Base.sum(term::PNTerm) return term.coeff end @@ -94,7 +97,7 @@ function PNBase.:-(term::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻ end function Base.inv(term::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} - return PNTerm{T,PNOrder}(⊖(c⁻¹exp(term)), Base.inv(term.coeff)) + return PNTerm{T,PNOrder}(⊖(c⁻¹Exponent), Base.inv(term.coeff)) end function PNBase.:√(term::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} @@ -109,14 +112,31 @@ function PNBase.:^( return PNTerm{typeof(coeff),PNOrder}(c⁻¹exp(term) ⊛ n, coeff) end +function PNBase.:*( + term1::PNTerm{T1,PNOrder,c⁻¹E1}, term2::PNTerm{T2,PNOrder,c⁻¹E2} +) where {T1,T2,PNOrder,c⁻¹E1,c⁻¹E2} + c⁻¹Exponent = c⁻¹exp(term1) ⊕ c⁻¹exp(term2) + coeff = term1.coeff ⊛ term2.coeff + return PNTerm{typeof(coeff),PNOrder,c⁻¹Exponent}(coeff) +end + function PNBase.:*( x::Number, term::PNTerm{T,PNOrder,c⁻¹Exponent} ) where {T,PNOrder,c⁻¹Exponent} coeff = x ⊛ term.coeff return PNTerm{typeof(coeff),PNOrder,c⁻¹Exponent}(coeff) end + PNBase.:*(term::PNTerm, x::Number) = PNBase.:*(x, term) +function PNBase.:/( + term1::PNTerm{T1,PNOrder,c⁻¹E1}, term2::PNTerm{T2,PNOrder,c⁻¹E2} +) where {T1,T2,PNOrder,c⁻¹E1,c⁻¹E2} + c⁻¹Exponent = c⁻¹E1 ⊖ c⁻¹E2 + coeff = term1.coeff ⊘ term2.coeff + return PNTerm{typeof(coeff),PNOrder,c⁻¹Exponent}(coeff) +end + function PNBase.:/( term::PNTerm{T,PNOrder,c⁻¹Exponent}, x::Number ) where {T,PNOrder,c⁻¹Exponent} @@ -131,22 +151,6 @@ function PNBase.:/( return PNTerm{typeof(coeff),PNOrder}(⊖(c⁻¹exp(term)), coeff) end -function PNBase.:*( - term1::PNTerm{T1,PNOrder,c⁻¹E1}, term2::PNTerm{T2,PNOrder,c⁻¹E2} -) where {T1,T2,PNOrder,c⁻¹E1,c⁻¹E2} - c⁻¹Exponent = c⁻¹exp(term1) ⊕ c⁻¹exp(term2) - coeff = term1.coeff ⊛ term2.coeff - return PNTerm{typeof(coeff),PNOrder,c⁻¹Exponent}(coeff) -end - -function PNBase.:/( - term1::PNTerm{T1,PNOrder,c⁻¹E1}, term2::PNTerm{T2,PNOrder,c⁻¹E2} -) where {T1,T2,PNOrder,c⁻¹E1,c⁻¹E2} - c⁻¹Exponent = c⁻¹E1 ⊖ c⁻¹E2 - coeff = term1.coeff ⊘ term2.coeff - return PNTerm{typeof(coeff),PNOrder,c⁻¹Exponent}(coeff) -end - end # baremodule PNTerms @testitem "PNTerm algebra" begin @@ -235,4 +239,71 @@ end # baremodule PNTerms @test_throws ArgumentError y / c + x * c^2 @test_throws ArgumentError x * c^2 + (y / c + z / c^2) end + + pn = BBH(randn(14), 2//1) + + # constant_convert should just return the term + t0 = PNTerm{Float64,2//1}(0, 5.0) + @test constant_convert(pn, t0) === t0 + + # Base.sum + @test sum(t0) == 5.0 + + # unary + (PNBase.+) + t1 = PNTerm{Float64,2}(1, 2.5) + @test +t1 === t1 + + # unary - (PNBase.-) + t1n = -t1 + @test c⁻¹exp(t1n) == c⁻¹exp(t1) + @test t1n.coeff == -2.5 + + # inv + ti = inv(t1) + @test c⁻¹exp(ti) == -c⁻¹exp(t1) + @test ti.coeff == 1/2.5 + + # sqrt on even exponent + t2 = PNTerm{Float64,2}(2, 4.0) + ts = √(t2) + @test c⁻¹exp(ts) == 1 + @test ts.coeff == 2.0 + + # sqrt on odd exponent should error + t3 = PNTerm{Float64,2}(1, 9.0) + @test_throws AssertionError √(t3) + + # power + tp = t1 ^ 3 + @test c⁻¹exp(tp) == 3*c⁻¹exp(t1) + @test tp.coeff == 2.5^3 + + # multiply term * term + ta = PNTerm{Float64,2}(1, 2.0) + tb = PNTerm{Float64,2}(2, 3.0) + tab = ta * tb + @test c⁻¹exp(tab) == 3 + @test tab.coeff == 6.0 + + # scalar * term and term * scalar + ts1 = 4.0 * ta + ts2 = ta * 4.0 + @test c⁻¹exp(ts1) == c⁻¹exp(ta) + @test ts1.coeff == 8.0 + @test ts2.coeff == 8.0 + + # term / term + td = tb / ta + @test c⁻¹exp(td) == 1 # 2 - 1 + @test td.coeff == 1.5 + + # term / scalar + td1 = tb / 2.0 + @test c⁻¹exp(td1) == c⁻¹exp(tb) + @test td1.coeff == 1.5 + + # scalar / term + td2 = 12.0 / ta + @test c⁻¹exp(td2) == -1 # -c⁻¹exp(ta) + @test td2.coeff == 6.0 end From db2a1aa2b0703e0bccc23820dc4f7f5426327fd8 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Fri, 11 Jul 2025 22:43:12 -0400 Subject: [PATCH 68/77] Organize methods --- src/core/PNExpansion.jl | 135 ++++++++++++++++++++-------------------- 1 file changed, 68 insertions(+), 67 deletions(-) diff --git a/src/core/PNExpansion.jl b/src/core/PNExpansion.jl index 6dbd9fe8..38d3e0f3 100644 --- a/src/core/PNExpansion.jl +++ b/src/core/PNExpansion.jl @@ -78,6 +78,9 @@ elements are currently in the coefficients, but is required to be 1 ≤ N ≤ NM end end +Base.Tuple(pn::PNExpansion) = pn.coeffs +SVector(pn::PNExpansion) = SVector(pn.coeffs) + PostNewtonian.pn_order(::PNExpansion{N,T,NMax}) where {N,T,NMax} = (NMax ⊖ 1)//2 Base.getindex(pn::PNExpansion, i::Int) = pn.coeffs[i] @@ -85,24 +88,15 @@ Base.length(pn::PNExpansion) = length(pn.coeffs) Base.eltype(pn::PNExpansion) = eltype(pn.coeffs) function Base.sum(pn_expansion::PNExpansion{N,T,NMax}) where {N,T,NMax} - return Base.sum(pn_expansion[i] for i ∈ 1:N; init=zero(T)) + return Base.sum(pn_expansion.coeffs; init=zero(T)) end function PNBase.:+(pn::PNExpansion{N,T1,NMax}, x::T2) where {N,T1,NMax,T2<:Number} T3 = promote_type(T1, T2) return PNExpansion(ntuple(i -> i == 1 ? pn[1] + x : T3(pn[i]), Val(N)), NMax) end -PNBase.:+(x::T, pn::PNExpansion) where {T<:Number} = PNBase.:+(pn, x) - -function PNBase.:-(pn::PNExpansion{N,T,NMax}) where {N,T,NMax} - return PNExpansion{N,T,NMax}((-).(pn.coeffs)) -end -function PNBase.:*(pn::PNExpansion{N,T1,NMax}, x::T2) where {N,T1,NMax,T2<:Number} - T3 = promote_type(T1, T2) - return PNExpansion{N,T3,NMax}(@. T3(pn.coeffs * x)) -end -PNBase.:*(x::T, pn::PNExpansion) where {T<:Number} = PNBase.:*(pn, x) +PNBase.:+(x::T, pn::PNExpansion) where {T<:Number} = PNBase.:+(pn, x) function PNBase.:+( pn1::PNExpansion{N1,T1,NMax1}, pn2::PNExpansion{N2,T2,NMax2} @@ -136,56 +130,6 @@ function sum_term( end end -function PNBase.:*( - pn1::PNExpansion{N1,T1,NMax1}, pn2::PNExpansion{N2,T2,NMax2} -) where {N1,N2,T1,T2,NMax1,NMax2} - throw( - ArgumentError( - "`PNExpansion` multiplication is only defined for objects of the same PN order." ⊛ - "\nGot NMax1=$(NMax1) and NMax2=$(NMax2).", - ), - ) -end - -function PNBase.:*( - pn1::PNExpansion{N1,T1,NMax}, pn2::PNExpansion{N2,T2,NMax} -) where {N1,N2,T1,T2,NMax} - if N1 > N2 - return PNBase.:*(pn2, pn1) - else - N3 = min(N1 + N2 - 1, NMax) - PNExpansion(ntuple(i -> product_term(i, pn1, pn2), Val(N3)), NMax) - end -end - -function product_term( - i, pn1::PNExpansion{N1,T1,NMax}, pn2::PNExpansion{N2,T2,NMax} -) where {N1,N2,T1,T2,NMax} - T3 = promote_type(T1, T2) - return Base.sum( - pn1.coeffs[j] ⊛ pn2.coeffs[i - j + 1] for j ∈ max(1, i - N2 + 1):min(i, N1); - init=zero(T3), - ) -end - -PNBase.:/(p::PNExpansion, x::Number) = p * (1 / x) - -# Note that an PNExpansion is really a *relative* expansion in `1/c` — *not* `v` or `x`. -# Therefore, the correct derivative with respect to any variable (other than `c`, which we -# never differentiate with respect to) extends to just derivatives of the coefficients, -# without any change to the exponent of `1/c`. -function FastDifferentiation.derivative( - pn_expansion::PNExpansion{N,T,NMax}, fd_node::FastDifferentiation.Node -) where {N,T,NMax} - @assert fd_node.node_value ≠ :c "Can't differentiate `PNExpansion` with respect to `c`." - return PNExpansion( - ntuple(i -> FastDifferentiation.derivative(pn_expansion[i], fd_node), Val(N)), NMax - ) -end - -Base.Tuple(pn::PNExpansion) = pn.coeffs -SVector(pn::PNExpansion) = SVector(pn.coeffs) - function PNBase.:+( x::T1, term::PNTerm{T2,PNOrder,c⁻¹Exponent} ) where {T1<:Number,T2,PNOrder,c⁻¹Exponent} @@ -210,6 +154,7 @@ function PNBase.:+( end return PNExpansion{N,T,NMax}(Tuple(coeffs)) end + PNBase.:+(term::PNTerm, x::Number) = PNBase.:+(x, term) function PNBase.:+( @@ -249,8 +194,6 @@ function PNBase.:+( return PNExpansion{N,T,NMax}(Tuple(coeffs)) end -PNBase.:-(term1::PNTerm, term2::PNTerm) = PNBase.:+(term1, -term2) - function PNBase.:+( term::PNTerm{T1,PNOrder,c⁻¹E1}, expansion::PNExpansion{N2,T2,NMax2} ) where {T1,PNOrder,c⁻¹E1,N2,T2,NMax2} @@ -280,8 +223,10 @@ function PNBase.:+( end return PNExpansion{N,T,NMax}(Tuple(coeffs)) end + PNBase.:+(expansion::PNExpansion, term::PNTerm) = PNBase.:+(term, expansion) +PNBase.:-(term1::PNTerm, term2::PNTerm) = PNBase.:+(term1, -term2) PNBase.:-(term::PNTerm, x::Number) = PNBase.:+(term, -x) PNBase.:-(x::Number, term::PNTerm) = PNBase.:+(x, -term) PNBase.:-(term::PNTerm, expansion::PNExpansion) = PNBase.:+(term, -expansion) @@ -289,6 +234,49 @@ PNBase.:-(expansion::PNExpansion, term::PNTerm) = PNBase.:+(expansion, -term) PNBase.:-(x::Number, expansion::PNExpansion) = PNBase.:+(x, -expansion) PNBase.:-(expansion::PNExpansion, x::Number) = PNBase.:+(expansion, -x) +function PNBase.:-(pn::PNExpansion{N,T,NMax}) where {N,T,NMax} + return PNExpansion{N,T,NMax}((-).(pn.coeffs)) +end + +function PNBase.:*(pn::PNExpansion{N,T1,NMax}, x::T2) where {N,T1,NMax,T2<:Number} + T3 = promote_type(T1, T2) + return PNExpansion{N,T3,NMax}(@. T3(pn.coeffs * x)) +end + +PNBase.:*(x::T, pn::PNExpansion) where {T<:Number} = PNBase.:*(pn, x) + +function PNBase.:*( + pn1::PNExpansion{N1,T1,NMax1}, pn2::PNExpansion{N2,T2,NMax2} +) where {N1,N2,T1,T2,NMax1,NMax2} + throw( + ArgumentError( + "`PNExpansion` multiplication is only defined for objects of the same PN order." ⊛ + "\nGot NMax1=$(NMax1) and NMax2=$(NMax2).", + ), + ) +end + +function PNBase.:*( + pn1::PNExpansion{N1,T1,NMax}, pn2::PNExpansion{N2,T2,NMax} +) where {N1,N2,T1,T2,NMax} + if N1 > N2 + return PNBase.:*(pn2, pn1) + else + N3 = min(N1 + N2 - 1, NMax) + PNExpansion(ntuple(i -> product_term(i, pn1, pn2), Val(N3)), NMax) + end +end + +function product_term( + i, pn1::PNExpansion{N1,T1,NMax}, pn2::PNExpansion{N2,T2,NMax} +) where {N1,N2,T1,T2,NMax} + T3 = promote_type(T1, T2) + return Base.sum( + pn1.coeffs[j] ⊛ pn2.coeffs[i - j + 1] for j ∈ max(1, i - N2 + 1):min(i, N1); + init=zero(T3), + ) +end + function PNBase.:*( expansion::PNExpansion{N1,T1,NMax1}, term::PNTerm{T2,PNOrder,c⁻¹E2} ) where {N1,T1,NMax1,T2,PNOrder,c⁻¹E2} @@ -313,21 +301,34 @@ function PNBase.:*( T = promote_type(T1, T2) coeffs = _efficient_vector(Val(N), Val(T)) coeffs .= zero(T) - @inbounds for i ∈ max(1, 1 - ΔN):min(N1, N - ΔN) - coeffs[i + ΔN] = expansion[i] ⊛ term.coeff + @inbounds for i ∈ max(1, 1 ⊖ ΔN):min(N1, N ⊖ ΔN) + coeffs[i ⊕ ΔN] = expansion[i] ⊛ term.coeff end return PNExpansion{N,T,NMax}(Tuple(coeffs)) end + PNBase.:*(term::PNTerm, expansion::PNExpansion) = PNBase.:*(expansion, term) # (a, b, c, d, e, f, g) * (c⁻¹^2) = (0, 0, a, b, c, d, e) -PNBase.:/(expansion::PNExpansion, term::PNTerm) = PNBase.:*(expansion, inv(term)) +PNBase.:/(expansion::PNExpansion, x) = PNBase.:*(expansion, Base.inv(x)) + +# Note that an PNExpansion is really a *relative* expansion in `1/c` — *not* `v` or `x`. +# Therefore, the correct derivative with respect to any variable (other than `c`, which we +# never differentiate with respect to) extends to just derivatives of the coefficients, +# without any change to the exponent of `1/c`. +function FastDifferentiation.derivative( + pn_expansion::PNExpansion{N,T,NMax}, fd_node::FastDifferentiation.Node +) where {N,T,NMax} + return PNExpansion( + ntuple(i -> FastDifferentiation.derivative(pn_expansion[i], fd_node), Val(N)), NMax + ) +end end # baremodule PNExpansions @testitem "PNExpansion algebra" begin using Symbolics: @variables, simplify, substitute - using PostNewtonian: PNExpansion + using PostNewtonian.PNExpansions: PNExpansion for N1 ∈ 1:9 for N2 ∈ 1:9 From 14e7cce6e3ea3db99a01e876b6199041297aecaa Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Fri, 11 Jul 2025 23:18:48 -0400 Subject: [PATCH 69/77] Increase coverage --- src/core/utilities/InlineExports.jl | 38 +++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/core/utilities/InlineExports.jl b/src/core/utilities/InlineExports.jl index 1705a3eb..ffc25c91 100644 --- a/src/core/utilities/InlineExports.jl +++ b/src/core/utilities/InlineExports.jl @@ -149,6 +149,30 @@ end # module InlineExports module Bla using PostNewtonian.InlineExports: @export, @public + @export macro garble(expr::Expr) + return esc(expr) + end + + @public macro gorble(expr::Expr) + return esc(expr) + end + + @export abstract type Flork{T} end + @public abstract type Flark{T} end + + @export m::Int = 3 + @public n::Int = 4 + + @export @eval :evala + @public @eval :evalb + + @export function funca(x) + x^2 + end + @public function funcb(x) + x^3 + end + @export begin "`const a` doc" const a = 2 @@ -204,6 +228,20 @@ end # module InlineExports using .Bla + @test (@macroexpand @garble x=3) == :(x=3) + @test (@macroexpand Bla.@gorble y=4) == :(y=4) + + @test m isa Int + @test m == 3 + @test Bla.n isa Int + @test Bla.n == 4 + + # @test hasproperty(Bla, :evala) + # @test hasproperty(Bla, :evalb) + + @test funca(3) == 9 + @test Bla.funcb(3) == 27 + @test f(T(a)) == 4 @test Bla.g(Bla.V(Bla.b)) == 27 @test Bla.h(Bla.X(Bla.c)) == 3125 From d019347032e94784345e8113af41b9b4a59e2496 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Fri, 11 Jul 2025 23:21:09 -0400 Subject: [PATCH 70/77] Update @pn_reference --- src/core/pn_reference.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/pn_reference.jl b/src/core/pn_reference.jl index 3628ad1a..a2ec6598 100644 --- a/src/core/pn_reference.jl +++ b/src/core/pn_reference.jl @@ -169,7 +169,7 @@ end output = quote baremodule Einstein1918 - using Base: Base, Val, π + using Base: Base, Val, π, @raw_str, @doc eval(x::Expr) = Core.eval(Einstein1918, x) include(p::AbstractString) = Base.include(Einstein1918, p) using PostNewtonian: @pn_expression, @pn_expansion, PNExpansionParameter, 𝒾, γₑ, ζ3 @@ -190,5 +190,6 @@ end end end - @test MacroTools.striplines(input).args[1] == MacroTools.striplines(output).args[1] + @test MacroTools.striplines(input).args[1].args[2].args[2] == + MacroTools.striplines(output).args[1] end From a61859d8b6bb6bc8bf32e4f8a638e2d60f057bbb Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Fri, 11 Jul 2025 23:22:13 -0400 Subject: [PATCH 71/77] Complete tests; increase coverage --- src/core/PNExpansion.jl | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/core/PNExpansion.jl b/src/core/PNExpansion.jl index 38d3e0f3..805c4c0d 100644 --- a/src/core/PNExpansion.jl +++ b/src/core/PNExpansion.jl @@ -84,8 +84,8 @@ SVector(pn::PNExpansion) = SVector(pn.coeffs) PostNewtonian.pn_order(::PNExpansion{N,T,NMax}) where {N,T,NMax} = (NMax ⊖ 1)//2 Base.getindex(pn::PNExpansion, i::Int) = pn.coeffs[i] -Base.length(pn::PNExpansion) = length(pn.coeffs) -Base.eltype(pn::PNExpansion) = eltype(pn.coeffs) +Base.length(pn::PNExpansion) = Base.length(pn.coeffs) +Base.eltype(pn::PNExpansion) = Base.eltype(pn.coeffs) function Base.sum(pn_expansion::PNExpansion{N,T,NMax}) where {N,T,NMax} return Base.sum(pn_expansion.coeffs; init=zero(T)) @@ -124,7 +124,7 @@ function sum_term( ) where {N1,N2,T1,T2,NMax} T3 = promote_type(T1, T2) if i ≤ N1 - return T3(pn1.coeffs[i] + pn2.coeffs[i]) + return T3(pn1.coeffs[i] ⊕ pn2.coeffs[i]) else return T3(pn2.coeffs[i]) end @@ -328,37 +328,39 @@ end # baremodule PNExpansions @testitem "PNExpansion algebra" begin using Symbolics: @variables, simplify, substitute + using Base: Base, one, zero, <, ÷, + as ⊕, - as ⊖, * as ⊛, / as ⊘, ^ as ↑ + using PostNewtonian.PNBase: ln, (√), (+), (-), (*), (/), (//), (^) using PostNewtonian.PNExpansions: PNExpansion for N1 ∈ 1:9 for N2 ∈ 1:9 - for NMax ∈ max(N1, N2):(N1 + N2 + 3) + for NMax ∈ max(N1, N2):(N1 ⊕ N2 ⊕ 3) @variables c⁻¹ x[1:N1] y[1:N2] z - poly(e::PNExpansion) = sum(e[i] ⊛ c⁻¹^(i - 1) for i ∈ 1:length(e)) + poly(e::PNExpansion) = sum(e[i] ⊛ c⁻¹↑(i ⊖ 1) for i ∈ 1:length(e)) eˣ = PNExpansion(tuple(x...), NMax) eʸ = PNExpansion(tuple(y...), NMax) # Test sums polysum = simplify(poly(eˣ + eʸ); expand=true) - sumpoly = simplify(poly(eˣ) + poly(eʸ); expand=true) - Δ = simplify(polysum - sumpoly; expand=true) + sumpoly = simplify(poly(eˣ) ⊕ poly(eʸ); expand=true) + Δ = simplify(polysum ⊖ sumpoly; expand=true) @test iszero(Δ) - @test_throws ArgumentError eˣ + PNExpansion(tuple(z, x...), NMax + 1) - @test_throws ArgumentError PNExpansion(tuple(z, x...), NMax + 1) + eˣ + @test_throws ArgumentError eˣ + PNExpansion(tuple(z, x...), NMax ⊕ 1) + @test_throws ArgumentError PNExpansion(tuple(z, x...), NMax ⊕ 1) + eˣ # Test products - polyprod = simplify(poly(eˣ ⊛ eʸ); expand=true) + polyprod = simplify(poly(eˣ * eʸ); expand=true) prodpoly = simplify( substitute( simplify(poly(eˣ) ⊛ poly(eʸ); expand=true), - Dict([c⁻¹^n => 0 for n ∈ NMax:(2NMax + 3)]), + Dict([c⁻¹↑n => 0 for n ∈ NMax:(2NMax ⊕ 3)]), ); expand=true, ) - Δ = simplify(polyprod - prodpoly; expand=true) + Δ = simplify(polyprod ⊖ prodpoly; expand=true) @test iszero(Δ) - @test_throws ArgumentError eˣ ⊛ PNExpansion(tuple(z, x...), NMax + 1) - @test_throws ArgumentError PNExpansion(tuple(z, x...), NMax + 1) ⊛ eˣ + @test_throws ArgumentError eˣ * PNExpansion(tuple(z, x...), NMax ⊕ 1) + @test_throws ArgumentError PNExpansion(tuple(z, x...), NMax ⊕ 1) * eˣ end end end From 2da223762e8b2e12460ee6e6b04ed0c0933d4b05 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Fri, 11 Jul 2025 23:22:59 -0400 Subject: [PATCH 72/77] Complete tests; increase coverage --- src/core/utilities/truncated_series_monoid.jl | 70 ++++++++++++++++--- 1 file changed, 60 insertions(+), 10 deletions(-) diff --git a/src/core/utilities/truncated_series_monoid.jl b/src/core/utilities/truncated_series_monoid.jl index 315c7c95..904d4f48 100644 --- a/src/core/utilities/truncated_series_monoid.jl +++ b/src/core/utilities/truncated_series_monoid.jl @@ -122,9 +122,7 @@ function truncated_series_product(a::NTuple{NT,T}, b, v) where {NT,T} end AB = b[begin + N] * a[begin + 0] for n ∈ (N - 1):-1:0 - AB = muladd( - v, AB, b[begin + n] * evalpoly(v, @view a[(begin + 0):(begin + (N - n))]) - ) + AB = muladd(v, AB, b[begin + n] * evalpoly(v, a[(begin + 0):(begin + (N - n))])) end return AB end @@ -164,14 +162,29 @@ This is different from `truncated_series_ratio(a, b, 1)` in that `a` and `b` may different lengths, and it should be somewhat more efficient. """ function truncated_series_ratio(a::NTuple{N1,T1}, b::NTuple{N2,T2}) where {N1,N2,T1,T2} - N = max(N1, N2) - T = promote_type(T1, T2) + if N1 == 0 && N2 == 0 + throw( + ArgumentError( + "In `truncated_series_ratio(a,b)`, both arguments have length 0; " * + "this is not allowed.", + ), + ) + end if N2 == 0 - throw(DomainError("In truncated_series_ratio(a,b): b must have at least one term")) - elseif N1 == 0 - return zero(T) + throw( + ArgumentError( + "In `truncated_series_ratio(a,b)`, argument `b` has length 0; " * + "this is not allowed.", + ), + ) + end + if N1 == 0 + return zero(T2) end + N = max(N1, N2) + T = promote_type(T1, T2) + # The uppercase `N`s represent the number of terms in the tuples, while the lowercase # `n`s represent the highest index of the terms. n1, n2, n = N1 - 1, N2 - 1, N - 1 @@ -205,13 +218,50 @@ end using Random using DoubleFloats using StaticArrays: SVector - import PostNewtonian: truncated_series_inverse, truncated_series_ratio + import PostNewtonian: + truncated_series_inverse, truncated_series_ratio, truncated_series_product Random.seed!(123) + + # truncated_series_product tests + # N < 0, empty vectors => always zero + @test truncated_series_product((), (), 3.14) == 0.0 + @test truncated_series_product(Float64[], Float64[], 3.14) == 0.0 + + # N = 0, single‐term series => a0*b0 + a0, b0 = 2.5, -1.5 + @test truncated_series_product([a0], [b0], 42) == a0 * b0 + + # N = 1, two‐term series with vector inputs + a = [1.0, 2.0] # 1 + 2v + b = [3.0, 4.0] # 3 + 4v + v = 5.0 + # (1 + 2v)*(3 + 4v) = 3 + (6+4)v + ··, truncate ⇒ 3 +10⋅5 = 53 + @test truncated_series_product(a, b, v) == 53.0 + + # same with tuple dispatch + aτ = (1, 2) + bτ = (3, 4) + @test truncated_series_product(aτ, bτ, v) == 53 + + # truncated_series_ratio edge cases + # N2 == 0 (empty b) must throw ArgumentError + a = (1.0, 2.0, 3.0) + b_empty = () + @test_throws ArgumentError truncated_series_ratio(a, b_empty) + a_empty = () + @test_throws ArgumentError truncated_series_ratio(a_empty, b_empty) + + # N1 == 0 (empty a) returns zero of promoted type + a_empty = () + b = (5, -2) + @test truncated_series_ratio(a_empty, b) === + zero(promote_type(eltype(a_empty), eltype(b))) + for T ∈ [Float32, Float64, Double64] for N ∈ 1:15 A = rand(T, N) A[1] = one(T) + rand(T) / 100 - a = Tuple(A) + local a = Tuple(A) x = rand(T) ϵ = eps(sum(a) * N) for N1 ∈ 1:N From de652f9af2cc82da5d8b7e0d6cae40719b32c6da Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Sat, 12 Jul 2025 14:56:05 -0400 Subject: [PATCH 73/77] Add some tests --- src/core/utilities/misc.jl | 57 +++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/src/core/utilities/misc.jl b/src/core/utilities/misc.jl index be8a532c..b0338bf3 100644 --- a/src/core/utilities/misc.jl +++ b/src/core/utilities/misc.jl @@ -52,7 +52,7 @@ julia> n=10_000_000; sum(1 ./ (1:n))-log(n) Return `x` or the value wrapped by the `Dual` number `x` """ -@public value(x::T) where {T} = hasproperty(x, :value) ? getproperty(x, :value) : x +@public value(x::T) where {T} = hasfield(T, :value) ? getfield(x, :value) : x """ find_symbols_of_type(mod, T) @@ -123,3 +123,58 @@ function apply_to_first_add!(expr, func) end end end + +@testitem "core.misc" begin + using DoubleFloats + using ForwardDiff: Dual + using PostNewtonian: ζ3, γₑ, value, find_symbols_of_type + using PostNewtonian: + iscall, isadd, ismul, flatten_add!, flatten_mul!, apply_to_first_add! + + # ζ3 formula + @test ζ3 ≈ sum((1:10_000_000) .^ -3) + + # γₑ formula + N = 10_000_000 + δγₑ = 1/2N + @test γₑ ≈ sum(1 ./ (1:N)) - log(N) - δγₑ + + # value + struct Dummy + value + end + struct Dummier + valuet + end + for T ∈ (Float16, Float32, Float64, Double16, Double32, Double64, BigFloat) + x = T(big"1.2") + @test value(x) isa T + @test value(x) == x + d = Dummy(x) + @test value(d) isa T + @test value(d) == x + dr = Dummier(x) + @test value(dr) isa Dummier + @test value(dr) == dr + v = (T(big"3.4"), T(big"5.6"), T(big"7.8")) + for i ∈ eachindex(v) + ẋ = Dual{:Taggo}(x, v[begin:i]) + @test value(ẋ) isa T + @test value(ẋ) == x + end + end + + # iscall and friends + @test isadd(:(1 + 2)) + @test isadd(:(1 + 2c + 3c^2)) + @test isadd(:(1 + (2n + 3m))) + @test isadd(Expr(:call, :+, 1, 2)) + @test isadd(Expr(:call, (+), 1, 2)) + @test !isadd(:(1 * 2)) + @test ismul(:(1 * 2)) + @test ismul(:(1 * 2c * 3c^2)) + @test ismul(:(1 * (2c * 3d))) + @test ismul(Expr(:call, :*, 1, 2)) + @test ismul(Expr(:call, (*), 1, 2)) + @test !ismul(:(1 + 2)) +end From d3c1090e6616b983e478023299c00017976aba3b Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Sat, 12 Jul 2025 14:57:46 -0400 Subject: [PATCH 74/77] Move functions that are not used outside of Symbolics extension to the extension --- ext/PostNewtonianSymbolicsExt.jl | 65 ++++++++++++++++++++++++++++++-- src/core/utilities/misc.jl | 61 ------------------------------ 2 files changed, 61 insertions(+), 65 deletions(-) diff --git a/ext/PostNewtonianSymbolicsExt.jl b/ext/PostNewtonianSymbolicsExt.jl index 9169cee2..e88d03cf 100644 --- a/ext/PostNewtonianSymbolicsExt.jl +++ b/ext/PostNewtonianSymbolicsExt.jl @@ -20,9 +20,6 @@ import PostNewtonian: prepare_pn_order, order_index, 𝓔′, - apply_to_first_add!, - flatten_add!, - flatten_mul!, pn_expression, pn_expansion, @pn_expansion, @@ -47,7 +44,6 @@ import PostNewtonian: ζ3, γₑ, _efficient_vector -#apply_to_first_add!, flatten_add!, pn_expression, using RuntimeGeneratedFunctions: init, @RuntimeGeneratedFunction init(@__MODULE__) @@ -56,6 +52,67 @@ function _efficient_vector(N, ::Type{Symbolics.Num}) return Symbolics.variables(string(gensym()), 1:N) end +### Moved from src/core/utilities/misc.jl + +""" + iscall(x, symbols) + +Return `true` if the `Expr` `x` is a call to any element of `symbols`. +""" +iscall(x, symbols) = MacroTools.isexpr(x, :call) && x.args[1] ∈ symbols + +""" + isadd(x) + +Return `true` if the `Expr` `x` is a call to `(+)` or `:+`. +""" +isadd(x) = iscall(x, ((+), :+)) + +""" + ismul(x) + +Return `true` if the `Expr` `x` is a call to `(*)` or `:*`. +""" +ismul(x) = iscall(x, ((*), :*)) + +""" + flatten_binary!(expr, symbols) + +Flatten nested binary operations — that is, apply associativity repeatedly. +""" +function flatten_binary!(expr, symbols) + while iscall(expr, symbols) && any(x -> iscall(x, symbols), expr.args[2:end]) + args = expr.args[2:end] + i₊ = findfirst(x -> iscall(x, symbols), args) + args′ = [first(symbols); args[1:(i₊ - 1)]; args[i₊].args[2:end]; args[(i₊ + 1):end]] + expr.args[:] = args′[1:length(expr.args)] + append!(expr.args, args′[(1 + length(expr.args)):end]) + end + return expr +end + +flatten_add!(expr) = flatten_binary!(expr, ((+), :+)) +flatten_mul!(expr) = flatten_binary!(expr, ((*), :*)) + +""" + apply_to_first_add!(expr, func) + +Apply `func` to the first sub-expression found in a "prewalk"-traversal of `expr` that +satisfies [`isadd`](@ref). If `func` acts in place, so does this function. In either case, +the expression should be returned. +""" +function apply_to_first_add!(expr, func) + found_add = false + MacroTools.prewalk(expr) do x + if !found_add && isadd(x) + found_add = true + func(x) + else + x + end + end +end + ### Moved from src/utilities/macros.jl hold(x) = x diff --git a/src/core/utilities/misc.jl b/src/core/utilities/misc.jl index b0338bf3..e08dc4d7 100644 --- a/src/core/utilities/misc.jl +++ b/src/core/utilities/misc.jl @@ -65,71 +65,10 @@ function find_symbols_of_type(mod, T) return filter(n -> getproperty(mod, n) isa T, names(mod)) end -""" - iscall(x, symbols) - -Return `true` if the `Expr` `x` is a call to any element of `symbols`. -""" -iscall(x, symbols) = MacroTools.isexpr(x, :call) && x.args[1] ∈ symbols - -""" - isadd(x) - -Return `true` if the `Expr` `x` is a call to `(+)` or `:+`. -""" -isadd(x) = iscall(x, ((+), :+)) - -""" - ismul(x) - -Return `true` if the `Expr` `x` is a call to `(*)` or `:*`. -""" -ismul(x) = iscall(x, ((*), :*)) - -""" - flatten_binary!(expr, symbols) - -Flatten nested binary operations — that is, apply associativity repeatedly. -""" -function flatten_binary!(expr, symbols) - while iscall(expr, symbols) && any(x -> iscall(x, symbols), expr.args[2:end]) - args = expr.args[2:end] - i₊ = findfirst(x -> iscall(x, symbols), args) - args′ = [first(symbols); args[1:(i₊ - 1)]; args[i₊].args[2:end]; args[(i₊ + 1):end]] - expr.args[:] = args′[1:length(expr.args)] - append!(expr.args, args′[(1 + length(expr.args)):end]) - end - return expr -end - -flatten_add!(expr) = flatten_binary!(expr, ((+), :+)) -flatten_mul!(expr) = flatten_binary!(expr, ((*), :*)) - -""" - apply_to_first_add!(expr, func) - -Apply `func` to the first sub-expression found in a "prewalk"-traversal of `expr` that -satisfies [`isadd`](@ref). If `func` acts in place, so does this function. In either case, -the expression should be returned. -""" -function apply_to_first_add!(expr, func) - found_add = false - MacroTools.prewalk(expr) do x - if !found_add && isadd(x) - found_add = true - func(x) - else - x - end - end -end - @testitem "core.misc" begin using DoubleFloats using ForwardDiff: Dual using PostNewtonian: ζ3, γₑ, value, find_symbols_of_type - using PostNewtonian: - iscall, isadd, ismul, flatten_add!, flatten_mul!, apply_to_first_add! # ζ3 formula @test ζ3 ≈ sum((1:10_000_000) .^ -3) From 94ad8d41f41b98a299deb6788bfcc7781ab21d14 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Sat, 12 Jul 2025 22:23:27 -0400 Subject: [PATCH 75/77] Move some functions back to core because they are actually used --- ext/PostNewtonianSymbolicsExt.jl | 23 ++------------ src/core/utilities/misc.jl | 31 ++++++++++++++----- src/core/utilities/truncated_series_monoid.jl | 6 ++-- 3 files changed, 27 insertions(+), 33 deletions(-) diff --git a/ext/PostNewtonianSymbolicsExt.jl b/ext/PostNewtonianSymbolicsExt.jl index e88d03cf..8785d74b 100644 --- a/ext/PostNewtonianSymbolicsExt.jl +++ b/ext/PostNewtonianSymbolicsExt.jl @@ -19,6 +19,8 @@ import PostNewtonian: causes_domain_error!, prepare_pn_order, order_index, + iscall, + isadd, 𝓔′, pn_expression, pn_expansion, @@ -54,27 +56,6 @@ end ### Moved from src/core/utilities/misc.jl -""" - iscall(x, symbols) - -Return `true` if the `Expr` `x` is a call to any element of `symbols`. -""" -iscall(x, symbols) = MacroTools.isexpr(x, :call) && x.args[1] ∈ symbols - -""" - isadd(x) - -Return `true` if the `Expr` `x` is a call to `(+)` or `:+`. -""" -isadd(x) = iscall(x, ((+), :+)) - -""" - ismul(x) - -Return `true` if the `Expr` `x` is a call to `(*)` or `:*`. -""" -ismul(x) = iscall(x, ((*), :*)) - """ flatten_binary!(expr, symbols) diff --git a/src/core/utilities/misc.jl b/src/core/utilities/misc.jl index e08dc4d7..189ba40a 100644 --- a/src/core/utilities/misc.jl +++ b/src/core/utilities/misc.jl @@ -1,3 +1,4 @@ +# Declared public in src/PostNewtonian.jl @irrational ζ3 1.2020569031595942 big"1.20205690315959428539973816151144999076498629234049888179227155534183820578631309018645587360933525814619915" """ ζ3 @@ -20,6 +21,7 @@ julia> sum((1:10_000_000).^-3) """ ζ3 # We document it this way because `@irrational` cannot handle docstrings. +# Declared public in src/PostNewtonian.jl @doc raw""" γₑ @@ -55,20 +57,30 @@ Return `x` or the value wrapped by the `Dual` number `x` @public value(x::T) where {T} = hasfield(T, :value) ? getfield(x, :value) : x """ - find_symbols_of_type(mod, T) + iscall(x, symbols) -Given a module `mod` (not just its name, but the actual imported module), find all objects -inside that module that are instances of the given type `T`. The returned quantity is a -vector of `Symbol`s. +Return `true` if the `Expr` `x` is a call to any element of `symbols`. """ -function find_symbols_of_type(mod, T) - return filter(n -> getproperty(mod, n) isa T, names(mod)) -end +iscall(x, symbols) = MacroTools.isexpr(x, :call) && x.args[1] ∈ symbols + +""" + isadd(x) + +Return `true` if the `Expr` `x` is a call to `(+)` or `:+`. +""" +isadd(x) = iscall(x, ((+), :+)) + +""" + ismul(x) + +Return `true` if the `Expr` `x` is a call to `(*)` or `:*`. +""" +ismul(x) = iscall(x, ((*), :*)) @testitem "core.misc" begin using DoubleFloats using ForwardDiff: Dual - using PostNewtonian: ζ3, γₑ, value, find_symbols_of_type + using PostNewtonian: ζ3, γₑ, value, iscall, isadd, ismul # ζ3 formula @test ζ3 ≈ sum((1:10_000_000) .^ -3) @@ -104,6 +116,9 @@ end end # iscall and friends + @test iscall(:(log(1.2)), (:log,)) + @test iscall(:(log(1.2)), (:log, :sin)) + @test !iscall(:(log(1.2)), (:sin,)) @test isadd(:(1 + 2)) @test isadd(:(1 + 2c + 3c^2)) @test isadd(:(1 + (2n + 3m))) diff --git a/src/core/utilities/truncated_series_monoid.jl b/src/core/utilities/truncated_series_monoid.jl index 904d4f48..3ac076e2 100644 --- a/src/core/utilities/truncated_series_monoid.jl +++ b/src/core/utilities/truncated_series_monoid.jl @@ -239,16 +239,14 @@ end @test truncated_series_product(a, b, v) == 53.0 # same with tuple dispatch - aτ = (1, 2) - bτ = (3, 4) - @test truncated_series_product(aτ, bτ, v) == 53 + @test truncated_series_product(Tuple(a), Tuple(b), v) == 53.0 # truncated_series_ratio edge cases # N2 == 0 (empty b) must throw ArgumentError a = (1.0, 2.0, 3.0) + a_empty = () b_empty = () @test_throws ArgumentError truncated_series_ratio(a, b_empty) - a_empty = () @test_throws ArgumentError truncated_series_ratio(a_empty, b_empty) # N1 == 0 (empty a) returns zero of promoted type From 887e3a7e7e581397589f381f9a73b17f343c2d15 Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Sat, 12 Jul 2025 22:28:20 -0400 Subject: [PATCH 76/77] =?UTF-8?q?Use=20"=E2=89=A1"=20consistently=20instea?= =?UTF-8?q?d=20of=20"=3D=3D=3D"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ext/PostNewtonianSymbolicsExt.jl | 2 +- src/core/PNBase.jl | 10 +++++----- src/core/PNTerm.jl | 8 ++++---- src/core/pn_expression.jl | 2 +- src/core/utilities/truncated_series_monoid.jl | 2 +- src/pn_expressions/binding_energy.jl | 2 +- src/pn_expressions/separation.jl | 2 +- test/aqua.jl | 17 ++++++++--------- 8 files changed, 22 insertions(+), 23 deletions(-) diff --git a/ext/PostNewtonianSymbolicsExt.jl b/ext/PostNewtonianSymbolicsExt.jl index 8785d74b..c7cd1b31 100644 --- a/ext/PostNewtonianSymbolicsExt.jl +++ b/ext/PostNewtonianSymbolicsExt.jl @@ -103,7 +103,7 @@ Symbolics.derivative(::typeof(hold), args::NTuple{1,Any}, ::Val{1}) = 1 function unhold(expr) MacroTools.postwalk(expr) do x m = MacroTools.trymatch(:(f_(i_)), x) - m === nothing || m[:f] !== hold ? x : Symbol(m[:i]) + m ≡ nothing || m[:f] !== hold ? x : Symbol(m[:i]) end end diff --git a/src/core/PNBase.jl b/src/core/PNBase.jl index e2d981c2..254ce2fe 100644 --- a/src/core/PNBase.jl +++ b/src/core/PNBase.jl @@ -119,15 +119,15 @@ PNBase end for f ∈ (:+, :*) - op = f===:+ ? sum : prod + op = f≡:+ ? sum : prod for w ∈ Z, x ∈ Z, y ∈ Z @test eval(:(Mod.$f))(pnsystem, w, x, y) isa NT @test eval(:(Mod.$f))(pnsystem, w, x, y) == op(NT, (w, x, y)) - @test eval(:(Mod.$f))(w, x, y) === op((w, x, y)) + @test eval(:(Mod.$f))(w, x, y) ≡ op((w, x, y)) for v ∈ Z @test eval(:(Mod.$f))(pnsystem, v, w, x, y) isa NT @test eval(:(Mod.$f))(pnsystem, v, w, x, y) == op(NT, (v, w, x, y)) - @test eval(:(Mod.$f))(v, w, x, y) === op((v, w, x, y)) + @test eval(:(Mod.$f))(v, w, x, y) ≡ op((v, w, x, y)) end end end @@ -149,12 +149,12 @@ PNBase end for f ∈ (:+, :-, :*, ://) for x ∈ Z, y ∈ Z - @test typeof(eval(:(Mod.$f))(x, y)) === typeof(eval(:($f))(x, y)) + @test typeof(eval(:(Mod.$f))(x, y)) ≡ typeof(eval(:($f))(x, y)) @test eval(:(Mod.$f))(x, y) == eval(:($f))(x, y) end end for x ∈ Z, y ∈ Z - @test typeof(eval(:(Mod.://))(pnsystem, x, y)) === typeof(x // y) + @test typeof(eval(:(Mod.://))(pnsystem, x, y)) ≡ typeof(x // y) @test eval(:(Mod.://))(pnsystem, x, y) == x // y end for f ∈ (:ln, :√) diff --git a/src/core/PNTerm.jl b/src/core/PNTerm.jl index e59d7eba..98280659 100644 --- a/src/core/PNTerm.jl +++ b/src/core/PNTerm.jl @@ -188,7 +188,7 @@ end # baremodule PNTerms @test term.coeff == coeff @test term.coeff isa eltype(pn) @test length(term) == 1 - @test eltype(term) === T + @test eltype(term) ≡ T end # Test PNExpressions @@ -225,7 +225,7 @@ end # baremodule PNTerms @test expr.coeffs == expected @test pn_order(expr) == pn_order(pn) @test sum(expr) == sum(expected) - @test eltype(expr) === T + @test eltype(expr) ≡ T end # Can't make a PNExpression with positive exponents @@ -244,14 +244,14 @@ end # baremodule PNTerms # constant_convert should just return the term t0 = PNTerm{Float64,2//1}(0, 5.0) - @test constant_convert(pn, t0) === t0 + @test constant_convert(pn, t0) ≡ t0 # Base.sum @test sum(t0) == 5.0 # unary + (PNBase.+) t1 = PNTerm{Float64,2}(1, 2.5) - @test +t1 === t1 + @test +t1 ≡ t1 # unary - (PNBase.-) t1n = -t1 diff --git a/src/core/pn_expression.jl b/src/core/pn_expression.jl index 1a44cdba..02221265 100644 --- a/src/core/pn_expression.jl +++ b/src/core/pn_expression.jl @@ -103,7 +103,7 @@ end function pn_expression(arg_index, func, pnsystem_functions, __module__, __source__) # Handle the default argument - if func === :(nothing) + if func ≡ :(nothing) arg_index, func = 1, arg_index end diff --git a/src/core/utilities/truncated_series_monoid.jl b/src/core/utilities/truncated_series_monoid.jl index 3ac076e2..c23ec022 100644 --- a/src/core/utilities/truncated_series_monoid.jl +++ b/src/core/utilities/truncated_series_monoid.jl @@ -252,7 +252,7 @@ end # N1 == 0 (empty a) returns zero of promoted type a_empty = () b = (5, -2) - @test truncated_series_ratio(a_empty, b) === + @test truncated_series_ratio(a_empty, b) ≡ zero(promote_type(eltype(a_empty), eltype(b))) for T ∈ [Float32, Float64, Double64] diff --git a/src/pn_expressions/binding_energy.jl b/src/pn_expressions/binding_energy.jl index f03fc740..8a4374e3 100644 --- a/src/pn_expressions/binding_energy.jl +++ b/src/pn_expressions/binding_energy.jl @@ -220,7 +220,7 @@ function 𝓔′code( ) 𝓔′statements[end] = 𝓔′return - if PNExpansionReducer === identity + if PNExpansionReducer ≡ identity # When `pn_expansion_reducer=Val(identity)` is passed, we return a PNExpansion NMax = Int(2PNOrder + 1) return quote diff --git a/src/pn_expressions/separation.jl b/src/pn_expressions/separation.jl index 31f1a573..cec73b4b 100644 --- a/src/pn_expressions/separation.jl +++ b/src/pn_expressions/separation.jl @@ -239,7 +239,7 @@ from the Newton iterations in [`γₚₙ′`](@ref). ) γₚₙ₀′statements[end] = γₚₙ₀′return - if PNExpansionReducer === identity + if PNExpansionReducer ≡ identity # When `pn_expansion_reducer=Val(identity)` is passed, we return a PNExpansion NMax = Int(2PNOrder + 1) return quote diff --git a/test/aqua.jl b/test/aqua.jl index 00074a42..7c4d8fd6 100644 --- a/test/aqua.jl +++ b/test/aqua.jl @@ -9,9 +9,9 @@ end @testitem "ExplicitImports tests" begin using ExplicitImports: ExplicitImports - @test ExplicitImports.check_no_implicit_imports(PostNewtonian) === nothing - @test ExplicitImports.check_all_explicit_imports_via_owners(PostNewtonian) === nothing - @test_broken ExplicitImports.check_all_explicit_imports_are_public(PostNewtonian) === + @test ExplicitImports.check_no_implicit_imports(PostNewtonian) ≡ nothing + @test ExplicitImports.check_all_explicit_imports_via_owners(PostNewtonian) ≡ nothing + @test_broken ExplicitImports.check_all_explicit_imports_are_public(PostNewtonian) ≡ nothing @test ExplicitImports.check_no_stale_explicit_imports( PostNewtonian; @@ -41,11 +41,10 @@ end :SVector, :Node, ), - ) === nothing - @test ExplicitImports.check_all_qualified_accesses_via_owners(PostNewtonian) === nothing - @test_broken ExplicitImports.check_all_qualified_accesses_are_public(PostNewtonian) === + ) ≡ nothing + @test ExplicitImports.check_all_qualified_accesses_via_owners(PostNewtonian) ≡ nothing + @test_broken ExplicitImports.check_all_qualified_accesses_are_public(PostNewtonian) ≡ + nothing + @test ExplicitImports.check_no_self_qualified_accesses(PostNewtonian; ignore=(:Ω, :v)) ≡ nothing - @test ExplicitImports.check_no_self_qualified_accesses( - PostNewtonian; ignore=(:Ω, :v) - ) === nothing end From df0701729223b5ffcdf277867d60f138c138631d Mon Sep 17 00:00:00 2001 From: Mike Boyle Date: Tue, 29 Jul 2025 15:47:24 -0400 Subject: [PATCH 77/77] Improve test coverage Note that some lines are tested, but don't appear to be so in coverage because of constant-folding and similar. --- src/core/PNBase.jl | 2 ++ src/core/PNExpansion.jl | 37 ++++++++++++++++++++++------- src/core/PNTerm.jl | 2 +- src/core/utilities/InlineExports.jl | 4 ++++ 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/core/PNBase.jl b/src/core/PNBase.jl index 254ce2fe..9e5872c6 100644 --- a/src/core/PNBase.jl +++ b/src/core/PNBase.jl @@ -69,7 +69,9 @@ using Base: @inline end # baremodule PNBase +# COV_EXCL_START (Covered, but missed due to const-folding) const pnbase_functions = filter(s -> isa(getfield(PNBase, s), Function), names(PNBase)) +# COV_EXCL_END @doc """ PNBase diff --git a/src/core/PNExpansion.jl b/src/core/PNExpansion.jl index 805c4c0d..44bf561d 100644 --- a/src/core/PNExpansion.jl +++ b/src/core/PNExpansion.jl @@ -60,19 +60,19 @@ elements are currently in the coefficients, but is required to be 1 ≤ N ≤ NM function PNExpansion{N,T,NMax}(coeffs) where {N,T,NMax} if N < 1 - throw(ArgumentError("`N=$N` must be >0.")) + throw(ArgumentError("`N=$N` must be greater than 0.")) # COV_EXCL_LINE end if N > NMax - throw(ArgumentError("`N=$N` must be <`NMax=$NMax`.")) + throw(ArgumentError("`N=$N` must be less than `NMax=$NMax`.")) # COV_EXCL_LINE end return new{N,T,NMax}(coeffs) end function PNExpansion(coeffs::NTuple{N,T}, NMax) where {N,T} if N < 1 - throw(ArgumentError("`N=$N` must be >0.")) + throw(ArgumentError("`N=$N` must be greater than 0.")) end if N > NMax - throw(ArgumentError("`N=$N` must be <`NMax=$NMax`.")) + throw(ArgumentError("`N=$N` must be less than `NMax=$NMax`.")) # COV_EXCL_LINE end return new{N,T,NMax}(coeffs) end @@ -286,13 +286,13 @@ function PNBase.:*( N = min(max(N1, N1 ⊕ ΔN), NMax) # Check that no terms from expansion will be lost to negative PN orders - @inbounds for i ∈ 1:min(max(0, -ΔN), N1) + @inbounds for i ∈ 1:min(max(0, ⊖(ΔN)), N1) if !iszero(expansion[i]) throw( ArgumentError( "Cannot multiply `PNExpansion` by `PNTerm` with negative exponent: " ⊛ - "c⁻¹exp(term)=$(c⁻¹exp(term))." ⊛ - "\nResult will be a `PNExpansion`, which cannot store positive exponents.", + "c⁻¹exp(term)=$(c⁻¹exp(term)).\nThe result will be a `PNExpansion`, " ⊛ + "which cannot store positive exponents.", ), ) end @@ -327,14 +327,35 @@ end end # baremodule PNExpansions @testitem "PNExpansion algebra" begin - using Symbolics: @variables, simplify, substitute using Base: Base, one, zero, <, ÷, + as ⊕, - as ⊖, * as ⊛, / as ⊘, ^ as ↑ + using StaticArrays: MVector, SVector + using Symbolics: @variables, simplify, substitute using PostNewtonian.PNBase: ln, (√), (+), (-), (*), (/), (//), (^) using PostNewtonian.PNExpansions: PNExpansion + # Test edge cases + @test_throws ArgumentError PNExpansion{0, Float64, 1}(()) + @test_throws ArgumentError PNExpansion{2, Float64, 1}((1.2, 3.4)) + @test_throws ArgumentError PNExpansion((), 0) + @test_throws ArgumentError PNExpansion((1.2, 3.4), 1) + for N1 ∈ 1:9 + # Test conversions + coeffs = ntuple(i -> i + 1.0, N1) + for NMax ∈ N1:(N1 ⊕ 3) + expansion = PNExpansion(coeffs, NMax) + @test Base.Tuple(expansion) == coeffs + @test SVector(expansion) == SVector(coeffs) + @test Base.eltype(expansion) == Float64 + @test Base.length(expansion) == N1 + for i ∈ 1:N1 + @test expansion[i] == coeffs[i] + end + end + for N2 ∈ 1:9 for NMax ∈ max(N1, N2):(N1 ⊕ N2 ⊕ 3) + @variables c⁻¹ x[1:N1] y[1:N2] z poly(e::PNExpansion) = sum(e[i] ⊛ c⁻¹↑(i ⊖ 1) for i ∈ 1:length(e)) eˣ = PNExpansion(tuple(x...), NMax) diff --git a/src/core/PNTerm.jl b/src/core/PNTerm.jl index 98280659..e5b5ad5a 100644 --- a/src/core/PNTerm.jl +++ b/src/core/PNTerm.jl @@ -69,7 +69,7 @@ automatically tracking the PN order of each term. return PNTerm{NT,PNOrder}(-1, one(T)) end -Base.length(pn::PNTerm) = 1 +Base.length(pn::PNTerm) = 1 # COV_EXCL_LINE (Covered, but missed due to const-folding) Base.eltype(pn::PNTerm{T}) where {T} = T @export c⁻¹exp(pn::PNTerm{T,PNOrder,c⁻¹Exponent}) where {T,PNOrder,c⁻¹Exponent} = diff --git a/src/core/utilities/InlineExports.jl b/src/core/utilities/InlineExports.jl index ffc25c91..7ce6408e 100644 --- a/src/core/utilities/InlineExports.jl +++ b/src/core/utilities/InlineExports.jl @@ -106,10 +106,12 @@ function handle(expr::Expr, export_or_public::Symbol) else Expr(export_or_public, r...) end + # COV_EXCL_START return esc(quote $ep Base.@__doc__ $expr end) + # COV_EXCL_END end handle(::Any) = nothing @@ -133,7 +135,9 @@ function handle(::Val{:macrocall}, expr) if expr.args[1]==Symbol("@doc") || (expr.args[1] == Core.GlobalRef(Core, Symbol("@doc"))) if length(expr.args) != 4 + # COV_EXCL_START error("@doc expression found with $(length(expr.args)) args:\n$expr") + # COV_EXCL_END end handle(expr.args[4]) else