diff --git a/.gitignore b/.gitignore index 6cd14d7274ceefe3d802ab464f1b398ced980a47..117c817abe52b602c4bc6f6474d10e57220724f5 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,10 @@ dist *.whl *.egg-info +# tests and coverage +.coverage +htmlcov +coverage.xml # mkdocs site \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d335f9555db59e60d471769899233b955388802b..cbaa0e6790a2da0ee6f4f8b99986bcc74d14876b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,10 +1,12 @@ stages: - - pretest - - test + - "Code Quality" + - "Tests" + - "Documentation" - deploy linter: - stage: pretest + stage: "Code Quality" + needs: [] except: variables: - $ENABLE_NIGHTLY_BUILDS @@ -15,7 +17,8 @@ linter: - docker typechecker: - stage: pretest + stage: "Code Quality" + needs: [] except: variables: - $ENABLE_NIGHTLY_BUILDS @@ -26,24 +29,46 @@ typechecker: tags: - docker +testsuite: + stage: "Tests" + image: i10git.cs.fau.de:5005/pycodegen/pycodegen/full + needs: [] + before_script: + - pip install "git+https://i10git.cs.fau.de/pycodegen/pystencils.git@v2.0-dev" + - pip install -e . + script: + - pytest -v --cov-report html --cov-report xml --cov-report term --cov=src/pystencilssfg tests + artifacts: + when: always + paths: + - htmlcov + reports: + coverage_report: + coverage_format: cobertura + path: coverage.xml + build-documentation: - stage: test + stage: "Documentation" image: i10git.cs.fau.de:5005/pycodegen/pycodegen/full + needs: [] + before_script: + - pip install "git+https://i10git.cs.fau.de/pycodegen/pystencils.git@v2.0-dev" + - pip install -e .[docs] script: - - pip install mkdocs mkdocs-material mkdocstrings[python] - - mkdocs build + - cd docs + - make html tags: - docker artifacts: paths: - - site + - docs/build/html pages: image: i10git.cs.fau.de:5005/pycodegen/pycodegen/full stage: deploy script: - ls -l - - mv site public # folder has to be named "public" for gitlab to publish it + - mv docs/build/html public # folder has to be named "public" for gitlab to publish it artifacts: paths: - public diff --git a/COPYING.txt b/COPYING.txt new file mode 100644 index 0000000000000000000000000000000000000000..be3f7b28e564e7dd05eaf59d64adba1a4065ac0e --- /dev/null +++ b/COPYING.txt @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +<https://www.gnu.org/licenses/>. diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..d0c3cbf1020d5c292abdedf27627c6abe25e2293 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/api/composer.md b/docs/api/composer.md deleted file mode 100644 index c0542aab02a9881a7a1776990d15ec5eb0e1992c..0000000000000000000000000000000000000000 --- a/docs/api/composer.md +++ /dev/null @@ -1,8 +0,0 @@ - -::: pystencilssfg.composer.SfgComposer - -::: pystencilssfg.composer.SfgBasicComposer - -::: pystencilssfg.composer.SfgClassComposer - -::: pystencilssfg.composer.make_sequence \ No newline at end of file diff --git a/docs/api/context.md b/docs/api/context.md deleted file mode 100644 index fcc494885cb57515c265c2ef6bcbda158c776dab..0000000000000000000000000000000000000000 --- a/docs/api/context.md +++ /dev/null @@ -1,2 +0,0 @@ - -::: pystencilssfg.context.SfgContext diff --git a/docs/api/cpp_std.md b/docs/api/cpp_std.md deleted file mode 100644 index bfd9aaa5fc69984b804b3099ad8b91a46cc841ff..0000000000000000000000000000000000000000 --- a/docs/api/cpp_std.md +++ /dev/null @@ -1,8 +0,0 @@ - -::: pystencilssfg.source_concepts.cpp.std_vector_ref - -::: pystencilssfg.source_concepts.cpp.mdspan_ref - -::: pystencilssfg.source_concepts.cpp.StdVector - -::: pystencilssfg.source_concepts.cpp.StdMdspan diff --git a/docs/api/emission.md b/docs/api/emission.md deleted file mode 100644 index fa6211ee2cfa0bcd6cea4ae86f3745ded44b2af7..0000000000000000000000000000000000000000 --- a/docs/api/emission.md +++ /dev/null @@ -1,14 +0,0 @@ - -## Output Configuration - -::: pystencilssfg.configuration.SfgOutputSpec - -## Header-Implementation-Pair Emission - -::: pystencilssfg.emission.HeaderImplPairEmitter - -## Code Style and `clang-format` - -::: pystencilssfg.configuration.SfgCodeStyle - -::: pystencilssfg.emission.clang_format.invoke_clang_format \ No newline at end of file diff --git a/docs/api/generator.md b/docs/api/generator.md deleted file mode 100644 index 1b2b77f1ac0fbc1572a67bdd708d644fcfdf7a75..0000000000000000000000000000000000000000 --- a/docs/api/generator.md +++ /dev/null @@ -1,6 +0,0 @@ - -::: pystencilssfg.generator.SourceFileGenerator - -::: pystencilssfg.configuration.SfgConfiguration - -::: pystencilssfg.configuration.DEFAULT_CONFIG diff --git a/docs/api/index.md b/docs/api/index.md deleted file mode 100644 index d3c9905d887aee50dd3c06ba5ec10cfbd0de56cc..0000000000000000000000000000000000000000 --- a/docs/api/index.md +++ /dev/null @@ -1,23 +0,0 @@ -# API Documentation - -These pages document the public API of *pystencils-sfg*. - -### Front End - - - [Source File Generator](generator.md) - - [Code Generation Context](context.md) - - [Composer](composer.md) - -### Source File Modelling - - - [Source File Components](source_components.md) - - [Kernel Call Tree](tree.md) - -### High-Level Language Concepts - - - [Base Classes](source_objects.md) - - [C++ Standard Library](cpp_std.md) - -### Code Generation - - - [Emission and Printing](emission.md) \ No newline at end of file diff --git a/docs/api/source_components.md b/docs/api/source_components.md deleted file mode 100644 index 29448be166e2c21dfebd2a50efee14ec3245b7d4..0000000000000000000000000000000000000000 --- a/docs/api/source_components.md +++ /dev/null @@ -1,36 +0,0 @@ - -## Kernels and Kernel Namespaces - -::: pystencilssfg.source_components.SfgKernelNamespace - -::: pystencilssfg.source_components.SfgKernelHandle - -## Includes - -::: pystencilssfg.source_components.SfgHeaderInclude - -## Functions - -::: pystencilssfg.source_components.SfgFunction - -## Classes - -::: pystencilssfg.source_components.SfgClassKeyword - -::: pystencilssfg.source_components.SfgClass - -### Visibility - -::: pystencilssfg.source_components.SfgVisibility - -::: pystencilssfg.source_components.SfgVisibilityBlock - -### Members - -::: pystencilssfg.source_components.SfgClassMember - -::: pystencilssfg.source_components.SfgInClassDefinition - -::: pystencilssfg.source_components.SfgConstructor - -::: pystencilssfg.source_components.SfgMethod diff --git a/docs/api/source_objects.md b/docs/api/source_objects.md deleted file mode 100644 index 2ba6d4ff3bd7c9fe8aa0d3d43e23bb2811680572..0000000000000000000000000000000000000000 --- a/docs/api/source_objects.md +++ /dev/null @@ -1,6 +0,0 @@ - -::: pystencilssfg.source_concepts.SrcObject - -::: pystencilssfg.source_concepts.SrcField - -::: pystencilssfg.source_concepts.SrcVector diff --git a/docs/api/tree.md b/docs/api/tree.md deleted file mode 100644 index c50e4dee760a7fc861673a89f4779f347e21bca0..0000000000000000000000000000000000000000 --- a/docs/api/tree.md +++ /dev/null @@ -1,30 +0,0 @@ - -## Base Classes - -::: pystencilssfg.tree.SfgCallTreeNode - -::: pystencilssfg.tree.SfgCallTreeLeaf - -::: pystencilssfg.tree.SfgEmptyNode - -## Utility Nodes - -::: pystencilssfg.tree.SfgFunctionParams - -::: pystencilssfg.tree.SfgRequireIncludes - -## Sequences of Statements - -::: pystencilssfg.tree.SfgSequence - -::: pystencilssfg.tree.SfgStatements - -## Structural and Conditional Constructs - -::: pystencilssfg.tree.SfgBlock - -## Kernel Calls - -::: pystencilssfg.tree.SfgKernelCallNode - - diff --git a/docs/css/mkdocstrings.css b/docs/css/mkdocstrings.css deleted file mode 100644 index c84bd345a3a1188fc523936ad437bf97021c0809..0000000000000000000000000000000000000000 --- a/docs/css/mkdocstrings.css +++ /dev/null @@ -1,35 +0,0 @@ - -h2.doc-heading { - font-size: x-large -} - -h3.doc-heading { - font-size: large; -} - -.doc-class>.doc-heading::before { - font-size: small; - content: "class "; - margin-right: 5pt; -} - -.doc-contents { - border-left: 3pt solid rgb(60, 60, 60); - padding-left: 10pt; -} - -.doc-class .doc-children .doc-attribute>.doc-heading::before { - font-size: small; - content: "attribute "; - margin-right: 5pt; -} - -.doc-class .doc-children .doc-function>.doc-heading::before { - font-size: small; - content: "function "; - margin-right: 5pt; -} - -.doc-children { - padding-left: 10pt; -} \ No newline at end of file diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index e5315634cfe19f81de2a3a3ad70772ee8bc2025e..0000000000000000000000000000000000000000 --- a/docs/index.md +++ /dev/null @@ -1,94 +0,0 @@ -# The pystencils Source File Generator - -[](https://i10git.cs.fau.de/pycodegen/pystencils-sfg/commits/master) -[](https://i10git.cs.fau.de/pycodegen/pystencils-sfg/-/blob/master/LICENSE) - -A bridge over the semantic gap between code emitted by [pystencils](https://pypi.org/project/pystencils/) -and your C/C++/Cuda/HIP framework. - -## Installation - -### From Git - -Install the package into your current Python environment from the git repository using pip -(usage of virtual environments is strongly encouraged!): - -```bash -pip install git+https://i10git.cs.fau.de/pycodegen/pystencils-sfg.git -``` - -### From PyPI - -Not yet available. - -## Primer - -With *pystencils-sfg*, including your *pystencils*-generated kernels with handwritten code becomes straightforward -and intuitive. To illustrate, generating a Jacobi smoother for the two-dimensional Poisson equation -and mapping it onto C++23 `std::mdspan`s takes just a few lines of code: - -```python -import sympy as sp - -from pystencils import fields, kernel - -from pystencilssfg import SourceFileGenerator, SfgComposer -from pystencilssfg.source_concepts.cpp import mdspan_ref - -with SourceFileGenerator() as ctx: - sfg = SfgComposer(ctx) - - u_src, u_dst, f = fields("u_src, u_dst, f(1) : double[2D]", layout="fzyx") - h = sp.Symbol("h") - - @kernel - def poisson_jacobi(): - u_dst[0,0] @= (h**2 * f[0, 0] + u_src[1, 0] + u_src[-1, 0] + u_src[0, 1] + u_src[0, -1]) / 4 - - poisson_kernel = sfg.kernels.create(poisson_jacobi) - - sfg.function("jacobi_smooth")( - sfg.map_field(u_src, mdspan_ref(u_src)), - sfg.map_field(u_dst, mdspan_ref(u_dst)), - sfg.map_field(f, mdspan_ref(f)), - sfg.call(poisson_kernel) - ) -``` - -Take this code, store it into a file `poisson_smoother.py`, and enter the magic words into a terminal: - -```shell -python poisson_smoother.py -``` - -This command will execute the code generator through the `SourceFileGenerator` context manager. -The code generator takes the name of your Python script, replaces `.py` with `.cpp` and `.h`, and writes -`poisson_smoother.cpp` and `poisson_smoother.h` into the current directory, ready to be `#include`d. - -The above is what we call a *generator script*; a Python script that, when executed, produces a pair -of source files of the same name, but with different extensions. -Generator scripts are the primary front-end pattern of *pystencils-sfg*; to learn more about them, -read the [Usage Guide](usage/generator_scripts.md). - -## CMake Integration - -*Pystencils-sfg* comes with a CMake module to register generator scripts for on-the-fly code generation. -With the module loaded, use the function `pystencilssfg_generate_target_sources` inside your `CMakeLists.txt` -to register one or multiple generator scripts; their outputs will automatically be added to the specified target. - -```CMake -pystencilssfg_generate_target_sources( <target name> - SCRIPTS kernels.py ... - FILE_EXTENSIONS .h .cpp -) -``` - -*Pystencils-sfg* makes sure that all generated files are on the project's include path. -To `#include` them, add the prefix `gen/<target name>`: - -```C++ -#include "gen/<target name>/kernels.h" -``` - -For details on how to add *pystencils-sfg* to your CMake project, refer to -[CLI and Build System Integration](usage/cli_and_build_system.md). diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000000000000000000000000000000000000..747ffb7b3033659bdd2d1e6eae41ecb00358a45e --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/source/api/composer.rst b/docs/source/api/composer.rst new file mode 100644 index 0000000000000000000000000000000000000000..75acd8abedc7ebe0c9c9024575f3bffe99f7c6bf --- /dev/null +++ b/docs/source/api/composer.rst @@ -0,0 +1,17 @@ +*************************************** +Composer API (`pystencilssfg.composer`) +*************************************** + +.. autoclass:: pystencilssfg.composer.SfgComposer + :members: + +.. autoclass:: pystencilssfg.composer.SfgIComposer + :members: + +.. autoclass:: pystencilssfg.composer.SfgBasicComposer + :members: + +.. autoclass:: pystencilssfg.composer.SfgClassComposer + :members: + +.. autofunction:: pystencilssfg.composer.make_sequence diff --git a/docs/source/api/generation.rst b/docs/source/api/generation.rst new file mode 100644 index 0000000000000000000000000000000000000000..45065c13edc444239e928ecb66a376bc700e7959 --- /dev/null +++ b/docs/source/api/generation.rst @@ -0,0 +1,11 @@ +************************** +Generator Script Interface +************************** + +.. autoclass:: pystencilssfg.SourceFileGenerator + :members: + +.. autoclass:: pystencilssfg.SfgConfiguration + :members: + +.. autoattribute:: pystencilssfg.configuration.DEFAULT_CONFIG diff --git a/docs/source/api/index.rst b/docs/source/api/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..681b6e851fffa26d94785dbdd71765953d6ba343 --- /dev/null +++ b/docs/source/api/index.rst @@ -0,0 +1,13 @@ +############# +API Reference +############# + +These pages provide a reference for the public API of *pystencils-sfg*. + +.. toctree:: + :maxdepth: 1 + + generation + composer + lang + ir diff --git a/docs/source/api/ir.rst b/docs/source/api/ir.rst new file mode 100644 index 0000000000000000000000000000000000000000..a9451f46c239b73b32e1f8a79b8a166d753ed8e6 --- /dev/null +++ b/docs/source/api/ir.rst @@ -0,0 +1,8 @@ +Internal Code Representation (`pystencilssfg.ir`) +================================================= + +.. autoclass:: pystencilssfg.SfgContext + :members: + +.. automodule:: pystencilssfg.ir + :members: diff --git a/docs/source/api/lang.rst b/docs/source/api/lang.rst new file mode 100644 index 0000000000000000000000000000000000000000..efc69d98c3b278a3fa34b5164a35f58f76c6e2d5 --- /dev/null +++ b/docs/source/api/lang.rst @@ -0,0 +1,25 @@ +Language Modelling (`pystencilssfg.lang`) +========================================= + +.. automodule:: pystencilssfg.lang + +Expressions +----------- + +.. automodule:: pystencilssfg.lang.expressions + :members: + +C++ Standard Library (`pystencilssfg.lang.cpp`) +----------------------------------------------- + +Quick Access +^^^^^^^^^^^^ + +.. automodule:: pystencilssfg.lang.cpp.std + :members: + +Implementation +^^^^^^^^^^^^^^ + +.. automodule:: pystencilssfg.lang.cpp + :members: diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000000000000000000000000000000000000..8a8d28d25178ddcdcbd34c962e9a7cca554e0319 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,71 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = "pystencils-sfg" +copyright = "2024, Frederik Hennig" +author = "Frederik Hennig" +release = "0.0a" + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + "myst_parser", + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", + "sphinx.ext.intersphinx", + "sphinx_autodoc_typehints", + "sphinx_design", + "sphinx_copybutton" +] + +templates_path = ["_templates"] +exclude_patterns = [] +source_suffix = { + ".rst": "restructuredtext", + ".md": "markdown", +} +master_doc = "index" +nitpicky = True + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = "furo" +# html_static_path = ['_static'] + +# Intersphinx + +intersphinx_mapping = { + "python": ("https://docs.python.org/3.8", None), + "numpy": ("https://docs.scipy.org/doc/numpy/", None), + "matplotlib": ("https://matplotlib.org/", None), + "sympy": ("https://docs.sympy.org/latest/", None), +} + + +# Autodoc options + +autodoc_member_order = "bysource" +autodoc_typehints = "description" + + +# Prepare code generation examples + +def build_examples(): + import subprocess + import os + + examples_dir = os.path.join("usage", "examples",) + + subprocess.run(["python", "build.py"], cwd=examples_dir).check_returncode() + + +print("Generating output of example scripts...") +build_examples() diff --git a/docs/source/index.md b/docs/source/index.md new file mode 100644 index 0000000000000000000000000000000000000000..5f74297c9213c091444caccbf85f366adaf4cb28 --- /dev/null +++ b/docs/source/index.md @@ -0,0 +1,212 @@ +# The pystencils Source File Generator + +```{toctree} +:maxdepth: 1 +:hidden: + +usage/index +api/index +``` + +[](https://i10git.cs.fau.de/pycodegen/pystencils-sfg/commits/master) +[](https://i10git.cs.fau.de/pycodegen/pystencils-sfg/-/blob/master/LICENSE) + +A bridge over the semantic gap between code emitted by [pystencils](https://pypi.org/project/pystencils/) +and your C/C++/Cuda/HIP framework. + +## Installation + +### From Git + +Install the package into your current Python environment from the git repository using pip +(usage of virtual environments is strongly encouraged!): + +```bash +pip install "git+https://i10git.cs.fau.de/pycodegen/pystencils-sfg.git" +``` + +````{caution} + +*pystencils-sfg* requires *pystencils 2.0* and is not compatible with *pystencils 1.3.x*. +However, *pystencils 2.0* is still under development and only available as a pre-release version. +To use *pystencils-sfg*, explicitly install *pystencils* from the v2.0 development branch: + +```bash +pip install "git+https://i10git.cs.fau.de/pycodegen/pystencils.git@v2.0-dev" +``` +```` + +### From PyPI + +Not yet available. + +## Primer + +With *pystencils-sfg*, including your *pystencils*-generated kernels with handwritten code becomes straightforward +and intuitive. To illustrate, generating a Jacobi smoother for the two-dimensional Poisson equation +and mapping it onto C++23 `std::mdspan`s takes just a few lines of code: + +```python +import sympy as sp + +from pystencils import fields, kernel + +from pystencilssfg import SourceFileGenerator +from pystencilssfg.lang.cpp import mdspan_ref + +with SourceFileGenerator() as sfg: + u_src, u_dst, f = fields("u_src, u_dst, f(1) : double[2D]", layout="fzyx") + h = sp.Symbol("h") + + @kernel + def poisson_jacobi(): + u_dst[0,0] @= (h**2 * f[0, 0] + u_src[1, 0] + u_src[-1, 0] + u_src[0, 1] + u_src[0, -1]) / 4 + + poisson_kernel = sfg.kernels.create(poisson_jacobi) + + sfg.function("jacobi_smooth")( + sfg.map_field(u_src, mdspan_ref(u_src)), + sfg.map_field(u_dst, mdspan_ref(u_dst)), + sfg.map_field(f, mdspan_ref(f)), + sfg.call(poisson_kernel) + ) +``` + +The script above, and the code within the region controlled by the `SourceFileGenerator`, +constructs a C++ header/implementation file pair by describing its contents. +We first describe our Jacobi smoother symbolically using *pystencils* +and then pass it to the `sfg` to add it to the output file. +Then, a wrapper function `jacobi_smooth` is defined which maps the symbolic fields onto `std::mdspan` +objects and then executes the kernel. + +Take this code, store it into a file `poisson_smoother.py`, and execute the script from a terminal: + +```shell +python poisson_smoother.py +``` + +During execution, *pystencils-sfg* assembles the above constructs into an internal representation of the C++ files. +It then takes the name of your Python script, replaces `.py` with `.cpp` and `.h`, +and exports the constructed code to the files +`poisson_smoother.cpp` and `poisson_smoother.h` into the current directory, ready to be `#include`d. + +````{dropdown} poisson_smoother.h + +```C++ +#pragma once + +#include <cstdint> +#include <experimental/mdspan> + +#define RESTRICT __restrict__ + +void jacobi_smooth( + std::mdspan<double, std::extents<uint64_t, std::dynamic_extent, std::dynamic_extent, 1>> &f, + const double h, + std::mdspan<double, std::extents<uint64_t, std::dynamic_extent, std::dynamic_extent>> &u_dst, + std::mdspan<double, std::extents<uint64_t, std::dynamic_extent, std::dynamic_extent>> &u_src +); +``` + +```` + +````{dropdown} poisson_smoother.cpp + +```C++ +#include "poisson_smoother.h" + +#include <math.h> + +#define FUNC_PREFIX inline + +/************************************************************************************* + * Kernels + *************************************************************************************/ + +namespace kernels { + +FUNC_PREFIX void kernel(const int64_t _size_f_0, const int64_t _size_f_1, + const int64_t _stride_f_0, const int64_t _stride_f_1, + const int64_t _stride_u_dst_0, + const int64_t _stride_u_dst_1, + const int64_t _stride_u_src_0, + const int64_t _stride_u_src_1, double *const f_data, + const double h, double *const u_dst_data, + double *const u_src_data) { + const double __c_1_0o4_0 = 1.0 / 4.0; + for (int64_t ctr_1 = 1LL; ctr_1 < _size_f_1 - 1LL; ctr_1 += 1LL) { + for (int64_t ctr_0 = 1LL; ctr_0 < _size_f_0 - 1LL; ctr_0 += 1LL) { + u_dst_data[ctr_0 * _stride_u_dst_0 + ctr_1 * _stride_u_dst_1] = + __c_1_0o4_0 * u_src_data[(ctr_0 + 1LL) * _stride_u_src_0 + + ctr_1 * _stride_u_src_1] + + __c_1_0o4_0 * u_src_data[ctr_0 * _stride_u_src_0 + + (ctr_1 + 1LL) * _stride_u_src_1] + + __c_1_0o4_0 * u_src_data[ctr_0 * _stride_u_src_0 + + (ctr_1 + -1LL) * _stride_u_src_1] + + __c_1_0o4_0 * u_src_data[(ctr_0 + -1LL) * _stride_u_src_0 + + ctr_1 * _stride_u_src_1] + + __c_1_0o4_0 * (h * h) * + f_data[ctr_0 * _stride_f_0 + ctr_1 * _stride_f_1]; + } + } +} + +} // namespace kernels + +/************************************************************************************* + * Functions + *************************************************************************************/ + +void jacobi_smooth( + std::mdspan<double, std::extents<uint64_t, std::dynamic_extent, std::dynamic_extent, 1>> &f, + const double h, + std::mdspan<double, std::extents<uint64_t, std::dynamic_extent, std::dynamic_extent>> &u_dst, + std::mdspan<double, std::extents<uint64_t, std::dynamic_extent, std::dynamic_extent>> &u_src) +{ + double *const u_src_data{u_src.data_handle()}; + const int64_t _stride_u_src_0{u_src.stride(0)}; + const int64_t _stride_u_src_1{u_src.stride(1)}; + double *const u_dst_data{u_dst.data_handle()}; + const int64_t _stride_u_dst_0{u_dst.stride(0)}; + const int64_t _stride_u_dst_1{u_dst.stride(1)}; + double *const f_data{f.data_handle()}; + const int64_t _size_f_0{f.extents().extent(0)}; + const int64_t _size_f_1{f.extents().extent(1)}; + /* f.extents().extent(2) == 1 */ + const int64_t _stride_f_0{f.stride(0)}; + const int64_t _stride_f_1{f.stride(1)}; + kernels::kernel(_size_f_0, _size_f_1, _stride_f_0, _stride_f_1, + _stride_u_dst_0, _stride_u_dst_1, _stride_u_src_0, + _stride_u_src_1, f_data, h, u_dst_data, u_src_data); +} +``` + +```` + +The above is what we call a *generator script*; a Python script that, when executed, produces a pair +of source files of the same name, but with different extensions. +Generator scripts are the primary front-end pattern of *pystencils-sfg*; to learn more about them, +read the [Usage Guide](usage/generator_scripts.md). + +## CMake Integration + +*Pystencils-sfg* comes with a CMake module to register generator scripts for on-the-fly code generation. +With the module loaded, use the function `pystencilssfg_generate_target_sources` inside your `CMakeLists.txt` +to register one or multiple generator scripts; their outputs will automatically be added to the specified target. + +```CMake +pystencilssfg_generate_target_sources( <target name> + SCRIPTS kernels.py ... + FILE_EXTENSIONS .h .cpp +) +``` + +*Pystencils-sfg* makes sure that all generated files are on the project's include path. +To `#include` them, add the prefix `gen/<target name>`: + +```C++ +#include "gen/<target name>/kernels.h" +``` + +For details on how to add *pystencils-sfg* to your CMake project, refer to +[CLI and Build System Integration](usage/cli_and_build_system.md). diff --git a/docs/usage/cli_and_build_system.md b/docs/source/usage/cli_and_build_system.md similarity index 98% rename from docs/usage/cli_and_build_system.md rename to docs/source/usage/cli_and_build_system.md index 05f38d70af50888cef8bacab8f4ce5124aa76af7..910b6b630e4f7b4501dd2f4ca0958042f4d43eda 100644 --- a/docs/usage/cli_and_build_system.md +++ b/docs/source/usage/cli_and_build_system.md @@ -1,3 +1,5 @@ +(guide:cli)= +# CLI and Build System ## Command Line Interface diff --git a/docs/source/usage/examples/.gitignore b/docs/source/usage/examples/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..26a5736a94d1260044161ac4963b05b3f3407025 --- /dev/null +++ b/docs/source/usage/examples/.gitignore @@ -0,0 +1,2 @@ +*.cpp +*.h \ No newline at end of file diff --git a/docs/source/usage/examples/build.py b/docs/source/usage/examples/build.py new file mode 100644 index 0000000000000000000000000000000000000000..4726887436c7949167d60f50139b165354d80306 --- /dev/null +++ b/docs/source/usage/examples/build.py @@ -0,0 +1,22 @@ +import os +import glob +import subprocess + +GROUPS = ["guide_generator_scripts"] +THIS_DIR = os.path.split(__file__)[0] + + +def main(): + for group in GROUPS: + group_folder = os.path.join(THIS_DIR, group) + + for group_entry in os.listdir(group_folder): + scripts_dir = os.path.join(group_folder, group_entry) + if os.path.isdir(scripts_dir): + query = os.path.join(scripts_dir, "*.py") + for script in glob.glob(query): + subprocess.run(["python", script], cwd=scripts_dir).check_returncode() + + +if __name__ == "__main__": + main() diff --git a/docs/source/usage/examples/guide_generator_scripts/01/kernels.py b/docs/source/usage/examples/guide_generator_scripts/01/kernels.py new file mode 100644 index 0000000000000000000000000000000000000000..a23e622ffd334f082f067602c037450790e5f636 --- /dev/null +++ b/docs/source/usage/examples/guide_generator_scripts/01/kernels.py @@ -0,0 +1,4 @@ +from pystencilssfg import SourceFileGenerator + +with SourceFileGenerator() as sfg: + pass diff --git a/docs/source/usage/examples/guide_generator_scripts/02/kernels.py b/docs/source/usage/examples/guide_generator_scripts/02/kernels.py new file mode 100644 index 0000000000000000000000000000000000000000..e897f1dd2b672ac24fa15ef2c5f0c8eac3a0abb6 --- /dev/null +++ b/docs/source/usage/examples/guide_generator_scripts/02/kernels.py @@ -0,0 +1,6 @@ +from pystencilssfg import SourceFileGenerator + +with SourceFileGenerator() as sfg: + sfg.include("<vector>") + sfg.include("<span>") + sfg.include("custom_header.hpp") diff --git a/docs/source/usage/examples/guide_generator_scripts/03/kernels.py b/docs/source/usage/examples/guide_generator_scripts/03/kernels.py new file mode 100644 index 0000000000000000000000000000000000000000..83fad64e80b3dad5cf62dede158df25eeeccc83e --- /dev/null +++ b/docs/source/usage/examples/guide_generator_scripts/03/kernels.py @@ -0,0 +1,16 @@ +from pystencilssfg import SourceFileGenerator + +import pystencils as ps +import sympy as sp + +with SourceFileGenerator() as sfg: + # Define a copy kernel + src, dst = ps.fields("src, dst: [1D]") + c = sp.Symbol("c") + + @ps.kernel + def scale(): + dst.center @= c * src.center() + + # Add it to the file + scale_kernel = sfg.kernels.create(scale, "scale") diff --git a/docs/source/usage/examples/guide_generator_scripts/04/kernels.py b/docs/source/usage/examples/guide_generator_scripts/04/kernels.py new file mode 100644 index 0000000000000000000000000000000000000000..ec26dd14abb801d79edd9c08abff943953c58cac --- /dev/null +++ b/docs/source/usage/examples/guide_generator_scripts/04/kernels.py @@ -0,0 +1,23 @@ +from pystencilssfg import SourceFileGenerator + +import pystencils as ps +import sympy as sp + +with SourceFileGenerator() as sfg: + # Define a copy kernel + src, dst = ps.fields("src, dst: [1D]") + c = sp.Symbol("c") + + @ps.kernel + def scale(): + dst.center @= c * src.center() + + # Add it to the file + scale_kernel = sfg.kernels.create(scale, "scale") + + # start + # ... see above ... + sfg.function("scale_kernel")( + sfg.call(scale_kernel) + ) + # end diff --git a/docs/source/usage/examples/guide_generator_scripts/05/kernels.py b/docs/source/usage/examples/guide_generator_scripts/05/kernels.py new file mode 100644 index 0000000000000000000000000000000000000000..431c4cf365a52ca4f9504a6c43aa3c7ebce3d535 --- /dev/null +++ b/docs/source/usage/examples/guide_generator_scripts/05/kernels.py @@ -0,0 +1,28 @@ +from pystencilssfg import SourceFileGenerator + +import pystencils as ps +import sympy as sp + +with SourceFileGenerator() as sfg: + # Define a copy kernel + src, dst = ps.fields("src, dst: [1D]") + c = sp.Symbol("c") + + @ps.kernel + def scale(): + dst.center @= c * src.center() + + # Add it to the file + scale_kernel = sfg.kernels.create(scale, "scale") + + # start + import pystencilssfg.lang.cpp.std as std + + sfg.include("<span>") + + sfg.function("scale_kernel")( + sfg.map_field(src, std.vector(src)), + sfg.map_field(dst, std.span(dst)), + sfg.call(scale_kernel) + ) + # end diff --git a/docs/source/usage/generator_scripts.md b/docs/source/usage/generator_scripts.md new file mode 100644 index 0000000000000000000000000000000000000000..57b9bcdf3e2aa56a942da0d884265cd669d445cc --- /dev/null +++ b/docs/source/usage/generator_scripts.md @@ -0,0 +1,194 @@ +(guide:generator_scripts)= +# Generator Scripts + +Writing generator scripts is the primary usage idiom of *pystencils-sfg*. +A generator script is a Python script, say `kernels.py`, which contains *pystencils-sfg* +code at the top level that, when executed, emits source code to a pair of files `kernels.h` +and `kernels.cpp`. This guide describes how to write such a generator script, its structure, and how +it can be used to generate code. + +## Anatomy + +The code generation process in a generator script is controlled by the `SourceFileGenerator` context manager. +It configures the code generator by combining configuration options from the +environment (e.g. a CMake build system) with options specified in the script, +and infers the names of the output files from the script's name. +It then returns a {py:class}`composer <pystencilssfg.composer.SfgComposer>` to the user, +which provides a convenient interface for constructing the source files. + +To start, place the following code in a Python script, e.g. `kernels.py`: + +```{literalinclude} examples/guide_generator_scripts/01/kernels.py +``` + +The source file is constructed within the context manager's managed region. +During execution of the script, when the region ends, a header/source file pair +`kernels.h` and `kernels.cpp` will be written to disk next to your script. +Execute the script as-is and inspect the generated files, which will of course still be empty: + +``````{dropdown} Generated Files +`````{tab-set} + +````{tab-item} kernels.h +```{literalinclude} examples/guide_generator_scripts/01/kernels.h +``` +```` + +````{tab-item} kernels.cpp +```{literalinclude} examples/guide_generator_scripts/01/kernels.cpp +``` +```` +````` +`````` + +<!-- A few notes on configuration: + + - The [SourceFileGenerator](#pystencilssfg.SourceFileGenerator) parses the script's command line arguments + for configuration options (refer to [CLI and Build System Integration](cli_and_build_system.md)). + If you intend to evaluate command-line parameters inside your + generator script, read them from `sfg.context.argv` instead of `sys.argv`. + There, all arguments meant for the code generator are already removed. + - The code generator's configuration is consolidated from a global project configuration which may + be provided by the build system; a number of command line arguments; and the + [SfgConfiguration](#pystencilssfg.SfgConfiguration) provided in the script. + The project configuration may safely be overridden by the latter two; however, conflicts + between command-line arguments and the configuration defined in the script will cause + an exception to be thrown. --> + +## Using the Composer + +The object `sfg` constructed in above snippet is an instance of [SfgComposer](#pystencilssfg.composer.SfgComposer). +The composer is the central part of the user front-end of *pystencils-sfg*. +It provides an interface for constructing source files that closely mimics +C++ syntactic structures within Python. +Here is an overview of its various functions: + +### Includes and Definitions + +With [`SfgComposer.include`](#pystencilssfg.composer.SfgBasicComposer.include), the code generator can be instructed to include header files. +As in C++, you can use the `<>` delimiters for system headers, and omit them for project headers. + +`````{tab-set} + +````{tab-item} kernels.py +```{literalinclude} examples/guide_generator_scripts/02/kernels.py +``` +```` + +````{tab-item} kernels.h +```{literalinclude} examples/guide_generator_scripts/02/kernels.h +``` +```` + +````{tab-item} kernels.cpp +```{literalinclude} examples/guide_generator_scripts/02/kernels.cpp +``` +```` +````` + +### Adding Kernels + +[pystencils](https://pycodegen.pages.i10git.cs.fau.de/pystencils/)-generated kernels are managed in *kernel namespaces*. +The default kernel namespace is called `kernels` and is available via +[`sfg.kernels`](#pystencilssfg.composer.SfgBasicComposer.kernels). +Adding an existing *pystencils* AST, or creating one from a list of assignments, is possible through +[`kernels.add`](#pystencilssfg.ir.SfgKernelNamespace.add) +and +[`kernels.create`](#pystencilssfg.ir.SfgKernelNamespace.create). +The latter is a wrapper around +[`pystencils.create_kernel`]( +https://pycodegen.pages.i10git.cs.fau.de/pystencils/sphinx/kernel_compile_and_call.html#pystencils.create_kernel +). +Both functions return a [kernel handle](#pystencilssfg.ir.SfgKernelHandle) +through which the kernel can be accessed, e.g. for calling it in a function. + +To access other kernel namespaces than the default one, +the [`sfg.kernel_namespace`](#pystencilssfg.composer.SfgBasicComposer.kernel_namespace) method can be used. + +`````{tab-set} + +````{tab-item} kernels.py +```{literalinclude} examples/guide_generator_scripts/03/kernels.py +``` +```` + +````{tab-item} kernels.h +```{literalinclude} examples/guide_generator_scripts/03/kernels.h +``` +```` + +````{tab-item} kernels.cpp +```{literalinclude} examples/guide_generator_scripts/03/kernels.cpp +``` +```` +````` + +### Building Functions + +Through the composer, you can define free functions in your generated C++ file. +These may contain arbitrary code; +their primary intended task however is to wrap kernel calls with the necessary boilerplate code +to integrate them into a framework. +The composer provides an interface for constructing functions that tries to mimic the look of the generated C++ code. +Use `sfg.function` to create a function, and `sfg.call` to call a kernel: + +`````{tab-set} + +````{tab-item} kernels.py +```{literalinclude} examples/guide_generator_scripts/04/kernels.py +:start-after: start +:end-before: end +``` +```` + +````{tab-item} kernels.h +```{literalinclude} examples/guide_generator_scripts/04/kernels.h +``` +```` + +````{tab-item} kernels.cpp +```{literalinclude} examples/guide_generator_scripts/04/kernels.cpp +``` +```` +````` + +Note the special syntax: To mimic the look of a C++ function, the composer uses a sequence of two calls +to construct the function. + +The function body can furthermore be populated with code to embedd the generated kernel into +the target C++ application. +If you examine the generated files of the previous example, you will notice that your +function `scale_kernel` has lots of raw pointers and integer indices in its interface. +We can wrap those up into proper C++ data structures, +such as, for example, `std::span` or `std::vector`, like this: + +`````{tab-set} + +````{tab-item} kernels.py +```{literalinclude} examples/guide_generator_scripts/05/kernels.py +:start-after: start +:end-before: end +``` +```` + +````{tab-item} kernels.h +```{literalinclude} examples/guide_generator_scripts/05/kernels.h +``` +```` + +````{tab-item} kernels.cpp +```{literalinclude} examples/guide_generator_scripts/05/kernels.cpp +``` +```` +````` + +If you now inspect the generated code, you will see that the interface of your function is +considerably simplified. +Also, all the necessary code was added to its body to extract the low-level information required +by the actual kernel from the data structures. + +The `sfg.map_field` API can be used to map pystencils fields to a variety of different data structures. +The pystencils-sfg provides modelling support for a number of C++ standard library classes +(see {any}`pystencilssfg.lang.cpp.std`). +It also provides the necessary infrastructure for modelling the data structures of any C++ framework +in a similar manner. diff --git a/docs/source/usage/index.md b/docs/source/usage/index.md new file mode 100644 index 0000000000000000000000000000000000000000..5783e9ebe71faade1b69dd233595eb8dcb3710ce --- /dev/null +++ b/docs/source/usage/index.md @@ -0,0 +1,38 @@ +# Usage Guides + +```{toctree} +:maxdepth: 1 +:hidden: + +generator_scripts +cli_and_build_system +tips_n_tricks +``` + +These pages provide an overview of how to use the pystencils Source File Generator. +A basic understanding of [pystencils](https://pycodegen.pages.i10git.cs.fau.de/pystencils/index.html) +is required. + +```{card} Writing Generator Scripts +:link: guide:generator_scripts +:link-type: ref + +Learn about *generator scripts*, the primary usage idiom of *pystencils-sfg*: +Embedd *pystencils*-generated kernels into C++ source files and augment them with +arbitrary C++ glue code. +``` + +```{card} CLI and Build System Integration +:link: guide:cli +:link-type: ref + +Learn how to control code generation from the command line +and how to embedd *pystencils-sfg* into your build system. +``` + +```{card} Tips and Tricks +:link: guide:tips_n_tricks +:link-type: ref + +A collection of various tricks that might come in handy when working with *pystencils-sfg*. +``` diff --git a/docs/usage/tips_n_tricks.md b/docs/source/usage/tips_n_tricks.md similarity index 95% rename from docs/usage/tips_n_tricks.md rename to docs/source/usage/tips_n_tricks.md index 06c74d6af6931a5d4773122a8dd06c7a2e8756cb..43b2d388e4cb6dae8c5641adef250f529b72997c 100644 --- a/docs/usage/tips_n_tricks.md +++ b/docs/source/usage/tips_n_tricks.md @@ -1,3 +1,5 @@ +(guide:tips_n_tricks)= +# Tips and Tricks ## Make CLion treat generated files as project sources diff --git a/docs/usage/building.md b/docs/usage/building.md deleted file mode 100644 index 01d129500861dfe25b9bb18152d2ac73842a004e..0000000000000000000000000000000000000000 --- a/docs/usage/building.md +++ /dev/null @@ -1,30 +0,0 @@ - -## Namespaces - -Conceptually, there exist two different kinds of namespaces: *kernel namespaces* for the generated kernels, -and a single *code namespace* for all the generated code. -Both get mapped to standard C++ namespaces, in the end, but they fulfill different purposes in the code generator. - -*Kernel namespaces* are used for grouping generated kernels together, e.g. to avoid name collisions. -If, for example, a code generation script combines kernels and functions produced by different components, each -component may create its own kernel namespace to isolate its kernels. - -The *code namespace*, in contrast, envelops all the generated code. Its fully qualified name is built from two parts: - - - The *outer namespace* is defined in the [generator configuration][pystencilssfg.SfgConfiguration], typically by - the global project configuration; - - The *inner namespace* is defined by the code generation script, e.g. via [`SfgComposer.namespace`][pystencilssfg.SfgComposer.namespace]. - -These namespaces will finally occur in the generated implementation file as: - -```C++ -namespace outer_namespace::inner_namespace { - -namespace kernels { - /* kernel definitions */ -} // namespace kernels - -/* function definitions */ - -} // namespace outer_namespace::inner_namespace -``` diff --git a/docs/usage/generator_scripts.md b/docs/usage/generator_scripts.md deleted file mode 100644 index 25f2bd4d1b8fd8664d177e56b22f00e9aca719a2..0000000000000000000000000000000000000000 --- a/docs/usage/generator_scripts.md +++ /dev/null @@ -1,186 +0,0 @@ - -Generator scripts are the primary way *pystencils-sfg* is meant to be used. -A generator script is a single Python script, say `kernels.py`, which contains *pystencils-sfg* -code at the top level such that, when executed, it emits source code to a pair of files `kernels.h` -and `kernels.cpp`. This guide describes how to write such a generator script, its structure, and how -it can be used to generate code. - -This page gives a general overview over the code generation process, but introduces only the -convenient high-level interface provided by the [SourceFileGenerator][pystencilssfg.SourceFileGenerator] -and [SfgComposer][pystencilssfg.SfgComposer] classes. -For a more in-depth look into building source files, and about using *pystencils-sfg* outside -of a generator script, please take a look at the [In-Depth Guide](building.md). - -## Anatomy - -The code generation process in a generator script is controlled by the -[SourceFileGenerator][pystencilssfg.SourceFileGenerator] context manager. -It configures the code generator by combining configuration options from the -environment (e.g. a CMake build system) with options specified in the script, -and infers the names of the output files from the script's name. -It then prepares and returns a code generation [context][pystencilssfg.SfgContext]. -This context may then be passed to a [composer][pystencilssfg.SfgComposer], -which provides a convenient interface for constructing the source files. - -To start, place the following code in a Python script, e.g. `kernels.py`: - -```Python -from pystencilssfg import SourceFileGenerator, SfgConfiguration, SfgComposer - -sfg_config = SfgConfiguration() -with SourceFileGenerator(sfg_config) as ctx: - sfg = SfgComposer(ctx) - -``` - -The source file is constructed within the context manager's managed region. -During execution of the script, when the region ends, a header/source file pair -`kernels.h` and `kernels.cpp` will be written to the file system next to your script. -Execute the script as-is and inspect the generated files, which will of course -still be empty. - -A few notes on configuration: - - - The [SourceFileGenerator][pystencilssfg.SourceFileGenerator] parses the script's command line arguments - for configuration options (refer to [CLI and Build System Integration](cli_and_build_system.md)). - If you intend to use command-line parameters in your - generation script, use [`sfg.context.argv`][pystencilssfg.SfgContext.argv] instead of `sys.argv`. - There, all arguments meant for the code generator are already removed. - - The code generator's configuration is consolidated from a global project configuration which may - be provided by the build system; a number of command line arguments; and the - [SfgConfiguration][pystencilssfg.SfgConfiguration] provided in the script. - The project configuration may safely be overridden by the latter two; however, conflicts - between command-line arguments and the configuration defined in the script will cause - an exception to be thrown. - -## Using the Composer - -The object `sfg` constructed in above snippet is an instance of [SfgComposer][pystencilssfg.SfgComposer]. -The composer is the central part of the user front-end of *pystencils-sfg*. -It provides an interface for constructing source files that attempts to closely mimic -C++ syntactic structures within Python. -Here is an overview of its various functions: - -### Includes and Definitions - -With [`SfgComposer.include`][pystencilssfg.SfgComposer.include], the code generator can be instructed -to include header files. - -```Python -with SourceFileGenerator(sfg_config) as ctx: - sfg = SfgComposer(ctx) - # ... - sfg.include("<vector>") - sfg.incldue("custom_header.h") -``` - -### Adding Kernels - -`pystencils`-generated kernels are managed in -[kernel namespaces][pystencilssfg.source_components.SfgKernelNamespace]. -The default kernel namespace is called `kernels` and is available via -[`SfgComposer.kernels`][pystencilssfg.SfgComposer.kernels]. -Adding an existing `pystencils` AST, or creating one from a list of assignments, is possible -through [`add`][pystencilssfg.source_components.SfgKernelNamespace.add] -and [`create`][pystencilssfg.source_components.SfgKernelNamespace.create]. -The latter is a wrapper around -[`pystencils.create_kernel`]( -https://pycodegen.pages.i10git.cs.fau.de/pystencils/sphinx/kernel_compile_and_call.html#pystencils.create_kernel -). -Both functions return a [kernel handle][pystencilssfg.source_components.SfgKernelHandle] -through which the kernel can be accessed, e.g. for calling it in a function. - -If required, use [`SfgComposer.kernel_namespace`][pystencilssfg.SfgComposer.kernel_namespace] -to access other kernel namespaces than the default one. - -```Python -with SourceFileGenerator(sfg_config) as ctx: - sfg = SfgComposer(ctx) - # ... - - ast = ps.create_kernel(assignments, config) - khandle = sfg.kernels.add(ast, "kernel_a") - - # is equivalent to - - khandle = sfg.kernels.create(assignments, "kernel_a", config) - - # You may use a different namespace - nspace = sfg.kernel_namespace("group_of_kernels") - nspace.create(assignments, "kernel_a", config) -``` - -### Building Functions - -[Functions][pystencilssfg.source_components.SfgFunction] form the link between your `pystencils` kernels -and your C++ framework. A function in *pystencils-sfg* translates to a simple C++ function, and should -fulfill just the following tasks: - - - Extract kernel parameters (pointers, sizes, strides, numerical coefficients) - from C++ objects (like fields, vectors, other data containers) - - Call one or more kernels in sequence or in conditional branches - -It is the philosophy of this project that anything more complicated than this should happen in handwritten -code; these generated functions are merely meant to close the remaining gap. - -The composer provides an interface for constructing functions that tries to mimic the look of the generated C++ -code. -Use [`SfgComposer.function`][pystencilssfg.SfgComposer.function] to create a function, -and [`SfgComposer.call`][pystencilssfg.SfgComposer.call] to call a kernel by its handle: - -```Python -with SourceFileGenerator(sfg_config) as ctx: - sfg = SfgComposer(ctx) - # ... - - sfg.function("MyFunction")( - sfg.call(khandle) - ) -``` - -Note the special syntax: To mimic the look of a C++ function, the composer uses a sequence of two calls -to construct the function. - -The function body may further be populated with the following things: - -#### Parameter Mappings - -Extract kernel parameters from C++ objects: - - - [`map_param`][pystencilssfg.SfgComposer.map_param]: Add a single line of code to define one parameter - depending on one other. - - [`map_field`][pystencilssfg.SfgComposer.map_field] maps a pystencils - [`Field`](https://pycodegen.pages.i10git.cs.fau.de/pystencils/sphinx/field.html) - to a field data structure providing the necessary pointers, sizes and stride information. - The field data structure must be provided as an instance of a subclass of - [`SrcField`][pystencilssfg.source_concepts.SrcField]. - Currently, *pystencils-sfg* provides mappings to - [`std::vector`](https://en.cppreference.com/w/cpp/container/vector) - (via [`std_vector_ref`][pystencilssfg.source_concepts.cpp.std_vector_ref]) - and - [`std::mdspan`](https://en.cppreference.com/w/cpp/container/mdspan) - (via [`mdspan_ref`][pystencilssfg.source_concepts.cpp.mdspan_ref]) - from the C++ standard library. - - [`map_vector`][pystencilssfg.SfgComposer.map_vector] maps a sequence of scalar numerical values - (given as `pystencils.TypedSymbol`s) to a vector data type. Currently, only `std::vector` is provided. - -#### Conditional Branches - -A conditonal branch may be added with [`SfgComposer.branch`][pystencilssfg.SfgComposer.branch] -using a special syntax: - -```Python -with SourceFileGenerator(sfg_config) as ctx: - sfg = SfgComposer(ctx) - # ... - - sfg.function("myFunction")( - # ... - sfg.branch("condition")( - # then-body - )( - # else-body (may be omitted) - ) - ) - -``` \ No newline at end of file diff --git a/docs/usage/index.md b/docs/usage/index.md deleted file mode 100644 index d4ceba027d6705188463f46a553698983ab04906..0000000000000000000000000000000000000000 --- a/docs/usage/index.md +++ /dev/null @@ -1,14 +0,0 @@ -# User Guides - -These pages provide an overview of how to use the pystencils Source File Generator. -A basic understanding of [pystencils](https://pycodegen.pages.i10git.cs.fau.de/pystencils/index.html) -is required. - -## Guides - - - [Writing Generator Scripts](generator_scripts.md) explains about the primary interface of *pystencils-sfg*: - Generator scripts, which are Python scripts that, when executed, emit *pystencils*-generated code to a header/source - file pair with the same name as the script. - - [In-Depth: Building Source Files](building.md) - - [CLI and Build System Integration](cli_and_build_system.md) - - [Tips And Tricks](tips_n_tricks.md): A collection of various tricks that might come in handy when working with *pystencils-sfg*. \ No newline at end of file diff --git a/integration/MakeDemo/kernels.py b/integration/MakeDemo/kernels.py index 43c0f858cdd6b9bf58566fcad342713d48e81394..87f2920b5c788a5031ddbe67a71e788a980183c7 100644 --- a/integration/MakeDemo/kernels.py +++ b/integration/MakeDemo/kernels.py @@ -5,7 +5,7 @@ import sympy as sp from pystencils import fields, kernel from pystencilssfg import SourceFileGenerator, SfgConfiguration, SfgComposer -from pystencilssfg.source_concepts.cpp import mdspan_ref +from pystencilssfg.lang.cpp import mdspan_ref sfg_config = SfgConfiguration( outer_namespace="make_demo" @@ -22,7 +22,7 @@ Author: Frederik Hennig <frederik.hennig@fau.de>""") sfg.namespace("jacobi") - u_src, u_dst, f = fields("u_src, u_dst, f(1) : double[2D]", layout="fzyx") + u_src, u_dst, f = fields("u_src, u_dst, f : double[2D]", layout="fzyx") h = sp.Symbol("h") @kernel diff --git a/integration/TestSequencing.py b/integration/TestSequencing.py index 3a0e040bfb0be6d97c1e5896ee0a201c905ec466..b25593a114082ab419de632b48478b0770f079b6 100644 --- a/integration/TestSequencing.py +++ b/integration/TestSequencing.py @@ -9,13 +9,11 @@ with SourceFileGenerator() as ctx: lb_config = LBMConfig(streaming_pattern='esotwist') lb_ast_even = create_lb_ast(lbm_config=lb_config, timestep=Timestep.EVEN) - lb_ast_even.function_name = "streamCollide_even" lb_ast_odd = create_lb_ast(lbm_config=lb_config, timestep=Timestep.ODD) - lb_ast_odd.function_name = "streamCollide_odd" - kernel_even = sfg.kernels.add(lb_ast_even) - kernel_odd = sfg.kernels.add(lb_ast_odd) + kernel_even = sfg.kernels.add(lb_ast_even, "lb_even") + kernel_odd = sfg.kernels.add(lb_ast_odd, "lb_odd") sfg.function("myFunction")( sfg.branch("(timestep & 1) ^ 1")( diff --git a/integration/test_class_composer.py b/integration/test_class_composer.py index 07039b7473b0b2cc7b9ead2eaa2af2e5d2dbdaf6..2a028b0e48175dc950798006fdf02ace1f408a9a 100644 --- a/integration/test_class_composer.py +++ b/integration/test_class_composer.py @@ -2,7 +2,6 @@ from pystencilssfg import SourceFileGenerator, SfgConfiguration, SfgComposer from pystencilssfg.configuration import SfgCodeStyle from pystencilssfg.composer import SfgClassComposer -from pystencilssfg.source_concepts import SrcObject from pystencils import fields, kernel @@ -19,7 +18,6 @@ f, g = fields("f, g(1): double[2D]") with SourceFileGenerator(sfg_config) as ctx: sfg = SfgComposer(ctx) - c = SfgClassComposer(ctx) @kernel def assignments(): @@ -27,29 +25,29 @@ with SourceFileGenerator(sfg_config) as ctx: khandle = sfg.kernels.create(assignments) - c.struct("DataStruct")( - SrcObject("coord", "uint32_t"), - SrcObject("value", "float") + sfg.struct("DataStruct")( + sfg.var("coord", "uint32_t"), + sfg.var("value", "float") ), - c.klass("MyClass", bases=("MyBaseClass",))( + sfg.klass("MyClass", bases=("MyBaseClass",))( # class body sequencer - c.constructor(SrcObject("a", "int")) + sfg.constructor(sfg.var("a", "int")) .init("a_(a)") .body( 'cout << "Hi!" << endl;' ), - c.private( - c.var("a_", "int"), + sfg.private( + sfg.var("a_", "int"), - c.method("getX", returns="int")( + sfg.method("getX", returns="int")( "return 2.0;" ) ), - c.public( + sfg.public( "using xtype = uint8_t;" ) ) diff --git a/integration/test_classes.py b/integration/test_classes.py index 7db94d2db5edf6d1a20450c9346a9ec92fd6fb7e..4be345197965ae8b15353f3ce0471e265d28b35d 100644 --- a/integration/test_classes.py +++ b/integration/test_classes.py @@ -1,9 +1,8 @@ # type: ignore from pystencilssfg import SourceFileGenerator, SfgConfiguration, SfgComposer from pystencilssfg.configuration import SfgCodeStyle -from pystencilssfg.types import SrcType -from pystencilssfg.source_concepts import SrcObject -from pystencilssfg.source_components import SfgClass, SfgMemberVariable, SfgConstructor, SfgMethod, SfgVisibility +from pystencils.types import PsCustomType +from pystencilssfg.ir.source_components import SfgClass, SfgMemberVariable, SfgConstructor, SfgMethod from pystencils import fields, kernel @@ -38,7 +37,7 @@ with SourceFileGenerator(sfg_config) as ctx: sfg.seq( "return -1.0;" ), - return_type=SrcType("double"), + return_type="double", inline=True, const=True )) @@ -48,20 +47,20 @@ with SourceFileGenerator(sfg_config) as ctx: sfg.seq( "return 2.0f;" ), - return_type=SrcType("float"), + return_type="float", inline=False, const=True )) cls.default.append_member( SfgMemberVariable( - "stuff", "std::vector< int >" + "stuff", PsCustomType("std::vector< int > &") ) ) cls.default.append_member( SfgConstructor( - [SrcObject("stuff", "std::vector< int > &")], + [sfg.var("stuff", PsCustomType("std::vector< int > &"))], ["stuff_(stuff)"] ) ) diff --git a/integration/test_cuda.py b/integration/test_cuda.py new file mode 100644 index 0000000000000000000000000000000000000000..56a478c7b08e684fb03398b5b7cb951982336fe8 --- /dev/null +++ b/integration/test_cuda.py @@ -0,0 +1,20 @@ +from pystencils import Target, CreateKernelConfig, create_kernel, no_jit +from lbmpy import create_lb_update_rule, LBMOptimisation +from pystencilssfg import SourceFileGenerator, SfgConfiguration +from pystencilssfg.lang.cpp import mdspan_ref + +sfg_config = SfgConfiguration( + output_directory="out/test_cuda", + outer_namespace="gen_code", + impl_extension="cu" +) + +with SourceFileGenerator(sfg_config) as sfg: + gen_config = CreateKernelConfig(target=Target.CUDA, jit=no_jit) + opt = LBMOptimisation(field_layout="fzyx") + update = create_lb_update_rule() + kernel = sfg.kernels.create(update, "lbm_update", gen_config) + + sfg.function("lb_update")( + sfg.call(kernel) + ) diff --git a/integration/test_sycl.py b/integration/test_sycl.py new file mode 100644 index 0000000000000000000000000000000000000000..d49edc71bbf8c10afa57bb7abbe9240043f00c5b --- /dev/null +++ b/integration/test_sycl.py @@ -0,0 +1,21 @@ +from pystencils import Target, CreateKernelConfig, create_kernel, no_jit +from lbmpy import create_lb_update_rule, LBMOptimisation +from pystencilssfg import SourceFileGenerator, SfgConfiguration, SfgOutputMode +from pystencilssfg.lang.cpp import mdspan_ref + +sfg_config = SfgConfiguration( + output_directory="out/test_sycl", + outer_namespace="gen_code", + impl_extension="ipp", + output_mode=SfgOutputMode.INLINE +) + +with SourceFileGenerator(sfg_config) as sfg: + gen_config = CreateKernelConfig(target=Target.SYCL, jit=no_jit) + opt = LBMOptimisation(field_layout="fzyx") + update = create_lb_update_rule() + kernel = sfg.kernels.create(update, "lbm_update", gen_config) + + sfg.function("lb_update")( + sfg.call(kernel) + ) diff --git a/mkdocs.yml b/mkdocs.yml deleted file mode 100644 index 86f75a5ba6a83cceb8f070c212c0f81565ecc308..0000000000000000000000000000000000000000 --- a/mkdocs.yml +++ /dev/null @@ -1,67 +0,0 @@ -site_name: pystencils Source File Generator Documentation -site_author: Frederik Hennig -copyright: © 2023 Frederik Hennig - -repo_name: GitLab -repo_url: https://i10git.cs.fau.de/pycodegen/pystencils-sfg - - -theme: - name: material - features: - - navigation.tabs - - content.code.copy - palette: - scheme: slate - primary: deep purple - -extra_css: - - css/mkdocstrings.css - -plugins: - - search - - autorefs - - mkdocstrings: - default_handler: python - handlers: - python: - paths: [src] - options: - heading_level: 2 - members_order: source - group_by_category: False - show_root_heading: True - show_root_full_path: True - show_symbol_type_heading: True - show_symbol_type_toc: True - show_source: False - show_signature_annotations: True - signature_crossrefs: True - -markdown_extensions: - - pymdownx.highlight: - anchor_linenums: true - - pymdownx.superfences - -nav: - - Home: index.md - - 'User Guides': - - 'Overview': usage/index.md - - 'Writing Generator Scripts': usage/generator_scripts.md - - 'In-Depth: Building Source Files': usage/building.md - - 'CLI and Build System Integration': usage/cli_and_build_system.md - - 'Tips and Tricks': usage/tips_n_tricks.md - - 'API Documentation': - - 'Overview': api/index.md - - 'Front End': - - 'Source File Generator': api/generator.md - - 'Code Generation Context': api/context.md - - 'Composer': api/composer.md - - 'Source File Modelling': - - 'Source File Components': api/source_components.md - - 'Kernel Call Tree': api/tree.md - - 'High-Level Language Concepts': - - 'Base Classes': 'api/source_objects.md' - - 'C++ Standard Library': 'api/cpp_std.md' - - 'Code Generation': - - 'Emission and Printing': api/emission.md diff --git a/pdm.lock b/pdm.lock deleted file mode 100644 index 2334d1622c09b5b837895ae626a248f71f46b03b..0000000000000000000000000000000000000000 --- a/pdm.lock +++ /dev/null @@ -1,1011 +0,0 @@ -# This file is @generated by PDM. -# It is not intended for manual editing. - -[metadata] -groups = ["default", "code_quality", "docs", "interactive"] -strategy = ["cross_platform", "inherit_metadata"] -lock_version = "4.4.1" -content_hash = "sha256:2c854f8da4b29c3080cd89c774409f95c47d3532c953cf10ecaa67d0b77ff9cf" - -[[package]] -name = "appdirs" -version = "1.4.4" -summary = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -groups = ["default"] -files = [ - {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, - {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, -] - -[[package]] -name = "asttokens" -version = "2.4.1" -summary = "Annotate AST trees with source code positions" -groups = ["interactive"] -dependencies = [ - "six>=1.12.0", -] -files = [ - {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, - {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, -] - -[[package]] -name = "babel" -version = "2.14.0" -requires_python = ">=3.7" -summary = "Internationalization utilities" -groups = ["docs"] -files = [ - {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, - {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, -] - -[[package]] -name = "certifi" -version = "2023.11.17" -requires_python = ">=3.6" -summary = "Python package for providing Mozilla's CA Bundle." -groups = ["docs"] -files = [ - {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, - {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.3.2" -requires_python = ">=3.7.0" -summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -groups = ["docs"] -files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, -] - -[[package]] -name = "click" -version = "8.1.7" -requires_python = ">=3.7" -summary = "Composable command line interface toolkit" -groups = ["docs"] -dependencies = [ - "colorama; platform_system == \"Windows\"", -] -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[[package]] -name = "colorama" -version = "0.4.6" -requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -summary = "Cross-platform colored terminal text." -groups = ["docs", "interactive"] -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "decorator" -version = "5.1.1" -requires_python = ">=3.5" -summary = "Decorators for Humans" -groups = ["interactive"] -files = [ - {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, - {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.2.0" -requires_python = ">=3.7" -summary = "Backport of PEP 654 (exception groups)" -groups = ["interactive"] -marker = "python_version < \"3.11\"" -files = [ - {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, - {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, -] - -[[package]] -name = "executing" -version = "2.0.1" -requires_python = ">=3.5" -summary = "Get the currently executing AST node of a frame, and other information" -groups = ["interactive"] -files = [ - {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, - {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, -] - -[[package]] -name = "flake8" -version = "7.0.0" -requires_python = ">=3.8.1" -summary = "the modular source code checker: pep8 pyflakes and co" -groups = ["code_quality"] -dependencies = [ - "mccabe<0.8.0,>=0.7.0", - "pycodestyle<2.12.0,>=2.11.0", - "pyflakes<3.3.0,>=3.2.0", -] -files = [ - {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, - {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, -] - -[[package]] -name = "ghp-import" -version = "2.1.0" -summary = "Copy your docs directly to the gh-pages branch." -groups = ["docs"] -dependencies = [ - "python-dateutil>=2.8.1", -] -files = [ - {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, - {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, -] - -[[package]] -name = "griffe" -version = "0.38.1" -requires_python = ">=3.8" -summary = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." -groups = ["docs"] -dependencies = [ - "colorama>=0.4", -] -files = [ - {file = "griffe-0.38.1-py3-none-any.whl", hash = "sha256:334c79d3b5964ade65c05dfcaf53518c576dedd387aaba5c9fd71212f34f1483"}, - {file = "griffe-0.38.1.tar.gz", hash = "sha256:bd68d7da7f3d87bc57eb9962b250db123efd9bbcc06c11c1a91b6e583b2a9361"}, -] - -[[package]] -name = "idna" -version = "3.6" -requires_python = ">=3.5" -summary = "Internationalized Domain Names in Applications (IDNA)" -groups = ["docs"] -files = [ - {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, - {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, -] - -[[package]] -name = "ipython" -version = "8.19.0" -requires_python = ">=3.10" -summary = "IPython: Productive Interactive Computing" -groups = ["interactive"] -dependencies = [ - "colorama; sys_platform == \"win32\"", - "decorator", - "exceptiongroup; python_version < \"3.11\"", - "jedi>=0.16", - "matplotlib-inline", - "pexpect>4.3; sys_platform != \"win32\"", - "prompt-toolkit<3.1.0,>=3.0.41", - "pygments>=2.4.0", - "stack-data", - "traitlets>=5", -] -files = [ - {file = "ipython-8.19.0-py3-none-any.whl", hash = "sha256:2f55d59370f59d0d2b2212109fe0e6035cfea436b1c0e6150ad2244746272ec5"}, - {file = "ipython-8.19.0.tar.gz", hash = "sha256:ac4da4ecf0042fb4e0ce57c60430c2db3c719fa8bdf92f8631d6bd8a5785d1f0"}, -] - -[[package]] -name = "jedi" -version = "0.19.1" -requires_python = ">=3.6" -summary = "An autocompletion tool for Python that can be used for text editors." -groups = ["interactive"] -dependencies = [ - "parso<0.9.0,>=0.8.3", -] -files = [ - {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, - {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, -] - -[[package]] -name = "jinja2" -version = "3.1.2" -requires_python = ">=3.7" -summary = "A very fast and expressive template engine." -groups = ["docs"] -dependencies = [ - "MarkupSafe>=2.0", -] -files = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, -] - -[[package]] -name = "joblib" -version = "1.3.2" -requires_python = ">=3.7" -summary = "Lightweight pipelining with Python functions" -groups = ["default"] -files = [ - {file = "joblib-1.3.2-py3-none-any.whl", hash = "sha256:ef4331c65f239985f3f2220ecc87db222f08fd22097a3dd5698f693875f8cbb9"}, - {file = "joblib-1.3.2.tar.gz", hash = "sha256:92f865e621e17784e7955080b6d042489e3b8e294949cc44c6eac304f59772b1"}, -] - -[[package]] -name = "markdown" -version = "3.5.1" -requires_python = ">=3.8" -summary = "Python implementation of John Gruber's Markdown." -groups = ["docs"] -files = [ - {file = "Markdown-3.5.1-py3-none-any.whl", hash = "sha256:5874b47d4ee3f0b14d764324d2c94c03ea66bee56f2d929da9f2508d65e722dc"}, - {file = "Markdown-3.5.1.tar.gz", hash = "sha256:b65d7beb248dc22f2e8a31fb706d93798093c308dc1aba295aedeb9d41a813bd"}, -] - -[[package]] -name = "markupsafe" -version = "2.1.3" -requires_python = ">=3.7" -summary = "Safely add untrusted strings to HTML/XML markup." -groups = ["docs"] -files = [ - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, - {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, -] - -[[package]] -name = "matplotlib-inline" -version = "0.1.6" -requires_python = ">=3.5" -summary = "Inline Matplotlib backend for Jupyter" -groups = ["interactive"] -dependencies = [ - "traitlets", -] -files = [ - {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"}, - {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, -] - -[[package]] -name = "mccabe" -version = "0.7.0" -requires_python = ">=3.6" -summary = "McCabe checker, plugin for flake8" -groups = ["code_quality"] -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "mergedeep" -version = "1.3.4" -requires_python = ">=3.6" -summary = "A deep merge function for ðŸ." -groups = ["docs"] -files = [ - {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, - {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, -] - -[[package]] -name = "mkdocs" -version = "1.5.3" -requires_python = ">=3.7" -summary = "Project documentation with Markdown." -groups = ["docs"] -dependencies = [ - "click>=7.0", - "colorama>=0.4; platform_system == \"Windows\"", - "ghp-import>=1.0", - "jinja2>=2.11.1", - "markdown>=3.2.1", - "markupsafe>=2.0.1", - "mergedeep>=1.3.4", - "packaging>=20.5", - "pathspec>=0.11.1", - "platformdirs>=2.2.0", - "pyyaml-env-tag>=0.1", - "pyyaml>=5.1", - "watchdog>=2.0", -] -files = [ - {file = "mkdocs-1.5.3-py3-none-any.whl", hash = "sha256:3b3a78e736b31158d64dbb2f8ba29bd46a379d0c6e324c2246c3bc3d2189cfc1"}, - {file = "mkdocs-1.5.3.tar.gz", hash = "sha256:eb7c99214dcb945313ba30426c2451b735992c73c2e10838f76d09e39ff4d0e2"}, -] - -[[package]] -name = "mkdocs-autorefs" -version = "0.5.0" -requires_python = ">=3.8" -summary = "Automatically link across pages in MkDocs." -groups = ["docs"] -dependencies = [ - "Markdown>=3.3", - "mkdocs>=1.1", -] -files = [ - {file = "mkdocs_autorefs-0.5.0-py3-none-any.whl", hash = "sha256:7930fcb8ac1249f10e683967aeaddc0af49d90702af111a5e390e8b20b3d97ff"}, - {file = "mkdocs_autorefs-0.5.0.tar.gz", hash = "sha256:9a5054a94c08d28855cfab967ada10ed5be76e2bfad642302a610b252c3274c0"}, -] - -[[package]] -name = "mkdocs-material" -version = "9.5.3" -requires_python = ">=3.8" -summary = "Documentation that simply works" -groups = ["docs"] -dependencies = [ - "babel~=2.10", - "colorama~=0.4", - "jinja2~=3.0", - "markdown~=3.2", - "mkdocs-material-extensions~=1.3", - "mkdocs~=1.5.3", - "paginate~=0.5", - "pygments~=2.16", - "pymdown-extensions~=10.2", - "regex>=2022.4", - "requests~=2.26", -] -files = [ - {file = "mkdocs_material-9.5.3-py3-none-any.whl", hash = "sha256:76c93a8525cceb0b395b9cedab3428bf518cf6439adef2b940f1c1574b775d89"}, - {file = "mkdocs_material-9.5.3.tar.gz", hash = "sha256:5899219f422f0a6de784232d9d40374416302ffae3c160cacc72969fcc1ee372"}, -] - -[[package]] -name = "mkdocs-material-extensions" -version = "1.3.1" -requires_python = ">=3.8" -summary = "Extension pack for Python Markdown and MkDocs Material." -groups = ["docs"] -files = [ - {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, - {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, -] - -[[package]] -name = "mkdocstrings" -version = "0.24.0" -requires_python = ">=3.8" -summary = "Automatic documentation from sources, for MkDocs." -groups = ["docs"] -dependencies = [ - "Jinja2>=2.11.1", - "Markdown>=3.3", - "MarkupSafe>=1.1", - "click>=7.0", - "mkdocs-autorefs>=0.3.1", - "mkdocs>=1.4", - "platformdirs>=2.2.0", - "pymdown-extensions>=6.3", -] -files = [ - {file = "mkdocstrings-0.24.0-py3-none-any.whl", hash = "sha256:f4908560c10f587326d8f5165d1908817b2e280bbf707607f601c996366a2264"}, - {file = "mkdocstrings-0.24.0.tar.gz", hash = "sha256:222b1165be41257b494a9d29b14135d2b7ca43f38161d5b10caae03b87bd4f7e"}, -] - -[[package]] -name = "mkdocstrings-python" -version = "1.7.5" -requires_python = ">=3.8" -summary = "A Python handler for mkdocstrings." -groups = ["docs"] -dependencies = [ - "griffe>=0.37", - "mkdocstrings>=0.20", -] -files = [ - {file = "mkdocstrings_python-1.7.5-py3-none-any.whl", hash = "sha256:5f6246026353f0c0785135db70c3fe9a5d9318990fc7ceb11d62097b8ffdd704"}, - {file = "mkdocstrings_python-1.7.5.tar.gz", hash = "sha256:c7d143728257dbf1aa550446555a554b760dcd40a763f077189d298502b800be"}, -] - -[[package]] -name = "mkdocstrings" -version = "0.24.0" -extras = ["python"] -requires_python = ">=3.8" -summary = "Automatic documentation from sources, for MkDocs." -groups = ["docs"] -dependencies = [ - "mkdocstrings-python>=0.5.2", - "mkdocstrings==0.24.0", -] -files = [ - {file = "mkdocstrings-0.24.0-py3-none-any.whl", hash = "sha256:f4908560c10f587326d8f5165d1908817b2e280bbf707607f601c996366a2264"}, - {file = "mkdocstrings-0.24.0.tar.gz", hash = "sha256:222b1165be41257b494a9d29b14135d2b7ca43f38161d5b10caae03b87bd4f7e"}, -] - -[[package]] -name = "mpmath" -version = "1.3.0" -summary = "Python library for arbitrary-precision floating-point arithmetic" -groups = ["default"] -files = [ - {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, - {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, -] - -[[package]] -name = "mypy" -version = "1.8.0" -requires_python = ">=3.8" -summary = "Optional static typing for Python" -groups = ["code_quality"] -dependencies = [ - "mypy-extensions>=1.0.0", - "tomli>=1.1.0; python_version < \"3.11\"", - "typing-extensions>=4.1.0", -] -files = [ - {file = "mypy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3"}, - {file = "mypy-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4"}, - {file = "mypy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d"}, - {file = "mypy-1.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9"}, - {file = "mypy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410"}, - {file = "mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae"}, - {file = "mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3"}, - {file = "mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817"}, - {file = "mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d"}, - {file = "mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835"}, - {file = "mypy-1.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd"}, - {file = "mypy-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"}, - {file = "mypy-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218"}, - {file = "mypy-1.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3"}, - {file = "mypy-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e"}, - {file = "mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d"}, - {file = "mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07"}, -] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -requires_python = ">=3.5" -summary = "Type system extensions for programs checked with the mypy type checker." -groups = ["code_quality"] -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - -[[package]] -name = "numpy" -version = "1.26.3" -requires_python = ">=3.9" -summary = "Fundamental package for array computing in Python" -groups = ["default"] -files = [ - {file = "numpy-1.26.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:806dd64230dbbfaca8a27faa64e2f414bf1c6622ab78cc4264f7f5f028fee3bf"}, - {file = "numpy-1.26.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02f98011ba4ab17f46f80f7f8f1c291ee7d855fcef0a5a98db80767a468c85cd"}, - {file = "numpy-1.26.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d45b3ec2faed4baca41c76617fcdcfa4f684ff7a151ce6fc78ad3b6e85af0a6"}, - {file = "numpy-1.26.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdd2b45bf079d9ad90377048e2747a0c82351989a2165821f0c96831b4a2a54b"}, - {file = "numpy-1.26.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:211ddd1e94817ed2d175b60b6374120244a4dd2287f4ece45d49228b4d529178"}, - {file = "numpy-1.26.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1240f767f69d7c4c8a29adde2310b871153df9b26b5cb2b54a561ac85146485"}, - {file = "numpy-1.26.3-cp310-cp310-win32.whl", hash = "sha256:21a9484e75ad018974a2fdaa216524d64ed4212e418e0a551a2d83403b0531d3"}, - {file = "numpy-1.26.3-cp310-cp310-win_amd64.whl", hash = "sha256:9e1591f6ae98bcfac2a4bbf9221c0b92ab49762228f38287f6eeb5f3f55905ce"}, - {file = "numpy-1.26.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b831295e5472954104ecb46cd98c08b98b49c69fdb7040483aff799a755a7374"}, - {file = "numpy-1.26.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9e87562b91f68dd8b1c39149d0323b42e0082db7ddb8e934ab4c292094d575d6"}, - {file = "numpy-1.26.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c66d6fec467e8c0f975818c1796d25c53521124b7cfb760114be0abad53a0a2"}, - {file = "numpy-1.26.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f25e2811a9c932e43943a2615e65fc487a0b6b49218899e62e426e7f0a57eeda"}, - {file = "numpy-1.26.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af36e0aa45e25c9f57bf684b1175e59ea05d9a7d3e8e87b7ae1a1da246f2767e"}, - {file = "numpy-1.26.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:51c7f1b344f302067b02e0f5b5d2daa9ed4a721cf49f070280ac202738ea7f00"}, - {file = "numpy-1.26.3-cp311-cp311-win32.whl", hash = "sha256:7ca4f24341df071877849eb2034948459ce3a07915c2734f1abb4018d9c49d7b"}, - {file = "numpy-1.26.3-cp311-cp311-win_amd64.whl", hash = "sha256:39763aee6dfdd4878032361b30b2b12593fb445ddb66bbac802e2113eb8a6ac4"}, - {file = "numpy-1.26.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a7081fd19a6d573e1a05e600c82a1c421011db7935ed0d5c483e9dd96b99cf13"}, - {file = "numpy-1.26.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12c70ac274b32bc00c7f61b515126c9205323703abb99cd41836e8125ea0043e"}, - {file = "numpy-1.26.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f784e13e598e9594750b2ef6729bcd5a47f6cfe4a12cca13def35e06d8163e3"}, - {file = "numpy-1.26.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f24750ef94d56ce6e33e4019a8a4d68cfdb1ef661a52cdaee628a56d2437419"}, - {file = "numpy-1.26.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:77810ef29e0fb1d289d225cabb9ee6cf4d11978a00bb99f7f8ec2132a84e0166"}, - {file = "numpy-1.26.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8ed07a90f5450d99dad60d3799f9c03c6566709bd53b497eb9ccad9a55867f36"}, - {file = "numpy-1.26.3-cp312-cp312-win32.whl", hash = "sha256:f73497e8c38295aaa4741bdfa4fda1a5aedda5473074369eca10626835445511"}, - {file = "numpy-1.26.3-cp312-cp312-win_amd64.whl", hash = "sha256:da4b0c6c699a0ad73c810736303f7fbae483bcb012e38d7eb06a5e3b432c981b"}, - {file = "numpy-1.26.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3c67423b3703f8fbd90f5adaa37f85b5794d3366948efe9a5190a5f3a83fc34e"}, - {file = "numpy-1.26.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46f47ee566d98849323f01b349d58f2557f02167ee301e5e28809a8c0e27a2d0"}, - {file = "numpy-1.26.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a8474703bffc65ca15853d5fd4d06b18138ae90c17c8d12169968e998e448bb5"}, - {file = "numpy-1.26.3.tar.gz", hash = "sha256:697df43e2b6310ecc9d95f05d5ef20eacc09c7c4ecc9da3f235d39e71b7da1e4"}, -] - -[[package]] -name = "packaging" -version = "23.2" -requires_python = ">=3.7" -summary = "Core utilities for Python packages" -groups = ["docs"] -files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, -] - -[[package]] -name = "paginate" -version = "0.5.6" -summary = "Divides large result sets into pages for easier browsing" -groups = ["docs"] -files = [ - {file = "paginate-0.5.6.tar.gz", hash = "sha256:5e6007b6a9398177a7e1648d04fdd9f8c9766a1a945bceac82f1929e8c78af2d"}, -] - -[[package]] -name = "parso" -version = "0.8.3" -requires_python = ">=3.6" -summary = "A Python Parser" -groups = ["interactive"] -files = [ - {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, - {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -requires_python = ">=3.8" -summary = "Utility library for gitignore style pattern matching of file paths." -groups = ["docs"] -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "pexpect" -version = "4.9.0" -summary = "Pexpect allows easy control of interactive console applications." -groups = ["interactive"] -marker = "sys_platform != \"win32\"" -dependencies = [ - "ptyprocess>=0.5", -] -files = [ - {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, - {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, -] - -[[package]] -name = "platformdirs" -version = "4.1.0" -requires_python = ">=3.8" -summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -groups = ["docs"] -files = [ - {file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"}, - {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"}, -] - -[[package]] -name = "prompt-toolkit" -version = "3.0.43" -requires_python = ">=3.7.0" -summary = "Library for building powerful interactive command lines in Python" -groups = ["interactive"] -dependencies = [ - "wcwidth", -] -files = [ - {file = "prompt_toolkit-3.0.43-py3-none-any.whl", hash = "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6"}, - {file = "prompt_toolkit-3.0.43.tar.gz", hash = "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d"}, -] - -[[package]] -name = "ptyprocess" -version = "0.7.0" -summary = "Run a subprocess in a pseudo terminal" -groups = ["interactive"] -marker = "sys_platform != \"win32\"" -files = [ - {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, - {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, -] - -[[package]] -name = "pure-eval" -version = "0.2.2" -summary = "Safely evaluate AST nodes without side effects" -groups = ["interactive"] -files = [ - {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, - {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, -] - -[[package]] -name = "pycodestyle" -version = "2.11.1" -requires_python = ">=3.8" -summary = "Python style guide checker" -groups = ["code_quality"] -files = [ - {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, - {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, -] - -[[package]] -name = "pyflakes" -version = "3.2.0" -requires_python = ">=3.8" -summary = "passive checker of Python programs" -groups = ["code_quality"] -files = [ - {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, - {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, -] - -[[package]] -name = "pygments" -version = "2.17.2" -requires_python = ">=3.7" -summary = "Pygments is a syntax highlighting package written in Python." -groups = ["docs", "interactive"] -files = [ - {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, - {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, -] - -[[package]] -name = "pymdown-extensions" -version = "10.7" -requires_python = ">=3.8" -summary = "Extension pack for Python Markdown." -groups = ["docs"] -dependencies = [ - "markdown>=3.5", - "pyyaml", -] -files = [ - {file = "pymdown_extensions-10.7-py3-none-any.whl", hash = "sha256:6ca215bc57bc12bf32b414887a68b810637d039124ed9b2e5bd3325cbb2c050c"}, - {file = "pymdown_extensions-10.7.tar.gz", hash = "sha256:c0d64d5cf62566f59e6b2b690a4095c931107c250a8c8e1351c1de5f6b036deb"}, -] - -[[package]] -name = "pystencils" -version = "1.3.3" -requires_python = ">=3.8" -summary = "Speeding up stencil computations on CPUs and GPUs" -groups = ["default"] -dependencies = [ - "appdirs", - "joblib", - "numpy>=1.8.0", - "sympy<=1.11.1,>=1.6", -] -files = [ - {file = "pystencils-1.3.3.tar.gz", hash = "sha256:d066d8dd6eee355671dd6e93454f9a1bf143ab2528f76076ce15f0c15b5d29c7"}, -] - -[[package]] -name = "python-dateutil" -version = "2.8.2" -requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -summary = "Extensions to the standard Python datetime module" -groups = ["docs"] -dependencies = [ - "six>=1.5", -] -files = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, -] - -[[package]] -name = "pyyaml" -version = "6.0.1" -requires_python = ">=3.6" -summary = "YAML parser and emitter for Python" -groups = ["docs"] -files = [ - {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, - {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, - {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, - {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, - {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, - {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, -] - -[[package]] -name = "pyyaml-env-tag" -version = "0.1" -requires_python = ">=3.6" -summary = "A custom YAML tag for referencing environment variables in YAML files. " -groups = ["docs"] -dependencies = [ - "pyyaml", -] -files = [ - {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, - {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, -] - -[[package]] -name = "regex" -version = "2023.12.25" -requires_python = ">=3.7" -summary = "Alternative regular expression module, to replace re." -groups = ["docs"] -files = [ - {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5"}, - {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8"}, - {file = "regex-2023.12.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d865984b3f71f6d0af64d0d88f5733521698f6c16f445bb09ce746c92c97c586"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0eabac536b4cc7f57a5f3d095bfa557860ab912f25965e08fe1545e2ed8b4c"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25a8ad70e716f96e13a637802813f65d8a6760ef48672aa3502f4c24ea8b400"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9b6d73353f777630626f403b0652055ebfe8ff142a44ec2cf18ae470395766e"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9cc99d6946d750eb75827cb53c4371b8b0fe89c733a94b1573c9dd16ea6c9e4"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88d1f7bef20c721359d8675f7d9f8e414ec5003d8f642fdfd8087777ff7f94b5"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cb3fe77aec8f1995611f966d0c656fdce398317f850d0e6e7aebdfe61f40e1cd"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7aa47c2e9ea33a4a2a05f40fcd3ea36d73853a2aae7b4feab6fc85f8bf2c9704"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:df26481f0c7a3f8739fecb3e81bc9da3fcfae34d6c094563b9d4670b047312e1"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c40281f7d70baf6e0db0c2f7472b31609f5bc2748fe7275ea65a0b4601d9b392"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d94a1db462d5690ebf6ae86d11c5e420042b9898af5dcf278bd97d6bda065423"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ba1b30765a55acf15dce3f364e4928b80858fa8f979ad41f862358939bdd1f2f"}, - {file = "regex-2023.12.25-cp310-cp310-win32.whl", hash = "sha256:150c39f5b964e4d7dba46a7962a088fbc91f06e606f023ce57bb347a3b2d4630"}, - {file = "regex-2023.12.25-cp310-cp310-win_amd64.whl", hash = "sha256:09da66917262d9481c719599116c7dc0c321ffcec4b1f510c4f8a066f8768105"}, - {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b9d811f72210fa9306aeb88385b8f8bcef0dfbf3873410413c00aa94c56c2b6"}, - {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d902a43085a308cef32c0d3aea962524b725403fd9373dea18110904003bac97"}, - {file = "regex-2023.12.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d166eafc19f4718df38887b2bbe1467a4f74a9830e8605089ea7a30dd4da8887"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7ad32824b7f02bb3c9f80306d405a1d9b7bb89362d68b3c5a9be53836caebdb"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:636ba0a77de609d6510235b7f0e77ec494d2657108f777e8765efc060094c98c"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fda75704357805eb953a3ee15a2b240694a9a514548cd49b3c5124b4e2ad01b"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f72cbae7f6b01591f90814250e636065850c5926751af02bb48da94dfced7baa"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db2a0b1857f18b11e3b0e54ddfefc96af46b0896fb678c85f63fb8c37518b3e7"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7502534e55c7c36c0978c91ba6f61703faf7ce733715ca48f499d3dbbd7657e0"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e8c7e08bb566de4faaf11984af13f6bcf6a08f327b13631d41d62592681d24fe"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:283fc8eed679758de38fe493b7d7d84a198b558942b03f017b1f94dda8efae80"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f44dd4d68697559d007462b0a3a1d9acd61d97072b71f6d1968daef26bc744bd"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:67d3ccfc590e5e7197750fcb3a2915b416a53e2de847a728cfa60141054123d4"}, - {file = "regex-2023.12.25-cp311-cp311-win32.whl", hash = "sha256:68191f80a9bad283432385961d9efe09d783bcd36ed35a60fb1ff3f1ec2efe87"}, - {file = "regex-2023.12.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d2af3f6b8419661a0c421584cfe8aaec1c0e435ce7e47ee2a97e344b98f794f"}, - {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8a0ccf52bb37d1a700375a6b395bff5dd15c50acb745f7db30415bae3c2b0715"}, - {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c3c4a78615b7762740531c27cf46e2f388d8d727d0c0c739e72048beb26c8a9d"}, - {file = "regex-2023.12.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad83e7545b4ab69216cef4cc47e344d19622e28aabec61574b20257c65466d6a"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7a635871143661feccce3979e1727c4e094f2bdfd3ec4b90dfd4f16f571a87a"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d498eea3f581fbe1b34b59c697512a8baef88212f92e4c7830fcc1499f5b45a5"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43f7cd5754d02a56ae4ebb91b33461dc67be8e3e0153f593c509e21d219c5060"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51f4b32f793812714fd5307222a7f77e739b9bc566dc94a18126aba3b92b98a3"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba99d8077424501b9616b43a2d208095746fb1284fc5ba490139651f971d39d9"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4bfc2b16e3ba8850e0e262467275dd4d62f0d045e0e9eda2bc65078c0110a11f"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8c2c19dae8a3eb0ea45a8448356ed561be843b13cbc34b840922ddf565498c1c"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:60080bb3d8617d96f0fb7e19796384cc2467447ef1c491694850ebd3670bc457"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b77e27b79448e34c2c51c09836033056a0547aa360c45eeeb67803da7b0eedaf"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d"}, - {file = "regex-2023.12.25-cp312-cp312-win32.whl", hash = "sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5"}, - {file = "regex-2023.12.25-cp312-cp312-win_amd64.whl", hash = "sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232"}, - {file = "regex-2023.12.25.tar.gz", hash = "sha256:29171aa128da69afdf4bde412d5bedc335f2ca8fcfe4489038577d05f16181e5"}, -] - -[[package]] -name = "requests" -version = "2.31.0" -requires_python = ">=3.7" -summary = "Python HTTP for Humans." -groups = ["docs"] -dependencies = [ - "certifi>=2017.4.17", - "charset-normalizer<4,>=2", - "idna<4,>=2.5", - "urllib3<3,>=1.21.1", -] -files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, -] - -[[package]] -name = "six" -version = "1.16.0" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -summary = "Python 2 and 3 compatibility utilities" -groups = ["docs", "interactive"] -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - -[[package]] -name = "stack-data" -version = "0.6.3" -summary = "Extract data from python stack frames and tracebacks for informative displays" -groups = ["interactive"] -dependencies = [ - "asttokens>=2.1.0", - "executing>=1.2.0", - "pure-eval", -] -files = [ - {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, - {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, -] - -[[package]] -name = "sympy" -version = "1.11.1" -requires_python = ">=3.8" -summary = "Computer algebra system (CAS) in Python" -groups = ["default"] -dependencies = [ - "mpmath>=0.19", -] -files = [ - {file = "sympy-1.11.1-py3-none-any.whl", hash = "sha256:938f984ee2b1e8eae8a07b884c8b7a1146010040fccddc6539c54f401c8f6fcf"}, - {file = "sympy-1.11.1.tar.gz", hash = "sha256:e32380dce63cb7c0108ed525570092fd45168bdae2faa17e528221ef72e88658"}, -] - -[[package]] -name = "tomli" -version = "2.0.1" -requires_python = ">=3.7" -summary = "A lil' TOML parser" -groups = ["code_quality"] -marker = "python_version < \"3.11\"" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - -[[package]] -name = "traitlets" -version = "5.14.1" -requires_python = ">=3.8" -summary = "Traitlets Python configuration system" -groups = ["interactive"] -files = [ - {file = "traitlets-5.14.1-py3-none-any.whl", hash = "sha256:2e5a030e6eff91737c643231bfcf04a65b0132078dad75e4936700b213652e74"}, - {file = "traitlets-5.14.1.tar.gz", hash = "sha256:8585105b371a04b8316a43d5ce29c098575c2e477850b62b848b964f1444527e"}, -] - -[[package]] -name = "typing-extensions" -version = "4.9.0" -requires_python = ">=3.8" -summary = "Backported and Experimental Type Hints for Python 3.8+" -groups = ["code_quality"] -files = [ - {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, - {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, -] - -[[package]] -name = "urllib3" -version = "2.1.0" -requires_python = ">=3.8" -summary = "HTTP library with thread-safe connection pooling, file post, and more." -groups = ["docs"] -files = [ - {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"}, - {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, -] - -[[package]] -name = "watchdog" -version = "3.0.0" -requires_python = ">=3.7" -summary = "Filesystem events monitoring" -groups = ["docs"] -files = [ - {file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:336adfc6f5cc4e037d52db31194f7581ff744b67382eb6021c868322e32eef41"}, - {file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a70a8dcde91be523c35b2bf96196edc5730edb347e374c7de7cd20c43ed95397"}, - {file = "watchdog-3.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:adfdeab2da79ea2f76f87eb42a3ab1966a5313e5a69a0213a3cc06ef692b0e96"}, - {file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b57a1e730af3156d13b7fdddfc23dea6487fceca29fc75c5a868beed29177ae"}, - {file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7ade88d0d778b1b222adebcc0927428f883db07017618a5e684fd03b83342bd9"}, - {file = "watchdog-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7e447d172af52ad204d19982739aa2346245cc5ba6f579d16dac4bfec226d2e7"}, - {file = "watchdog-3.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:13bbbb462ee42ec3c5723e1205be8ced776f05b100e4737518c67c8325cf6100"}, - {file = "watchdog-3.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8f3ceecd20d71067c7fd4c9e832d4e22584318983cabc013dbf3f70ea95de346"}, - {file = "watchdog-3.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c9d8c8ec7efb887333cf71e328e39cffbf771d8f8f95d308ea4125bf5f90ba64"}, - {file = "watchdog-3.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0e06ab8858a76e1219e68c7573dfeba9dd1c0219476c5a44d5333b01d7e1743a"}, - {file = "watchdog-3.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44"}, - {file = "watchdog-3.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:c07253088265c363d1ddf4b3cdb808d59a0468ecd017770ed716991620b8f77a"}, - {file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:5113334cf8cf0ac8cd45e1f8309a603291b614191c9add34d33075727a967709"}, - {file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:51f90f73b4697bac9c9a78394c3acbbd331ccd3655c11be1a15ae6fe289a8c83"}, - {file = "watchdog-3.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:ba07e92756c97e3aca0912b5cbc4e5ad802f4557212788e72a72a47ff376950d"}, - {file = "watchdog-3.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33"}, - {file = "watchdog-3.0.0-py3-none-win32.whl", hash = "sha256:3ed7c71a9dccfe838c2f0b6314ed0d9b22e77d268c67e015450a29036a81f60f"}, - {file = "watchdog-3.0.0-py3-none-win_amd64.whl", hash = "sha256:4c9956d27be0bb08fc5f30d9d0179a855436e655f046d288e2bcc11adfae893c"}, - {file = "watchdog-3.0.0-py3-none-win_ia64.whl", hash = "sha256:5d9f3a10e02d7371cd929b5d8f11e87d4bad890212ed3901f9b4d68767bee759"}, - {file = "watchdog-3.0.0.tar.gz", hash = "sha256:4d98a320595da7a7c5a18fc48cb633c2e73cda78f93cac2ef42d42bf609a33f9"}, -] - -[[package]] -name = "wcwidth" -version = "0.2.13" -summary = "Measures the displayed width of unicode strings in a terminal" -groups = ["interactive"] -files = [ - {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, - {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, -] diff --git a/pyproject.toml b/pyproject.toml index 8a0029dc1e2bfd0e98ce23732a87e228022f0439..5b4e66b32136796c2f1b2cc2494b4a2054cedad8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,11 +5,11 @@ authors = [ {name = "Frederik Hennig", email = "frederik.hennig@fau.de"}, ] dependencies = [ - "pystencils>=1.3.2", + "pystencils>=2.0.dev0", ] requires-python = ">=3.10" readme = "README.md" -license = {text = "noneyet"} +license = { file = "COPYING.txt" } dynamic = ["version"] [project.scripts] @@ -18,23 +18,25 @@ sfg-cli = "pystencilssfg.cli:cli_main" [build-system] requires = [ "setuptools>=69", - "versioneer>=0.29", - "tomli; python_version < '3.11'" + "versioneer[toml]>=0.29", ] build-backend = "setuptools.build_meta" -[tool.pdm.dev-dependencies] +[project.optional-dependencies] interactive = [ "ipython>=8.17.2", ] -code_quality = [ +testing = [ "flake8>=6.1.0", "mypy>=1.7.0", ] docs = [ - "mkdocs>=1.5.3", - "mkdocs-material>=9.4.8", - "mkdocstrings[python]>=0.24.0", + "sphinx", + "furo", + "myst-parser", + "sphinx_design", + "sphinx_autodoc_typehints", + "sphinx-copybutton" ] [tool.versioneer] @@ -42,5 +44,5 @@ VCS = "git" style = "pep440" versionfile_source = "src/pystencilssfg/_version.py" versionfile_build = "pystencilssfg/_version.py" -tag_prefix = "" +tag_prefix = "v" parentdir_prefix = "pystencilssfg-" diff --git a/src/pystencilssfg/__init__.py b/src/pystencilssfg/__init__.py index 457c89ccde0ff32341d6555abb991ddf660e56f6..247800ca5c095a808b9e0f03ccab66e40a134035 100644 --- a/src/pystencilssfg/__init__.py +++ b/src/pystencilssfg/__init__.py @@ -1,10 +1,17 @@ -from .configuration import SfgConfiguration +from .configuration import SfgConfiguration, SfgOutputMode from .generator import SourceFileGenerator from .composer import SfgComposer from .context import SfgContext +from .lang import AugExpr -__all__ = ["SourceFileGenerator", "SfgComposer", "SfgConfiguration", "SfgContext"] +__all__ = [ + "SourceFileGenerator", + "SfgComposer", + "SfgConfiguration", + "SfgOutputMode", + "SfgContext", + "AugExpr", +] from . import _version - -__version__ = _version.get_versions()["version"] +__version__ = _version.get_versions()['version'] diff --git a/src/pystencilssfg/_version.py b/src/pystencilssfg/_version.py index c9dee16820d7d0e6957171a9472d2b8e6c513b05..3ac6be9aa8bbf0b129bd1165fa7ac8d68ba69a6d 100644 --- a/src/pystencilssfg/_version.py +++ b/src/pystencilssfg/_version.py @@ -1,3 +1,4 @@ + # This file helps to compute a version number in source trees obtained from # git-archive tarball (such as those provided by githubs download-from-tag # feature). Distribution tarballs (built by setup.py sdist) and build @@ -50,7 +51,7 @@ def get_config() -> VersioneerConfig: cfg = VersioneerConfig() cfg.VCS = "git" cfg.style = "pep440" - cfg.tag_prefix = "" + cfg.tag_prefix = "v" cfg.parentdir_prefix = "pystencilssfg-" cfg.versionfile_source = "src/pystencilssfg/_version.py" cfg.verbose = False @@ -67,14 +68,12 @@ HANDLERS: Dict[str, Dict[str, Callable]] = {} def register_vcs_handler(vcs: str, method: str) -> Callable: # decorator """Create decorator to mark a method as the handler of a VCS.""" - def decorate(f: Callable) -> Callable: """Store f in HANDLERS[vcs][method].""" if vcs not in HANDLERS: HANDLERS[vcs] = {} HANDLERS[vcs][method] = f return f - return decorate @@ -101,14 +100,10 @@ def run_command( try: dispcmd = str([command] + args) # remember shell=False, so use git.cmd on windows, not just git - process = subprocess.Popen( - [command] + args, - cwd=cwd, - env=env, - stdout=subprocess.PIPE, - stderr=(subprocess.PIPE if hide_stderr else None), - **popen_kwargs, - ) + process = subprocess.Popen([command] + args, cwd=cwd, env=env, + stdout=subprocess.PIPE, + stderr=(subprocess.PIPE if hide_stderr + else None), **popen_kwargs) break except OSError as e: if e.errno == errno.ENOENT: @@ -146,21 +141,15 @@ def versions_from_parentdir( for _ in range(3): dirname = os.path.basename(root) if dirname.startswith(parentdir_prefix): - return { - "version": dirname[len(parentdir_prefix) :], - "full-revisionid": None, - "dirty": False, - "error": None, - "date": None, - } + return {"version": dirname[len(parentdir_prefix):], + "full-revisionid": None, + "dirty": False, "error": None, "date": None} rootdirs.append(root) root = os.path.dirname(root) # up a level if verbose: - print( - "Tried directories %s but none started with prefix %s" - % (str(rootdirs), parentdir_prefix) - ) + print("Tried directories %s but none started with prefix %s" % + (str(rootdirs), parentdir_prefix)) raise NotThisMethod("rootdir doesn't start with parentdir_prefix") @@ -223,7 +212,7 @@ def git_versions_from_keywords( # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of # just "foo-1.0". If we see a "tag: " prefix, prefer those. TAG = "tag: " - tags = {r[len(TAG) :] for r in refs if r.startswith(TAG)} + tags = {r[len(TAG):] for r in refs if r.startswith(TAG)} if not tags: # Either we're using git < 1.8.3, or there really are no tags. We use # a heuristic: assume all version tags have a digit. The old git %d @@ -232,7 +221,7 @@ def git_versions_from_keywords( # between branches and tags. By ignoring refnames without digits, we # filter out many common branch names like "release" and # "stabilization", as well as "HEAD" and "master". - tags = {r for r in refs if re.search(r"\d", r)} + tags = {r for r in refs if re.search(r'\d', r)} if verbose: print("discarding '%s', no digits" % ",".join(refs - tags)) if verbose: @@ -240,36 +229,32 @@ def git_versions_from_keywords( for ref in sorted(tags): # sorting will prefer e.g. "2.0" over "2.0rc1" if ref.startswith(tag_prefix): - r = ref[len(tag_prefix) :] + r = ref[len(tag_prefix):] # Filter out refs that exactly match prefix or that don't start # with a number once the prefix is stripped (mostly a concern # when prefix is '') - if not re.match(r"\d", r): + if not re.match(r'\d', r): continue if verbose: print("picking %s" % r) - return { - "version": r, - "full-revisionid": keywords["full"].strip(), - "dirty": False, - "error": None, - "date": date, - } + return {"version": r, + "full-revisionid": keywords["full"].strip(), + "dirty": False, "error": None, + "date": date} # no suitable tags, so version is "0+unknown", but full hex is still there if verbose: print("no suitable tags, using unknown + full revision id") - return { - "version": "0+unknown", - "full-revisionid": keywords["full"].strip(), - "dirty": False, - "error": "no suitable tags", - "date": None, - } + return {"version": "0+unknown", + "full-revisionid": keywords["full"].strip(), + "dirty": False, "error": "no suitable tags", "date": None} @register_vcs_handler("git", "pieces_from_vcs") def git_pieces_from_vcs( - tag_prefix: str, root: str, verbose: bool, runner: Callable = run_command + tag_prefix: str, + root: str, + verbose: bool, + runner: Callable = run_command ) -> Dict[str, Any]: """Get version from 'git describe' in the root of the source tree. @@ -288,7 +273,8 @@ def git_pieces_from_vcs( env.pop("GIT_DIR", None) runner = functools.partial(runner, env=env) - _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=not verbose) + _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, + hide_stderr=not verbose) if rc != 0: if verbose: print("Directory %s not under git control" % root) @@ -296,19 +282,10 @@ def git_pieces_from_vcs( # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] # if there isn't one, this yields HEX[-dirty] (no NUM) - describe_out, rc = runner( - GITS, - [ - "describe", - "--tags", - "--dirty", - "--always", - "--long", - "--match", - f"{tag_prefix}[[:digit:]]*", - ], - cwd=root, - ) + describe_out, rc = runner(GITS, [ + "describe", "--tags", "--dirty", "--always", "--long", + "--match", f"{tag_prefix}[[:digit:]]*" + ], cwd=root) # --long was added in git-1.5.5 if describe_out is None: raise NotThisMethod("'git describe' failed") @@ -323,7 +300,8 @@ def git_pieces_from_vcs( pieces["short"] = full_out[:7] # maybe improved later pieces["error"] = None - branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], cwd=root) + branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], + cwd=root) # --abbrev-ref was added in git-1.6.3 if rc != 0 or branch_name is None: raise NotThisMethod("'git rev-parse --abbrev-ref' returned error") @@ -363,16 +341,17 @@ def git_pieces_from_vcs( dirty = git_describe.endswith("-dirty") pieces["dirty"] = dirty if dirty: - git_describe = git_describe[: git_describe.rindex("-dirty")] + git_describe = git_describe[:git_describe.rindex("-dirty")] # now we have TAG-NUM-gHEX or HEX if "-" in git_describe: # TAG-NUM-gHEX - mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe) + mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) if not mo: # unparsable. Maybe git-describe is misbehaving? - pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out + pieces["error"] = ("unable to parse git-describe output: '%s'" + % describe_out) return pieces # tag @@ -381,12 +360,10 @@ def git_pieces_from_vcs( if verbose: fmt = "tag '%s' doesn't start with prefix '%s'" print(fmt % (full_tag, tag_prefix)) - pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % ( - full_tag, - tag_prefix, - ) + pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" + % (full_tag, tag_prefix)) return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix) :] + pieces["closest-tag"] = full_tag[len(tag_prefix):] # distance: number of commits since tag pieces["distance"] = int(mo.group(2)) @@ -435,7 +412,8 @@ def render_pep440(pieces: Dict[str, Any]) -> str: rendered += ".dirty" else: # exception #1 - rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) + rendered = "0+untagged.%d.g%s" % (pieces["distance"], + pieces["short"]) if pieces["dirty"]: rendered += ".dirty" return rendered @@ -464,7 +442,8 @@ def render_pep440_branch(pieces: Dict[str, Any]) -> str: rendered = "0" if pieces["branch"] != "master": rendered += ".dev0" - rendered += "+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) + rendered += "+untagged.%d.g%s" % (pieces["distance"], + pieces["short"]) if pieces["dirty"]: rendered += ".dirty" return rendered @@ -625,13 +604,11 @@ def render_git_describe_long(pieces: Dict[str, Any]) -> str: def render(pieces: Dict[str, Any], style: str) -> Dict[str, Any]: """Render the given version pieces into the requested style.""" if pieces["error"]: - return { - "version": "unknown", - "full-revisionid": pieces.get("long"), - "dirty": None, - "error": pieces["error"], - "date": None, - } + return {"version": "unknown", + "full-revisionid": pieces.get("long"), + "dirty": None, + "error": pieces["error"], + "date": None} if not style or style == "default": style = "pep440" # the default @@ -655,13 +632,9 @@ def render(pieces: Dict[str, Any], style: str) -> Dict[str, Any]: else: raise ValueError("unknown style '%s'" % style) - return { - "version": rendered, - "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], - "error": None, - "date": pieces.get("date"), - } + return {"version": rendered, "full-revisionid": pieces["long"], + "dirty": pieces["dirty"], "error": None, + "date": pieces.get("date")} def get_versions() -> Dict[str, Any]: @@ -675,7 +648,8 @@ def get_versions() -> Dict[str, Any]: verbose = cfg.verbose try: - return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, verbose) + return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, + verbose) except NotThisMethod: pass @@ -684,16 +658,13 @@ def get_versions() -> Dict[str, Any]: # versionfile_source is the relative path from the top of the source # tree (where the .git directory might live) to this file. Invert # this to find the root from __file__. - for _ in cfg.versionfile_source.split("/"): + for _ in cfg.versionfile_source.split('/'): root = os.path.dirname(root) except NameError: - return { - "version": "0+unknown", - "full-revisionid": None, - "dirty": None, - "error": "unable to find root of source tree", - "date": None, - } + return {"version": "0+unknown", "full-revisionid": None, + "dirty": None, + "error": "unable to find root of source tree", + "date": None} try: pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) @@ -707,10 +678,6 @@ def get_versions() -> Dict[str, Any]: except NotThisMethod: pass - return { - "version": "0+unknown", - "full-revisionid": None, - "dirty": None, - "error": "unable to compute version", - "date": None, - } + return {"version": "0+unknown", "full-revisionid": None, + "dirty": None, + "error": "unable to compute version", "date": None} diff --git a/src/pystencilssfg/cmake/modules/PystencilsSfg.cmake b/src/pystencilssfg/cmake/modules/PystencilsSfg.cmake index 9d477cd23c52200da42fe8d349679080e5cc6f16..a29d70e7a49f74174c4f6e5a45a0da711e2a1e94 100644 --- a/src/pystencilssfg/cmake/modules/PystencilsSfg.cmake +++ b/src/pystencilssfg/cmake/modules/PystencilsSfg.cmake @@ -43,14 +43,15 @@ endfunction() function(pystencilssfg_generate_target_sources TARGET) - set(options HEADER_ONLY) + set(options) + set(oneValueArgs OUTPUT_MODE) set(multiValueArgs SCRIPTS DEPENDS FILE_EXTENSIONS) cmake_parse_arguments(_pssfg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) set(generatorArgs) - if(_pssfg_HEADER_ONLY) - list(APPEND generatorArgs "--sfg-header-only") + if(DEFINED _pssfg_OUTPUT_MODE) + list(APPEND generatorArgs "--sfg-output-mode=${_pssfg_OUTPUT_MODE}") endif() if(DEFINED PystencilsSfg_CONFIGURATOR_SCRIPT) diff --git a/src/pystencilssfg/composer/__init__.py b/src/pystencilssfg/composer/__init__.py index c20f7def3ca09aedcbd043f8be71abb82e435d9d..f6af76b8b9c36445990fc451983fec5c14a4cf34 100644 --- a/src/pystencilssfg/composer/__init__.py +++ b/src/pystencilssfg/composer/__init__.py @@ -1,5 +1,23 @@ from .composer import SfgComposer -from .basic_composer import SfgBasicComposer, make_sequence +from .basic_composer import ( + SfgIComposer, + SfgBasicComposer, + make_sequence, + make_statements, + SequencerArg, + ExprLike, +) +from .mixin import SfgComposerMixIn from .class_composer import SfgClassComposer -__all__ = ["SfgComposer", "make_sequence", "SfgBasicComposer", "SfgClassComposer"] +__all__ = [ + "SfgIComposer", + "SfgComposer", + "SfgComposerMixIn", + "make_sequence", + "make_statements", + "SequencerArg", + "ExprLike", + "SfgBasicComposer", + "SfgClassComposer", +] diff --git a/src/pystencilssfg/composer/basic_composer.py b/src/pystencilssfg/composer/basic_composer.py index 1421aac2f07efacc0ac831f6851b9b25aacdf655..59938026ed670cb0e22c678220e7254312146eef 100644 --- a/src/pystencilssfg/composer/basic_composer.py +++ b/src/pystencilssfg/composer/basic_composer.py @@ -1,24 +1,34 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Sequence +from typing import Sequence from abc import ABC, abstractmethod import numpy as np +import sympy as sp +from functools import reduce -from pystencils import Field, TypedSymbol -from pystencils.astnodes import KernelFunction +from pystencils import Field +from pystencils.backend import KernelParameter, KernelFunction +from pystencils.types import create_type, UserTypeSpec, PsCustomType, PsPointerType +from ..context import SfgContext from .custom import CustomGenerator -from ..tree import ( +from ..ir import ( SfgCallTreeNode, SfgKernelCallNode, + SfgCudaKernelInvocation, SfgStatements, SfgFunctionParams, SfgRequireIncludes, SfgSequence, SfgBlock, + SfgBranch, + SfgSwitch, ) -from ..tree.deferred_nodes import SfgDeferredFieldMapping -from ..tree.conditional import SfgCondition, SfgCustomCondition, SfgBranch, SfgSwitch -from ..source_components import ( +from ..ir.postprocessing import ( + SfgDeferredParamMapping, + SfgDeferredFieldMapping, + SfgDeferredVectorMapping, +) +from ..ir.source_components import ( SfgFunction, SfgHeaderInclude, SfgKernelNamespace, @@ -27,25 +37,38 @@ from ..source_components import ( SfgConstructor, SfgMemberVariable, SfgClassKeyword, + SfgVar, ) -from ..source_concepts import SrcObject, SrcField, TypedSymbolOrObject, SrcVector -from ..types import cpp_typename, SrcType +from ..lang import IFieldExtraction, SrcVector, AugExpr, SrcField from ..exceptions import SfgException -if TYPE_CHECKING: - from ..context import SfgContext - - -class SfgBasicComposer: - """Composer for basic source components.""" +class SfgIComposer(ABC): def __init__(self, ctx: SfgContext): - self._ctx: SfgContext = ctx + self._ctx = ctx @property def context(self): return self._ctx + +class SfgNodeBuilder(ABC): + @abstractmethod + def resolve(self) -> SfgCallTreeNode: + pass + + +ExprLike = str | SfgVar | AugExpr +SequencerArg = tuple | str | AugExpr | SfgCallTreeNode | SfgNodeBuilder + + +class SfgBasicComposer(SfgIComposer): + """Composer for basic source components, and base class for all composer mix-ins.""" + + def __init__(self, sfg: SfgContext | SfgIComposer): + ctx: SfgContext = sfg if isinstance(sfg, SfgContext) else sfg.context + super().__init__(ctx) + def prelude(self, content: str): """Append a string to the prelude comment, to be printed at the top of both generated files. @@ -75,11 +98,13 @@ class SfgBasicComposer: @property def kernels(self) -> SfgKernelNamespace: - """The default kernel namespace. Add kernels like: - ```Python - sfg.kernels.add(ast, "kernel_name") - sfg.kernels.create(assignments, "kernel_name", config) - ```""" + """The default kernel namespace. + + Add kernels like:: + + sfg.kernels.add(ast, "kernel_name") + sfg.kernels.create(assignments, "kernel_name", config) + """ return self._ctx._default_kernel_namespace def kernel_namespace(self, name: str) -> SfgKernelNamespace: @@ -99,7 +124,7 @@ class SfgBasicComposer: private: If `True`, in header-implementation code generation, the header file is only included in the implementation file. """ - self._ctx.add_include(parse_include(header_file, private)) + self._ctx.add_include(SfgHeaderInclude.parse(header_file, private)) def numpy_struct( self, name: str, dtype: np.dtype, add_constructor: bool = True @@ -143,14 +168,13 @@ class SfgBasicComposer: The syntax of this function adder uses a chain of two calls to mimic C++ syntax: - ```Python - sfg.function("FunctionName")( - # Function Body - ) - ``` + .. code-block:: Python + + sfg.function("FunctionName")( + # Function Body + ) - The function body is constructed via sequencing; - refer to [make_sequence][pystencilssfg.composer.make_sequence]. + The function body is constructed via sequencing (see `make_sequence`). """ if self._ctx.get_function(name) is not None: raise ValueError(f"Function {name} already exists.") @@ -162,137 +186,205 @@ class SfgBasicComposer: return sequencer - def call(self, kernel_handle: SfgKernelHandle) -> SfgKernelCallNode: - """Use inside a function body to generate a kernel call. + def call(self, kernel_handle: SfgKernelHandle) -> SfgCallTreeNode: + """Use inside a function body to directly call a kernel. + + When using `call`, the given kernel will simply be called as a function. + To invoke a GPU kernel on a specified launch grid, use `cuda_invoke` + or the interfaces of `pystencilssfg.extensions.sycl` instead. Args: kernel_handle: Handle to a kernel previously added to some kernel namespace. """ return SfgKernelCallNode(kernel_handle) + def cuda_invoke( + self, + kernel_handle: SfgKernelHandle, + num_blocks: ExprLike, + threads_per_block: ExprLike, + stream: ExprLike | None, + ): + num_blocks_str = str(num_blocks) + tpb_str = str(threads_per_block) + stream_str = str(stream) if stream is not None else None + depends = _depends(num_blocks) | _depends(threads_per_block) | _depends(stream) + return SfgCudaKernelInvocation( + kernel_handle, num_blocks_str, tpb_str, stream_str, depends + ) + def seq(self, *args: tuple | str | SfgCallTreeNode | SfgNodeBuilder) -> SfgSequence: - """Syntax sequencing. For details, refer to [make_sequence][pystencilssfg.composer.make_sequence]""" + """Syntax sequencing. For details, see `make_sequence`""" return make_sequence(*args) - def params(self, *args: TypedSymbolOrObject) -> SfgFunctionParams: + def params(self, *args: SfgVar) -> SfgFunctionParams: """Use inside a function body to add parameters to the function.""" return SfgFunctionParams(args) def require(self, *includes: str | SfgHeaderInclude) -> SfgRequireIncludes: - return SfgRequireIncludes(list(parse_include(incl) for incl in includes)) + return SfgRequireIncludes( + list(SfgHeaderInclude.parse(incl) for incl in includes) + ) + + def cpptype( + self, + typename: UserTypeSpec, + ptr: bool = False, + ref: bool = False, + const: bool = False, + ): + if ptr and ref: + raise SfgException("Create either a pointer, or a ref type, not both!") + + ref_qual = "&" if ref else "" + try: + base_type = create_type(typename) + except ValueError: + if not isinstance(typename, str): + raise ValueError(f"Could not parse type: {typename}") + + base_type = PsCustomType(typename + ref_qual, const=const) + + if ptr: + return PsPointerType(base_type) + else: + return base_type + + def var(self, name: str, dtype: UserTypeSpec) -> SfgVar: + """Create a variable with given name and data type.""" + return SfgVar(name, create_type(dtype)) + + def init(self, lhs: SfgVar) -> SfgInplaceInitBuilder: + """Create a C++ in-place initialization. + + Usage: + + .. code-block:: Python + + obj = sfg.var("obj", "SomeClass") + sfg.init(obj)(arg1, arg2, arg3) + + becomes + + .. code-block:: C++ + + SomeClass obj { arg1, arg2, arg3 }; + """ + return SfgInplaceInitBuilder(lhs) + + def expr(self, fmt: str, *deps, **kwdeps): + return AugExpr.format(fmt, *deps, **kwdeps) @property def branch(self) -> SfgBranchBuilder: """Use inside a function body to create an if/else conditonal branch. The syntax is: - ```Python - sfg.branch("condition")( - # then-body - )( - # else-body (may be omitted) - ) - ``` + + .. code-block:: Python + + sfg.branch("condition")( + # then-body + )( + # else-body (may be omitted) + ) """ return SfgBranchBuilder() - def switch(self, switch_arg: str | TypedSymbolOrObject) -> SfgSwitchBuilder: + def switch(self, switch_arg: ExprLike) -> SfgSwitchBuilder: return SfgSwitchBuilder(switch_arg) - def map_field(self, field: Field, src_object: SrcField) -> SfgDeferredFieldMapping: + def map_field( + self, field: Field, index_provider: IFieldExtraction | SrcField + ) -> SfgDeferredFieldMapping: """Map a pystencils field to a field data structure, from which pointers, sizes and strides should be extracted. Args: field: The pystencils field to be mapped - src_object: A [SrcField][pystencilssfg.source_concepts.SrcField] object representing a field data structure. + src_object: A `IFieldIndexingProvider` object representing a field data structure. """ - return SfgDeferredFieldMapping(field, src_object) + return SfgDeferredFieldMapping(field, index_provider) def map_param( self, - lhs: TypedSymbolOrObject, - rhs: TypedSymbolOrObject | Sequence[TypedSymbolOrObject], + lhs: SfgVar, + rhs: SfgVar | Sequence[SfgVar], mapping: str, ): """Arbitrary parameter mapping: Add a single line of code to define a left-hand side object from one or multiple right-hand side dependencies.""" - if isinstance(rhs, (TypedSymbol, SrcObject)): - return SfgStatements(mapping, (lhs,), (rhs,)) - else: - return SfgStatements(mapping, (lhs,), rhs) + if isinstance(rhs, (KernelParameter, SfgVar)): + rhs = [rhs] + return SfgDeferredParamMapping(lhs, set(rhs), mapping) - def map_vector(self, lhs_components: Sequence[TypedSymbolOrObject], rhs: SrcVector): + def map_vector(self, lhs_components: Sequence[SfgVar | sp.Symbol], rhs: SrcVector): """Extracts scalar numerical values from a vector data type. Args: lhs_components: Vector components as a list of symbols. - rhs: A [SrcVector][pystencilssfg.source_concepts.SrcVector] object representing a vector data structure. + rhs: A `SrcVector` object representing a vector data structure. """ - return make_sequence( - *( - rhs.extract_component(dest, coord) - for coord, dest in enumerate(lhs_components) - ) - ) - - -class SfgNodeBuilder(ABC): - @abstractmethod - def resolve(self) -> SfgCallTreeNode: - pass + return SfgDeferredVectorMapping(lhs_components, rhs) -SequencerArg = tuple | str | SfgCallTreeNode | SfgNodeBuilder +def make_statements(arg: ExprLike) -> SfgStatements: + match arg: + case str(): + return SfgStatements(arg, (), ()) + case SfgVar(name, _): + return SfgStatements(name, (), (arg,)) + case AugExpr(): + return SfgStatements(str(arg), (), arg.depends) + case _: + assert False def make_sequence(*args: SequencerArg) -> SfgSequence: """Construct a sequence of C++ code from various kinds of arguments. `make_sequence` is ubiquitous throughout the function building front-end; - among others, it powers the syntax of - [SfgComposer.function][pystencilssfg.SfgComposer.function] and - [SfgComposer.branch][pystencilssfg.SfgComposer.branch]. + among others, it powers the syntax of `SfgComposer.function` and `SfgComposer.branch`. `make_sequence` constructs an abstract syntax tree for code within a function body, accepting various - types of arguments which then get turned into C++ code. These are: + types of arguments which then get turned into C++ code. These are - - Strings (`str`) are printed as-is - - Tuples (`tuple`) signify *blocks*, i.e. C++ code regions enclosed in `{ }` - - Sub-ASTs and AST builders, which are often produced by the syntactic sugar and - factory methods of [SfgComposer][pystencilssfg.SfgComposer]. + - Strings (`str`) are printed as-is + - Tuples (`tuple`) signify *blocks*, i.e. C++ code regions enclosed in ``{ }`` + - Sub-ASTs and AST builders, which are often produced by the syntactic sugar and + factory methods of `SfgComposer`. Its usage is best shown by example: - ```Python - tree = make_sequence( - "int a = 0;", - "int b = 1;", - ( - "int tmp = b;", - "b = a;", - "a = tmp;" - ), - SfgKernelCall(kernel_handle) - ) - - sfg.context.add_function("myFunction", tree) - ``` + .. code-block:: Python + + tree = make_sequence( + "int a = 0;", + "int b = 1;", + ( + "int tmp = b;", + "b = a;", + "a = tmp;" + ), + SfgKernelCall(kernel_handle) + ) + + sfg.context.add_function("myFunction", tree) will translate to - ```C++ - void myFunction() { - int a = 0; - int b = 0; - { - int tmp = b; - b = a; - a = tmp; + .. code-block:: C++ + + void myFunction() { + int a = 0; + int b = 0; + { + int tmp = b; + b = a; + a = tmp; + } + kernels::kernel( ... ); } - kernels::kernel( ... ); - } - ``` """ children = [] for i, arg in enumerate(args): @@ -300,6 +392,8 @@ def make_sequence(*args: SequencerArg) -> SfgSequence: children.append(arg.resolve()) elif isinstance(arg, SfgCallTreeNode): children.append(arg) + elif isinstance(arg, AugExpr): + children.append(SfgStatements(str(arg), (), arg.depends)) elif isinstance(arg, str): children.append(SfgStatements(arg, (), ())) elif isinstance(arg, tuple): @@ -312,13 +406,41 @@ def make_sequence(*args: SequencerArg) -> SfgSequence: return SfgSequence(children) +class SfgInplaceInitBuilder(SfgNodeBuilder): + def __init__(self, lhs: SfgVar) -> None: + self._lhs: SfgVar = lhs + self._depends: set[SfgVar] = set() + self._rhs: str | None = None + + def __call__( + self, + *rhs: str | AugExpr, + ) -> SfgInplaceInitBuilder: + if self._rhs is not None: + raise SfgException("Assignment builder used multiple times.") + + self._rhs = ", ".join(str(expr) for expr in rhs) + self._depends = reduce( + set.union, (obj.depends for obj in rhs if isinstance(obj, AugExpr)), set() + ) + return self + + def resolve(self) -> SfgCallTreeNode: + assert self._rhs is not None + return SfgStatements( + f"{self._lhs.dtype} {self._lhs.name} {{ {self._rhs} }};", + [self._lhs], + self._depends, + ) + + class SfgBranchBuilder(SfgNodeBuilder): - def __init__(self): + def __init__(self) -> None: self._phase = 0 - self._cond = None + self._cond: ExprLike | None = None self._branch_true = SfgSequence(()) - self._branch_false = None + self._branch_false: SfgSequence | None = None def __call__(self, *args) -> SfgBranchBuilder: match self._phase: @@ -328,16 +450,7 @@ class SfgBranchBuilder(SfgNodeBuilder): "Must specify exactly one argument as branch condition!" ) - cond = args[0] - - if isinstance(cond, str): - cond = SfgCustomCondition(cond) - elif not isinstance(cond, SfgCondition): - raise ValueError( - "Invalid type for branch condition. Must be either `str` or a subclass of `SfgCondition`." - ) - - self._cond = cond + self._cond = args[0] case 1: # Then-branch self._branch_true = make_sequence(*args) @@ -352,14 +465,16 @@ class SfgBranchBuilder(SfgNodeBuilder): def resolve(self) -> SfgCallTreeNode: assert self._cond is not None - return SfgBranch(self._cond, self._branch_true, self._branch_false) + return SfgBranch( + make_statements(self._cond), self._branch_true, self._branch_false + ) class SfgSwitchBuilder(SfgNodeBuilder): - def __init__(self, switch_arg: str | TypedSymbolOrObject): + def __init__(self, switch_arg: ExprLike): self._switch_arg = switch_arg - self._cases: dict[str, SfgCallTreeNode] = dict() - self._default: SfgCallTreeNode | None = None + self._cases: dict[str, SfgSequence] = dict() + self._default: SfgSequence | None = None def case(self, label: str): if label in self._cases: @@ -387,19 +502,7 @@ class SfgSwitchBuilder(SfgNodeBuilder): return self def resolve(self) -> SfgCallTreeNode: - return SfgSwitch(self._switch_arg, self._cases, self._default) - - -def parse_include(incl: str | SfgHeaderInclude, private: bool = False): - if isinstance(incl, SfgHeaderInclude): - return incl - - system_header = False - if incl.startswith("<") and incl.endswith(">"): - incl = incl[1:-1] - system_header = True - - return SfgHeaderInclude(incl, system_header=system_header, private=private) + return SfgSwitch(make_statements(self._switch_arg), self._cases, self._default) def struct_from_numpy_dtype( @@ -415,11 +518,11 @@ def struct_from_numpy_dtype( constr_inits = [] for member_name, type_info in fields.items(): - member_type = SrcType(cpp_typename(type_info[0])) + member_type = create_type(type_info[0]) member = SfgMemberVariable(member_name, member_type) - arg = SrcObject(f"{member_name}_", member_type) + arg = SfgVar(f"{member_name}_", member_type) cls.default.append_member(member) @@ -430,3 +533,15 @@ def struct_from_numpy_dtype( cls.default.append_member(SfgConstructor(constr_params, constr_inits)) return cls + + +def _depends(expr: ExprLike | Sequence[ExprLike] | None) -> set[SfgVar]: + match expr: + case None | str(): + return set() + case SfgVar(): + return {expr} + case AugExpr(): + return expr.depends + case _: + raise ValueError(f"Invalid expression: {expr}") diff --git a/src/pystencilssfg/composer/class_composer.py b/src/pystencilssfg/composer/class_composer.py index 714ecc839e27e46ceefe370868cdad0886e788a1..63588b7829b2a94122e5c9d7d38770cabce9d6f5 100644 --- a/src/pystencilssfg/composer/class_composer.py +++ b/src/pystencilssfg/composer/class_composer.py @@ -1,8 +1,10 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Sequence +from typing import Sequence -from ..tree import SfgCallTreeNode -from ..source_components import ( +from pystencils.types import PsCustomType, UserTypeSpec + +from ..ir import SfgCallTreeNode +from ..ir.source_components import ( SfgClass, SfgClassMember, SfgInClassDefinition, @@ -12,38 +14,27 @@ from ..source_components import ( SfgClassKeyword, SfgVisibility, SfgVisibilityBlock, + SfgVar, ) -from ..source_concepts import SrcObject -from ..types import SrcType from ..exceptions import SfgException -from .basic_composer import SfgBasicComposer, SfgNodeBuilder, make_sequence - -if TYPE_CHECKING: - from ..context import SfgContext +from .mixin import SfgComposerMixIn +from .basic_composer import SfgNodeBuilder, make_sequence -class SfgClassComposer: +class SfgClassComposer(SfgComposerMixIn): """Composer for classes and structs. This class cannot be instantiated on its own but must be mixed in with - [SfgBasicComposer][pystencilssfg.composer.SfgBasicComposer]. - Use through [SfgComposer][pystencilssfg.SfgComposer]. + :class:`SfgBasicComposer`. + Its interface is exposed by :class:`SfgComposer`. """ - def __init__(self, ctx: SfgContext): - if not isinstance(self, SfgBasicComposer): - raise Exception("SfgClassComposer must be mixed-in with SfgBasicComposer.") - self._ctx: SfgContext = ctx - class VisibilityContext: """Represent a visibility block in the composer syntax. - Returned by - [private][pystencilssfg.composer.SfgClassComposer.private], - [public][pystencilssfg.composer.SfgClassComposer.public] and - [protected][pystencilssfg.composer.SfgClassComposer.protected]. + Returned by `private`, `public`, and `protected`. """ def __init__(self, visibility: SfgVisibility): @@ -55,7 +46,7 @@ class SfgClassComposer: def __call__( self, *args: ( - SfgClassMember | SfgClassComposer.ConstructorBuilder | SrcObject | str + SfgClassMember | SfgClassComposer.ConstructorBuilder | SfgVar | str ), ): for arg in args: @@ -69,10 +60,10 @@ class SfgClassComposer: class ConstructorBuilder: """Composer syntax for constructor building. - Returned by [constructor][pystencilssfg.composer.SfgClassComposer.constructor]. + Returned by `constructor`. """ - def __init__(self, *params: SrcObject): + def __init__(self, *params: SfgVar): self._params = params self._initializers: list[str] = [] self._body: str | None = None @@ -129,16 +120,7 @@ class SfgClassComposer: """Create a `private` visibility block in a class or struct body""" return SfgClassComposer.VisibilityContext(SfgVisibility.PRIVATE) - def var(self, name: str, dtype: SrcType): - """In a class or struct body or visibility block, and a member variable. - - Args: - name: The variable's name - dtype: The variable's data type - """ - return SfgMemberVariable(name, dtype) - - def constructor(self, *params: SrcObject): + def constructor(self, *params: SfgVar): """In a class or struct body or visibility block, add a constructor. Args: @@ -149,12 +131,12 @@ class SfgClassComposer: def method( self, name: str, - returns: SrcType = SrcType("void"), + returns: UserTypeSpec = PsCustomType("void"), inline: bool = False, const: bool = False, ): """In a class or struct body or visibility block, add a method. - The usage is similar to [SfgBasicComposer.function][pystencilssfg.composer.SfgBasicComposer.function]. + The usage is similar to :any:`SfgBasicComposer.function`. Args: name: The method name @@ -166,7 +148,11 @@ class SfgClassComposer: def sequencer(*args: str | tuple | SfgCallTreeNode | SfgNodeBuilder): tree = make_sequence(*args) return SfgMethod( - name, tree, return_type=returns, inline=inline, const=const + name, + tree, + return_type=self._composer.cpptype(returns), + inline=inline, + const=const, ) return sequencer @@ -185,7 +171,7 @@ class SfgClassComposer: SfgClassComposer.VisibilityContext | SfgClassMember | SfgClassComposer.ConstructorBuilder - | SrcObject + | SfgVar | str ), ): @@ -200,7 +186,7 @@ class SfgClassComposer: ( SfgClassMember, SfgClassComposer.ConstructorBuilder, - SrcObject, + SfgVar, str, ), ): @@ -218,9 +204,9 @@ class SfgClassComposer: @staticmethod def _resolve_member( - arg: SfgClassMember | SfgClassComposer.ConstructorBuilder | SrcObject | str, + arg: SfgClassMember | SfgClassComposer.ConstructorBuilder | SfgVar | str, ): - if isinstance(arg, SrcObject): + if isinstance(arg, SfgVar): return SfgMemberVariable(arg.name, arg.dtype) elif isinstance(arg, str): return SfgInClassDefinition(arg) diff --git a/src/pystencilssfg/composer/composer.py b/src/pystencilssfg/composer/composer.py index 3ed79c587ba40dad2215d4dccc851768d7664a50..bba479e3289f2b589a1d32e61997bdaa915eb47e 100644 --- a/src/pystencilssfg/composer/composer.py +++ b/src/pystencilssfg/composer/composer.py @@ -11,12 +11,11 @@ if TYPE_CHECKING: class SfgComposer(SfgBasicComposer, SfgClassComposer): """Primary interface for constructing source files in pystencils-sfg. - The SfgComposer combines the [SfgBasicComposer][pystencilssfg.composer.SfgBasicComposer] + The SfgComposer combines the `SfgBasicComposer` for the basic components (kernel namespaces, includes, definitions, and functions) - and the [SfgClassComposer][pystencilssfg.composer.SfgClassComposer] for constructing - `struct`s and `class`es. + and the `SfgClassComposer` for constructing ``struct`` s and ``class`` es. """ - def __init__(self, ctx: SfgContext): - SfgBasicComposer.__init__(self, ctx) - SfgClassComposer.__init__(self, ctx) + def __init__(self, sfg: SfgContext | SfgBasicComposer): + SfgBasicComposer.__init__(self, sfg) + SfgClassComposer.__init__(self) diff --git a/src/pystencilssfg/composer/mixin.py b/src/pystencilssfg/composer/mixin.py new file mode 100644 index 0000000000000000000000000000000000000000..3ee8efa61227184686001045bf3f8cb23525bf02 --- /dev/null +++ b/src/pystencilssfg/composer/mixin.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +from ..context import SfgContext +from .basic_composer import SfgBasicComposer + + +class SfgComposerMixIn: + # type: ignore + def __new__(cls, *args, **kwargs): + if not issubclass(cls, SfgBasicComposer): + raise Exception(f"{cls} must be mixed-in with SfgBasicComposer.") + else: + return super().__new__(cls) + + def __init__(self) -> None: + self._ctx: SfgContext + + @property + def _composer(self) -> SfgBasicComposer: + assert isinstance(self, SfgBasicComposer) + return self diff --git a/src/pystencilssfg/configuration.py b/src/pystencilssfg/configuration.py index 9a8071eb0d7b7088d15175488f3ad3cdef15c74a..2eb3efe7855af24cfe21e2815d7a40d6eea60686 100644 --- a/src/pystencilssfg/configuration.py +++ b/src/pystencilssfg/configuration.py @@ -13,9 +13,6 @@ from importlib import util as iutil from .exceptions import SfgException -HEADER_FILE_EXTENSIONS = {"h", "hpp"} -IMPL_FILE_EXTENSIONS = {"c", "cpp", ".impl.h"} - class SfgConfigSource(Enum): DEFAULT = auto() @@ -53,6 +50,31 @@ class SfgCodeStyle: return indent(s, prefix) +class SfgOutputMode(Enum): + STANDALONE = auto() + """Generate a header/implementation file pair (e.g. ``.hpp/.cpp``) where the implementation file will + be compiled to a standalone object.""" + + INLINE = auto() + """Generate a header/inline implementation file pair (e.g. ``.hpp/.ipp``) where all implementations + are inlined by including the implementation file at the end of the header file.""" + + HEADER_ONLY = auto() + """Generate only a header file. + + At the moment, header-only mode does not support generation of kernels and requires that all functions + and methods are marked `inline`. + """ + + +HEADER_FILE_EXTENSIONS = {"h", "hpp", "cuh"} +IMPL_FILE_EXTENSIONS: dict[SfgOutputMode, set[str]] = { + SfgOutputMode.STANDALONE: {"c", "cpp", "cu"}, + SfgOutputMode.INLINE: {".impl.h", "ipp"}, + SfgOutputMode.HEADER_ONLY: set(), +} + + @dataclass class SfgOutputSpec: """Name and path specification for files output by the code generator. @@ -87,36 +109,36 @@ class SfgOutputSpec: @dataclass class SfgConfiguration: """ - Configuration for the [SfgSourceFileGenerator][pystencilssfg.SourceFileGenerator]. + Configuration for the `SfgSourceFileGenerator`. The source file generator draws configuration from a total of four sources: - - The [default configuration][pystencilssfg.configuration.DEFAULT_CONFIG]; - - The project configuration; - - Command-line arguments; - - The user configuration passed to the constructor of `SourceFileGenerator`. + - The default configuration (`pystencilssfg.configuration.DEFAULT_CONFIG`); + - The project configuration; + - Command-line arguments; + - The user configuration passed to the constructor of `SourceFileGenerator`. They take precedence in the following way: - - Project configuration overrides the default configuration - - Command line arguments override the project configuration - - User configuration overrides default and project configuration, - and must not conflict with command-line arguments; otherwise, an error is thrown. + - Project configuration overrides the default configuration + - Command line arguments override the project configuration + - User configuration overrides default and project configuration, + and must not conflict with command-line arguments; otherwise, an error is thrown. - ### Project Configuration via Configurator Script + **Project Configuration via Configurator Script** Currently, the only way to define the project configuration is via a configuration module. A configurator module is a Python file defining the following function at the top-level: - ```Python - from pystencilssfg import SfgConfiguration + .. code-block:: Python + + from pystencilssfg import SfgConfiguration - def sfg_config() -> SfgConfiguration: - # ... - return SfgConfiguration( + def sfg_config() -> SfgConfiguration: # ... - ) - ``` + return SfgConfiguration( + # ... + ) The configuration module is passed to the code generation script via the command-line argument `--sfg-config-module`. @@ -130,12 +152,12 @@ class SfgConfiguration: impl_extension: str | None = None """File extension for generated implementation file.""" - header_only: bool | None = None - """If set to `True`, generate only a header file without accompaning source file.""" + output_mode: SfgOutputMode | None = None + """The generator's output mode; defines which files to generate, and the set of legal file extensions.""" outer_namespace: str | None = None """The outermost namespace in the generated file. May be a valid C++ nested namespace qualifier - (like `a::b::c`) or `None` if no outer namespace should be generated.""" + (like ``a::b::c``) or `None` if no outer namespace should be generated.""" codestyle: SfgCodeStyle | None = None """Code style that should be used by the code generator.""" @@ -147,11 +169,6 @@ class SfgConfiguration: """Object for managing project-specific information. To be set by the configurator script.""" def __post_init__(self, cfg_src: SfgConfigSource | None = None): - if self.header_only: - raise SfgConfigException( - cfg_src, "Header-only code generation is not implemented yet." - ) - if self.header_extension and self.header_extension[0] == ".": self.header_extension = self.header_extension[1:] @@ -178,12 +195,12 @@ DEFAULT_CONFIG = SfgConfiguration( config_source=SfgConfigSource.DEFAULT, header_extension="h", impl_extension="cpp", - header_only=False, + output_mode=SfgOutputMode.STANDALONE, outer_namespace=None, codestyle=SfgCodeStyle(), output_directory=".", ) -"""Default configuration for the [SourceFileGenerator][pystencilssfg.SourceFileGenerator].""" +"""Default configuration for the `SourceFileGenerator`.""" def run_configurator(configurator_script: str): @@ -230,7 +247,11 @@ def add_config_args_to_parser(parser: ArgumentParser): help="Comma-separated list of file extensions", ) config_group.add_argument( - "--sfg-header-only", default=None, action="store_true", dest="header_only" + "--sfg-output-mode", + type=str, + default=None, + choices=("standalone", "inline", "header-only"), + dest="output_mode", ) config_group.add_argument( "--sfg-config-module", type=str, default=None, dest="configurator_script" @@ -245,10 +266,23 @@ def config_from_parser_args(args): else: project_config = None + if args.output_mode is not None: + match args.output_mode.lower(): + case "standalone": + output_mode = SfgOutputMode.STANDALONE + case "inline": + output_mode = SfgOutputMode.INLINE + case "header-only": + output_mode = SfgOutputMode.HEADER_ONLY + case _: + assert False, "invalid output mode" + else: + output_mode = None + if args.file_extensions is not None: file_extentions = list(args.file_extensions.split(",")) h_ext, src_ext = _get_file_extensions( - SfgConfigSource.COMMANDLINE, file_extentions + SfgConfigSource.COMMANDLINE, file_extentions, output_mode ) else: h_ext, src_ext = None, None @@ -257,7 +291,7 @@ def config_from_parser_args(args): config_source=SfgConfigSource.COMMANDLINE, header_extension=h_ext, impl_extension=src_ext, - header_only=args.header_only, + output_mode=output_mode, output_directory=args.output_directory, ) @@ -312,7 +346,9 @@ def merge_configurations( return config -def _get_file_extensions(cfgsrc: SfgConfigSource, extensions: Sequence[str]): +def _get_file_extensions( + cfgsrc: SfgConfigSource, extensions: Sequence[str], output_mode: SfgOutputMode +): h_ext = None src_ext = None @@ -325,7 +361,7 @@ def _get_file_extensions(cfgsrc: SfgConfigSource, extensions: Sequence[str]): cfgsrc, "Multiple header file extensions specified." ) h_ext = ext - elif ext in IMPL_FILE_EXTENSIONS: + elif ext in IMPL_FILE_EXTENSIONS[output_mode]: if src_ext is not None: raise SfgConfigException( cfgsrc, "Multiple source file extensions specified." @@ -333,7 +369,7 @@ def _get_file_extensions(cfgsrc: SfgConfigSource, extensions: Sequence[str]): src_ext = ext else: raise SfgConfigException( - cfgsrc, f"Don't know how to interpret file extension '.{ext}'" + cfgsrc, f"Invalid file extension '.{ext}' for output mode {output_mode}" ) return h_ext, src_ext diff --git a/src/pystencilssfg/context.py b/src/pystencilssfg/context.py index 5dcf35e5b76d82f6e54928bd23dde3b0485cec5c..69ad83f8e2ab8ddf9436e1ebc3505b819d6a5cf2 100644 --- a/src/pystencilssfg/context.py +++ b/src/pystencilssfg/context.py @@ -1,7 +1,7 @@ from typing import Generator, Sequence, Any from .configuration import SfgCodeStyle -from .source_components import ( +from .ir.source_components import ( SfgHeaderInclude, SfgKernelNamespace, SfgFunction, @@ -13,34 +13,33 @@ from .exceptions import SfgException class SfgContext: """Represents a header/implementation file pair in the code generator. - ## Source File Properties and Components + **Source File Properties and Components** The SfgContext collects all properties and components of a header/implementation file pair (or just the header file, if header-only generation is used). These are: - - The code namespace, which is combined from the [outer_namespace][pystencilssfg.SfgContext.outer_namespace] - and the [inner_namespace][pystencilssfg.SfgContext.inner_namespace]. The outer namespace is meant to be set - externally e.g. by the project configuration, while the inner namespace is meant to be set by the generator - script. - - The [prelude comment][pystencilssfg.SfgContext.prelude_comment] is a block of text printed as a comment block - at the top of both generated files. Typically, it contains authorship and licence information. - - The set of [Included header files][pystencilssfg.SfgContext.includes]. - - Custom [definitions][pystencilssfg.SfgContext.definitions], which are just arbitrary code strings. - - Any number of [kernel namespaces][pystencilssfg.SfgContext.kernel_namespaces], within which *pystencils* - kernels are managed. - - Any number of [functions][pystencilssfg.SfgContext.functions], which are meant to serve as wrappers - around kernel calls. - - Any number of [classes][pystencilssfg.SfgContext.classes], which can be used to build more extensive wrappers - around kernels. - - ## Order of Definitions + - The code namespace, which is combined from the `outer_namespace` + and the `pystencilssfg.SfgContext.inner_namespace`. The outer namespace is meant to be set + externally e.g. by the project configuration, while the inner namespace is meant to be set by the generator + script. + - The `prelude comment` is a block of text printed as a comment block + at the top of both generated files. Typically, it contains authorship and licence information. + - The set of included header files (`pystencilssfg.SfgContext.includes`). + - Custom `definitions`, which are just arbitrary code strings. + - Any number of kernel namespaces (`pystencilssfg.SfgContext.kernel_namespaces`), within which *pystencils* + kernels are managed. + - Any number of functions (`pystencilssfg.SfgContext.functions`), which are meant to serve as wrappers + around kernel calls. + - Any number of classes (`pystencilssfg.SfgContext.classes`), which can be used to build more extensive wrappers + around kernels. + + **Order of Definitions** To honor C/C++ use-after-declare rules, the context preserves the order in which definitions, functions and classes are added to it. The header file printers implemented in *pystencils-sfg* will print the declarations accordingly. - The declarations can retrieved in order of definition via - [declarations_ordered][pystencilssfg.SfgContext.declarations_ordered]. + The declarations can retrieved in order of definition via `declarations_ordered`. """ def __init__( diff --git a/src/pystencilssfg/emission/__init__.py b/src/pystencilssfg/emission/__init__.py index d7478e0381b8593ebc3a02564045e38852e7b6d9..3280e459ff72a23bea06e711e5fb0aa73989cc74 100644 --- a/src/pystencilssfg/emission/__init__.py +++ b/src/pystencilssfg/emission/__init__.py @@ -1,3 +1,5 @@ +from .emitter import AbstractEmitter from .header_impl_pair import HeaderImplPairEmitter +from .header_only import HeaderOnlyEmitter -__all__ = ["HeaderImplPairEmitter"] +__all__ = ["AbstractEmitter", "HeaderImplPairEmitter", "HeaderOnlyEmitter"] diff --git a/src/pystencilssfg/emission/emitter.py b/src/pystencilssfg/emission/emitter.py new file mode 100644 index 0000000000000000000000000000000000000000..55fe43c337069e49d38ed27a85e2f33929dfdc54 --- /dev/null +++ b/src/pystencilssfg/emission/emitter.py @@ -0,0 +1,15 @@ +from typing import Sequence +from abc import ABC, abstractmethod + +from ..context import SfgContext + + +class AbstractEmitter(ABC): + @property + @abstractmethod + def output_files(self) -> Sequence[str]: + pass + + @abstractmethod + def write_files(self, ctx: SfgContext): + pass diff --git a/src/pystencilssfg/emission/header_impl_pair.py b/src/pystencilssfg/emission/header_impl_pair.py index 53b6857c5fd7ebc098f72efbf2bb68514a4deafc..8d2cd2cf437e0dfbfa8527d6f288d291c27e0a83 100644 --- a/src/pystencilssfg/emission/header_impl_pair.py +++ b/src/pystencilssfg/emission/header_impl_pair.py @@ -1,3 +1,4 @@ +from typing import Sequence from os import path, makedirs from ..configuration import SfgOutputSpec @@ -6,21 +7,24 @@ from .prepare import prepare_context from .printers import SfgHeaderPrinter, SfgImplPrinter from .clang_format import invoke_clang_format +from .emitter import AbstractEmitter -class HeaderImplPairEmitter: + +class HeaderImplPairEmitter(AbstractEmitter): """Emits a header-implementation file pair.""" - def __init__(self, output_spec: SfgOutputSpec): + def __init__(self, output_spec: SfgOutputSpec, inline_impl: bool = False): """Create a `HeaderImplPairEmitter` from an [SfgOutputSpec][pystencilssfg.configuration.SfgOutputSpec].""" self._basename = output_spec.basename self._output_directory = output_spec.output_directory self._header_filename = output_spec.get_header_filename() self._impl_filename = output_spec.get_impl_filename() + self._inline_impl = inline_impl self._ospec = output_spec @property - def output_files(self) -> tuple[str, str]: + def output_files(self) -> Sequence[str]: """The files that will be written by `write_files`.""" return ( path.join(self._output_directory, self._header_filename), @@ -32,8 +36,8 @@ class HeaderImplPairEmitter: specified by the output specification.""" ctx = prepare_context(ctx) - header_printer = SfgHeaderPrinter(ctx, self._ospec) - impl_printer = SfgImplPrinter(ctx, self._ospec) + header_printer = SfgHeaderPrinter(ctx, self._ospec, self._inline_impl) + impl_printer = SfgImplPrinter(ctx, self._ospec, self._inline_impl) header = header_printer.get_code() impl = impl_printer.get_code() diff --git a/src/pystencilssfg/emission/header_only.py b/src/pystencilssfg/emission/header_only.py new file mode 100644 index 0000000000000000000000000000000000000000..7347a61059ac48ad2f5b7c320aa3a2965c5b8452 --- /dev/null +++ b/src/pystencilssfg/emission/header_only.py @@ -0,0 +1,37 @@ +from typing import Sequence +from os import path, makedirs + +from ..configuration import SfgOutputSpec +from ..context import SfgContext +from .prepare import prepare_context +from .printers import SfgHeaderPrinter +from .clang_format import invoke_clang_format + +from .emitter import AbstractEmitter + + +class HeaderOnlyEmitter(AbstractEmitter): + def __init__(self, output_spec: SfgOutputSpec): + """Create a `HeaderImplPairEmitter` from an [SfgOutputSpec][pystencilssfg.configuration.SfgOutputSpec].""" + self._basename = output_spec.basename + self._output_directory = output_spec.output_directory + self._header_filename = output_spec.get_header_filename() + + self._ospec = output_spec + + @property + def output_files(self) -> Sequence[str]: + """The files that will be written by `write_files`.""" + return (path.join(self._output_directory, self._header_filename),) + + def write_files(self, ctx: SfgContext): + ctx = prepare_context(ctx) + + header_printer = SfgHeaderPrinter(ctx, self._ospec) + header = header_printer.get_code() + header = invoke_clang_format(header, ctx.codestyle) + + makedirs(self._output_directory, exist_ok=True) + + with open(self._ospec.get_header_filepath(), "w") as headerfile: + headerfile.write(header) diff --git a/src/pystencilssfg/emission/prepare.py b/src/pystencilssfg/emission/prepare.py index 28493bbb99abecad9dc0fbaf62d0cb1582373fac..55e7fe035d0561f2579f7badc6f7588ac243ab4e 100644 --- a/src/pystencilssfg/emission/prepare.py +++ b/src/pystencilssfg/emission/prepare.py @@ -1,6 +1,74 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from functools import reduce + +from ..exceptions import SfgException +from ..ir import SfgCallTreeNode +from ..ir.source_components import ( + SfgFunction, + SfgClass, + SfgConstructor, + SfgMemberVariable, + SfgInClassDefinition, +) from ..context import SfgContext -from ..visitors import CollectIncludes +if TYPE_CHECKING: + from ..ir.source_components import SfgHeaderInclude + + +class CollectIncludes: + def __call__(self, obj: object) -> set[SfgHeaderInclude]: + return self.visit(obj) + + def visit(self, obj: object) -> set[SfgHeaderInclude]: + match obj: + case SfgContext(): + includes = set() + for func in obj.functions(): + includes |= self.visit(func) + + for cls in obj.classes(): + includes |= self.visit(cls) + + return includes + + case SfgCallTreeNode(): + return reduce( + lambda accu, child: accu | self.visit(child), + obj.children, + obj.required_includes, + ) + + case SfgFunction(_, tree, _): + return self.visit(tree) + + case SfgClass(): + return reduce( + lambda accu, member: accu | (self.visit(member)), + obj.members(), + set(), + ) + + case SfgConstructor(): + return reduce( + lambda accu, obj: accu | obj.required_includes, + obj.parameters, + set(), + ) + + case SfgMemberVariable(): + return obj.required_includes + + case SfgInClassDefinition(): + return set() + + case _: + raise SfgException( + f"Can't collect includes from object of type {type(obj)}" + ) def prepare_context(ctx: SfgContext): diff --git a/src/pystencilssfg/emission/printers.py b/src/pystencilssfg/emission/printers.py index 5c86d5f5527805b26a2733d8e1f545afeb455ec4..1b3a805cdb3efbac12e81005b7747859d0e4e2cb 100644 --- a/src/pystencilssfg/emission/printers.py +++ b/src/pystencilssfg/emission/printers.py @@ -3,16 +3,15 @@ from __future__ import annotations from textwrap import indent from itertools import chain, repeat, cycle -from pystencils.astnodes import KernelFunction -from pystencils import Backend -from pystencils.backends import generate_c +from pystencils import KernelFunction +from pystencils.backend.emission import emit_code from ..context import SfgContext from ..configuration import SfgOutputSpec from ..visitors import visitor from ..exceptions import SfgException -from ..source_components import ( +from ..ir.source_components import ( SfgEmptyLines, SfgHeaderInclude, SfgKernelNamespace, @@ -71,9 +70,12 @@ class SfgGeneralPrinter: class SfgHeaderPrinter(SfgGeneralPrinter): - def __init__(self, ctx: SfgContext, output_spec: SfgOutputSpec): + def __init__( + self, ctx: SfgContext, output_spec: SfgOutputSpec, inline_impl: bool = False + ): self._output_spec = output_spec self._ctx = ctx + self._inline_impl = inline_impl def get_code(self) -> str: return self.visit(self._ctx) @@ -103,6 +105,9 @@ class SfgHeaderPrinter(SfgGeneralPrinter): if fq_namespace is not None: code += f"}} // namespace {fq_namespace}\n" + if self._inline_impl: + code += f'#include "{self._output_spec.get_impl_filename()}"\n' + return code @visit.case(SfgFunction) @@ -182,9 +187,12 @@ def delimiter(content): class SfgImplPrinter(SfgGeneralPrinter): - def __init__(self, ctx: SfgContext, output_spec: SfgOutputSpec): + def __init__( + self, ctx: SfgContext, output_spec: SfgOutputSpec, inline_impl: bool = False + ): self._output_spec = output_spec self._ctx = ctx + self._inline_impl = inline_impl def get_code(self) -> str: return self.visit(self._ctx) @@ -197,7 +205,8 @@ class SfgImplPrinter(SfgGeneralPrinter): def frame(self, ctx: SfgContext) -> str: code = super().prelude(ctx) - code += f'\n#include "{self._output_spec.get_header_filename()}"\n\n' + if not self._inline_impl: + code += f'\n#include "{self._output_spec.get_header_filename()}"\n\n' includes = filter(lambda incl: incl.private, ctx.includes()) code += "\n".join(self.visit(incl) for incl in includes) @@ -230,17 +239,20 @@ class SfgImplPrinter(SfgGeneralPrinter): @visit.case(SfgKernelNamespace) def kernel_namespace(self, kns: SfgKernelNamespace) -> str: code = f"namespace {kns.name} {{\n\n" - code += "\n\n".join(self.visit(ast) for ast in kns.asts) + code += "\n\n".join(self.visit(ast) for ast in kns.kernel_functions) code += f"\n}} // namespace {kns.name}\n" return code @visit.case(KernelFunction) def kernel(self, kfunc: KernelFunction) -> str: - return generate_c(kfunc, dialect=Backend.C) + return emit_code(kfunc) @visit.case(SfgFunction) def function(self, func: SfgFunction) -> str: - code = f"{func.return_type} {func.name} ({self.param_list(func)})" + inline_prefix = "inline " if self._inline_impl else "" + code = ( + f"{inline_prefix} {func.return_type} {func.name} ({self.param_list(func)})" + ) code += ( "{\n" + self._ctx.codestyle.indent(func.tree.get_code(self._ctx)) + "}\n" ) @@ -253,8 +265,9 @@ class SfgImplPrinter(SfgGeneralPrinter): @visit.case(SfgMethod) def sfg_method(self, method: SfgMethod) -> str: + inline_prefix = "inline " if self._inline_impl else "" const_qual = "const" if method.const else "" - code = f"{method.return_type} {method.owning_class.class_name}::{method.name}" + code = f"{inline_prefix}{method.return_type} {method.owning_class.class_name}::{method.name}" code += f"({self.param_list(method)}) {const_qual}" code += ( " {\n" + self._ctx.codestyle.indent(method.tree.get_code(self._ctx)) + "}\n" diff --git a/src/pystencilssfg/extensions/__init__.py b/src/pystencilssfg/extensions/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..098690f34561fc696afe94c5ee7da0b79c32a761 --- /dev/null +++ b/src/pystencilssfg/extensions/__init__.py @@ -0,0 +1,3 @@ +from .sycl import SyclComposer + +__all__ = ["SyclComposer"] diff --git a/src/pystencilssfg/extensions/sycl.py b/src/pystencilssfg/extensions/sycl.py new file mode 100644 index 0000000000000000000000000000000000000000..cc3f83fa6a049febd76d1e62dcdda39d4a502575 --- /dev/null +++ b/src/pystencilssfg/extensions/sycl.py @@ -0,0 +1,270 @@ +from __future__ import annotations +from typing import Sequence +from enum import Enum +import re + +from pystencils.types import PsType, PsCustomType +from pystencils.enums import Target +from pystencils.backend.kernelfunction import KernelParameter + +from ..exceptions import SfgException +from ..context import SfgContext +from ..composer import ( + SfgBasicComposer, + SfgClassComposer, + SfgComposer, + SfgComposerMixIn, +) +from ..ir.source_components import SfgKernelHandle, SfgHeaderInclude +from ..ir.source_components import SfgVar, SfgSymbolLike +from ..ir import ( + SfgCallTreeNode, + SfgCallTreeLeaf, + SfgKernelCallNode, +) + +from ..lang import AugExpr + + +class SyclComposerMixIn(SfgComposerMixIn): + """Composer mix-in for SYCL code generation""" + + def sycl_handler(self, name: str) -> SyclHandler: + """Obtain a `SyclHandler`, which represents a ``sycl::handler`` object.""" + return SyclHandler(self._ctx).var(name) + + def sycl_group(self, dims: int, name: str) -> SyclGroup: + """Obtain a `SyclHandler`, which represents a ``sycl::handler`` object.""" + return SyclGroup(dims, self._ctx).var(name) + + def sycl_range(self, dims: int, name: str, ref: bool = False) -> SfgVar: + ref_str = " &" if ref else "" + return SfgVar(name, PsCustomType(f"sycl::range< {dims} >{ref_str}")) + + +class SyclComposer(SfgBasicComposer, SfgClassComposer, SyclComposerMixIn): + """Composer extension providing SYCL code generation capabilities""" + + def __init__(self, sfg: SfgContext | SfgComposer): + super().__init__(sfg) + + +class SyclHandler(AugExpr): + """Represents a SYCL command group handler (``sycl::handler``).""" + + def __init__(self, ctx: SfgContext): + dtype = PsCustomType("sycl::handler &") + super().__init__(dtype) + + self._ctx = ctx + + def parallel_for(self, range: SfgVar | Sequence[int], kernel: SfgKernelHandle): + """Generate a ``parallel_for`` kernel invocation using this command group handler. + + Args: + range: Object, or tuple of integers, indicating the kernel's iteration range + kernel: Handle to the pystencils-kernel to be executed + """ + self._ctx.add_include(SfgHeaderInclude("sycl/sycl.hpp", system_header=True)) + + kfunc = kernel.get_kernel_function() + if kfunc.target != Target.SYCL: + raise SfgException( + f"Kernel given to `parallel_for` is no SYCL kernel: {kernel.kernel_name}" + ) + + id_regex = re.compile(r"sycl::(id|item|nd_item)<\s*[0-9]\s*>") + + def filter_id(param: SfgSymbolLike[KernelParameter]) -> bool: + return ( + isinstance(param.dtype, PsCustomType) + and id_regex.search(param.dtype.c_string()) is not None + ) + + id_param = list(filter(filter_id, kernel.scalar_parameters))[0] + + tree = SfgKernelCallNode(kernel) + + kernel_lambda = SfgLambda(("=",), (id_param,), tree, None) + return SyclKernelInvoke(self, SyclInvokeType.ParallelFor, range, kernel_lambda) + + +class SyclGroup(AugExpr): + """Represents a SYCL group (``sycl::group``).""" + + def __init__(self, dimensions: int, ctx: SfgContext): + dtype = PsCustomType(f"sycl::group< {dimensions} > &") + super().__init__(dtype) + + self._dimensions = dimensions + self._ctx = ctx + + def parallel_for_work_item( + self, range: SfgVar | Sequence[int], kernel: SfgKernelHandle + ): + """Generate a ``parallel_for_work_item` kernel invocation on this group.` + + Args: + range: Object, or tuple of integers, indicating the kernel's iteration range + kernel: Handle to the pystencils-kernel to be executed + """ + + self._ctx.add_include(SfgHeaderInclude("sycl/sycl.hpp", system_header=True)) + + kfunc = kernel.get_kernel_function() + if kfunc.target != Target.SYCL: + raise SfgException( + f"Kernel given to `parallel_for` is no SYCL kernel: {kernel.kernel_name}" + ) + + id_regex = re.compile(r"sycl::id<\s*[0-9]\s*>") + + def filter_id(param: SfgSymbolLike[KernelParameter]) -> bool: + return ( + isinstance(param.dtype, PsCustomType) + and id_regex.search(param.dtype.c_string()) is not None + ) + + id_param = list(filter(filter_id, kernel.scalar_parameters))[0] + h_item = SfgVar("item", PsCustomType("sycl::h_item< 3 >")) + + comp = SfgComposer(self._ctx) + tree = comp.seq( + comp.map_param( + id_param, + h_item, + f"{id_param.dtype} {id_param.name} = {h_item}.get_local_id();", + ), + SfgKernelCallNode(kernel), + ) + + kernel_lambda = SfgLambda(("=",), (h_item,), tree, None) + return SyclKernelInvoke( + self, SyclInvokeType.ParallelForWorkItem, range, kernel_lambda + ) + + +class SfgLambda: + """Models a C++ lambda expression""" + + def __init__( + self, + captures: Sequence[str], + params: Sequence[SfgVar], + tree: SfgCallTreeNode, + return_type: PsType | None = None, + ) -> None: + self._captures = tuple(captures) + self._params = tuple(params) + self._tree = tree + self._return_type = return_type + + from ..ir.postprocessing import CallTreePostProcessing + + postprocess = CallTreePostProcessing() + self._required_params = postprocess(self._tree).function_params - set( + self._params + ) + + @property + def captures(self) -> tuple[str, ...]: + return self._captures + + @property + def parameters(self) -> tuple[SfgVar, ...]: + return self._params + + @property + def body(self) -> SfgCallTreeNode: + return self._tree + + @property + def return_type(self) -> PsType | None: + return self._return_type + + @property + def required_parameters(self) -> set[SfgVar]: + return self._required_params + + def get_code(self, ctx: SfgContext): + captures = ", ".join(self._captures) + params = ", ".join(f"{p.dtype} {p.name}" for p in self._params) + body = self._tree.get_code(ctx) + body = ctx.codestyle.indent(body) + rtype = ( + f"-> {self._return_type.c_string()} " + if self._return_type is not None + else "" + ) + + return f"[{captures}] ({params}) {rtype}{{\n{body}\n}}" + + +class SyclInvokeType(Enum): + ParallelFor = ("parallel_for", SyclHandler) + ParallelForWorkItem = ("parallel_for_work_item", SyclGroup) + + @property + def method(self) -> str: + return self.value[0] + + @property + def invoker_class(self) -> type: + return self.value[1] + + +class SyclKernelInvoke(SfgCallTreeLeaf): + """A SYCL kernel invocation on a given handler or group""" + + def __init__( + self, + invoker: SyclHandler | SyclGroup, + invoke_type: SyclInvokeType, + range: SfgVar | Sequence[int], + lamb: SfgLambda, + ): + if not isinstance(invoker, invoke_type.invoker_class): + raise SfgException( + f"Cannot invoke kernel via `{invoke_type.method}` on a {type(invoker)}" + ) + + super().__init__() + self._invoker = invoker + self._invoke_type = invoke_type + self._range: SfgVar | tuple[int, ...] = ( + range if isinstance(range, SfgVar) else tuple(range) + ) + self._lambda = lamb + + self._required_params = invoker.depends | lamb.required_parameters + + if isinstance(range, SfgVar): + self._required_params.add(range) + + @property + def invoker(self) -> SyclHandler | SyclGroup: + return self._invoker + + @property + def range(self) -> SfgVar | tuple[int, ...]: + return self._range + + @property + def kernel(self) -> SfgLambda: + return self._lambda + + @property + def depends(self) -> set[SfgVar]: + return self._required_params + + def get_code(self, ctx: SfgContext) -> str: + if isinstance(self._range, SfgVar): + range_code = self._range.name + else: + range_code = "{ " + ", ".join(str(r) for r in self._range) + " }" + + kernel_code = self._lambda.get_code(ctx) + invoker = str(self._invoker) + method = self._invoke_type.method + + return f"{invoker}.{method}({range_code}, {kernel_code});" diff --git a/src/pystencilssfg/generator.py b/src/pystencilssfg/generator.py index 9e5f0fbbdb9a32d5e76818dddf30c81f46821464..22db7e2cc54d0670bd4a378dae88c95559d04e18 100644 --- a/src/pystencilssfg/generator.py +++ b/src/pystencilssfg/generator.py @@ -7,14 +7,33 @@ from os import path from .configuration import ( SfgConfiguration, + SfgOutputMode, config_from_commandline, merge_configurations, ) from .context import SfgContext +from .composer import SfgComposer +from .emission import AbstractEmitter class SourceFileGenerator: - """Context manager that controls the code generation process in generator scripts.""" + """Context manager that controls the code generation process in generator scripts. + + **Usage:** The `SourceFileGenerator` must be used as a context manager by calling it within + a ``with`` statement in the top-level code of a generator script (see :ref:`guide:generator_scripts`). + Upon entry to its context, it creates an :class:`SfgComposer` which can be used to populate the generated files. + When the managed region finishes, the code files are generated and written to disk at the locations + defined by the configuration. + Existing copies of the target files are deleted on entry to the managed region, + and if an exception occurs within the managed region, no files are exported. + + **Configuration:** The `SourceFileGenerator` optionally takes a user-defined configuration + object which is merged with configuration obtained from the build system; for details + on configuration sources, refer to :class:`SfgConfiguration`. + + Args: + sfg_config: User configuration for the code generator + """ def __init__(self, sfg_config: SfgConfiguration | None = None): if sfg_config and not isinstance(sfg_config, SfgConfiguration): @@ -39,18 +58,31 @@ class SourceFileGenerator: project_info=config.project_info, ) - from .emission import HeaderImplPairEmitter + self._emitter: AbstractEmitter + match config.output_mode: + case SfgOutputMode.HEADER_ONLY: + from .emission import HeaderOnlyEmitter + + self._emitter = HeaderOnlyEmitter(config.get_output_spec(basename)) + case SfgOutputMode.INLINE: + from .emission import HeaderImplPairEmitter + + self._emitter = HeaderImplPairEmitter( + config.get_output_spec(basename), inline_impl=True + ) + case SfgOutputMode.STANDALONE: + from .emission import HeaderImplPairEmitter - self._emitter = HeaderImplPairEmitter(config.get_output_spec(basename)) + self._emitter = HeaderImplPairEmitter(config.get_output_spec(basename)) def clean_files(self): for file in self._emitter.output_files: if path.exists(file): os.remove(file) - def __enter__(self) -> SfgContext: + def __enter__(self) -> SfgComposer: self.clean_files() - return self._context + return SfgComposer(self._context) def __exit__(self, exc_type, exc_value, traceback): if exc_type is None: diff --git a/src/pystencilssfg/ir/__init__.py b/src/pystencilssfg/ir/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..43660069ce5e049f60b42193698427f1756d2fae --- /dev/null +++ b/src/pystencilssfg/ir/__init__.py @@ -0,0 +1,66 @@ +from .call_tree import ( + SfgCallTreeNode, + SfgCallTreeLeaf, + SfgEmptyNode, + SfgKernelCallNode, + SfgCudaKernelInvocation, + SfgBlock, + SfgSequence, + SfgStatements, + SfgFunctionParams, + SfgRequireIncludes, + SfgBranch, + SfgSwitchCase, + SfgSwitch, +) + +from .source_components import ( + SfgHeaderInclude, + SfgEmptyLines, + SfgKernelNamespace, + SfgKernelHandle, + SfgVar, + SfgSymbolLike, + SfgFunction, + SfgVisibility, + SfgClassKeyword, + SfgClassMember, + SfgVisibilityBlock, + SfgInClassDefinition, + SfgMemberVariable, + SfgMethod, + SfgConstructor, + SfgClass, +) + +__all__ = [ + "SfgCallTreeNode", + "SfgCallTreeLeaf", + "SfgEmptyNode", + "SfgKernelCallNode", + "SfgCudaKernelInvocation", + "SfgSequence", + "SfgBlock", + "SfgStatements", + "SfgFunctionParams", + "SfgRequireIncludes", + "SfgBranch", + "SfgSwitchCase", + "SfgSwitch", + "SfgHeaderInclude", + "SfgEmptyLines", + "SfgKernelNamespace", + "SfgKernelHandle", + "SfgVar", + "SfgSymbolLike", + "SfgFunction", + "SfgVisibility", + "SfgClassKeyword", + "SfgClassMember", + "SfgVisibilityBlock", + "SfgInClassDefinition", + "SfgMemberVariable", + "SfgMethod", + "SfgConstructor", + "SfgClass", +] diff --git a/src/pystencilssfg/ir/call_tree.py b/src/pystencilssfg/ir/call_tree.py new file mode 100644 index 0000000000000000000000000000000000000000..34d50182b7bca8348a161553e4f7edaa4ae3c0d0 --- /dev/null +++ b/src/pystencilssfg/ir/call_tree.py @@ -0,0 +1,419 @@ +from __future__ import annotations +from typing import TYPE_CHECKING, Sequence, Iterable, NewType + +from abc import ABC, abstractmethod +from itertools import chain + +from .source_components import SfgHeaderInclude, SfgKernelHandle, SfgVar + +if TYPE_CHECKING: + from ..context import SfgContext + + +class SfgCallTreeNode(ABC): + """Base class for all nodes comprising SFG call trees. + + ## Code Printing + + For extensibility, code printing is implemented inside the call tree. + Therefore, every instantiable call tree node must implement the method `get_code`. + By convention, the string returned by `get_code` should not contain a trailing newline. + + ## Branching Structure + + The branching structure of the call tree is managed uniformly through the `children` interface + of SfgCallTreeNode. Each subclass must ensure that access to and modification of + the branching structure through the `children` property and the `child` and `set_child` + methods is possible, if necessary by overriding the property and methods. + """ + + @property + @abstractmethod + def children(self) -> Sequence[SfgCallTreeNode]: + """This node's children""" + + @abstractmethod + def get_code(self, ctx: SfgContext) -> str: + """Returns the code of this node. + + By convention, the code block emitted by this function should not contain a trailing newline. + """ + + @property + def required_includes(self) -> set[SfgHeaderInclude]: + """Return a set of header includes required by this node""" + return set() + + +class SfgCallTreeLeaf(SfgCallTreeNode, ABC): + """A leaf node of the call tree. + + Leaf nodes must implement `required_parameters` for automatic parameter collection. + """ + + def __init__(self): + super().__init__() + + @property + def children(self) -> Sequence[SfgCallTreeNode]: + return () + + @property + @abstractmethod + def depends(self) -> set[SfgVar]: + """Set of objects this leaf depends on""" + + +class SfgEmptyNode(SfgCallTreeLeaf): + """A leaf node that does not emit any code. + + Empty nodes must still implement `required_parameters`. + """ + + def __init__(self): + super().__init__() + + def get_code(self, ctx: SfgContext) -> str: + return "" + + +class SfgStatements(SfgCallTreeLeaf): + """Represents (a sequence of) statements in the source language. + + This class groups together arbitrary code strings + (e.g. sequences of C++ statements, cf. https://en.cppreference.com/w/cpp/language/statements), + and annotates them with the set of symbols read and written by these statements. + + It is the user's responsibility to ensure that the code string is valid code in the output language, + and that the lists of required and defined objects are correct and complete. + + Args: + code_string: Code to be printed out. + defined_params: Variables that will be newly defined and visible to code in sequence after these statements. + required_params: Variables that are required as input to these statements. + """ + + def __init__( + self, + code_string: str, + defines: Iterable[SfgVar], + depends: Iterable[SfgVar], + ): + super().__init__() + + self._code_string = code_string + + self._defines = set(defines) + self._depends = set(depends) + + self._required_includes = set() + for obj in chain(depends, defines): + if isinstance(obj, SfgVar): + self._required_includes |= obj.required_includes + + @property + def depends(self) -> set[SfgVar]: + return self._depends + + @property + def defines(self) -> set[SfgVar]: + return self._defines + + @property + def required_includes(self) -> set[SfgHeaderInclude]: + return self._required_includes + + def get_code(self, ctx: SfgContext) -> str: + return self._code_string + + +class SfgFunctionParams(SfgEmptyNode): + def __init__(self, parameters: Sequence[SfgVar]): + super().__init__() + self._params = set(parameters) + + self._required_includes = set() + for obj in parameters: + if isinstance(obj, SfgVar): + self._required_includes |= obj.required_includes + + @property + def depends(self) -> set[SfgVar]: + return self._params + + @property + def required_includes(self) -> set[SfgHeaderInclude]: + return self._required_includes + + +class SfgRequireIncludes(SfgEmptyNode): + def __init__(self, includes: Sequence[SfgHeaderInclude]): + super().__init__() + self._required_includes = set(includes) + + @property + def depends(self) -> set[SfgVar]: + return set() + + @property + def required_includes(self) -> set[SfgHeaderInclude]: + return self._required_includes + + +class SfgSequence(SfgCallTreeNode): + __match_args__ = ("children",) + + def __init__(self, children: Sequence[SfgCallTreeNode]): + self._children = list(children) + + @property + def children(self) -> Sequence[SfgCallTreeNode]: + return self._children + + @children.setter + def children(self, cs: Sequence[SfgCallTreeNode]): + self._children = list(cs) + + def __getitem__(self, idx: int) -> SfgCallTreeNode: + return self._children[idx] + + def __setitem__(self, idx: int, c: SfgCallTreeNode): + self._children[idx] = c + + def get_code(self, ctx: SfgContext) -> str: + return "\n".join(c.get_code(ctx) for c in self._children) + + +class SfgBlock(SfgCallTreeNode): + def __init__(self, seq: SfgSequence): + self._seq = seq + + @property + def sequence(self) -> SfgSequence: + return self._seq + + @property + def children(self) -> Sequence[SfgCallTreeNode]: + return (self._seq,) + + def get_code(self, ctx: SfgContext) -> str: + seq_code = ctx.codestyle.indent(self._seq.get_code(ctx)) + + return "{\n" + seq_code + "\n}" + + +# class SfgForLoop(SfgCallTreeNode): +# def __init__(self, control_line: SfgStatements, body: SfgCallTreeNode): +# super().__init__(control_line, body) + +# @property +# def body(self) -> SfgStatements: +# return cast(SfgStatements) + + +class SfgKernelCallNode(SfgCallTreeLeaf): + def __init__(self, kernel_handle: SfgKernelHandle): + super().__init__() + self._kernel_handle = kernel_handle + + @property + def depends(self) -> set[SfgVar]: + return set(self._kernel_handle.parameters) + + def get_code(self, ctx: SfgContext) -> str: + ast_params = self._kernel_handle.parameters + fnc_name = self._kernel_handle.fully_qualified_name + call_parameters = ", ".join([p.name for p in ast_params]) + + return f"{fnc_name}({call_parameters});" + + +class SfgCudaKernelInvocation(SfgCallTreeLeaf): + def __init__( + self, + kernel_handle: SfgKernelHandle, + num_blocks_code: str, + threads_per_block_code: str, + stream_code: str | None, + depends: set[SfgVar], + ): + from pystencils import Target + from pystencils.backend.kernelfunction import GpuKernelFunction + + func = kernel_handle.get_kernel_function() + if not (isinstance(func, GpuKernelFunction) and func.target == Target.CUDA): + raise ValueError( + "An `SfgCudaKernelInvocation` node can only call a CUDA kernel." + ) + + super().__init__() + self._kernel_handle = kernel_handle + self._num_blocks = num_blocks_code + self._threads_per_block = threads_per_block_code + self._stream = stream_code + self._depends = depends + + @property + def depends(self) -> set[SfgVar]: + return set(self._kernel_handle.parameters) | self._depends + + def get_code(self, ctx: SfgContext) -> str: + ast_params = self._kernel_handle.parameters + fnc_name = self._kernel_handle.fully_qualified_name + call_parameters = ", ".join([p.name for p in ast_params]) + + grid_args = [self._num_blocks, self._threads_per_block] + if self._stream is not None: + grid_args += [self._stream] + + grid = "<<< " + ", ".join(grid_args) + " >>>" + return f"{fnc_name}{grid}({call_parameters});" + + +class SfgBranch(SfgCallTreeNode): + def __init__( + self, + cond: SfgStatements, + branch_true: SfgSequence, + branch_false: SfgSequence | None = None, + ): + self._cond = cond + self._branch_true = branch_true + self._branch_false = branch_false + + @property + def condition(self) -> SfgStatements: + return self._cond + + @property + def branch_true(self) -> SfgSequence: + return self._branch_true + + @property + def branch_false(self) -> SfgSequence | None: + return self._branch_false + + @property + def children(self) -> Sequence[SfgCallTreeNode]: + return ( + self._cond, + self._branch_true, + ) + ((self.branch_false,) if self.branch_false is not None else ()) + + def get_code(self, ctx: SfgContext) -> str: + code = f"if({self.condition.get_code(ctx)}) {{\n" + code += ctx.codestyle.indent(self.branch_true.get_code(ctx)) + code += "\n}" + + if self.branch_false is not None: + code += "else {\n" + code += ctx.codestyle.indent(self.branch_false.get_code(ctx)) + code += "\n}" + + return code + + +class SfgSwitchCase(SfgCallTreeNode): + DefaultCaseType = NewType("DefaultCaseType", object) + Default = DefaultCaseType(object()) + + def __init__(self, label: str | SfgSwitchCase.DefaultCaseType, body: SfgSequence): + self._label = label + self._body = body + + @property + def label(self) -> str | DefaultCaseType: + return self._label + + @property + def body(self) -> SfgSequence: + return self._body + + @property + def children(self) -> Sequence[SfgCallTreeNode]: + return (self._body,) + + @property + def is_default(self) -> bool: + return self._label == SfgSwitchCase.Default + + def get_code(self, ctx: SfgContext) -> str: + code = "" + if self._label == SfgSwitchCase.Default: + code += "default: {\n" + else: + code += f"case {self._label}: {{\n" + code += ctx.codestyle.indent(self.body.get_code(ctx)) + code += "\nbreak;\n}" + return code + + +class SfgSwitch(SfgCallTreeNode): + def __init__( + self, + switch_arg: SfgStatements, + cases_dict: dict[str, SfgSequence], + default: SfgSequence | None = None, + ): + self._cases = [SfgSwitchCase(label, body) for label, body in cases_dict.items()] + if default is not None: + # invariant: the default case is always the last child + self._cases += [SfgSwitchCase(SfgSwitchCase.Default, default)] + self._switch_arg = switch_arg + self._default = ( + SfgSwitchCase(SfgSwitchCase.Default, default) + if default is not None + else None + ) + + @property + def switch_arg(self) -> str | SfgStatements: + return self._switch_arg + + @property + def default(self) -> SfgCallTreeNode | None: + return self._default + + @property + def children(self) -> tuple[SfgCallTreeNode, ...]: + return (self._switch_arg,) + tuple(self._cases) + + @property + def cases(self) -> tuple[SfgCallTreeNode, ...]: + if self._default is not None: + return tuple(self._cases[:-1]) + else: + return tuple(self._cases) + + @cases.setter + def cases(self, cs: Sequence[SfgSwitchCase]) -> None: + if len(cs) != len(self._cases): + raise ValueError("The number of child nodes must remain the same!") + + self._default = None + for i, c in enumerate(cs): + if c.is_default: + if i != len(cs) - 1: + raise ValueError("Default case must be listed last.") + else: + self._default = c + + self._children = list(cs) + + def set_case(self, idx: int, c: SfgSwitchCase): + if c.is_default: + if idx != len(self._children) - 1: + raise ValueError("Default case must be the last child.") + elif self._default is None: + raise ValueError("Cannot replace normal case with default case.") + else: + self._default = c + self._children[-1] = c + else: + self._children[idx] = c + + def get_code(self, ctx: SfgContext) -> str: + code = f"switch({self._switch_arg.get_code(ctx)}) {{\n" + code += "\n".join(c.get_code(ctx) for c in self.children) + code += "}" + return code diff --git a/src/pystencilssfg/ir/postprocessing.py b/src/pystencilssfg/ir/postprocessing.py new file mode 100644 index 0000000000000000000000000000000000000000..c30910d59f6c6626dd6fc147f93491e9b175dace --- /dev/null +++ b/src/pystencilssfg/ir/postprocessing.py @@ -0,0 +1,303 @@ +from __future__ import annotations +from typing import TYPE_CHECKING, Sequence +import warnings +from functools import reduce +from dataclasses import dataclass + +from abc import ABC, abstractmethod + +import sympy as sp + +from pystencils import Field, TypedSymbol +from pystencils.backend.kernelfunction import ( + FieldPointerParam, + FieldShapeParam, + FieldStrideParam, +) + +from ..exceptions import SfgException + +from .call_tree import SfgCallTreeNode, SfgCallTreeLeaf, SfgSequence, SfgStatements +from ..ir.source_components import SfgVar, SfgSymbolLike +from ..lang import IFieldExtraction, SrcField, SrcVector + +if TYPE_CHECKING: + from ..context import SfgContext + from .source_components import SfgClass + + +class FlattenSequences: + """Flattens any nested sequences occuring in a kernel call tree.""" + + def __call__(self, node: SfgCallTreeNode) -> None: + self.visit(node) + + def visit(self, node: SfgCallTreeNode): + match node: + case SfgSequence(): + self.flatten(node) + case _: + for c in node.children: + self.visit(c) + + def flatten(self, sequence: SfgSequence) -> None: + children_flattened: list[SfgCallTreeNode] = [] + + def flatten(seq: SfgSequence): + for c in seq.children: + if isinstance(c, SfgSequence): + flatten(c) + else: + children_flattened.append(c) + + flatten(sequence) + + for c in children_flattened: + self.visit(c) + + sequence.children = children_flattened + + +class PostProcessingContext: + def __init__(self, enclosing_class: SfgClass | None = None) -> None: + self.enclosing_class: SfgClass | None = enclosing_class + self.live_objects: set[SfgVar] = set() + + def is_method(self) -> bool: + return self.enclosing_class is not None + + def get_enclosing_class(self) -> SfgClass: + if self.enclosing_class is None: + raise SfgException("Cannot get the enclosing class of a free function.") + + return self.enclosing_class + + +@dataclass(frozen=True) +class PostProcessingResult: + function_params: set[SfgVar] + + +class CallTreePostProcessing: + def __init__(self, enclosing_class: SfgClass | None = None): + self._enclosing_class = enclosing_class + self._flattener = FlattenSequences() + + def __call__(self, ast: SfgCallTreeNode) -> PostProcessingResult: + params = self.get_live_objects(ast) + params_by_name: dict[str, SfgVar] = dict() + + for param in params: + if param.name in params_by_name: + other = params_by_name[param.name] + + if param.dtype == other.dtype: + warnings.warn( + "Encountered two non-identical parameters with same name and data type:\n" + f" {repr(param)}\n" + "and\n" + f" {repr(other)}\n" + ) + else: + raise SfgException( + "Encountered two parameters with same name but different data types:\n" + f" {repr(param)}\n" + "and\n" + f" {repr(other)}" + ) + params_by_name[param.name] = param + + return PostProcessingResult(set(params_by_name.values())) + + def handle_sequence(self, seq: SfgSequence, ppc: PostProcessingContext): + def iter_nested_sequences(seq: SfgSequence): + for i in range(len(seq.children) - 1, -1, -1): + c = seq.children[i] + + if isinstance(c, SfgDeferredNode): + c = c.expand(ppc) + seq[i] = c + + if isinstance(c, SfgSequence): + iter_nested_sequences(c) + else: + if isinstance(c, SfgStatements): + ppc.live_objects -= c.defines + + ppc.live_objects |= self.get_live_objects(c) + + iter_nested_sequences(seq) + + def get_live_objects(self, node: SfgCallTreeNode) -> set[SfgVar]: + match node: + case SfgSequence(): + ppc = self._ppc() + self.handle_sequence(node, ppc) + return ppc.live_objects + + case SfgCallTreeLeaf(): + return node.depends + + case SfgDeferredNode(): + raise SfgException("Deferred nodes can only occur inside a sequence.") + + case _: + return reduce( + lambda x, y: x | y, + (self.get_live_objects(c) for c in node.children), + set(), + ) + + def _ppc(self) -> PostProcessingContext: + return PostProcessingContext(enclosing_class=self._enclosing_class) + + +class SfgDeferredNode(SfgCallTreeNode, ABC): + """Nodes of this type are inserted as placeholders into the kernel call tree + and need to be expanded at a later time. + + Subclasses of SfgDeferredNode correspond to nodes that cannot be created yet + because information required for their construction is not yet known. + """ + + @property + def children(self) -> Sequence[SfgCallTreeNode]: + raise SfgException( + "Invalid access into deferred node; deferred nodes must be expanded first." + ) + + @abstractmethod + def expand(self, ppc: PostProcessingContext) -> SfgCallTreeNode: + pass + + def get_code(self, ctx: SfgContext) -> str: + raise SfgException( + "Invalid access into deferred node; deferred nodes must be expanded first." + ) + + +class SfgDeferredParamMapping(SfgDeferredNode): + def __init__(self, lhs: SfgVar, rhs: set[SfgVar], mapping: str): + self._lhs = lhs + self._rhs = rhs + self._mapping = mapping + + def expand(self, ppc: PostProcessingContext) -> SfgCallTreeNode: + if self._lhs in ppc.live_objects: + return SfgStatements(self._mapping, (self._lhs,), tuple(self._rhs)) + else: + return SfgSequence([]) + + +class SfgDeferredFieldMapping(SfgDeferredNode): + def __init__(self, psfield: Field, extraction: IFieldExtraction | SrcField): + self._field = psfield + self._extraction: IFieldExtraction = ( + extraction + if isinstance(extraction, IFieldExtraction) + else extraction.get_extraction() + ) + + # type: ignore + def expand(self, ppc: PostProcessingContext) -> SfgCallTreeNode: + # Find field pointer + ptr: SfgSymbolLike[FieldPointerParam] | None = None + shape: list[SfgSymbolLike[FieldShapeParam] | int | None] = [None] * len( + self._field.shape + ) + strides: list[SfgSymbolLike[FieldStrideParam] | int | None] = [None] * len( + self._field.strides + ) + + for param in ppc.live_objects: + # idk why, but mypy does not understand these pattern matches + match param: + case SfgSymbolLike(FieldPointerParam(_, _, field)) if field == self._field: # type: ignore + ptr = param + case SfgSymbolLike( + FieldShapeParam(_, _, field, coord) # type: ignore + ) if field == self._field: # type: ignore + shape[coord] = param # type: ignore + case SfgSymbolLike( + FieldStrideParam(_, _, field, coord) # type: ignore + ) if field == self._field: # type: ignore + strides[coord] = param # type: ignore + + # Find constant sizes + for coord, s in enumerate(self._field.shape): + if not isinstance(s, TypedSymbol): + shape[coord] = s + + # Find constant strides + for coord, s in enumerate(self._field.strides): + if not isinstance(s, TypedSymbol): + strides[coord] = s + + # Now we have all the symbols, start extracting them + nodes = [] + + if ptr is not None: + expr = self._extraction.ptr() + nodes.append( + SfgStatements( + f"{ptr.dtype} {ptr.name} {{ {expr} }};", (ptr,), expr.depends + ) + ) + + def get_shape(coord, symb: SfgSymbolLike | int): + expr = self._extraction.size(coord) + + if expr is None: + raise SfgException( + f"Cannot extract shape in coordinate {coord} from {self._extraction}" + ) + + if isinstance(symb, SfgSymbolLike): + return SfgStatements( + f"{symb.dtype} {symb.name} {{ {expr} }};", (symb,), expr.depends + ) + else: + return SfgStatements(f"/* {expr} == {symb} */", (), ()) + + def get_stride(coord, symb: SfgSymbolLike | int): + expr = self._extraction.stride(coord) + + if expr is None: + raise SfgException( + f"Cannot extract stride in coordinate {coord} from {self._extraction}" + ) + + if isinstance(symb, SfgSymbolLike): + return SfgStatements( + f"{symb.dtype} {symb.name} {{ {expr} }};", (symb,), expr.depends + ) + else: + return SfgStatements(f"/* {expr} == {symb} */", (), ()) + + nodes += [get_shape(c, s) for c, s in enumerate(shape) if s is not None] + nodes += [get_stride(c, s) for c, s in enumerate(strides) if s is not None] + + return SfgSequence(nodes) + + +class SfgDeferredVectorMapping(SfgDeferredNode): + def __init__(self, scalars: Sequence[sp.Symbol | SfgVar], vector: SrcVector): + self._scalars = {sc.name: (i, sc) for i, sc in enumerate(scalars)} + self._vector = vector + + def expand(self, ppc: PostProcessingContext) -> SfgCallTreeNode: + nodes = [] + + for param in ppc.live_objects: + if param.name in self._scalars: + idx, _ = self._scalars[param.name] + expr = self._vector.extract_component(idx) + nodes.append( + SfgStatements( + f"{param.dtype} {param.name} {{ {expr} }};", + (param,), + expr.depends, + ) + ) + + return SfgSequence(nodes) diff --git a/src/pystencilssfg/source_components.py b/src/pystencilssfg/ir/source_components.py similarity index 71% rename from src/pystencilssfg/source_components.py rename to src/pystencilssfg/ir/source_components.py index 43d58a069712f7465278b548622079f311cfcff2..6aace6991d8ef0931e963ede1849d5df0a6b350a 100644 --- a/src/pystencilssfg/source_components.py +++ b/src/pystencilssfg/ir/source_components.py @@ -2,20 +2,23 @@ from __future__ import annotations from abc import ABC from enum import Enum, auto -from typing import TYPE_CHECKING, Sequence, Generator +from typing import TYPE_CHECKING, Sequence, Generator, TypeVar, Generic, Any from dataclasses import replace from itertools import chain -from pystencils import CreateKernelConfig, create_kernel -from pystencils.astnodes import KernelFunction +from pystencils import CreateKernelConfig, create_kernel, Field +from pystencils.backend.kernelfunction import ( + KernelFunction, + KernelParameter, + FieldParameter, +) +from pystencils.types import PsType, PsCustomType -from .types import SrcType -from .source_concepts import SrcObject -from .exceptions import SfgException +from ..exceptions import SfgException if TYPE_CHECKING: - from .tree import SfgCallTreeNode - from .context import SfgContext + from . import SfgCallTreeNode + from ..context import SfgContext class SfgEmptyLines: @@ -28,6 +31,19 @@ class SfgEmptyLines: class SfgHeaderInclude: + + @staticmethod + def parse(incl: str | SfgHeaderInclude, private: bool = False): + if isinstance(incl, SfgHeaderInclude): + return incl + + system_header = False + if incl.startswith("<") and incl.endswith(">"): + incl = incl[1:-1] + system_header = True + + return SfgHeaderInclude(incl, system_header=system_header, private=private) + def __init__( self, header_file: str, system_header: bool = False, private: bool = False ): @@ -60,38 +76,51 @@ class SfgHeaderInclude: class SfgKernelNamespace: - def __init__(self, ctx, name: str): + """A namespace grouping together a number of kernels.""" + + def __init__(self, ctx: SfgContext, name: str): self._ctx = ctx self._name = name - self._asts: dict[str, KernelFunction] = dict() + self._kernel_functions: dict[str, KernelFunction] = dict() @property def name(self): return self._name @property - def asts(self): - yield from self._asts.values() + def kernel_functions(self): + yield from self._kernel_functions.values() + + def get_kernel_function(self, khandle: SfgKernelHandle) -> KernelFunction: + if khandle.kernel_namespace is not self: + raise ValueError( + f"Kernel handle does not belong to this namespace: {khandle}" + ) - def add(self, ast: KernelFunction, name: str | None = None): + return self._kernel_functions[khandle.kernel_name] + + def add(self, kernel: KernelFunction, name: str | None = None): """Adds an existing pystencils AST to this namespace. If a name is specified, the AST's function name is changed.""" if name is not None: astname = name else: - astname = ast.function_name + astname = kernel.name - if astname in self._asts: + if astname in self._kernel_functions: raise ValueError( f"Duplicate ASTs: An AST with name {astname} already exists in namespace {self._name}" ) if name is not None: - ast.function_name = name + kernel.name = name + + self._kernel_functions[astname] = kernel - self._asts[astname] = ast + for header in kernel.required_headers: + self._ctx.add_include(SfgHeaderInclude.parse(header, private=True)) - return SfgKernelHandle(self._ctx, astname, self, ast.get_parameters()) + return SfgKernelHandle(self._ctx, astname, self, kernel.parameters) def create( self, @@ -100,18 +129,14 @@ class SfgKernelNamespace: config: CreateKernelConfig | None = None, ): """Creates a new pystencils kernel from a list of assignments and a configuration. - This is a wrapper around - [`pystencils.create_kernel`]( - https://pycodegen.pages.i10git.cs.fau.de/pystencils/ - sphinx/kernel_compile_and_call.html#pystencils.create_kernel - ) - with a subsequent call to [`add`][pystencilssfg.source_components.SfgKernelNamespace.add]. + This is a wrapper around `pystencils.create_kernel` + with a subsequent call to `add`. """ if config is None: config = CreateKernelConfig() if name is not None: - if name in self._asts: + if name in self._kernel_functions: raise ValueError( f"Duplicate ASTs: An AST with name {name} already exists in namespace {self._name}" ) @@ -123,26 +148,28 @@ class SfgKernelNamespace: class SfgKernelHandle: + """A handle that represents a pystencils kernel within a kernel namespace.""" + def __init__( self, ctx: SfgContext, name: str, namespace: SfgKernelNamespace, - parameters: Sequence[KernelFunction.Parameter], + parameters: Sequence[KernelParameter], ): self._ctx = ctx self._name = name self._namespace = namespace - self._parameters = parameters + self._parameters = [SfgSymbolLike(p) for p in parameters] - self._scalar_params = set() - self._fields = set() + self._scalar_params: set[SfgSymbolLike] = set() + self._fields: set[Field] = set() for param in self._parameters: - if param.is_field_parameter: - self._fields |= set(param.fields) + if isinstance(param.wrapped, FieldParameter): + self._fields.add(param.wrapped.field) else: - self._scalar_params.add(param.symbol) + self._scalar_params.add(param) @property def kernel_name(self): @@ -161,7 +188,7 @@ class SfgKernelHandle: return f"{fqn}::{self.kernel_namespace.name}::{self.kernel_name}" @property - def parameters(self): + def parameters(self) -> Sequence[SfgSymbolLike]: return self._parameters @property @@ -172,34 +199,112 @@ class SfgKernelHandle: def fields(self): return self.fields + def get_kernel_function(self) -> KernelFunction: + return self._namespace.get_kernel_function(self) + + +class SfgVar: + __match_args__ = ("name", "dtype") + + def __init__( + self, + name: str, + dtype: PsType, + required_includes: set[SfgHeaderInclude] | None = None, + ): + self._name = name + self._dtype = dtype + + self._required_includes = ( + required_includes if required_includes is not None else set() + ) + + @property + def name(self) -> str: + return self._name + + @property + def dtype(self) -> PsType: + return self._dtype + + def _args(self) -> tuple[Any, ...]: + return (self._name, self._dtype) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SfgVar): + return False + + return self._args() == other._args() + + def __hash__(self) -> int: + return hash(self._args()) + + @property + def required_includes(self) -> set[SfgHeaderInclude]: + return self._required_includes + + def __str__(self) -> str: + return self._name + + def __repr__(self) -> str: + return f"SfgVar( {self._name}, {repr(self._dtype)} )" + + +SymbolLike_T = TypeVar("SymbolLike_T", bound=KernelParameter) + + +class SfgSymbolLike(SfgVar, Generic[SymbolLike_T]): + __match_args__ = ("wrapped",) + + """Cast pystencils- or SymPy-native symbol-like objects as a `SfgVar`.""" + + def __init__(self, param: SymbolLike_T): + self._param = param + super().__init__(param.name, param.dtype) + + @property + def wrapped(self) -> SymbolLike_T: + return self._param + + def _args(self): + return (self._param,) + class SfgFunction: + __match_args__ = ("name", "tree", "parameters") + def __init__( - self, name: str, tree: SfgCallTreeNode, return_type: SrcType = SrcType("void") + self, + name: str, + tree: SfgCallTreeNode, + return_type: PsType = PsCustomType("void"), + _is_method: bool = False, ): self._name = name self._tree = tree self._return_type = return_type - from .visitors.tree_visitors import ExpandingParameterCollector + self._parameters: set[SfgVar] + if not _is_method: + from .postprocessing import CallTreePostProcessing - param_collector = ExpandingParameterCollector() - self._parameters = param_collector.visit(self._tree) + param_collector = CallTreePostProcessing() + self._parameters = param_collector(self._tree).function_params @property - def name(self): + def name(self) -> str: return self._name @property - def parameters(self): + def parameters(self) -> set[SfgVar]: return self._parameters @property - def tree(self): + def tree(self) -> SfgCallTreeNode: return self._tree @property - def return_type(self) -> SrcType: + def return_type(self) -> PsType: return self._return_type @@ -310,9 +415,9 @@ class SfgInClassDefinition(SfgClassMember): return self._text -class SfgMemberVariable(SrcObject, SfgClassMember): - def __init__(self, name: str, dtype: SrcType): - SrcObject.__init__(self, name, dtype) +class SfgMemberVariable(SfgVar, SfgClassMember): + def __init__(self, name: str, dtype: PsType): + SfgVar.__init__(self, name, dtype) SfgClassMember.__init__(self) @@ -321,16 +426,21 @@ class SfgMethod(SfgFunction, SfgClassMember): self, name: str, tree: SfgCallTreeNode, - return_type: SrcType = SrcType("void"), + return_type: PsType = PsCustomType("void"), inline: bool = False, const: bool = False, ): - SfgFunction.__init__(self, name, tree, return_type=return_type) + SfgFunction.__init__(self, name, tree, return_type=return_type, _is_method=True) SfgClassMember.__init__(self) self._inline = inline self._const = const + from .postprocessing import CallTreePostProcessing + + param_collector = CallTreePostProcessing() + self._parameters: set[SfgVar] = param_collector(self._tree).function_params + @property def inline(self) -> bool: return self._inline @@ -343,7 +453,7 @@ class SfgMethod(SfgFunction, SfgClassMember): class SfgConstructor(SfgClassMember): def __init__( self, - parameters: Sequence[SrcObject] = (), + parameters: Sequence[SfgVar] = (), initializers: Sequence[str] = (), body: str = "", ): @@ -353,7 +463,7 @@ class SfgConstructor(SfgClassMember): self._body = body @property - def parameters(self) -> tuple[SrcObject, ...]: + def parameters(self) -> tuple[SfgVar, ...]: return self._parameters @property @@ -382,6 +492,8 @@ class SfgClass: [SfgClassComposer][pystencilssfg.composer.SfgClassComposer]. """ + __match_args__ = ("class_name",) + def __init__( self, class_name: str, @@ -409,8 +521,8 @@ class SfgClass: return self._class_name @property - def src_type(self) -> SrcType: - return SrcType(self._class_name) + def src_type(self) -> PsType: + return PsCustomType(self._class_name) @property def base_classes(self) -> tuple[str, ...]: diff --git a/src/pystencilssfg/lang/__init__.py b/src/pystencilssfg/lang/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..543d3094131f7dd4d0d8edf1673c2e5a27597065 --- /dev/null +++ b/src/pystencilssfg/lang/__init__.py @@ -0,0 +1,15 @@ +from .expressions import ( + DependentExpression, + AugExpr, + IFieldExtraction, + SrcField, + SrcVector, +) + +__all__ = [ + "DependentExpression", + "AugExpr", + "IFieldExtraction", + "SrcField", + "SrcVector", +] diff --git a/src/pystencilssfg/source_concepts/cpp/__init__.py b/src/pystencilssfg/lang/cpp/__init__.py similarity index 77% rename from src/pystencilssfg/source_concepts/cpp/__init__.py rename to src/pystencilssfg/lang/cpp/__init__.py index 7a2a9106115caa3bcb0b4b99659db065b0494b7c..a00c2c2b3ea1c279f04ef6d460320b97aad60111 100644 --- a/src/pystencilssfg/source_concepts/cpp/__init__.py +++ b/src/pystencilssfg/lang/cpp/__init__.py @@ -1,6 +1,7 @@ from .std_mdspan import StdMdspan, mdspan_ref from .std_vector import StdVector, std_vector_ref from .std_tuple import StdTuple, std_tuple_ref +from .std_span import StdSpan, std_span_ref __all__ = [ "StdMdspan", @@ -9,4 +10,6 @@ __all__ = [ "std_vector_ref", "StdTuple", "std_tuple_ref", + "StdSpan", + "std_span_ref", ] diff --git a/src/pystencilssfg/lang/cpp/std.py b/src/pystencilssfg/lang/cpp/std.py new file mode 100644 index 0000000000000000000000000000000000000000..48cd249496b182eb911f1264a8e1a5410aa141e3 --- /dev/null +++ b/src/pystencilssfg/lang/cpp/std.py @@ -0,0 +1,12 @@ +from .std_span import std_span_ref +from .std_mdspan import mdspan_ref +from .std_vector import std_vector_ref + +span = std_span_ref +"""Create an ``std::span`` reference for a 1D pystencils field""" + +mdspan = mdspan_ref +"""Create an ``std::mdspan`` reference for a pystencils field""" + +vector = std_vector_ref +"""Create an ``std::vector`` reference for a 1D pystencils field""" diff --git a/src/pystencilssfg/lang/cpp/std_mdspan.py b/src/pystencilssfg/lang/cpp/std_mdspan.py new file mode 100644 index 0000000000000000000000000000000000000000..0b460fd13e7a924f17a3745bf230add517be53c1 --- /dev/null +++ b/src/pystencilssfg/lang/cpp/std_mdspan.py @@ -0,0 +1,92 @@ +from typing import cast +from sympy import Symbol + +from pystencils import Field +from pystencils.types import ( + PsType, + PsCustomType, + PsSignedIntegerType, + PsUnsignedIntegerType, +) + +from pystencilssfg.lang.expressions import AugExpr + +from ...lang import SrcField, IFieldExtraction +from ...ir.source_components import SfgHeaderInclude + + +class StdMdspan(SrcField): + dynamic_extent = "std::dynamic_extent" + + def __init__( + self, + T: PsType, + extents: tuple[int | str, ...], + extents_type: PsType = PsSignedIntegerType(64), + reference: bool = False, + ): + cpp_typestr = T.c_string() + extents_type_str = extents_type.c_string() + + extents_str = ( + f"std::extents< {extents_type_str}, {', '.join(str(e) for e in extents)} >" + ) + typestring = ( + f"std::mdspan< {cpp_typestr}, {extents_str} > {'&' if reference else ''}" + ) + super().__init__(PsCustomType(typestring)) + + self._extents = extents + self._dim = len(extents) + + @property + def required_includes(self) -> set[SfgHeaderInclude]: + return {SfgHeaderInclude("experimental/mdspan", system_header=True)} + + def get_extraction(self) -> IFieldExtraction: + mdspan = self + + class Extraction(IFieldExtraction): + def ptr(self) -> AugExpr: + return AugExpr.format("{}.data_handle()", mdspan) + + def size(self, coordinate: int) -> AugExpr | None: + if coordinate > mdspan._dim: + return None + else: + return AugExpr.format("{}.extents().extent({})", mdspan, coordinate) + + def stride(self, coordinate: int) -> AugExpr | None: + if coordinate > mdspan._dim: + return None + else: + return AugExpr.format("{}.stride({})", mdspan, coordinate) + + return Extraction() + + +def mdspan_ref(field: Field, extents_type: PsType = PsUnsignedIntegerType(64)): + """Creates a `std::mdspan &` for a given pystencils field.""" + from pystencils.field import layout_string_to_tuple + + if field.layout != layout_string_to_tuple("soa", field.spatial_dimensions): + raise NotImplementedError( + "mdspan mapping is currently only available for structure-of-arrays fields" + ) + + extents: list[str | int] = [] + + for s in field.spatial_shape: + extents.append( + StdMdspan.dynamic_extent if isinstance(s, Symbol) else cast(int, s) + ) + + for s in field.index_shape: + extents.append(StdMdspan.dynamic_extent if isinstance(s, Symbol) else s) + + return StdMdspan( + field.dtype, + tuple(extents), + extents_type=extents_type, + reference=True, + ).var(field.name) diff --git a/src/pystencilssfg/lang/cpp/std_span.py b/src/pystencilssfg/lang/cpp/std_span.py new file mode 100644 index 0000000000000000000000000000000000000000..37ee04236cdde70a04e7155c2d0f06487b385634 --- /dev/null +++ b/src/pystencilssfg/lang/cpp/std_span.py @@ -0,0 +1,50 @@ +from typing import Union + +from pystencils.field import Field +from pystencils.types import PsType, PsCustomType + +from ...lang import SrcField, IFieldExtraction, AugExpr +from ...ir.source_components import SfgHeaderInclude +from ...exceptions import SfgException + + +class StdSpan(SrcField): + def __init__(self, T: Union[PsCustomType, PsType], ref=True, const=False): + src_type = f"{'const ' if const else ''}std::span< {T.c_string()} > {'&' if ref else ''}" + self._element_type = T + super().__init__(PsCustomType(src_type)) + + @property + def required_includes(self) -> set[SfgHeaderInclude]: + return { + SfgHeaderInclude("span", system_header=True), + } + + def get_extraction(self) -> IFieldExtraction: + span = self + + class Extraction(IFieldExtraction): + def ptr(self) -> AugExpr: + return AugExpr.format("{}.data()", span) + + def size(self, coordinate: int) -> AugExpr | None: + if coordinate > 0: + return None + else: + return AugExpr.format("{}.size()", span) + + def stride(self, coordinate: int) -> AugExpr | None: + if coordinate > 0: + return None + else: + return AugExpr.format("1") + + return Extraction() + + +def std_span_ref(field: Field): + if field.spatial_dimensions > 1 or field.index_shape not in ((), (1,)): + raise SfgException( + "Only one-dimensional fields with trivial index dimensions can be mapped onto `std::span`" + ) + return StdSpan(field.dtype, True, False).var(field.name) diff --git a/src/pystencilssfg/lang/cpp/std_tuple.py b/src/pystencilssfg/lang/cpp/std_tuple.py new file mode 100644 index 0000000000000000000000000000000000000000..82b2c4d169b23bf38bd08586dec5555aa901c9e9 --- /dev/null +++ b/src/pystencilssfg/lang/cpp/std_tuple.py @@ -0,0 +1,40 @@ +from typing import Sequence + +from pystencils.types import PsType, PsCustomType +from pystencils.backend.kernelfunction import KernelParameter + +from ...lang import SrcVector, AugExpr +from ...ir.source_components import SfgHeaderInclude + + +class StdTuple(SrcVector): + def __init__( + self, + element_types: Sequence[PsType], + const: bool = False, + ref: bool = False, + ): + self._element_types = element_types + self._length = len(element_types) + elt_type_strings = tuple(t.c_string() for t in self._element_types) + tuple_type = f"{'const' if const else ''} std::tuple< {', '.join(elt_type_strings)} > {'&' if ref else ''}" + super().__init__(PsCustomType(tuple_type)) + + @property + def required_includes(self) -> set[SfgHeaderInclude]: + return {SfgHeaderInclude("tuple", system_header=True)} + + def extract_component(self, coordinate: int) -> AugExpr: + if coordinate < 0 or coordinate >= self._length: + raise ValueError( + f"Index {coordinate} out-of-bounds for std::tuple with {self._length} entries." + ) + + return AugExpr.format("std::get< {} >({})", coordinate, self) + + +def std_tuple_ref( + identifier: str, components: Sequence[KernelParameter], const: bool = True +): + elt_types = tuple(c.dtype for c in components) + return StdTuple(elt_types, const=const, ref=True).var(identifier) diff --git a/src/pystencilssfg/lang/cpp/std_vector.py b/src/pystencilssfg/lang/cpp/std_vector.py new file mode 100644 index 0000000000000000000000000000000000000000..a3b4b52f91bcb79b6378051c541719e59048d3cb --- /dev/null +++ b/src/pystencilssfg/lang/cpp/std_vector.py @@ -0,0 +1,56 @@ +from pystencils.field import Field +from pystencils.types import PsType, PsCustomType + +from ...lang import SrcField, SrcVector, AugExpr, IFieldExtraction +from ...ir.source_components import SfgHeaderInclude + + +class StdVector(SrcVector, SrcField): + def __init__( + self, + T: PsType, + unsafe: bool = False, + reference: bool = True, + ): + typestring = f"std::vector< {(T.c_string())} > {'&' if reference else ''}" + super(StdVector, self).__init__(PsCustomType(typestring)) + + self._element_type = T + self._unsafe = unsafe + + @property + def required_includes(self) -> set[SfgHeaderInclude]: + return { + SfgHeaderInclude("vector", system_header=True), + } + + def get_extraction(self) -> IFieldExtraction: + vec = self + + class Extraction(IFieldExtraction): + def ptr(self) -> AugExpr: + return AugExpr.format("{}.data()", vec) + + def size(self, coordinate: int) -> AugExpr | None: + if coordinate > 0: + return None + else: + return AugExpr.format("{}.size()", vec) + + def stride(self, coordinate: int) -> AugExpr | None: + if coordinate > 0: + return None + else: + return AugExpr.format("1") + + return Extraction() + + def extract_component(self, coordinate: int) -> AugExpr: + if self._unsafe: + return AugExpr.format("{}[{}]", self, coordinate) + else: + return AugExpr.format("{}.at({})", self, coordinate) + + +def std_vector_ref(field: Field): + return StdVector(field.dtype, unsafe=False, reference=True).var(field.name) diff --git a/src/pystencilssfg/lang/expressions.py b/src/pystencilssfg/lang/expressions.py new file mode 100644 index 0000000000000000000000000000000000000000..c456194a11fd6da541c96b76f2bf1e5b3a8bc984 --- /dev/null +++ b/src/pystencilssfg/lang/expressions.py @@ -0,0 +1,153 @@ +from __future__ import annotations +from typing import Iterable +from itertools import chain +from abc import ABC, abstractmethod + +from pystencils.types import PsType + +from ..ir.source_components import SfgVar, SfgHeaderInclude +from ..exceptions import SfgException + + +class DependentExpression: + __match_args__ = ("expr", "depends") + + def __init__(self, expr: str, depends: Iterable[SfgVar | AugExpr]): + self._expr: str = expr + deps: set[SfgVar] = set() + for obj in depends: + if isinstance(obj, AugExpr): + deps |= obj.depends + else: + deps.add(obj) + + self._depends = tuple(deps) + + @property + def expr(self) -> str: + return self._expr + + @property + def depends(self) -> set[SfgVar]: + return set(self._depends) + + def __hash_contents__(self): + return (self._expr, self._depends) + + def __eq__(self, other: object): + if not isinstance(other, DependentExpression): + return False + + return self.__hash_contents__() == other.__hash_contents__() + + def __hash__(self): + return hash(self.__hash_contents__()) + + def __str__(self) -> str: + return self.expr + + def __add__(self, other: DependentExpression): + return DependentExpression(self.expr + other.expr, self.depends | other.depends) + + +class AugExpr: + def __init__(self, dtype: PsType | None = None): + self._dtype = dtype + self._bound: DependentExpression | None = None + + def var(self, name: str): + v = SfgVar(name, self.get_dtype(), self.required_includes) + expr = DependentExpression(name, (v,)) + return self._bind(expr) + + @staticmethod + def make(code: str, depends: Iterable[SfgVar | AugExpr]): + return AugExpr()._bind(DependentExpression(code, depends)) + + @staticmethod + def format(fmt: str, *deps, **kwdeps) -> AugExpr: + return AugExpr().bind(fmt, *deps, **kwdeps) + + def bind(self, fmt: str, *deps, **kwdeps): + depends = filter( + lambda obj: isinstance(obj, (SfgVar, AugExpr)), chain(deps, kwdeps.values()) + ) + code = fmt.format(*deps, **kwdeps) + self._bind(DependentExpression(code, depends)) + return self + + def expr(self) -> DependentExpression: + if self._bound is None: + raise SfgException("No syntax bound to this AugExpr.") + + return self._bound + + @property + def depends(self) -> set[SfgVar]: + if self._bound is None: + raise SfgException("No syntax bound to this AugExpr.") + + return self._bound.depends + + @property + def dtype(self) -> PsType | None: + return self._dtype + + def get_dtype(self) -> PsType: + if self._dtype is None: + raise SfgException("This AugExpr has no known data type.") + + return self._dtype + + @property + def required_includes(self) -> set[SfgHeaderInclude]: + return set() + + def __str__(self) -> str: + if self._bound is None: + return "/* [ERROR] unbound AugExpr */" + else: + return str(self._bound) + + def _bind(self, expr: DependentExpression): + if self._bound is not None: + raise SfgException("Attempting to bind an already-bound AugExpr.") + + self._bound = expr + return self + + def _is_bound(self) -> bool: + return self._bound is not None + + +class IFieldExtraction(ABC): + """Interface for objects defining how to extract low-level field parameters + from high-level data structures.""" + + @abstractmethod + def ptr(self) -> AugExpr: + pass + + @abstractmethod + def size(self, coordinate: int) -> AugExpr | None: + pass + + @abstractmethod + def stride(self, coordinate: int) -> AugExpr | None: + pass + + +class SrcField(AugExpr): + """Represents a C++ data structure that can be mapped to a *pystencils* field.""" + + @abstractmethod + def get_extraction(self) -> IFieldExtraction: + pass + + +class SrcVector(AugExpr, ABC): + """Represents a C++ data structure that represents a mathematical vector.""" + + @abstractmethod + def extract_component(self, coordinate: int) -> AugExpr: + pass diff --git a/src/pystencilssfg/source_concepts/__init__.py b/src/pystencilssfg/source_concepts/__init__.py deleted file mode 100644 index aa4817f7b1bec919de6a68617e1e02d187c60686..0000000000000000000000000000000000000000 --- a/src/pystencilssfg/source_concepts/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .source_objects import SrcObject, SrcField, SrcVector, TypedSymbolOrObject - -__all__ = ["SrcObject", "SrcField", "SrcVector", "TypedSymbolOrObject"] diff --git a/src/pystencilssfg/source_concepts/cpp/std_mdspan.py b/src/pystencilssfg/source_concepts/cpp/std_mdspan.py deleted file mode 100644 index 11645a536bf0064099ab44bff93e05376eb7ae13..0000000000000000000000000000000000000000 --- a/src/pystencilssfg/source_concepts/cpp/std_mdspan.py +++ /dev/null @@ -1,134 +0,0 @@ -from typing import Union, cast - -import numpy as np - -from pystencils import Field -from pystencils.typing import FieldPointerSymbol, FieldStrideSymbol, FieldShapeSymbol - -from ...tree import SfgStatements -from ..source_objects import SrcField -from ...source_components import SfgHeaderInclude -from ...types import PsType, cpp_typename, SrcType -from ...exceptions import SfgException - - -class StdMdspan(SrcField): - dynamic_extent = "std::dynamic_extent" - - def __init__( - self, - identifer: str, - T: PsType, - extents: tuple[int | str, ...], - extents_type: PsType = int, - reference: bool = False, - ): - cpp_typestr = cpp_typename(T) - extents_type_str = cpp_typename(extents_type) - - extents_str = ( - f"std::extents< {extents_type_str}, {', '.join(str(e) for e in extents)} >" - ) - typestring = ( - f"std::mdspan< {cpp_typestr}, {extents_str} > {'&' if reference else ''}" - ) - super().__init__(identifer, SrcType(typestring)) - - self._extents = extents - - @property - def required_includes(self) -> set[SfgHeaderInclude]: - return {SfgHeaderInclude("experimental/mdspan", system_header=True)} - - def extract_ptr(self, ptr_symbol: FieldPointerSymbol): - return SfgStatements( - f"{ptr_symbol.dtype} {ptr_symbol.name} = {self._identifier}.data_handle();", - (ptr_symbol,), - (self,), - ) - - def extract_size( - self, coordinate: int, size: Union[int, FieldShapeSymbol] - ) -> SfgStatements: - dim = len(self._extents) - if coordinate >= dim: - if isinstance(size, FieldShapeSymbol): - raise SfgException( - f"Cannot extract size in coordinate {coordinate} from a {dim}-dimensional mdspan!" - ) - elif size != 1: - raise SfgException( - f"Cannot map field with size {size} in coordinate {coordinate} to {dim}-dimensional mdspan!" - ) - else: - # trivial trailing index dimensions are OK -> do nothing - return SfgStatements( - f"// {self._identifier}.extents().extent({coordinate}) == 1", (), () - ) - - if isinstance(size, FieldShapeSymbol): - return SfgStatements( - f"{size.dtype} {size.name} = {self._identifier}.extents().extent({coordinate});", - (size,), - (self,), - ) - else: - return SfgStatements( - f"assert( {self._identifier}.extents().extent({coordinate}) == {size} );", - (), - (self,), - ) - - def extract_stride( - self, coordinate: int, stride: Union[int, FieldStrideSymbol] - ) -> SfgStatements: - if coordinate >= len(self._extents): - raise SfgException( - f"Cannot extract stride in coordinate {coordinate} from a {len(self._extents)}-dimensional mdspan" - ) - - if isinstance(stride, FieldStrideSymbol): - return SfgStatements( - f"{stride.dtype} {stride.name} = {self._identifier}.stride({coordinate});", - (stride,), - (self,), - ) - else: - return SfgStatements( - f"assert( {self._identifier}.stride({coordinate}) == {stride} );", - (), - (self,), - ) - - -def mdspan_ref(field: Field, extents_type: type = np.uint32): - """Creates a `std::mdspan &` for a given pystencils field.""" - from pystencils.field import layout_string_to_tuple - - if field.layout != layout_string_to_tuple("soa", field.spatial_dimensions): - raise NotImplementedError( - "mdspan mapping is currently only available for structure-of-arrays fields" - ) - - extents: list[str | int] = [] - - for s in field.spatial_shape: - extents.append( - StdMdspan.dynamic_extent - if isinstance(s, FieldShapeSymbol) - else cast(int, s) - ) - - if field.index_shape != (1,): - for s in field.index_shape: - extents += ( - StdMdspan.dynamic_extent if isinstance(s, FieldShapeSymbol) else s - ) - - return StdMdspan( - field.name, - field.dtype, - tuple(extents), - extents_type=extents_type, - reference=True, - ) diff --git a/src/pystencilssfg/source_concepts/cpp/std_tuple.py b/src/pystencilssfg/source_concepts/cpp/std_tuple.py deleted file mode 100644 index 717a83c4e8b5918ba80d6398de8e625285a6fab2..0000000000000000000000000000000000000000 --- a/src/pystencilssfg/source_concepts/cpp/std_tuple.py +++ /dev/null @@ -1,53 +0,0 @@ -from typing import Sequence - -from pystencils.typing import BasicType, TypedSymbol - -from ...tree import SfgStatements -from ..source_objects import SrcVector -from ..source_objects import TypedSymbolOrObject -from ...types import SrcType, cpp_typename -from ...source_components import SfgHeaderInclude - - -class StdTuple(SrcVector): - def __init__( - self, - identifier: str, - element_types: Sequence[BasicType], - const: bool = False, - ref: bool = False, - ): - self._element_types = element_types - self._length = len(element_types) - elt_type_strings = tuple(cpp_typename(t) for t in self._element_types) - src_type = f"{'const' if const else ''} std::tuple< {', '.join(elt_type_strings)} > {'&' if ref else ''}" - super().__init__(identifier, SrcType(src_type)) - - @property - def required_includes(self) -> set[SfgHeaderInclude]: - return {SfgHeaderInclude("tuple", system_header=True)} - - def extract_component(self, destination: TypedSymbolOrObject, coordinate: int): - if coordinate < 0 or coordinate >= self._length: - raise ValueError( - f"Index {coordinate} out-of-bounds for std::tuple with {self._length} entries." - ) - - if destination.dtype != self._element_types[coordinate]: - raise ValueError( - f"Cannot extract type {destination.dtype} from std::tuple entry " - "of type {self._element_types[coordinate]}" - ) - - return SfgStatements( - f"{destination.dtype} {destination.name} = std::get< {coordinate} >({self.identifier});", - (destination,), - (self,), - ) - - -def std_tuple_ref( - identifier: str, components: Sequence[TypedSymbol], const: bool = True -): - elt_types = tuple(c.dtype for c in components) - return StdTuple(identifier, elt_types, const=const, ref=True) diff --git a/src/pystencilssfg/source_concepts/cpp/std_vector.py b/src/pystencilssfg/source_concepts/cpp/std_vector.py deleted file mode 100644 index a63220607c895f7577035015a512a7f67d6ea7b0..0000000000000000000000000000000000000000 --- a/src/pystencilssfg/source_concepts/cpp/std_vector.py +++ /dev/null @@ -1,119 +0,0 @@ -from typing import Union - -from pystencils.field import Field, FieldType -from pystencils.typing import FieldPointerSymbol, FieldStrideSymbol, FieldShapeSymbol - -from ...tree import SfgStatements -from ..source_objects import SrcField, SrcVector -from ..source_objects import TypedSymbolOrObject -from ...types import SrcType, PsType, cpp_typename -from ...source_components import SfgHeaderInclude, SfgClass -from ...exceptions import SfgException - - -class StdVector(SrcVector, SrcField): - def __init__( - self, - identifer: str, - T: Union[SrcType, PsType], - unsafe: bool = False, - reference: bool = True, - ): - typestring = f"std::vector< {cpp_typename(T)} > {'&' if reference else ''}" - super(StdVector, self).__init__(identifer, SrcType(typestring)) - - self._element_type = T - self._unsafe = unsafe - - @property - def required_includes(self) -> set[SfgHeaderInclude]: - return { - SfgHeaderInclude("cassert", system_header=True), - SfgHeaderInclude("vector", system_header=True), - } - - def extract_ptr(self, ptr_symbol: FieldPointerSymbol): - if ptr_symbol.dtype != self._element_type: - if self._unsafe: - mapping = f"{ptr_symbol.dtype} {ptr_symbol.name} = ({ptr_symbol.dtype}) {self._identifier}.data();" - else: - raise SfgException( - "Field type and std::vector element type do not match, and unsafe extraction was not enabled." - ) - else: - mapping = ( - f"{ptr_symbol.dtype} {ptr_symbol.name} = {self._identifier}.data();" - ) - - return SfgStatements(mapping, (ptr_symbol,), (self,)) - - def extract_size( - self, coordinate: int, size: Union[int, FieldShapeSymbol] - ) -> SfgStatements: - if coordinate > 0: - if isinstance(size, FieldShapeSymbol): - raise SfgException( - f"Cannot extract size in coordinate {coordinate} from std::vector!" - ) - elif size != 1: - raise SfgException( - f"Cannot map field with size {size} in coordinate {coordinate} to std::vector!" - ) - else: - # trivial trailing index dimensions are OK -> do nothing - return SfgStatements( - f"// {self._identifier}.size({coordinate}) == 1", (), () - ) - - if isinstance(size, FieldShapeSymbol): - return SfgStatements( - f"{size.dtype} {size.name} = ({size.dtype}) {self._identifier}.size();", - (size,), - (self,), - ) - else: - return SfgStatements( - f"assert( {self._identifier}.size() == {size} );", (), (self,) - ) - - def extract_stride( - self, coordinate: int, stride: Union[int, FieldStrideSymbol] - ) -> SfgStatements: - if coordinate == 1: - if stride != 1: - raise SfgException( - "Can only map fields with trivial index stride onto std::vector!" - ) - - if coordinate > 1: - raise SfgException( - f"Cannot extract stride in coordinate {coordinate} from std::vector" - ) - - if isinstance(stride, FieldStrideSymbol): - return SfgStatements(f"{stride.dtype} {stride.name} = 1;", (stride,), ()) - elif stride != 1: - raise SfgException( - "Can only map fields with trivial strides onto std::vector!" - ) - else: - return SfgStatements( - f"// {self._identifier}.stride({coordinate}) == 1", (), () - ) - - def extract_component( - self, destination: TypedSymbolOrObject, coordinate: int - ) -> SfgStatements: - if self._unsafe: - mapping = f"{destination.dtype} {destination.name} = {self._identifier}[{coordinate}];" - else: - mapping = f"{destination.dtype} {destination.name} = {self._identifier}.at({coordinate});" - - return SfgStatements(mapping, (destination,), (self,)) - - -def std_vector_ref(field: Field, src_struct: SfgClass): - if field.field_type != FieldType.INDEXED: - raise ValueError("Can only create std::vector for index fields") - - return StdVector(field.name, src_struct.src_type, unsafe=True, reference=True) diff --git a/src/pystencilssfg/source_concepts/source_objects.py b/src/pystencilssfg/source_concepts/source_objects.py deleted file mode 100644 index 584d20ca700f9e4835be7f240ff31ffd1c029905..0000000000000000000000000000000000000000 --- a/src/pystencilssfg/source_concepts/source_objects.py +++ /dev/null @@ -1,122 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Union, TypeAlias - -from abc import ABC, abstractmethod - -from pystencils import TypedSymbol, Field -from pystencils.typing import FieldPointerSymbol, FieldStrideSymbol, FieldShapeSymbol - -from ..types import SrcType - -if TYPE_CHECKING: - from ..source_components import SfgHeaderInclude - from ..tree import SfgStatements, SfgSequence - - -class SrcObject: - """C/C++ object of nonprimitive type. - - Two objects are identical if they have the same identifier and type string.""" - - def __init__(self, identifier: str, src_type: SrcType): - self._identifier = identifier - self._src_type = src_type - - @property - def identifier(self): - return self._identifier - - @property - def name(self) -> str: - """For interface compatibility with ps.TypedSymbol""" - return self._identifier - - @property - def dtype(self): - return self._src_type - - @property - def required_includes(self) -> set[SfgHeaderInclude]: - return set() - - def __hash__(self) -> int: - return hash((self._identifier, self._src_type)) - - def __eq__(self, other: object) -> bool: - return ( - isinstance(other, SrcObject) - and self._identifier == other._identifier - and self._src_type == other._src_type - ) - - def __str__(self) -> str: - return self.name - - -TypedSymbolOrObject: TypeAlias = TypedSymbol | SrcObject - - -class SrcField(SrcObject, ABC): - """Represents a C++ data structure that can be mapped to a *pystencils* field. - - Subclasses of `SrcField` are meant to be used in [SfgComposer.map_field][pystencilssfg.SfgComposer.map_field] - to produce the necessary mapping code from a high-level C++ field data structure to a pystencils field. - - Subclasses of `SrcField` must implement `extract_ptr`, `extract_size` and `extract_stride` - to emit code extracting field pointers and indexing information from the high-level concept. - - Currently, *pystencils-sfg* provides an implementation for the C++ `std::vector` and `std::mdspan` classes via - [StdVector][pystencilssfg.source_concepts.cpp.StdVector] and - [StdMdspan][pystencilssfg.source_concepts.cpp.StdMdspan]. - """ - - def __init__(self, identifier: str, src_type: SrcType): - super().__init__(identifier, src_type) - - @abstractmethod - def extract_ptr(self, ptr_symbol: FieldPointerSymbol) -> SfgStatements: - pass - - @abstractmethod - def extract_size( - self, coordinate: int, size: Union[int, FieldShapeSymbol] - ) -> SfgStatements: - pass - - @abstractmethod - def extract_stride( - self, coordinate: int, stride: Union[int, FieldStrideSymbol] - ) -> SfgStatements: - pass - - def extract_parameters(self, field: Field) -> SfgSequence: - ptr = FieldPointerSymbol(field.name, field.dtype, False) - - from ..composer import make_sequence - - return make_sequence( - self.extract_ptr(ptr), - *(self.extract_size(c, s) for c, s in enumerate(field.shape)), - *(self.extract_stride(c, s) for c, s in enumerate(field.strides)), - ) - - -class SrcVector(SrcObject, ABC): - """Represents a C++ abstraction of a mathematical vector that can be mapped to a vector of symbols. - - Subclasses of `SrcVector` are meant to be used in [SfgComposer.map_vector][pystencilssfg.SfgComposer.map_vector] - to produce the necessary mapping code from a high-level C++ vector data structure to a vector of symbols. - - Subclasses of `SrcVector` must implement `extract_component` to emit code extracting scalar values - from the high-level vector. - - Currently, *pystencils-sfg* provides an implementation for the C++ `std::vector` via - [StdVector][pystencilssfg.source_concepts.cpp.StdVector]. - """ - - @abstractmethod - def extract_component( - self, destination: TypedSymbolOrObject, coordinate: int - ) -> SfgStatements: - pass diff --git a/src/pystencilssfg/tree/__init__.py b/src/pystencilssfg/tree/__init__.py deleted file mode 100644 index 15cd329c57a0d1c74a8492f0fb74204693bd277f..0000000000000000000000000000000000000000 --- a/src/pystencilssfg/tree/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -from .basic_nodes import ( - SfgCallTreeNode, - SfgCallTreeLeaf, - SfgEmptyNode, - SfgKernelCallNode, - SfgBlock, - SfgSequence, - SfgStatements, - SfgFunctionParams, - SfgRequireIncludes, -) -from .conditional import SfgBranch, SfgCondition, IntEven, IntOdd - -__all__ = [ - "SfgCallTreeNode", - "SfgCallTreeLeaf", - "SfgEmptyNode", - "SfgKernelCallNode", - "SfgSequence", - "SfgBlock", - "SfgStatements", - "SfgFunctionParams", - "SfgRequireIncludes", - "SfgCondition", - "SfgBranch", - "IntEven", - "IntOdd", -] diff --git a/src/pystencilssfg/tree/basic_nodes.py b/src/pystencilssfg/tree/basic_nodes.py deleted file mode 100644 index 26fe8ed49873cf60c46fb5f6f0bce98fbeed1d50..0000000000000000000000000000000000000000 --- a/src/pystencilssfg/tree/basic_nodes.py +++ /dev/null @@ -1,226 +0,0 @@ -from __future__ import annotations -from typing import TYPE_CHECKING, Sequence - -from abc import ABC, abstractmethod -from itertools import chain - -from ..source_components import SfgHeaderInclude, SfgKernelHandle -from ..source_concepts.source_objects import SrcObject, TypedSymbolOrObject - -if TYPE_CHECKING: - from ..context import SfgContext - - -class SfgCallTreeNode(ABC): - """Base class for all nodes comprising SFG call trees. - - ## Code Printing - - For extensibility, code printing is implemented inside the call tree. - Therefore, every instantiable call tree node must implement the method `get_code`. - By convention, the string returned by `get_code` should not contain a trailing newline. - - ## Branching Structure - - The branching structure of the call tree is managed uniformly through the `children` interface - of SfgCallTreeNode. Each subclass must ensure that access to and modification of - the branching structure through the `children` property and the `child` and `set_child` - methods is possible, if necessary by overriding the property and methods. - """ - - def __init__(self, *children: SfgCallTreeNode): - self._children = list(children) - - @property - def children(self) -> tuple[SfgCallTreeNode, ...]: - """This node's children""" - return tuple(self._children) - - @children.setter - def children(self, cs: Sequence[SfgCallTreeNode]) -> None: - """Replaces this node's children. By default, the number of child nodes must not change.""" - if len(cs) != len(self._children): - raise ValueError("The number of child nodes must remain the same!") - self._children = list(cs) - - def child(self, idx: int) -> SfgCallTreeNode: - """Gets the child at index idx.""" - return self._children[idx] - - def set_child(self, idx: int, c: SfgCallTreeNode): - """Replaces the child at index idx.""" - self._children[idx] = c - - def __getitem__(self, idx: int) -> SfgCallTreeNode: - return self.child(idx) - - def __setitem__(self, idx: int, c: SfgCallTreeNode) -> None: - self.set_child(idx, c) - - @abstractmethod - def get_code(self, ctx: SfgContext) -> str: - """Returns the code of this node. - - By convention, the code block emitted by this function should not contain a trailing newline. - """ - - @property - def required_includes(self) -> set[SfgHeaderInclude]: - """Return a set of header includes required by this node""" - return set() - - -class SfgCallTreeLeaf(SfgCallTreeNode, ABC): - """A leaf node of the call tree. - - Leaf nodes must implement `required_parameters` for automatic parameter collection. - """ - - @property - @abstractmethod - def required_parameters(self) -> set[TypedSymbolOrObject]: ... - - -class SfgEmptyNode(SfgCallTreeLeaf): - """A leaf node that does not emit any code. - - Empty nodes must still implement `required_parameters`. - """ - - def __init__(self): - super().__init__() - - def get_code(self, ctx: SfgContext) -> str: - return "" - - -class SfgStatements(SfgCallTreeLeaf): - """Represents (a sequence of) statements in the source language. - - This class groups together arbitrary code strings - (e.g. sequences of C++ statements, cf. https://en.cppreference.com/w/cpp/language/statements), - and annotates them with the set of symbols read and written by these statements. - - It is the user's responsibility to ensure that the code string is valid code in the output language, - and that the lists of required and defined objects are correct and complete. - - Args: - code_string: Code to be printed out. - defined_params: Objects (as `SrcObject` or `TypedSymbol`) that will be newly defined and visible to - code in sequence after these statements. - required_params: Objects (as `SrcObject` or `TypedSymbol`) that are required as input to these statements. - """ - - def __init__( - self, - code_string: str, - defined_params: Sequence[TypedSymbolOrObject], - required_params: Sequence[TypedSymbolOrObject], - ): - super().__init__() - - self._code_string = code_string - - self._defined_params = set(defined_params) - self._required_params = set(required_params) - - self._required_includes = set() - for obj in chain(required_params, defined_params): - if isinstance(obj, SrcObject): - self._required_includes |= obj.required_includes - - @property - def required_parameters(self) -> set[TypedSymbolOrObject]: - return self._required_params - - @property - def defined_parameters(self) -> set[TypedSymbolOrObject]: - return self._defined_params - - @property - def required_includes(self) -> set[SfgHeaderInclude]: - return self._required_includes - - def get_code(self, ctx: SfgContext) -> str: - return self._code_string - - -class SfgFunctionParams(SfgEmptyNode): - def __init__(self, parameters: Sequence[TypedSymbolOrObject]): - super().__init__() - self._params = set(parameters) - - self._required_includes = set() - for obj in parameters: - if isinstance(obj, SrcObject): - self._required_includes |= obj.required_includes - - @property - def required_parameters(self) -> set[TypedSymbolOrObject]: - return self._params - - @property - def required_includes(self) -> set[SfgHeaderInclude]: - return self._required_includes - - -class SfgRequireIncludes(SfgEmptyNode): - def __init__(self, includes: Sequence[SfgHeaderInclude]): - super().__init__() - self._required_includes = set(includes) - - @property - def required_parameters(self) -> set[TypedSymbolOrObject]: - return set() - - @property - def required_includes(self) -> set[SfgHeaderInclude]: - return self._required_includes - - -class SfgSequence(SfgCallTreeNode): - def __init__(self, children: Sequence[SfgCallTreeNode]): - super().__init__(*children) - - def get_code(self, ctx: SfgContext) -> str: - return "\n".join(c.get_code(ctx) for c in self._children) - - -class SfgBlock(SfgCallTreeNode): - def __init__(self, subtree: SfgCallTreeNode): - super().__init__(subtree) - - @property - def subtree(self) -> SfgCallTreeNode: - return self._children[0] - - def get_code(self, ctx: SfgContext) -> str: - subtree_code = ctx.codestyle.indent(self.subtree.get_code(ctx)) - - return "{\n" + subtree_code + "\n}" - - -# class SfgForLoop(SfgCallTreeNode): -# def __init__(self, control_line: SfgStatements, body: SfgCallTreeNode): -# super().__init__(control_line, body) - -# @property -# def body(self) -> SfgStatements: -# return cast(SfgStatements) - - -class SfgKernelCallNode(SfgCallTreeLeaf): - def __init__(self, kernel_handle: SfgKernelHandle): - super().__init__() - self._kernel_handle = kernel_handle - - @property - def required_parameters(self) -> set[TypedSymbolOrObject]: - return set(p.symbol for p in self._kernel_handle.parameters) - - def get_code(self, ctx: SfgContext) -> str: - ast_params = self._kernel_handle.parameters - fnc_name = self._kernel_handle.fully_qualified_name - call_parameters = ", ".join([p.symbol.name for p in ast_params]) - - return f"{fnc_name}({call_parameters});" diff --git a/src/pystencilssfg/tree/conditional.py b/src/pystencilssfg/tree/conditional.py deleted file mode 100644 index 65f1f87e827ca137b6497ce29a2865eef1cb6159..0000000000000000000000000000000000000000 --- a/src/pystencilssfg/tree/conditional.py +++ /dev/null @@ -1,200 +0,0 @@ -from __future__ import annotations -from typing import TYPE_CHECKING, Optional, cast, Generator, Sequence, NewType - -from pystencils.typing import TypedSymbol, BasicType - -from .basic_nodes import SfgCallTreeNode, SfgCallTreeLeaf -from ..source_concepts.source_objects import TypedSymbolOrObject - -if TYPE_CHECKING: - from ..context import SfgContext - - -class SfgCondition(SfgCallTreeLeaf): - pass - - -class SfgCustomCondition(SfgCondition): - def __init__(self, cond_text: str): - super().__init__() - self._cond_text = cond_text - - @property - def required_parameters(self) -> set[TypedSymbolOrObject]: - return set() - - def get_code(self, ctx: SfgContext) -> str: - return self._cond_text - - -class IntEven(SfgCondition): - def __init__(self, symbol: TypedSymbol): - super().__init__() - if not isinstance(symbol.dtype, BasicType) or not symbol.dtype.is_int(): - raise ValueError(f"Symbol {symbol} does not have integer type.") - - self._symbol = symbol - - @property - def required_parameters(self) -> set[TypedSymbolOrObject]: - return {self._symbol} - - def get_code(self, ctx: SfgContext) -> str: - return f"(({self._symbol.name} & 1) ^ 1)" - - -class IntOdd(SfgCondition): - def __init__(self, symbol: TypedSymbol): - super().__init__() - if not isinstance(symbol.dtype, BasicType) or not symbol.dtype.is_int(): - raise ValueError(f"Symbol {symbol} does not have integer type.") - - self._symbol = symbol - - @property - def required_parameters(self) -> set[TypedSymbolOrObject]: - return {self._symbol} - - def get_code(self, ctx: SfgContext) -> str: - return f"({self._symbol.name} & 1)" - - -class SfgBranch(SfgCallTreeNode): - def __init__( - self, - cond: SfgCondition, - branch_true: SfgCallTreeNode, - branch_false: Optional[SfgCallTreeNode] = None, - ): - super().__init__(cond, branch_true, *((branch_false,) if branch_false else ())) - - @property - def condition(self) -> SfgCondition: - return cast(SfgCondition, self._children[0]) - - @property - def branch_true(self) -> SfgCallTreeNode: - return self._children[1] - - @property - def branch_false(self) -> SfgCallTreeNode: - return self._children[2] - - def get_code(self, ctx: SfgContext) -> str: - code = f"if({self.condition.get_code(ctx)}) {{\n" - code += ctx.codestyle.indent(self.branch_true.get_code(ctx)) - code += "\n}" - - if self.branch_false is not None: - code += "else {\n" - code += ctx.codestyle.indent(self.branch_false.get_code(ctx)) - code += "\n}" - - return code - - -class SfgSwitchCase(SfgCallTreeNode): - DefaultCaseType = NewType("DefaultCaseType", object) - Default = DefaultCaseType(object()) - - def __init__(self, label: str | DefaultCaseType, body: SfgCallTreeNode): - self._label = label - super().__init__(body) - - @property - def label(self) -> str | DefaultCaseType: - return self._label - - @property - def body(self) -> SfgCallTreeNode: - return self._children[0] - - @property - def is_default(self) -> bool: - return self._label == SfgSwitchCase.Default - - def get_code(self, ctx: SfgContext) -> str: - code = "" - if self._label == SfgSwitchCase.Default: - code += "default: {\n" - else: - code += f"case {self._label}: {{\n" - code += ctx.codestyle.indent(self.body.get_code(ctx)) - code += "\nbreak;\n}" - return code - - -class SfgSwitch(SfgCallTreeNode): - def __init__( - self, - switch_arg: str | TypedSymbolOrObject, - cases_dict: dict[str, SfgCallTreeNode], - default: SfgCallTreeNode | None = None, - ): - children = [SfgSwitchCase(label, body) for label, body in cases_dict.items()] - if default is not None: - # invariant: the default case is always the last child - children += [SfgSwitchCase(SfgSwitchCase.Default, default)] - self._switch_arg = switch_arg - self._default = default - super().__init__(*children) - - @property - def switch_arg(self) -> str | TypedSymbolOrObject: - return self._switch_arg - - def cases(self) -> Generator[SfgCallTreeNode, None, None]: - if self._default is not None: - yield from self._children[:-1] - else: - yield from self._children - - @property - def default(self) -> SfgCallTreeNode | None: - return self._default - - @property - def children(self) -> tuple[SfgCallTreeNode, ...]: - return tuple(self._children) - - @children.setter - def children(self, cs: Sequence[SfgCallTreeNode]) -> None: - if len(cs) != len(self._children): - raise ValueError("The number of child nodes must remain the same!") - - self._default = None - for i, c in enumerate(cs): - if not isinstance(c, SfgSwitchCase): - raise ValueError( - "An SfgSwitch node can only have SfgSwitchCases as children." - ) - if c.is_default: - if i != len(cs) - 1: - raise ValueError("Default case must be listed last.") - else: - self._default = c - - self._children = list(cs) - - def set_child(self, idx: int, c: SfgCallTreeNode): - if not isinstance(c, SfgSwitchCase): - raise ValueError( - "An SfgSwitch node can only have SfgSwitchCases as children." - ) - - if c.is_default: - if idx != len(self._children) - 1: - raise ValueError("Default case must be the last child.") - elif self._default is None: - raise ValueError("Cannot replace normal case with default case.") - else: - self._default = c - self._children[-1] = c - else: - self._children[idx] = c - - def get_code(self, ctx: SfgContext) -> str: - code = f"switch({self._switch_arg}) {{\n" - code += "\n".join(c.get_code(ctx) for c in self.children) - code += "}" - return code diff --git a/src/pystencilssfg/tree/deferred_nodes.py b/src/pystencilssfg/tree/deferred_nodes.py deleted file mode 100644 index 040b51b4c87a168873bbf03d644be85f716087a6..0000000000000000000000000000000000000000 --- a/src/pystencilssfg/tree/deferred_nodes.py +++ /dev/null @@ -1,91 +0,0 @@ -from __future__ import annotations -from typing import TYPE_CHECKING - -from abc import ABC, abstractmethod - -from pystencils import Field -from pystencils.typing import FieldPointerSymbol, FieldShapeSymbol, FieldStrideSymbol - -from ..exceptions import SfgException - -from .basic_nodes import SfgCallTreeNode, SfgSequence - -from ..source_concepts import SrcField -from ..source_concepts.source_objects import TypedSymbolOrObject - -if TYPE_CHECKING: - from ..context import SfgContext - - -class SfgDeferredNode(SfgCallTreeNode, ABC): - """Nodes of this type are inserted as placeholders into the kernel call tree - and need to be expanded at a later time. - - Subclasses of SfgDeferredNode correspond to nodes that cannot be created yet - because information required for their construction is not yet known. - """ - - class InvalidAccess: - def __get__(self): - raise SfgException( - "Invalid access into deferred node; deferred nodes must be expanded first." - ) - - def __init__(self): - self._children = SfgDeferredNode.InvalidAccess - - def get_code(self, ctx: SfgContext) -> str: - raise SfgException( - "Invalid access into deferred node; deferred nodes must be expanded first." - ) - - -class SfgParamCollectionDeferredNode(SfgDeferredNode, ABC): - @abstractmethod - def expand(self, visible_params: set[TypedSymbolOrObject]) -> SfgCallTreeNode: ... - - -class SfgDeferredFieldMapping(SfgParamCollectionDeferredNode): - def __init__(self, field: Field, src_field: SrcField): - self._field = field - self._src_field = src_field - - def expand(self, visible_params: set[TypedSymbolOrObject]) -> SfgCallTreeNode: - # Find field pointer - ptr = None - for param in visible_params: - if ( - isinstance(param, FieldPointerSymbol) - and param.field_name == self._field.name - ): - if param.dtype.base_type != self._field.dtype: - raise SfgException( - "Data type mismatch between field and encountered pointer symbol" - ) - ptr = param - - # Find required sizes - shape = [] - for c, s in enumerate(self._field.shape): - if isinstance(s, FieldShapeSymbol) and s not in visible_params: - continue - else: - shape.append((c, s)) - - # Find required strides - strides = [] - for c, s in enumerate(self._field.strides): - if isinstance(s, FieldStrideSymbol) and s not in visible_params: - continue - else: - strides.append((c, s)) - - nodes = [] - - if ptr is not None: - nodes += [self._src_field.extract_ptr(ptr)] - - nodes += [self._src_field.extract_size(c, s) for c, s in shape] - nodes += [self._src_field.extract_stride(c, s) for c, s in strides] - - return SfgSequence(nodes) diff --git a/src/pystencilssfg/types.py b/src/pystencilssfg/types.py deleted file mode 100644 index 49367985c66c5e034954c6807ff91170b333e69c..0000000000000000000000000000000000000000 --- a/src/pystencilssfg/types.py +++ /dev/null @@ -1,44 +0,0 @@ -from typing import Union, TypeAlias, NewType -import numpy as np - -from pystencils.typing import AbstractType, numpy_name_to_c - - -PsType: TypeAlias = Union[type, np.dtype, AbstractType] -"""Types used in interacting with pystencils. - -PsType represents various ways of specifying types within pystencils. -In particular, it encompasses most ways to construct an instance of `AbstractType`, -for example via `create_type`. - -(Note that, while `create_type` does accept strings, they are excluded here for -reasons of safety. It is discouraged to use strings for type specifications when working -with pystencils!) - -PsType is a temporary solution and will be removed in the future -in favor of the consolidated pystencils backend typing system. -""" - -SrcType = NewType("SrcType", str) -"""C/C++-Types occuring during source file generation. - -When necessary, the SFG package checks equality of types by their name strings; it does -not care about typedefs, aliases, namespaces, etc! - -SrcType is a temporary solution and will be removed in the future -in favor of the consolidated pystencils backend typing system. -""" - - -def cpp_typename(type_obj: Union[str, SrcType, PsType]): - """Accepts type specifications in various ways and returns a valid typename to be used in code.""" - # if isinstance(type_obj, str): - # return type_obj - if isinstance(type_obj, str): - return type_obj - elif isinstance(type_obj, AbstractType): - return str(type_obj) - elif isinstance(type_obj, np.dtype) or isinstance(type_obj, type): - return numpy_name_to_c(np.dtype(type_obj).name) - else: - raise ValueError(f"Don't know how to interpret type object {type_obj}.") diff --git a/src/pystencilssfg/visitors/__init__.py b/src/pystencilssfg/visitors/__init__.py index 48c673a164e3567b2f694decd7816d30be37eedf..fc7af1b6363bdb1167ee6c0e164ba87e31fbb6b0 100644 --- a/src/pystencilssfg/visitors/__init__.py +++ b/src/pystencilssfg/visitors/__init__.py @@ -1,10 +1,5 @@ from .dispatcher import visitor -from .collectors import CollectIncludes -from .tree_visitors import FlattenSequences, ExpandingParameterCollector __all__ = [ "visitor", - "CollectIncludes", - "FlattenSequences", - "ExpandingParameterCollector", ] diff --git a/src/pystencilssfg/visitors/collectors.py b/src/pystencilssfg/visitors/collectors.py deleted file mode 100644 index fef8955bb8a5eab7c80d2c812b41e64ce7f938ba..0000000000000000000000000000000000000000 --- a/src/pystencilssfg/visitors/collectors.py +++ /dev/null @@ -1,69 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING - -from functools import reduce - -from .dispatcher import visitor -from ..exceptions import SfgException -from ..tree import SfgCallTreeNode -from ..source_components import ( - SfgFunction, - SfgClass, - SfgConstructor, - SfgMemberVariable, - SfgInClassDefinition, -) -from ..context import SfgContext - -if TYPE_CHECKING: - from ..source_components import SfgHeaderInclude - - -class CollectIncludes: - @visitor - def visit(self, obj: object) -> set[SfgHeaderInclude]: - raise SfgException(f"Can't collect includes from object of type {type(obj)}") - - @visit.case(SfgContext) - def context(self, ctx: SfgContext) -> set[SfgHeaderInclude]: - includes = set() - for func in ctx.functions(): - includes |= self.visit(func) - - for cls in ctx.classes(): - includes |= self.visit(cls) - - return includes - - @visit.case(SfgCallTreeNode) - def tree_node(self, node: SfgCallTreeNode) -> set[SfgHeaderInclude]: - return reduce( - lambda accu, child: accu | self.visit(child), - node.children, - node.required_includes, - ) - - @visit.case(SfgFunction) - def sfg_function(self, func: SfgFunction) -> set[SfgHeaderInclude]: - return self.visit(func.tree) - - @visit.case(SfgClass) - def sfg_class(self, cls: SfgClass) -> set[SfgHeaderInclude]: - return reduce( - lambda accu, member: accu | (self.visit(member)), cls.members(), set() - ) - - @visit.case(SfgConstructor) - def sfg_constructor(self, constr: SfgConstructor) -> set[SfgHeaderInclude]: - return reduce( - lambda accu, obj: accu | obj.required_includes, constr.parameters, set() - ) - - @visit.case(SfgMemberVariable) - def sfg_member_var(self, var: SfgMemberVariable) -> set[SfgHeaderInclude]: - return var.required_includes - - @visit.case(SfgInClassDefinition) - def sfg_cls_def(self, _: SfgInClassDefinition) -> set[SfgHeaderInclude]: - return set() diff --git a/src/pystencilssfg/visitors/tree_visitors.py b/src/pystencilssfg/visitors/tree_visitors.py deleted file mode 100644 index bb596b5132fd44eda9d30947d2dac3714c7f9430..0000000000000000000000000000000000000000 --- a/src/pystencilssfg/visitors/tree_visitors.py +++ /dev/null @@ -1,141 +0,0 @@ -from __future__ import annotations - -# from typing import TYPE_CHECKING - -from functools import reduce - -from ..tree.basic_nodes import ( - SfgCallTreeNode, - SfgCallTreeLeaf, - SfgSequence, - SfgStatements, -) -from ..tree.deferred_nodes import SfgParamCollectionDeferredNode -from ..tree.conditional import SfgSwitch -from .dispatcher import visitor -from ..source_concepts.source_objects import TypedSymbol, SrcObject, TypedSymbolOrObject - - -class FlattenSequences: - """Flattens any nested sequences occuring in a kernel call tree.""" - - @visitor - def visit(self, node: SfgCallTreeNode) -> None: - for c in node.children: - self.visit(c) - - @visit.case(SfgSequence) - def sequence(self, sequence: SfgSequence) -> None: - children_flattened: list[SfgCallTreeNode] = [] - - def flatten(seq: SfgSequence): - for c in seq.children: - if isinstance(c, SfgSequence): - flatten(c) - else: - children_flattened.append(c) - - flatten(sequence) - - for c in children_flattened: - self.visit(c) - - sequence._children = children_flattened - - -class ExpandingParameterCollector: - """Collects all parameters required but not defined in a kernel call tree. - Expands any deferred nodes of type `SfgParamCollectionDeferredNode` found within sequences on the way. - """ - - def __init__(self) -> None: - self._flattener = FlattenSequences() - - @visitor - def visit(self, node: SfgCallTreeNode) -> set[TypedSymbolOrObject]: - return self.branching_node(node) - - @visit.case(SfgCallTreeLeaf) - def leaf(self, leaf: SfgCallTreeLeaf) -> set[TypedSymbolOrObject]: - return leaf.required_parameters - - @visit.case(SfgSwitch) - def switch(self, sw: SfgSwitch) -> set[TypedSymbolOrObject]: - params = self.branching_node(sw) - if isinstance(sw.switch_arg, (TypedSymbol, SrcObject)): - params.add(sw.switch_arg) - return params - - @visit.case(SfgSequence) - def sequence(self, sequence: SfgSequence) -> set[TypedSymbolOrObject]: - """ - Only in a sequence may parameters be defined and visible to subsequent nodes. - """ - - params: set[TypedSymbolOrObject] = set() - - def iter_nested_sequences( - seq: SfgSequence, visible_params: set[TypedSymbolOrObject] - ): - for i in range(len(seq.children) - 1, -1, -1): - c = seq.children[i] - - if isinstance(c, SfgParamCollectionDeferredNode): - c = c.expand(visible_params=visible_params) - seq[i] = c - - if isinstance(c, SfgSequence): - iter_nested_sequences(c, visible_params) - else: - if isinstance(c, SfgStatements): - visible_params -= c.defined_parameters - - visible_params |= self.visit(c) - - iter_nested_sequences(sequence, params) - - return params - - def branching_node(self, node: SfgCallTreeNode) -> set[TypedSymbolOrObject]: - """ - Each interior node that is not a sequence simply requires the union of all parameters - required by its children. - """ - return reduce(lambda x, y: x | y, (self.visit(c) for c in node.children), set()) - - -class ParameterCollector: - """Collects all parameters required but not defined in a kernel call tree. - - Requires that all sequences in the tree are flattened. - """ - - @visitor - def visit(self, node: SfgCallTreeNode) -> set[TypedSymbolOrObject]: - return self.branching_node(node) - - @visit.case(SfgCallTreeLeaf) - def leaf(self, leaf: SfgCallTreeLeaf) -> set[TypedSymbolOrObject]: - return leaf.required_parameters - - @visit.case(SfgSequence) - def sequence(self, sequence: SfgSequence) -> set[TypedSymbolOrObject]: - """ - Only in a sequence may parameters be defined and visible to subsequent nodes. - """ - - params: set[TypedSymbolOrObject] = set() - for c in sequence.children[::-1]: - if isinstance(c, SfgStatements): - params -= c.defined_parameters - - assert not isinstance(c, SfgSequence), "Sequence not flattened." - params |= self.visit(c) - return params - - def branching_node(self, node: SfgCallTreeNode) -> set[TypedSymbolOrObject]: - """ - Each interior node that is not a sequence simply requires the union of all parameters - required by its children. - """ - return reduce(lambda x, y: x | y, (self.visit(c) for c in node.children), set()) diff --git a/tests/integration/deps/mdspan/include/experimental/__p0009_bits/compressed_pair.hpp b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/compressed_pair.hpp new file mode 100644 index 0000000000000000000000000000000000000000..25389a2fa5e7be9c2f3bdb35d0a5ff4a746b027a --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/compressed_pair.hpp @@ -0,0 +1,195 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER +#pragma once + +#include "macros.hpp" +#include "trait_backports.hpp" + +#if !defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) +# include "no_unique_address.hpp" +#endif + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace detail { + +// For no unique address emulation, this is the case taken when neither are empty. +// For real `[[no_unique_address]]`, this case is always taken. +template <class _T1, class _T2, class _Enable = void> struct __compressed_pair { + _MDSPAN_NO_UNIQUE_ADDRESS _T1 __t1_val{}; + _MDSPAN_NO_UNIQUE_ADDRESS _T2 __t2_val{}; + MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 _T1 &__first() noexcept { return __t1_val; } + MDSPAN_FORCE_INLINE_FUNCTION constexpr _T1 const &__first() const noexcept { + return __t1_val; + } + MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 _T2 &__second() noexcept { return __t2_val; } + MDSPAN_FORCE_INLINE_FUNCTION constexpr _T2 const &__second() const noexcept { + return __t2_val; + } + + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr __compressed_pair() = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr __compressed_pair(__compressed_pair const &) = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr __compressed_pair(__compressed_pair &&) = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + _MDSPAN_CONSTEXPR_14_DEFAULTED __compressed_pair & + operator=(__compressed_pair const &) = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + _MDSPAN_CONSTEXPR_14_DEFAULTED __compressed_pair & + operator=(__compressed_pair &&) = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + ~__compressed_pair() = default; + template <class _T1Like, class _T2Like> + MDSPAN_INLINE_FUNCTION constexpr __compressed_pair(_T1Like &&__t1, _T2Like &&__t2) + : __t1_val((_T1Like &&) __t1), __t2_val((_T2Like &&) __t2) {} +}; + +#if !defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) + +// First empty. +template <class _T1, class _T2> +struct __compressed_pair< + _T1, _T2, + std::enable_if_t<_MDSPAN_TRAIT(std::is_empty, _T1) && !_MDSPAN_TRAIT(std::is_empty, _T2)>> + : private _T1 { + _T2 __t2_val{}; + MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 _T1 &__first() noexcept { + return *static_cast<_T1 *>(this); + } + MDSPAN_FORCE_INLINE_FUNCTION constexpr _T1 const &__first() const noexcept { + return *static_cast<_T1 const *>(this); + } + MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 _T2 &__second() noexcept { return __t2_val; } + MDSPAN_FORCE_INLINE_FUNCTION constexpr _T2 const &__second() const noexcept { + return __t2_val; + } + + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr __compressed_pair() = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr __compressed_pair(__compressed_pair const &) = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr __compressed_pair(__compressed_pair &&) = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + _MDSPAN_CONSTEXPR_14_DEFAULTED __compressed_pair & + operator=(__compressed_pair const &) = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + _MDSPAN_CONSTEXPR_14_DEFAULTED __compressed_pair & + operator=(__compressed_pair &&) = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + ~__compressed_pair() = default; + template <class _T1Like, class _T2Like> + MDSPAN_INLINE_FUNCTION constexpr __compressed_pair(_T1Like &&__t1, _T2Like &&__t2) + : _T1((_T1Like &&) __t1), __t2_val((_T2Like &&) __t2) {} +}; + +// Second empty. +template <class _T1, class _T2> +struct __compressed_pair< + _T1, _T2, + std::enable_if_t<!_MDSPAN_TRAIT(std::is_empty, _T1) && _MDSPAN_TRAIT(std::is_empty, _T2)>> + : private _T2 { + _T1 __t1_val{}; + MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 _T1 &__first() noexcept { return __t1_val; } + MDSPAN_FORCE_INLINE_FUNCTION constexpr _T1 const &__first() const noexcept { + return __t1_val; + } + MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 _T2 &__second() noexcept { + return *static_cast<_T2 *>(this); + } + MDSPAN_FORCE_INLINE_FUNCTION constexpr _T2 const &__second() const noexcept { + return *static_cast<_T2 const *>(this); + } + + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr __compressed_pair() = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr __compressed_pair(__compressed_pair const &) = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr __compressed_pair(__compressed_pair &&) = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + _MDSPAN_CONSTEXPR_14_DEFAULTED __compressed_pair & + operator=(__compressed_pair const &) = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + _MDSPAN_CONSTEXPR_14_DEFAULTED __compressed_pair & + operator=(__compressed_pair &&) = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + ~__compressed_pair() = default; + + template <class _T1Like, class _T2Like> + MDSPAN_INLINE_FUNCTION constexpr __compressed_pair(_T1Like &&__t1, _T2Like &&__t2) + : _T2((_T2Like &&) __t2), __t1_val((_T1Like &&) __t1) {} +}; + +// Both empty. +template <class _T1, class _T2> +struct __compressed_pair< + _T1, _T2, + std::enable_if_t<_MDSPAN_TRAIT(std::is_empty, _T1) && _MDSPAN_TRAIT(std::is_empty, _T2)>> + // We need to use the __no_unique_address_emulation wrapper here to avoid + // base class ambiguities. +#ifdef _MDSPAN_COMPILER_MSVC +// MSVC doesn't allow you to access public static member functions of a type +// when you *happen* to privately inherit from that type. + : protected __no_unique_address_emulation<_T1, 0>, + protected __no_unique_address_emulation<_T2, 1> +#else + : private __no_unique_address_emulation<_T1, 0>, + private __no_unique_address_emulation<_T2, 1> +#endif +{ + using __first_base_t = __no_unique_address_emulation<_T1, 0>; + using __second_base_t = __no_unique_address_emulation<_T2, 1>; + + MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 _T1 &__first() noexcept { + return this->__first_base_t::__ref(); + } + MDSPAN_FORCE_INLINE_FUNCTION constexpr _T1 const &__first() const noexcept { + return this->__first_base_t::__ref(); + } + MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 _T2 &__second() noexcept { + return this->__second_base_t::__ref(); + } + MDSPAN_FORCE_INLINE_FUNCTION constexpr _T2 const &__second() const noexcept { + return this->__second_base_t::__ref(); + } + + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr __compressed_pair() = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr __compressed_pair(__compressed_pair const &) = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr __compressed_pair(__compressed_pair &&) = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + _MDSPAN_CONSTEXPR_14_DEFAULTED __compressed_pair & + operator=(__compressed_pair const &) = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + _MDSPAN_CONSTEXPR_14_DEFAULTED __compressed_pair & + operator=(__compressed_pair &&) = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + ~__compressed_pair() = default; + template <class _T1Like, class _T2Like> + MDSPAN_INLINE_FUNCTION constexpr __compressed_pair(_T1Like &&__t1, _T2Like &&__t2) noexcept + : __first_base_t(_T1((_T1Like &&) __t1)), + __second_base_t(_T2((_T2Like &&) __t2)) + { } +}; + +#endif // !defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) + +} // end namespace detail +} // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/tests/integration/deps/mdspan/include/experimental/__p0009_bits/config.hpp b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/config.hpp new file mode 100644 index 0000000000000000000000000000000000000000..24166462e7abd5e96a941b8ba9f5d302369d07c7 --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/config.hpp @@ -0,0 +1,281 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER +#pragma once + +#ifndef __has_include +# define __has_include(x) 0 +#endif + +#if __has_include(<version>) +# include <version> +#else +# include <type_traits> +# include <utility> +#endif + +#ifdef _MSVC_LANG +#define _MDSPAN_CPLUSPLUS _MSVC_LANG +#else +#define _MDSPAN_CPLUSPLUS __cplusplus +#endif + +#define MDSPAN_CXX_STD_14 201402L +#define MDSPAN_CXX_STD_17 201703L +#define MDSPAN_CXX_STD_20 202002L +// Note GCC has not updated this in version 13 +#ifdef __clang__ +#define MDSPAN_CXX_STD_23 202302L +#else +#define MDSPAN_CXX_STD_23 202100L +#endif + +#define MDSPAN_HAS_CXX_14 (_MDSPAN_CPLUSPLUS >= MDSPAN_CXX_STD_14) +#define MDSPAN_HAS_CXX_17 (_MDSPAN_CPLUSPLUS >= MDSPAN_CXX_STD_17) +#define MDSPAN_HAS_CXX_20 (_MDSPAN_CPLUSPLUS >= MDSPAN_CXX_STD_20) +#define MDSPAN_HAS_CXX_23 (_MDSPAN_CPLUSPLUS >= MDSPAN_CXX_STD_23) + +static_assert(_MDSPAN_CPLUSPLUS >= MDSPAN_CXX_STD_14, "mdspan requires C++14 or later."); + +#ifndef _MDSPAN_COMPILER_CLANG +# if defined(__clang__) +# define _MDSPAN_COMPILER_CLANG __clang__ +# endif +#endif + +#if !defined(_MDSPAN_COMPILER_MSVC) && !defined(_MDSPAN_COMPILER_MSVC_CLANG) +# if defined(_MSC_VER) +# if !defined(_MDSPAN_COMPILER_CLANG) +# define _MDSPAN_COMPILER_MSVC _MSC_VER +# else +# define _MDSPAN_COMPILER_MSVC_CLANG _MSC_VER +# endif +# endif +#endif + +#ifndef _MDSPAN_COMPILER_INTEL +# ifdef __INTEL_COMPILER +# define _MDSPAN_COMPILER_INTEL __INTEL_COMPILER +# endif +#endif + +#ifndef _MDSPAN_COMPILER_APPLECLANG +# ifdef __apple_build_version__ +# define _MDSPAN_COMPILER_APPLECLANG __apple_build_version__ +# endif +#endif + +#ifndef _MDSPAN_HAS_CUDA +# if defined(__CUDACC__) +# define _MDSPAN_HAS_CUDA __CUDACC__ +# endif +#endif + +#ifndef _MDSPAN_HAS_HIP +# if defined(__HIPCC__) +# define _MDSPAN_HAS_HIP __HIPCC__ +# endif +#endif + +#ifndef _MDSPAN_HAS_SYCL +# if defined(SYCL_LANGUAGE_VERSION) +# define _MDSPAN_HAS_SYCL SYCL_LANGUAGE_VERSION +# endif +#endif + +#ifndef __has_cpp_attribute +# define __has_cpp_attribute(x) 0 +#endif + +#ifndef _MDSPAN_PRESERVE_STANDARD_LAYOUT +// Preserve standard layout by default, but we're not removing the old version +// that turns this off until we're sure this doesn't have an unreasonable cost +// to the compiler or optimizer. +# define _MDSPAN_PRESERVE_STANDARD_LAYOUT 1 +#endif + +#if !defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) +# if ((__has_cpp_attribute(no_unique_address) >= 201803L) && \ + (!defined(__NVCC__) || MDSPAN_HAS_CXX_20) && \ + (!defined(_MDSPAN_COMPILER_MSVC) || MDSPAN_HAS_CXX_20)) +# define _MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS 1 +# define _MDSPAN_NO_UNIQUE_ADDRESS [[no_unique_address]] +# else +# define _MDSPAN_NO_UNIQUE_ADDRESS +# endif +#endif + +// NVCC older than 11.6 chokes on the no-unique-address-emulation +// so just pretend to use it (to avoid the full blown EBO workaround +// which NVCC also doesn't like ...), and leave the macro empty +#ifndef _MDSPAN_NO_UNIQUE_ADDRESS +# if defined(__NVCC__) +# define _MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS 1 +# define _MDSPAN_USE_FAKE_ATTRIBUTE_NO_UNIQUE_ADDRESS +# endif +# define _MDSPAN_NO_UNIQUE_ADDRESS +#endif + +// AMDs HIP compiler seems to have issues with concepts +// it pretends concepts exist, but doesn't ship <concept> +#ifndef __HIPCC__ +#ifndef _MDSPAN_USE_CONCEPTS +# if defined(__cpp_concepts) && __cpp_concepts >= 201507L +# define _MDSPAN_USE_CONCEPTS 1 +# endif +#endif +#endif + +#ifndef _MDSPAN_USE_FOLD_EXPRESSIONS +# if (defined(__cpp_fold_expressions) && __cpp_fold_expressions >= 201603L) \ + || (!defined(__cpp_fold_expressions) && MDSPAN_HAS_CXX_17) +# define _MDSPAN_USE_FOLD_EXPRESSIONS 1 +# endif +#endif + +#ifndef _MDSPAN_USE_INLINE_VARIABLES +# if defined(__cpp_inline_variables) && __cpp_inline_variables >= 201606L \ + || (!defined(__cpp_inline_variables) && MDSPAN_HAS_CXX_17) +# define _MDSPAN_USE_INLINE_VARIABLES 1 +# endif +#endif + +#ifndef _MDSPAN_NEEDS_TRAIT_VARIABLE_TEMPLATE_BACKPORTS +# if (!(defined(__cpp_lib_type_trait_variable_templates) && __cpp_lib_type_trait_variable_templates >= 201510L) \ + || !MDSPAN_HAS_CXX_17) +# if !(defined(_MDSPAN_COMPILER_APPLECLANG) && MDSPAN_HAS_CXX_17) +# define _MDSPAN_NEEDS_TRAIT_VARIABLE_TEMPLATE_BACKPORTS 1 +# endif +# endif +#endif + +#ifndef _MDSPAN_USE_VARIABLE_TEMPLATES +# if (defined(__cpp_variable_templates) && __cpp_variable_templates >= 201304 && MDSPAN_HAS_CXX_17) \ + || (!defined(__cpp_variable_templates) && MDSPAN_HAS_CXX_17) +# define _MDSPAN_USE_VARIABLE_TEMPLATES 1 +# endif +#endif // _MDSPAN_USE_VARIABLE_TEMPLATES + +#ifndef _MDSPAN_USE_CONSTEXPR_14 +# if (defined(__cpp_constexpr) && __cpp_constexpr >= 201304) \ + || (!defined(__cpp_constexpr) && MDSPAN_HAS_CXX_14) \ + && (!(defined(__INTEL_COMPILER) && __INTEL_COMPILER <= 1700)) +# define _MDSPAN_USE_CONSTEXPR_14 1 +# endif +#endif + +#ifndef _MDSPAN_USE_INTEGER_SEQUENCE +# if defined(_MDSPAN_COMPILER_MSVC) +# if (defined(__cpp_lib_integer_sequence) && __cpp_lib_integer_sequence >= 201304) +# define _MDSPAN_USE_INTEGER_SEQUENCE 1 +# endif +# endif +#endif +#ifndef _MDSPAN_USE_INTEGER_SEQUENCE +# if (defined(__cpp_lib_integer_sequence) && __cpp_lib_integer_sequence >= 201304) \ + || (!defined(__cpp_lib_integer_sequence) && MDSPAN_HAS_CXX_14) \ + /* as far as I can tell, libc++ seems to think this is a C++11 feature... */ \ + || (defined(__GLIBCXX__) && __GLIBCXX__ > 20150422 && __GNUC__ < 5 && !defined(__INTEL_CXX11_MODE__)) + // several compilers lie about integer_sequence working properly unless the C++14 standard is used +# define _MDSPAN_USE_INTEGER_SEQUENCE 1 +# elif defined(_MDSPAN_COMPILER_APPLECLANG) && MDSPAN_HAS_CXX_14 + // appleclang seems to be missing the __cpp_lib_... macros, but doesn't seem to lie about C++14 making + // integer_sequence work +# define _MDSPAN_USE_INTEGER_SEQUENCE 1 +# endif +#endif + +#ifndef _MDSPAN_USE_RETURN_TYPE_DEDUCTION +# if (defined(__cpp_return_type_deduction) && __cpp_return_type_deduction >= 201304) \ + || (!defined(__cpp_return_type_deduction) && MDSPAN_HAS_CXX_14) +# define _MDSPAN_USE_RETURN_TYPE_DEDUCTION 1 +# endif +#endif + +#ifndef _MDSPAN_USE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION +# if (!defined(__NVCC__) || (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__ * 10 >= 1170)) && \ + ((defined(__cpp_deduction_guides) && __cpp_deduction_guides >= 201703) || \ + (!defined(__cpp_deduction_guides) && MDSPAN_HAS_CXX_17)) +# define _MDSPAN_USE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION 1 +# endif +#endif + +#ifndef _MDSPAN_USE_STANDARD_TRAIT_ALIASES +# if (defined(__cpp_lib_transformation_trait_aliases) && __cpp_lib_transformation_trait_aliases >= 201304) \ + || (!defined(__cpp_lib_transformation_trait_aliases) && MDSPAN_HAS_CXX_14) +# define _MDSPAN_USE_STANDARD_TRAIT_ALIASES 1 +# elif defined(_MDSPAN_COMPILER_APPLECLANG) && MDSPAN_HAS_CXX_14 + // appleclang seems to be missing the __cpp_lib_... macros, but doesn't seem to lie about C++14 +# define _MDSPAN_USE_STANDARD_TRAIT_ALIASES 1 +# endif +#endif + +#ifndef _MDSPAN_DEFAULTED_CONSTRUCTORS_INHERITANCE_WORKAROUND +# ifdef __GNUC__ +# if __GNUC__ < 9 +# define _MDSPAN_DEFAULTED_CONSTRUCTORS_INHERITANCE_WORKAROUND 1 +# endif +# endif +#endif + +#ifndef MDSPAN_CONDITIONAL_EXPLICIT +# if MDSPAN_HAS_CXX_20 +# define MDSPAN_CONDITIONAL_EXPLICIT(COND) explicit(COND) +# else +# define MDSPAN_CONDITIONAL_EXPLICIT(COND) +# endif +#endif + +#ifndef MDSPAN_USE_BRACKET_OPERATOR +# if defined(__cpp_multidimensional_subscript) +# define MDSPAN_USE_BRACKET_OPERATOR 1 +# else +# define MDSPAN_USE_BRACKET_OPERATOR 0 +# endif +#endif + +#ifndef MDSPAN_USE_PAREN_OPERATOR +# if !MDSPAN_USE_BRACKET_OPERATOR +# define MDSPAN_USE_PAREN_OPERATOR 1 +# else +# define MDSPAN_USE_PAREN_OPERATOR 0 +# endif +#endif + +#if MDSPAN_USE_BRACKET_OPERATOR +# define __MDSPAN_OP(mds,...) mds[__VA_ARGS__] +// Corentins demo compiler for subscript chokes on empty [] call, +// though I believe the proposal supports it? +#ifdef MDSPAN_NO_EMPTY_BRACKET_OPERATOR +# define __MDSPAN_OP0(mds) mds.accessor().access(mds.data_handle(),0) +#else +# define __MDSPAN_OP0(mds) mds[] +#endif +# define __MDSPAN_OP1(mds, a) mds[a] +# define __MDSPAN_OP2(mds, a, b) mds[a,b] +# define __MDSPAN_OP3(mds, a, b, c) mds[a,b,c] +# define __MDSPAN_OP4(mds, a, b, c, d) mds[a,b,c,d] +# define __MDSPAN_OP5(mds, a, b, c, d, e) mds[a,b,c,d,e] +# define __MDSPAN_OP6(mds, a, b, c, d, e, f) mds[a,b,c,d,e,f] +#else +# define __MDSPAN_OP(mds,...) mds(__VA_ARGS__) +# define __MDSPAN_OP0(mds) mds() +# define __MDSPAN_OP1(mds, a) mds(a) +# define __MDSPAN_OP2(mds, a, b) mds(a,b) +# define __MDSPAN_OP3(mds, a, b, c) mds(a,b,c) +# define __MDSPAN_OP4(mds, a, b, c, d) mds(a,b,c,d) +# define __MDSPAN_OP5(mds, a, b, c, d, e) mds(a,b,c,d,e) +# define __MDSPAN_OP6(mds, a, b, c, d, e, f) mds(a,b,c,d,e,f) +#endif diff --git a/tests/integration/deps/mdspan/include/experimental/__p0009_bits/default_accessor.hpp b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/default_accessor.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ea0f537b2fe191ace9c6f5271d5b7331e172a4fd --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/default_accessor.hpp @@ -0,0 +1,56 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER +#pragma once + +#include "macros.hpp" + +#include <cstddef> // size_t + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { + +template <class ElementType> +struct default_accessor { + + using offset_policy = default_accessor; + using element_type = ElementType; + using reference = ElementType&; + using data_handle_type = ElementType*; + + MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr default_accessor() noexcept = default; + + MDSPAN_TEMPLATE_REQUIRES( + class OtherElementType, + /* requires */ ( + _MDSPAN_TRAIT(std::is_convertible, OtherElementType(*)[], element_type(*)[]) + ) + ) + MDSPAN_INLINE_FUNCTION + constexpr default_accessor(default_accessor<OtherElementType>) noexcept {} + + MDSPAN_INLINE_FUNCTION + constexpr data_handle_type + offset(data_handle_type p, size_t i) const noexcept { + return p + i; + } + + MDSPAN_FORCE_INLINE_FUNCTION + constexpr reference access(data_handle_type p, size_t i) const noexcept { + return p[i]; + } + +}; + +} // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/tests/integration/deps/mdspan/include/experimental/__p0009_bits/dynamic_extent.hpp b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/dynamic_extent.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2e29da13d6adfd1107fe7ea3ff022bfcf376f189 --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/dynamic_extent.hpp @@ -0,0 +1,35 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER +#pragma once + +#include "macros.hpp" + +#if defined(__cpp_lib_span) +#include <span> +#endif + +#include <cstddef> // size_t +#include <limits> // numeric_limits + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +#if defined(__cpp_lib_span) +using std::dynamic_extent; +#else +_MDSPAN_INLINE_VARIABLE constexpr auto dynamic_extent = std::numeric_limits<size_t>::max(); +#endif +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE + +//============================================================================================================== diff --git a/tests/integration/deps/mdspan/include/experimental/__p0009_bits/extents.hpp b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/extents.hpp new file mode 100644 index 0000000000000000000000000000000000000000..98a57e34e649bb6e026df34af073531883a3abbf --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/extents.hpp @@ -0,0 +1,691 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#pragma once +#include "dynamic_extent.hpp" +#include "utility.hpp" + +#ifdef __cpp_lib_span +#include <span> +#endif +#include <array> +#include <type_traits> + +#include <cassert> +#include <cinttypes> + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace detail { + +// Function used to check compatibility of extents in converting constructor +// can't be a private member function for some reason. +template <size_t... Extents, size_t... OtherExtents> +static constexpr std::integral_constant<bool, false> __check_compatible_extents( + std::integral_constant<bool, false>, + std::integer_sequence<size_t, Extents...>, + std::integer_sequence<size_t, OtherExtents...>) noexcept { + return {}; +} + +// This helper prevents ICE's on MSVC. +template <size_t Lhs, size_t Rhs> +struct __compare_extent_compatible : std::integral_constant<bool, + Lhs == dynamic_extent || + Rhs == dynamic_extent || + Lhs == Rhs> +{}; + +template <size_t... Extents, size_t... OtherExtents> +static constexpr std::integral_constant< + bool, _MDSPAN_FOLD_AND(__compare_extent_compatible<Extents, OtherExtents>::value)> +__check_compatible_extents( + std::integral_constant<bool, true>, + std::integer_sequence<size_t, Extents...>, + std::integer_sequence<size_t, OtherExtents...>) noexcept { + return {}; +} + +template<class IndexType, class ... Arguments> +MDSPAN_INLINE_FUNCTION +static constexpr bool are_valid_indices() { + return + _MDSPAN_FOLD_AND(std::is_convertible<Arguments, IndexType>::value) && + _MDSPAN_FOLD_AND(std::is_nothrow_constructible<IndexType, Arguments>::value); +} + +// ------------------------------------------------------------------ +// ------------ static_array ---------------------------------------- +// ------------------------------------------------------------------ + +// array like class which provides an array of static values with get +// function and operator []. + +// Implementation of Static Array with recursive implementation of get. +template <size_t R, class T, T... Extents> struct static_array_impl; + +template <size_t R, class T, T FirstExt, T... Extents> +struct static_array_impl<R, T, FirstExt, Extents...> { + MDSPAN_INLINE_FUNCTION + constexpr static T get(size_t r) { + if (r == R) + return FirstExt; + else + return static_array_impl<R + 1, T, Extents...>::get(r); + } + template <size_t r> MDSPAN_INLINE_FUNCTION constexpr static T get() { +#if MDSPAN_HAS_CXX_17 + if constexpr (r == R) + return FirstExt; + else + return static_array_impl<R + 1, T, Extents...>::template get<r>(); +#else + get(r); +#endif + } +}; + +// End the recursion +template <size_t R, class T, T FirstExt> +struct static_array_impl<R, T, FirstExt> { + MDSPAN_INLINE_FUNCTION + constexpr static T get(size_t) { return FirstExt; } + template <size_t> MDSPAN_INLINE_FUNCTION constexpr static T get() { + return FirstExt; + } +}; + +// Don't start recursion if size 0 +template <class T> struct static_array_impl<0, T> { + MDSPAN_INLINE_FUNCTION + constexpr static T get(size_t) { return T(); } + template <size_t> MDSPAN_INLINE_FUNCTION constexpr static T get() { + return T(); + } +}; + +// Static array, provides get<r>(), get(r) and operator[r] +template <class T, T... Values> struct static_array: + public static_array_impl<0, T, Values...> { + +public: + using value_type = T; + + MDSPAN_INLINE_FUNCTION + constexpr static size_t size() { return sizeof...(Values); } +}; + + +// ------------------------------------------------------------------ +// ------------ index_sequence_scan --------------------------------- +// ------------------------------------------------------------------ + +// index_sequence_scan takes compile time values and provides get(r) +// and get<r>() which return the sum of the first r-1 values. + +// Recursive implementation for get +template <size_t R, size_t... Values> struct index_sequence_scan_impl; + +template <size_t R, size_t FirstVal, size_t... Values> +struct index_sequence_scan_impl<R, FirstVal, Values...> { + MDSPAN_INLINE_FUNCTION + constexpr static size_t get(size_t r) { + if (r > R) + return FirstVal + index_sequence_scan_impl<R + 1, Values...>::get(r); + else + return 0; + } +}; + +template <size_t R, size_t FirstVal> +struct index_sequence_scan_impl<R, FirstVal> { +#if defined(__NVCC__) || defined(__NVCOMPILER) || \ + defined(_MDSPAN_COMPILER_INTEL) + // NVCC warns about pointless comparison with 0 for R==0 and r being const + // evaluatable and also 0. + MDSPAN_INLINE_FUNCTION + constexpr static size_t get(size_t r) { + return static_cast<int64_t>(R) > static_cast<int64_t>(r) ? FirstVal : 0; + } +#else + MDSPAN_INLINE_FUNCTION + constexpr static size_t get(size_t r) { return R > r ? FirstVal : 0; } +#endif +}; +template <> struct index_sequence_scan_impl<0> { + MDSPAN_INLINE_FUNCTION + constexpr static size_t get(size_t) { return 0; } +}; + +// ------------------------------------------------------------------ +// ------------ possibly_empty_array ------------------------------- +// ------------------------------------------------------------------ + +// array like class which provides get function and operator [], and +// has a specialization for the size 0 case. +// This is needed to make the maybe_static_array be truly empty, for +// all static values. + +template <class T, size_t N> struct possibly_empty_array { + T vals[N]{}; + MDSPAN_INLINE_FUNCTION + constexpr T &operator[](size_t r) { return vals[r]; } + MDSPAN_INLINE_FUNCTION + constexpr const T &operator[](size_t r) const { return vals[r]; } +}; + +template <class T> struct possibly_empty_array<T, 0> { + MDSPAN_INLINE_FUNCTION + constexpr T operator[](size_t) { return T(); } + MDSPAN_INLINE_FUNCTION + constexpr const T operator[](size_t) const { return T(); } +}; + +// ------------------------------------------------------------------ +// ------------ maybe_static_array ---------------------------------- +// ------------------------------------------------------------------ + +// array like class which has a mix of static and runtime values but +// only stores the runtime values. +// The type of the static and the runtime values can be different. +// The position of a dynamic value is indicated through a tag value. +template <class TDynamic, class TStatic, TStatic dyn_tag, TStatic... Values> +struct maybe_static_array { + + static_assert(std::is_convertible<TStatic, TDynamic>::value, "maybe_static_array: TStatic must be convertible to TDynamic"); + static_assert(std::is_convertible<TDynamic, TStatic>::value, "maybe_static_array: TDynamic must be convertible to TStatic"); + +private: + // Static values member + using static_vals_t = static_array<TStatic, Values...>; + constexpr static size_t m_size = sizeof...(Values); + constexpr static size_t m_size_dynamic = + _MDSPAN_FOLD_PLUS_RIGHT((Values == dyn_tag), 0); + + // Dynamic values member + _MDSPAN_NO_UNIQUE_ADDRESS possibly_empty_array<TDynamic, m_size_dynamic> + m_dyn_vals; + + // static mapping of indices to the position in the dynamic values array + using dyn_map_t = index_sequence_scan_impl<0, static_cast<size_t>(Values == dyn_tag)...>; +public: + + // two types for static and dynamic values + using value_type = TDynamic; + using static_value_type = TStatic; + // tag value indicating dynamic value + constexpr static static_value_type tag_value = dyn_tag; + + constexpr maybe_static_array() = default; + + // constructor for all static values + // TODO: add precondition check? + MDSPAN_TEMPLATE_REQUIRES(class... Vals, + /* requires */ ((m_size_dynamic == 0) && + (sizeof...(Vals) > 0))) + MDSPAN_INLINE_FUNCTION + constexpr maybe_static_array(Vals...) : m_dyn_vals{} {} + + // constructors from dynamic values only + MDSPAN_TEMPLATE_REQUIRES(class... DynVals, + /* requires */ (sizeof...(DynVals) == + m_size_dynamic && + m_size_dynamic > 0)) + MDSPAN_INLINE_FUNCTION + constexpr maybe_static_array(DynVals... vals) + : m_dyn_vals{static_cast<TDynamic>(vals)...} {} + + + MDSPAN_TEMPLATE_REQUIRES(class T, size_t N, + /* requires */ (N == m_size_dynamic && N > 0)) + MDSPAN_INLINE_FUNCTION + constexpr maybe_static_array(const std::array<T, N> &vals) { + for (size_t r = 0; r < N; r++) + m_dyn_vals[r] = static_cast<TDynamic>(vals[r]); + } + + MDSPAN_TEMPLATE_REQUIRES(class T, size_t N, + /* requires */ (N == m_size_dynamic && N == 0)) + MDSPAN_INLINE_FUNCTION + constexpr maybe_static_array(const std::array<T, N> &) : m_dyn_vals{} {} + +#ifdef __cpp_lib_span + MDSPAN_TEMPLATE_REQUIRES(class T, size_t N, + /* requires */ (N == m_size_dynamic && N > 0)) + MDSPAN_INLINE_FUNCTION + constexpr maybe_static_array(const std::span<T, N> &vals) { + for (size_t r = 0; r < N; r++) + m_dyn_vals[r] = static_cast<TDynamic>(vals[r]); + } + + MDSPAN_TEMPLATE_REQUIRES(class T, size_t N, + /* requires */ (N == m_size_dynamic && N == 0)) + MDSPAN_INLINE_FUNCTION + constexpr maybe_static_array(const std::span<T, N> &) : m_dyn_vals{} {} +#endif + + // constructors from all values + MDSPAN_TEMPLATE_REQUIRES(class... DynVals, + /* requires */ (sizeof...(DynVals) != + m_size_dynamic && + m_size_dynamic > 0)) + MDSPAN_INLINE_FUNCTION + constexpr maybe_static_array(DynVals... vals) + : m_dyn_vals{} { + static_assert((sizeof...(DynVals) == m_size), "Invalid number of values."); + TDynamic values[m_size]{static_cast<TDynamic>(vals)...}; + for (size_t r = 0; r < m_size; r++) { + TStatic static_val = static_vals_t::get(r); + if (static_val == dyn_tag) { + m_dyn_vals[dyn_map_t::get(r)] = values[r]; + } +// Precondition check +#ifdef _MDSPAN_DEBUG + else { + assert(values[r] == static_cast<TDynamic>(static_val)); + } +#endif + } + } + + MDSPAN_TEMPLATE_REQUIRES( + class T, size_t N, + /* requires */ (N != m_size_dynamic && m_size_dynamic > 0)) + MDSPAN_INLINE_FUNCTION + constexpr maybe_static_array(const std::array<T, N> &vals) { + static_assert((N == m_size), "Invalid number of values."); +// Precondition check +#ifdef _MDSPAN_DEBUG + assert(N == m_size); +#endif + for (size_t r = 0; r < m_size; r++) { + TStatic static_val = static_vals_t::get(r); + if (static_val == dyn_tag) { + m_dyn_vals[dyn_map_t::get(r)] = static_cast<TDynamic>(vals[r]); + } +// Precondition check +#ifdef _MDSPAN_DEBUG + else { + assert(static_cast<TDynamic>(vals[r]) == + static_cast<TDynamic>(static_val)); + } +#endif + } + } + +#ifdef __cpp_lib_span + MDSPAN_TEMPLATE_REQUIRES( + class T, size_t N, + /* requires */ (N != m_size_dynamic && m_size_dynamic > 0)) + MDSPAN_INLINE_FUNCTION + constexpr maybe_static_array(const std::span<T, N> &vals) { + static_assert((N == m_size) || (m_size == dynamic_extent)); +#ifdef _MDSPAN_DEBUG + assert(N == m_size); +#endif + for (size_t r = 0; r < m_size; r++) { + TStatic static_val = static_vals_t::get(r); + if (static_val == dyn_tag) { + m_dyn_vals[dyn_map_t::get(r)] = static_cast<TDynamic>(vals[r]); + } +#ifdef _MDSPAN_DEBUG + else { + assert(static_cast<TDynamic>(vals[r]) == + static_cast<TDynamic>(static_val)); + } +#endif + } + } +#endif + + // access functions + MDSPAN_INLINE_FUNCTION + constexpr static TStatic static_value(size_t r) { return static_vals_t::get(r); } + + MDSPAN_INLINE_FUNCTION + constexpr TDynamic value(size_t r) const { + TStatic static_val = static_vals_t::get(r); + return static_val == dyn_tag ? m_dyn_vals[dyn_map_t::get(r)] + : static_cast<TDynamic>(static_val); + } + MDSPAN_INLINE_FUNCTION + constexpr TDynamic operator[](size_t r) const { return value(r); } + + + // observers + MDSPAN_INLINE_FUNCTION + constexpr static size_t size() { return m_size; } + MDSPAN_INLINE_FUNCTION + constexpr static size_t size_dynamic() { return m_size_dynamic; } +}; + +} // namespace detail +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { + +// ------------------------------------------------------------------ +// ------------ extents --------------------------------------------- +// ------------------------------------------------------------------ + +// Class to describe the extents of a multi dimensional array. +// Used by mdspan, mdarray and layout mappings. +// See ISO C++ standard [mdspan.extents] + +template <class IndexType, size_t... Extents> class extents { +public: + // typedefs for integral types used + using index_type = IndexType; + using size_type = std::make_unsigned_t<index_type>; + using rank_type = size_t; + + static_assert(std::is_integral<index_type>::value && !std::is_same<index_type, bool>::value, + MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::extents::index_type must be a signed or unsigned integer type"); +private: + constexpr static rank_type m_rank = sizeof...(Extents); + constexpr static rank_type m_rank_dynamic = + _MDSPAN_FOLD_PLUS_RIGHT((Extents == dynamic_extent), /* + ... + */ 0); + + // internal storage type using maybe_static_array + using vals_t = + detail::maybe_static_array<IndexType, size_t, dynamic_extent, Extents...>; + _MDSPAN_NO_UNIQUE_ADDRESS vals_t m_vals; + +public: + // [mdspan.extents.obs], observers of multidimensional index space + MDSPAN_INLINE_FUNCTION + constexpr static rank_type rank() noexcept { return m_rank; } + MDSPAN_INLINE_FUNCTION + constexpr static rank_type rank_dynamic() noexcept { return m_rank_dynamic; } + + MDSPAN_INLINE_FUNCTION + constexpr index_type extent(rank_type r) const noexcept { return m_vals.value(r); } + MDSPAN_INLINE_FUNCTION + constexpr static size_t static_extent(rank_type r) noexcept { + return vals_t::static_value(r); + } + + // [mdspan.extents.cons], constructors + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr extents() noexcept = default; + + // Construction from just dynamic or all values. + // Precondition check is deferred to maybe_static_array constructor + MDSPAN_TEMPLATE_REQUIRES( + class... OtherIndexTypes, + /* requires */ ( + _MDSPAN_FOLD_AND(_MDSPAN_TRAIT(std::is_convertible, OtherIndexTypes, + index_type) /* && ... */) && + _MDSPAN_FOLD_AND(_MDSPAN_TRAIT(std::is_nothrow_constructible, index_type, + OtherIndexTypes) /* && ... */) && + (sizeof...(OtherIndexTypes) == m_rank || + sizeof...(OtherIndexTypes) == m_rank_dynamic))) + MDSPAN_INLINE_FUNCTION + constexpr explicit extents(OtherIndexTypes... dynvals) noexcept + : m_vals(static_cast<index_type>(dynvals)...) {} + + MDSPAN_TEMPLATE_REQUIRES( + class OtherIndexType, size_t N, + /* requires */ + ( + _MDSPAN_TRAIT(std::is_convertible, const OtherIndexType&, index_type) && + _MDSPAN_TRAIT(std::is_nothrow_constructible, index_type, + const OtherIndexType&) && + (N == m_rank || N == m_rank_dynamic))) + MDSPAN_INLINE_FUNCTION + MDSPAN_CONDITIONAL_EXPLICIT(N != m_rank_dynamic) + constexpr extents(const std::array<OtherIndexType, N> &exts) noexcept + : m_vals(std::move(exts)) {} + +#ifdef __cpp_lib_span + MDSPAN_TEMPLATE_REQUIRES( + class OtherIndexType, size_t N, + /* requires */ + (_MDSPAN_TRAIT(std::is_convertible, const OtherIndexType&, index_type) && + _MDSPAN_TRAIT(std::is_nothrow_constructible, index_type, const OtherIndexType&) && + (N == m_rank || N == m_rank_dynamic))) + MDSPAN_INLINE_FUNCTION + MDSPAN_CONDITIONAL_EXPLICIT(N != m_rank_dynamic) + constexpr extents(const std::span<OtherIndexType, N> &exts) noexcept + : m_vals(std::move(exts)) {} +#endif + +private: + // Function to construct extents storage from other extents. + // With C++ 17 the first two variants could be collapsed using if constexpr + // in which case you don't need all the requires clauses. + // in C++ 14 mode that doesn't work due to infinite recursion + MDSPAN_TEMPLATE_REQUIRES( + size_t DynCount, size_t R, class OtherExtents, class... DynamicValues, + /* requires */ ((R < m_rank) && (static_extent(R) == dynamic_extent))) + MDSPAN_INLINE_FUNCTION + constexpr + vals_t __construct_vals_from_extents(std::integral_constant<size_t, DynCount>, + std::integral_constant<size_t, R>, + const OtherExtents &exts, + DynamicValues... dynamic_values) noexcept { + return __construct_vals_from_extents( + std::integral_constant<size_t, DynCount + 1>(), + std::integral_constant<size_t, R + 1>(), exts, dynamic_values..., + exts.extent(R)); + } + + MDSPAN_TEMPLATE_REQUIRES( + size_t DynCount, size_t R, class OtherExtents, class... DynamicValues, + /* requires */ ((R < m_rank) && (static_extent(R) != dynamic_extent))) + MDSPAN_INLINE_FUNCTION + constexpr + vals_t __construct_vals_from_extents(std::integral_constant<size_t, DynCount>, + std::integral_constant<size_t, R>, + const OtherExtents &exts, + DynamicValues... dynamic_values) noexcept { + return __construct_vals_from_extents( + std::integral_constant<size_t, DynCount>(), + std::integral_constant<size_t, R + 1>(), exts, dynamic_values...); + } + + MDSPAN_TEMPLATE_REQUIRES( + size_t DynCount, size_t R, class OtherExtents, class... DynamicValues, + /* requires */ ((R == m_rank) && (DynCount == m_rank_dynamic))) + MDSPAN_INLINE_FUNCTION + constexpr + vals_t __construct_vals_from_extents(std::integral_constant<size_t, DynCount>, + std::integral_constant<size_t, R>, + const OtherExtents &, + DynamicValues... dynamic_values) noexcept { + return vals_t{static_cast<index_type>(dynamic_values)...}; + } + +public: + + // Converting constructor from other extents specializations + MDSPAN_TEMPLATE_REQUIRES( + class OtherIndexType, size_t... OtherExtents, + /* requires */ + ( + /* multi-stage check to protect from invalid pack expansion when sizes + don't match? */ + decltype(detail::__check_compatible_extents( + // using: sizeof...(Extents) == sizeof...(OtherExtents) as the second argument fails with MSVC+NVCC with some obscure expansion error + // MSVC: 19.38.33133 NVCC: 12.0 + std::integral_constant<bool, extents<int, Extents...>::rank() == extents<int, OtherExtents...>::rank()>{}, + std::integer_sequence<size_t, Extents...>{}, + std::integer_sequence<size_t, OtherExtents...>{}))::value + ) + ) + MDSPAN_INLINE_FUNCTION + MDSPAN_CONDITIONAL_EXPLICIT((((Extents != dynamic_extent) && + (OtherExtents == dynamic_extent)) || + ...) || + (std::numeric_limits<index_type>::max() < + std::numeric_limits<OtherIndexType>::max())) + constexpr extents(const extents<OtherIndexType, OtherExtents...> &other) noexcept + : m_vals(__construct_vals_from_extents( + std::integral_constant<size_t, 0>(), + std::integral_constant<size_t, 0>(), other)) {} + + // Comparison operator + template <class OtherIndexType, size_t... OtherExtents> + MDSPAN_INLINE_FUNCTION friend constexpr bool + operator==(const extents &lhs, + const extents<OtherIndexType, OtherExtents...> &rhs) noexcept { + return + rank() == extents<OtherIndexType, OtherExtents...>::rank() && + detail::rankwise_equal(detail::with_rank<rank()>{}, rhs, lhs, detail::extent); + } + +#if !(MDSPAN_HAS_CXX_20) + template <class OtherIndexType, size_t... OtherExtents> + MDSPAN_INLINE_FUNCTION friend constexpr bool + operator!=(extents const &lhs, + extents<OtherIndexType, OtherExtents...> const &rhs) noexcept { + return !(lhs == rhs); + } +#endif +}; + +// Recursive helper classes to implement dextents alias for extents +namespace detail { + +template <class IndexType, size_t Rank, + class Extents = ::MDSPAN_IMPL_STANDARD_NAMESPACE::extents<IndexType>> +struct __make_dextents; + +template <class IndexType, size_t Rank, size_t... ExtentsPack> +struct __make_dextents< + IndexType, Rank, ::MDSPAN_IMPL_STANDARD_NAMESPACE::extents<IndexType, ExtentsPack...>> +{ + using type = typename __make_dextents< + IndexType, Rank - 1, + ::MDSPAN_IMPL_STANDARD_NAMESPACE::extents<IndexType, + ::MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent, + ExtentsPack...>>::type; +}; + +template <class IndexType, size_t... ExtentsPack> +struct __make_dextents< + IndexType, 0, ::MDSPAN_IMPL_STANDARD_NAMESPACE::extents<IndexType, ExtentsPack...>> +{ + using type = ::MDSPAN_IMPL_STANDARD_NAMESPACE::extents<IndexType, ExtentsPack...>; +}; + +} // end namespace detail + +// [mdspan.extents.dextents], alias template +template <class IndexType, size_t Rank> +using dextents = typename detail::__make_dextents<IndexType, Rank>::type; + +// Deduction guide for extents +#if defined(_MDSPAN_USE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION) +template <class... IndexTypes> +extents(IndexTypes...) + -> extents<size_t, + ((void) sizeof(IndexTypes), ::MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent)...>; +#endif + +// Helper type traits for identifying a class as extents. +namespace detail { + +template <class T> struct __is_extents : ::std::false_type {}; + +template <class IndexType, size_t... ExtentsPack> +struct __is_extents<::MDSPAN_IMPL_STANDARD_NAMESPACE::extents<IndexType, ExtentsPack...>> + : ::std::true_type {}; + +template <class T> +#if MDSPAN_HAS_CXX_17 +inline +#else +static +#endif +constexpr bool __is_extents_v = __is_extents<T>::value; + +template<class InputIndexType, class ExtentsIndexType> +MDSPAN_INLINE_FUNCTION +constexpr void +check_lower_bound(InputIndexType user_index, + ExtentsIndexType /* current_extent */, + std::true_type /* is_signed */) +{ + (void) user_index; // prevent unused variable warning +#ifdef _MDSPAN_DEBUG + assert(static_cast<ExtentsIndexType>(user_index) >= 0); +#endif +} + +template<class InputIndexType, class ExtentsIndexType> +MDSPAN_INLINE_FUNCTION +constexpr void +check_lower_bound(InputIndexType /* user_index */, + ExtentsIndexType /* current_extent */, + std::false_type /* is_signed */) +{} + +template<class InputIndexType, class ExtentsIndexType> +MDSPAN_INLINE_FUNCTION +constexpr void +check_upper_bound(InputIndexType user_index, + ExtentsIndexType current_extent) +{ + (void) user_index; // prevent unused variable warnings + (void) current_extent; +#ifdef _MDSPAN_DEBUG + assert(static_cast<ExtentsIndexType>(user_index) < current_extent); +#endif +} + +// Returning true to use AND fold instead of comma +// CPP14 mode doesn't like the use of void expressions +// with the way the _MDSPAN_FOLD_AND is set up +template<class InputIndex, class ExtentsIndexType> +MDSPAN_INLINE_FUNCTION +constexpr bool +check_one_index(InputIndex user_index, + ExtentsIndexType current_extent) +{ + check_lower_bound(user_index, current_extent, + std::integral_constant<bool, std::is_signed<ExtentsIndexType>::value>{}); + check_upper_bound(user_index, current_extent); + return true; +} + +template<size_t ... RankIndices, + class ExtentsIndexType, size_t ... Exts, + class ... Indices> +MDSPAN_INLINE_FUNCTION +constexpr void +check_all_indices_helper(std::index_sequence<RankIndices...>, + const extents<ExtentsIndexType, Exts...>& exts, + Indices... indices) +{ + // Suppress warning about statement has no effect + (void) _MDSPAN_FOLD_AND( + (check_one_index(indices, exts.extent(RankIndices))) + ); +} + +template<class ExtentsIndexType, size_t ... Exts, + class ... Indices> +MDSPAN_INLINE_FUNCTION +constexpr void +check_all_indices(const extents<ExtentsIndexType, Exts...>& exts, + Indices... indices) +{ + check_all_indices_helper(std::make_index_sequence<sizeof...(Indices)>(), + exts, indices...); +} + +} // namespace detail +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/tests/integration/deps/mdspan/include/experimental/__p0009_bits/full_extent_t.hpp b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/full_extent_t.hpp new file mode 100644 index 0000000000000000000000000000000000000000..bd4b5c6a8baa31a21281b1528ae2f57a264a7d53 --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/full_extent_t.hpp @@ -0,0 +1,26 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER +#pragma once + +#include "macros.hpp" + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { + +struct full_extent_t { explicit full_extent_t() = default; }; + +_MDSPAN_INLINE_VARIABLE constexpr auto full_extent = full_extent_t{ }; + +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/tests/integration/deps/mdspan/include/experimental/__p0009_bits/layout_left.hpp b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/layout_left.hpp new file mode 100644 index 0000000000000000000000000000000000000000..222fba7aa04951df4f73b7894db14b6ab34233c3 --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/layout_left.hpp @@ -0,0 +1,266 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER +#pragma once + +#include "macros.hpp" +#include "trait_backports.hpp" +#include "extents.hpp" +#include "layout_stride.hpp" +#include "utility.hpp" +#if MDSPAN_HAS_CXX_17 +#include "../__p2642_bits/layout_padded_fwd.hpp" +#endif +#include <type_traits> + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { + +//============================================================================== + +template <class Extents> +class layout_left::mapping { + public: + using extents_type = Extents; + using index_type = typename extents_type::index_type; + using size_type = typename extents_type::size_type; + using rank_type = typename extents_type::rank_type; + using layout_type = layout_left; + private: + + static_assert(detail::__is_extents_v<extents_type>, + MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::layout_left::mapping must be instantiated with a specialization of " MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::extents."); + + template <class> + friend class mapping; + + // i0+(i1 + E(1)*(i2 + E(2)*i3)) + template <size_t r, size_t Rank> + struct __rank_count {}; + + template <size_t r, size_t Rank, class I, class... Indices> + _MDSPAN_HOST_DEVICE + constexpr index_type __compute_offset( + __rank_count<r,Rank>, const I& i, Indices... idx) const { + return __compute_offset(__rank_count<r+1,Rank>(), idx...) * + __extents.extent(r) + i; + } + + template<class I> + _MDSPAN_HOST_DEVICE + constexpr index_type __compute_offset( + __rank_count<extents_type::rank()-1,extents_type::rank()>, const I& i) const { + return i; + } + + _MDSPAN_HOST_DEVICE + constexpr index_type __compute_offset(__rank_count<0,0>) const { return 0; } + + public: + + //-------------------------------------------------------------------------------- + + MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping() noexcept = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping(mapping const&) noexcept = default; + + _MDSPAN_HOST_DEVICE + constexpr mapping(extents_type const& __exts) noexcept + :__extents(__exts) + { } + + MDSPAN_TEMPLATE_REQUIRES( + class OtherExtents, + /* requires */ ( + _MDSPAN_TRAIT(std::is_constructible, extents_type, OtherExtents) + ) + ) + MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible<OtherExtents, extents_type>::value)) // needs two () due to comma + MDSPAN_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 + mapping(mapping<OtherExtents> const& other) noexcept // NOLINT(google-explicit-constructor) + :__extents(other.extents()) + { + /* + * TODO: check precondition + * other.required_span_size() is a representable value of type index_type + */ + } + + MDSPAN_TEMPLATE_REQUIRES( + class OtherExtents, + /* requires */ ( + _MDSPAN_TRAIT(std::is_constructible, extents_type, OtherExtents) && + (extents_type::rank() <= 1) + ) + ) + MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible<OtherExtents, extents_type>::value)) // needs two () due to comma + MDSPAN_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 + mapping(layout_right::mapping<OtherExtents> const& other) noexcept // NOLINT(google-explicit-constructor) + :__extents(other.extents()) + { + /* + * TODO: check precondition + * other.required_span_size() is a representable value of type index_type + */ + } + +#if MDSPAN_HAS_CXX_17 + /** + * Converting constructor from `layout_left_padded::mapping`. + * + * This overload participates in overload resolution only if _Mapping is a layout_left_padded mapping and + * extents_type is constructible from _Mapping::extents_type. + * + * \note There is currently a difference from p2642r2, where this function is specified as taking + * `layout_left_padded< padding_value >::mapping< Extents>`. However, this makes `padding_value` non-deducible. + */ + MDSPAN_TEMPLATE_REQUIRES( + class _Mapping, + /* requires */ ( + MDSPAN_IMPL_PROPOSED_NAMESPACE::detail::is_layout_left_padded_mapping<_Mapping>::value + && std::is_constructible_v<extents_type, typename _Mapping::extents_type> + ) + ) + MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible_v<typename _Mapping::extents_type, extents_type>)) + mapping(const _Mapping& __other) noexcept + : __extents(__other.extents()) + { + MDSPAN_IMPL_PROPOSED_NAMESPACE::detail:: + check_padded_layout_converting_constructor_mandates< + extents_type, _Mapping>(detail::with_rank<extents_type::rank()>{}); + MDSPAN_IMPL_PROPOSED_NAMESPACE::detail:: + check_padded_layout_converting_constructor_preconditions< + extents_type>(detail::with_rank<extents_type::rank()>{}, __other); + } +#endif + + MDSPAN_TEMPLATE_REQUIRES( + class OtherExtents, + /* requires */ ( + _MDSPAN_TRAIT(std::is_constructible, extents_type, OtherExtents) + ) + ) + MDSPAN_CONDITIONAL_EXPLICIT((extents_type::rank() > 0)) + MDSPAN_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 + mapping(layout_stride::mapping<OtherExtents> const& other) noexcept // NOLINT(google-explicit-constructor) + :__extents(other.extents()) + { + /* + * TODO: check precondition + * other.required_span_size() is a representable value of type index_type + */ + detail::validate_strides(detail::with_rank<extents_type::rank()>{}, layout_left{}, __extents, other); + } + + MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED mapping& operator=(mapping const&) noexcept = default; + + MDSPAN_INLINE_FUNCTION + constexpr const extents_type& extents() const noexcept { + return __extents; + } + + MDSPAN_INLINE_FUNCTION + constexpr index_type required_span_size() const noexcept { + index_type value = 1; + for(rank_type r=0; r<extents_type::rank(); r++) value*=__extents.extent(r); + return value; + } + + //-------------------------------------------------------------------------------- + + MDSPAN_TEMPLATE_REQUIRES( + class... Indices, + /* requires */ ( + (sizeof...(Indices) == extents_type::rank()) && + (detail::are_valid_indices<index_type, Indices...>()) + ) + ) + _MDSPAN_HOST_DEVICE + constexpr index_type operator()(Indices... idxs) const noexcept { +#if ! defined(NDEBUG) + detail::check_all_indices(this->extents(), idxs...); +#endif // ! NDEBUG + return __compute_offset(__rank_count<0, extents_type::rank()>(), static_cast<index_type>(idxs)...); + } + + + + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_unique() noexcept { return true; } + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_exhaustive() noexcept { return true; } + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_strided() noexcept { return true; } + + MDSPAN_INLINE_FUNCTION static constexpr bool is_unique() noexcept { return true; } + MDSPAN_INLINE_FUNCTION static constexpr bool is_exhaustive() noexcept { return true; } + MDSPAN_INLINE_FUNCTION static constexpr bool is_strided() noexcept { return true; } + + MDSPAN_INLINE_FUNCTION + constexpr index_type stride(rank_type i) const noexcept +#if MDSPAN_HAS_CXX_20 + requires ( Extents::rank() > 0 ) +#endif + { + index_type value = 1; + for(rank_type r=0; r<i; r++) value*=__extents.extent(r); + return value; + } + + MDSPAN_TEMPLATE_REQUIRES( + class OtherExtents, + /* requires */ ( Extents::rank() == OtherExtents::rank()) + ) + MDSPAN_INLINE_FUNCTION + friend constexpr bool operator==(mapping const& lhs, mapping<OtherExtents> const& rhs) noexcept { + return lhs.extents() == rhs.extents(); + } + + // In C++ 20 the not equal exists if equal is found +#if !(MDSPAN_HAS_CXX_20) + MDSPAN_TEMPLATE_REQUIRES( + class OtherExtents, + /* requires */ ( Extents::rank() == OtherExtents::rank()) + ) + MDSPAN_INLINE_FUNCTION + friend constexpr bool operator!=(mapping const& lhs, mapping<OtherExtents> const& rhs) noexcept { + return lhs.extents() != rhs.extents(); + } +#endif + + // Not really public, but currently needed to implement fully constexpr useable submdspan: + template<size_t N, class SizeType, size_t ... E, size_t ... Idx> + constexpr index_type __get_stride(MDSPAN_IMPL_STANDARD_NAMESPACE::extents<SizeType, E...>,std::integer_sequence<size_t, Idx...>) const { + return _MDSPAN_FOLD_TIMES_RIGHT((Idx<N? __extents.template __extent<Idx>():1),1); + } + template<size_t N> + constexpr index_type __stride() const noexcept { + return __get_stride<N>(__extents, std::make_index_sequence<extents_type::rank()>()); + } + +private: + _MDSPAN_NO_UNIQUE_ADDRESS extents_type __extents{}; + + // [mdspan.submdspan.mapping], submdspan mapping specialization + template<class... SliceSpecifiers> + MDSPAN_INLINE_FUNCTION + constexpr auto submdspan_mapping_impl( + SliceSpecifiers... slices) const; + + template<class... SliceSpecifiers> + friend constexpr auto submdspan_mapping( + const mapping& src, SliceSpecifiers... slices) { + return src.submdspan_mapping_impl(slices...); + } +}; + + +} // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE + diff --git a/tests/integration/deps/mdspan/include/experimental/__p0009_bits/layout_right.hpp b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/layout_right.hpp new file mode 100644 index 0000000000000000000000000000000000000000..284569f6533251667d07a8b98de2feb01bbd9381 --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/layout_right.hpp @@ -0,0 +1,262 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER +#pragma once + +#include "macros.hpp" +#include "trait_backports.hpp" +#include "extents.hpp" +#include "layout_stride.hpp" +#include "utility.hpp" +#if MDSPAN_HAS_CXX_17 +#include "../__p2642_bits/layout_padded_fwd.hpp" +#endif + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { + +//============================================================================== +template <class Extents> +class layout_right::mapping { + public: + using extents_type = Extents; + using index_type = typename extents_type::index_type; + using size_type = typename extents_type::size_type; + using rank_type = typename extents_type::rank_type; + using layout_type = layout_right; + private: + + static_assert(detail::__is_extents_v<extents_type>, + MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::layout_right::mapping must be instantiated with a specialization of " MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::extents."); + + template <class> + friend class mapping; + + // i0+(i1 + E(1)*(i2 + E(2)*i3)) + template <size_t r, size_t Rank> + struct __rank_count {}; + + template <size_t r, size_t Rank, class I, class... Indices> + _MDSPAN_HOST_DEVICE + constexpr index_type __compute_offset( + index_type offset, __rank_count<r,Rank>, const I& i, Indices... idx) const { + return __compute_offset(offset * __extents.extent(r) + i,__rank_count<r+1,Rank>(), idx...); + } + + template<class I, class ... Indices> + _MDSPAN_HOST_DEVICE + constexpr index_type __compute_offset( + __rank_count<0,extents_type::rank()>, const I& i, Indices... idx) const { + return __compute_offset(i,__rank_count<1,extents_type::rank()>(),idx...); + } + + _MDSPAN_HOST_DEVICE + constexpr index_type __compute_offset(size_t offset, __rank_count<extents_type::rank(), extents_type::rank()>) const { + return static_cast<index_type>(offset); + } + + _MDSPAN_HOST_DEVICE + constexpr index_type __compute_offset(__rank_count<0,0>) const { return 0; } + + public: + + //-------------------------------------------------------------------------------- + + MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping() noexcept = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping(mapping const&) noexcept = default; + + _MDSPAN_HOST_DEVICE + constexpr mapping(extents_type const& __exts) noexcept + :__extents(__exts) + { } + + MDSPAN_TEMPLATE_REQUIRES( + class OtherExtents, + /* requires */ ( + _MDSPAN_TRAIT(std::is_constructible, extents_type, OtherExtents) + ) + ) + MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible<OtherExtents, extents_type>::value)) // needs two () due to comma + MDSPAN_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 + mapping(mapping<OtherExtents> const& other) noexcept // NOLINT(google-explicit-constructor) + :__extents(other.extents()) + { + /* + * TODO: check precondition + * other.required_span_size() is a representable value of type index_type + */ + } + + MDSPAN_TEMPLATE_REQUIRES( + class OtherExtents, + /* requires */ ( + _MDSPAN_TRAIT(std::is_constructible, extents_type, OtherExtents) && + (extents_type::rank() <= 1) + ) + ) + MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible<OtherExtents, extents_type>::value)) // needs two () due to comma + MDSPAN_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 + mapping(layout_left::mapping<OtherExtents> const& other) noexcept // NOLINT(google-explicit-constructor) + :__extents(other.extents()) + { + /* + * TODO: check precondition + * other.required_span_size() is a representable value of type index_type + */ + } + + /** + * Converting constructor from `layout_right_padded::mapping`. + * + * This overload participates in overload resolution only if _Mapping is a layout_right_padded mapping and + * extents_type is constructible from _Mapping::extents_type. + * + * \note There is currently a difference from p2642r2, where this function is specified as taking + * `layout_right_padded< padding_value >::mapping< Extents>`. However, this makes `padding_value` non-deducible. + */ +#if MDSPAN_HAS_CXX_17 + MDSPAN_TEMPLATE_REQUIRES( + class _Mapping, + /* requires */ ( + MDSPAN_IMPL_PROPOSED_NAMESPACE::detail::is_layout_right_padded_mapping<_Mapping>::value + && std::is_constructible_v<extents_type, typename _Mapping::extents_type>)) + MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible_v<typename _Mapping::extents_type, extents_type>)) + mapping(const _Mapping &__other) noexcept + : __extents(__other.extents()) + { + MDSPAN_IMPL_PROPOSED_NAMESPACE::detail:: + check_padded_layout_converting_constructor_mandates< + extents_type, _Mapping>(detail::with_rank<extents_type::rank()>{}); + MDSPAN_IMPL_PROPOSED_NAMESPACE::detail:: + check_padded_layout_converting_constructor_preconditions< + extents_type>(detail::with_rank<extents_type::rank()>{}, __other); + } +#endif + + MDSPAN_TEMPLATE_REQUIRES( + class OtherExtents, + /* requires */ ( + _MDSPAN_TRAIT(std::is_constructible, extents_type, OtherExtents) + ) + ) + MDSPAN_CONDITIONAL_EXPLICIT((extents_type::rank() > 0)) + MDSPAN_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 + mapping(layout_stride::mapping<OtherExtents> const& other) noexcept // NOLINT(google-explicit-constructor) + :__extents(other.extents()) + { + /* + * TODO: check precondition + * other.required_span_size() is a representable value of type index_type + */ + detail::validate_strides(detail::with_rank<extents_type::rank()>{}, layout_right{}, __extents, other); + } + + MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED mapping& operator=(mapping const&) noexcept = default; + + MDSPAN_INLINE_FUNCTION + constexpr const extents_type& extents() const noexcept { + return __extents; + } + + MDSPAN_INLINE_FUNCTION + constexpr index_type required_span_size() const noexcept { + index_type value = 1; + for(rank_type r=0; r != extents_type::rank(); ++r) value*=__extents.extent(r); + return value; + } + + //-------------------------------------------------------------------------------- + + MDSPAN_TEMPLATE_REQUIRES( + class ... Indices, + /* requires */ ( + (sizeof...(Indices) == extents_type::rank()) && + (detail::are_valid_indices<index_type, Indices...>()) + ) + ) + _MDSPAN_HOST_DEVICE + constexpr index_type operator()(Indices... idxs) const noexcept { +#if ! defined(NDEBUG) + detail::check_all_indices(this->extents(), idxs...); +#endif // ! NDEBUG + return __compute_offset(__rank_count<0, extents_type::rank()>(), static_cast<index_type>(idxs)...); + } + + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_unique() noexcept { return true; } + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_exhaustive() noexcept { return true; } + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_strided() noexcept { return true; } + MDSPAN_INLINE_FUNCTION static constexpr bool is_unique() noexcept { return true; } + MDSPAN_INLINE_FUNCTION static constexpr bool is_exhaustive() noexcept { return true; } + MDSPAN_INLINE_FUNCTION static constexpr bool is_strided() noexcept { return true; } + + MDSPAN_INLINE_FUNCTION + constexpr index_type stride(rank_type i) const noexcept +#if MDSPAN_HAS_CXX_20 + requires ( Extents::rank() > 0 ) +#endif + { + index_type value = 1; + for(rank_type r=extents_type::rank()-1; r>i; r--) value*=__extents.extent(r); + return value; + } + + MDSPAN_TEMPLATE_REQUIRES( + class OtherExtents, + /* requires */ ( Extents::rank() == OtherExtents::rank()) + ) + MDSPAN_INLINE_FUNCTION + friend constexpr bool operator==(mapping const& lhs, mapping<OtherExtents> const& rhs) noexcept { + return lhs.extents() == rhs.extents(); + } + + // In C++ 20 the not equal exists if equal is found +#if !(MDSPAN_HAS_CXX_20) + MDSPAN_TEMPLATE_REQUIRES( + class OtherExtents, + /* requires */ (Extents::rank() == OtherExtents::rank()) + ) + MDSPAN_INLINE_FUNCTION + friend constexpr bool operator!=(mapping const& lhs, mapping<OtherExtents> const& rhs) noexcept { + return lhs.extents() != rhs.extents(); + } +#endif + + // Not really public, but currently needed to implement fully constexpr useable submdspan: + template<size_t N, class SizeType, size_t ... E, size_t ... Idx> + constexpr index_type __get_stride(MDSPAN_IMPL_STANDARD_NAMESPACE::extents<SizeType, E...>,std::integer_sequence<size_t, Idx...>) const { + return _MDSPAN_FOLD_TIMES_RIGHT((Idx>N? __extents.template __extent<Idx>():1),1); + } + template<size_t N> + constexpr index_type __stride() const noexcept { + return __get_stride<N>(__extents, std::make_index_sequence<extents_type::rank()>()); + } + +private: + _MDSPAN_NO_UNIQUE_ADDRESS extents_type __extents{}; + + // [mdspan.submdspan.mapping], submdspan mapping specialization + template<class... SliceSpecifiers> + MDSPAN_INLINE_FUNCTION + constexpr auto submdspan_mapping_impl( + SliceSpecifiers... slices) const; + + template<class... SliceSpecifiers> + friend constexpr auto submdspan_mapping( + const mapping& src, SliceSpecifiers... slices) { + return src.submdspan_mapping_impl(slices...); + } +}; + +} // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE + diff --git a/tests/integration/deps/mdspan/include/experimental/__p0009_bits/layout_stride.hpp b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/layout_stride.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ea2cd3802fd942d30dd8293217cc5c0dc498b8b1 --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/layout_stride.hpp @@ -0,0 +1,667 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER +#pragma once + +#include "macros.hpp" +#include "extents.hpp" +#include "trait_backports.hpp" +#include "compressed_pair.hpp" +#include "utility.hpp" + +#if !defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) +# include "no_unique_address.hpp" +#endif + +#include <array> +#include <type_traits> +#include <utility> + +#ifdef __cpp_lib_span +#include <span> +#endif +#if defined(_MDSPAN_USE_CONCEPTS) && MDSPAN_HAS_CXX_20 && defined(__cpp_lib_concepts) +# include <concepts> +#endif + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { + +struct layout_left { + template<class Extents> + class mapping; +}; +struct layout_right { + template<class Extents> + class mapping; +}; + +namespace detail { + template<class Layout, class Mapping> + constexpr bool __is_mapping_of = + std::is_same<typename Layout::template mapping<typename Mapping::extents_type>, Mapping>::value; + +#if defined(_MDSPAN_USE_CONCEPTS) && MDSPAN_HAS_CXX_20 +# if !defined(__cpp_lib_concepts) + namespace internal { + namespace detail { + template <typename _Tp, typename _Up> + concept __same_as = std::is_same_v<_Tp, _Up>; + } // namespace detail + template <class T, class U> + concept __same_as = detail::__same_as<T, U> && detail::__same_as<U, T>; + } // namespace internal +# endif + + template<class M> + concept __layout_mapping_alike = requires { + requires __is_extents<typename M::extents_type>::value; +#if defined(__cpp_lib_concepts) + { M::is_always_strided() } -> std::same_as<bool>; + { M::is_always_exhaustive() } -> std::same_as<bool>; + { M::is_always_unique() } -> std::same_as<bool>; +#else + { M::is_always_strided() } -> internal::__same_as<bool>; + { M::is_always_exhaustive() } -> internal::__same_as<bool>; + { M::is_always_unique() } -> internal::__same_as<bool>; +#endif + std::bool_constant<M::is_always_strided()>::value; + std::bool_constant<M::is_always_exhaustive()>::value; + std::bool_constant<M::is_always_unique()>::value; + }; +#endif + +} // namespace detail + +struct layout_stride { + template <class Extents> + class mapping +#if !defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) + : private detail::__no_unique_address_emulation< + detail::__compressed_pair< + Extents, + detail::possibly_empty_array<typename Extents::index_type, Extents::rank()> + > + > +#endif + { + public: + using extents_type = Extents; + using index_type = typename extents_type::index_type; + using size_type = typename extents_type::size_type; + using rank_type = typename extents_type::rank_type; + using layout_type = layout_stride; + + // This could be a `requires`, but I think it's better and clearer as a `static_assert`. + static_assert(detail::__is_extents_v<Extents>, + MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::layout_stride::mapping must be instantiated with a specialization of " MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::extents."); + + + private: + + //---------------------------------------------------------------------------- + + using __strides_storage_t = detail::possibly_empty_array<index_type, extents_type::rank()>; + using __member_pair_t = detail::__compressed_pair<extents_type, __strides_storage_t>; + +#if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) + _MDSPAN_NO_UNIQUE_ADDRESS __member_pair_t __members; +#else + using __base_t = detail::__no_unique_address_emulation<__member_pair_t>; +#endif + + MDSPAN_FORCE_INLINE_FUNCTION constexpr __strides_storage_t const& + __strides_storage() const noexcept { +#if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) + return __members.__second(); +#else + return this->__base_t::__ref().__second(); +#endif + } + MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 __strides_storage_t& + __strides_storage() noexcept { +#if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) + return __members.__second(); +#else + return this->__base_t::__ref().__second(); +#endif + } + + template<class SizeType, size_t ... Ep, size_t ... Idx> + _MDSPAN_HOST_DEVICE + constexpr index_type __get_size(::MDSPAN_IMPL_STANDARD_NAMESPACE::extents<SizeType, Ep...>,std::integer_sequence<size_t, Idx...>) const { + return _MDSPAN_FOLD_TIMES_RIGHT( static_cast<index_type>(extents().extent(Idx)), 1 ); + } + + //---------------------------------------------------------------------------- + + template <class> + friend class mapping; + + //---------------------------------------------------------------------------- + + // Workaround for non-deducibility of the index sequence template parameter if it's given at the top level + template <class> + struct __deduction_workaround; + + template <size_t... Idxs> + struct __deduction_workaround<std::index_sequence<Idxs...>> + { + template <class OtherExtents> + MDSPAN_INLINE_FUNCTION + static constexpr bool _eq_impl(mapping const& self, mapping<OtherExtents> const& other) noexcept { + using common_t = std::common_type_t<index_type, typename OtherExtents::index_type>; + return _MDSPAN_FOLD_AND((static_cast<common_t>(self.stride(Idxs)) == static_cast<common_t>(other.stride(Idxs))) /* && ... */) + && _MDSPAN_FOLD_AND((static_cast<common_t>(self.extents().extent(Idxs)) == static_cast<common_t>(other.extents().extent(Idxs))) /* || ... */); + } + template <class OtherExtents> + MDSPAN_INLINE_FUNCTION + static constexpr bool _not_eq_impl(mapping const& self, mapping<OtherExtents> const& other) noexcept { + using common_t = std::common_type_t<index_type, typename OtherExtents::index_type>; + return _MDSPAN_FOLD_OR((static_cast<common_t>(self.stride(Idxs)) != static_cast<common_t>(other.stride(Idxs))) /* || ... */) + || _MDSPAN_FOLD_OR((static_cast<common_t>(self.extents().extent(Idxs)) != static_cast<common_t>(other.extents().extent(Idxs))) /* || ... */); + } + + template <class... Integral> + MDSPAN_FORCE_INLINE_FUNCTION + static constexpr size_t _call_op_impl(mapping const& self, Integral... idxs) noexcept { + return _MDSPAN_FOLD_PLUS_RIGHT((idxs * self.stride(Idxs)), /* + ... + */ 0); + } + + MDSPAN_INLINE_FUNCTION + static constexpr size_t _req_span_size_impl(mapping const& self) noexcept { + // assumes no negative strides; not sure if I'm allowed to assume that or not + return __impl::_call_op_impl(self, (self.extents().template __extent<Idxs>() - 1)...) + 1; + } + + template<class OtherMapping> + MDSPAN_INLINE_FUNCTION + static constexpr const __strides_storage_t fill_strides(const OtherMapping& map) { + return __strides_storage_t{static_cast<index_type>(map.stride(Idxs))...}; + } + + MDSPAN_INLINE_FUNCTION + static constexpr const __strides_storage_t& fill_strides(const __strides_storage_t& s) { + return s; + } + + template<class IntegralType> + MDSPAN_INLINE_FUNCTION + static constexpr const __strides_storage_t fill_strides(const std::array<IntegralType,extents_type::rank()>& s) { + return __strides_storage_t{static_cast<index_type>(s[Idxs])...}; + } + + MDSPAN_TEMPLATE_REQUIRES( + class IntegralType, + // The is_convertible condition is added to make sfinae valid + // the extents_type::rank() > 0 is added to avoid use of non-standard zero length c-array + (std::is_convertible<IntegralType, typename extents_type::index_type>::value && (extents_type::rank() > 0)) + ) + MDSPAN_INLINE_FUNCTION + // despite the requirement some compilers still complain about zero length array during parsing + // making it length 1 now, but since the thing can't be instantiated due to requirement the actual + // instantiation of strides_storage will not fail despite mismatching length + static constexpr const __strides_storage_t fill_strides(mdspan_non_standard_tag, const IntegralType (&s)[extents_type::rank()>0?extents_type::rank():1]) { + return __strides_storage_t{static_cast<index_type>(s[Idxs])...}; + } + +#ifdef __cpp_lib_span + template<class IntegralType> + MDSPAN_INLINE_FUNCTION + static constexpr const __strides_storage_t fill_strides(const std::span<IntegralType,extents_type::rank()>& s) { + return __strides_storage_t{static_cast<index_type>(s[Idxs])...}; + } +#endif + + MDSPAN_INLINE_FUNCTION + static constexpr std::array<index_type, extents_type::rank()> return_strides(const __strides_storage_t& s) { + return std::array<index_type, extents_type::rank()>{s[Idxs]...}; + } + + template<size_t K> + MDSPAN_INLINE_FUNCTION + static constexpr size_t __return_zero() { return 0; } + + template<class Mapping> + MDSPAN_INLINE_FUNCTION + static constexpr typename Mapping::index_type + __OFFSET(const Mapping& m) { return m(__return_zero<Idxs>()...); } + }; + + // Can't use defaulted parameter in the __deduction_workaround template because of a bug in MSVC warning C4348. + using __impl = __deduction_workaround<std::make_index_sequence<Extents::rank()>>; + + static constexpr __strides_storage_t strides_storage(detail::with_rank<0>) { + return {}; + } + template <std::size_t N> + static constexpr __strides_storage_t strides_storage(detail::with_rank<N>) { + __strides_storage_t s{}; + + extents_type e; + index_type stride = 1; + for(int r = static_cast<int>(extents_type::rank() - 1); r >= 0; r--) { + s[r] = stride; + stride *= e.extent(r); + } + + return s; + } + + //---------------------------------------------------------------------------- + +#if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) + MDSPAN_INLINE_FUNCTION constexpr explicit + mapping(__member_pair_t&& __m) : __members(::std::move(__m)) {} +#else + MDSPAN_INLINE_FUNCTION constexpr explicit + mapping(__base_t&& __b) : __base_t(::std::move(__b)) {} +#endif + + public: + + //-------------------------------------------------------------------------------- + + MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping() noexcept +#if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) + : __members{ +#else + : __base_t(__base_t{__member_pair_t( +#endif + extents_type(), + __strides_storage_t(strides_storage(detail::with_rank<extents_type::rank()>{})) +#if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) + } +#else + )}) +#endif + {} + + MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping(mapping const&) noexcept = default; + + MDSPAN_TEMPLATE_REQUIRES( + class IntegralTypes, + /* requires */ ( + // MSVC 19.32 does not like using index_type here, requires the typename Extents::index_type + // error C2641: cannot deduce template arguments for 'MDSPAN_IMPL_STANDARD_NAMESPACE::layout_stride::mapping' + _MDSPAN_TRAIT(std::is_convertible, const std::remove_const_t<IntegralTypes>&, typename Extents::index_type) && + _MDSPAN_TRAIT(std::is_nothrow_constructible, typename Extents::index_type, const std::remove_const_t<IntegralTypes>&) + ) + ) + MDSPAN_INLINE_FUNCTION + constexpr + mapping( + extents_type const& e, + std::array<IntegralTypes, extents_type::rank()> const& s + ) noexcept +#if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) + : __members{ +#else + : __base_t(__base_t{__member_pair_t( +#endif + e, __strides_storage_t(__impl::fill_strides(s)) +#if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) + } +#else + )}) +#endif + { + /* + * TODO: check preconditions + * - s[i] > 0 is true for all i in the range [0, rank_ ). + * - REQUIRED-SPAN-SIZE(e, s) is a representable value of type index_type ([basic.fundamental]). + * - If rank_ is greater than 0, then there exists a permutation P of the integers in the + * range [0, rank_), such that s[ pi ] >= s[ pi − 1 ] * e.extent( pi − 1 ) is true for + * all i in the range [1, rank_ ), where pi is the ith element of P. + */ + } + + MDSPAN_TEMPLATE_REQUIRES( + class IntegralTypes, + /* requires */ ( + // MSVC 19.32 does not like using index_type here, requires the typename Extents::index_type + // error C2641: cannot deduce template arguments for 'MDSPAN_IMPL_STANDARD_NAMESPACE::layout_stride::mapping' + _MDSPAN_TRAIT(std::is_convertible, const std::remove_const_t<IntegralTypes>&, typename Extents::index_type) && + _MDSPAN_TRAIT(std::is_nothrow_constructible, typename Extents::index_type, const std::remove_const_t<IntegralTypes>&) && + (Extents::rank() > 0) + ) + ) + MDSPAN_INLINE_FUNCTION + constexpr + mapping( + mdspan_non_standard_tag, + extents_type const& e, + // despite the requirement some compilers still complain about zero length array during parsing + // making it length 1 now, but since the thing can't be instantiated due to requirement the actual + // instantiation of strides_storage will not fail despite mismatching length + IntegralTypes (&s)[extents_type::rank()>0?extents_type::rank():1] + ) noexcept +#if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) + : __members{ +#else + : __base_t(__base_t{__member_pair_t( +#endif + e, __strides_storage_t(__impl::fill_strides(mdspan_non_standard, s)) +#if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) + } +#else + )}) +#endif + { + /* + * TODO: check preconditions + * - s[i] > 0 is true for all i in the range [0, rank_ ). + * - REQUIRED-SPAN-SIZE(e, s) is a representable value of type index_type ([basic.fundamental]). + * - If rank_ is greater than 0, then there exists a permutation P of the integers in the + * range [0, rank_), such that s[ pi ] >= s[ pi − 1 ] * e.extent( pi − 1 ) is true for + * all i in the range [1, rank_ ), where pi is the ith element of P. + */ + } + +#ifdef __cpp_lib_span + MDSPAN_TEMPLATE_REQUIRES( + class IntegralTypes, + /* requires */ ( + // MSVC 19.32 does not like using index_type here, requires the typename Extents::index_type + // error C2641: cannot deduce template arguments for 'MDSPAN_IMPL_STANDARD_NAMESPACE::layout_stride::mapping' + _MDSPAN_TRAIT(std::is_convertible, const std::remove_const_t<IntegralTypes>&, typename Extents::index_type) && + _MDSPAN_TRAIT(std::is_nothrow_constructible, typename Extents::index_type, const std::remove_const_t<IntegralTypes>&) + ) + ) + MDSPAN_INLINE_FUNCTION + constexpr + mapping( + extents_type const& e, + std::span<IntegralTypes, extents_type::rank()> const& s + ) noexcept +#if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) + : __members{ +#else + : __base_t(__base_t{__member_pair_t( +#endif + e, __strides_storage_t(__impl::fill_strides(s)) +#if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) + } +#else + )}) +#endif + { + /* + * TODO: check preconditions + * - s[i] > 0 is true for all i in the range [0, rank_ ). + * - REQUIRED-SPAN-SIZE(e, s) is a representable value of type index_type ([basic.fundamental]). + * - If rank_ is greater than 0, then there exists a permutation P of the integers in the + * range [0, rank_), such that s[ pi ] >= s[ pi − 1 ] * e.extent( pi − 1 ) is true for + * all i in the range [1, rank_ ), where pi is the ith element of P. + */ + } +#endif // __cpp_lib_span + +#if !(defined(_MDSPAN_USE_CONCEPTS) && MDSPAN_HAS_CXX_20) + MDSPAN_TEMPLATE_REQUIRES( + class StridedLayoutMapping, + /* requires */ ( + _MDSPAN_TRAIT(std::is_constructible, extents_type, typename StridedLayoutMapping::extents_type) && + detail::__is_mapping_of<typename StridedLayoutMapping::layout_type, StridedLayoutMapping> && + StridedLayoutMapping::is_always_unique() && + StridedLayoutMapping::is_always_strided() + ) + ) +#else + template<class StridedLayoutMapping> + requires( + detail::__layout_mapping_alike<StridedLayoutMapping> && + _MDSPAN_TRAIT(std::is_constructible, extents_type, typename StridedLayoutMapping::extents_type) && + StridedLayoutMapping::is_always_unique() && + StridedLayoutMapping::is_always_strided() + ) +#endif + MDSPAN_CONDITIONAL_EXPLICIT( + !(std::is_convertible<typename StridedLayoutMapping::extents_type, extents_type>::value && + (detail::__is_mapping_of<layout_left, StridedLayoutMapping> || + detail::__is_mapping_of<layout_right, StridedLayoutMapping> || + detail::__is_mapping_of<layout_stride, StridedLayoutMapping>)) + ) // needs two () due to comma + MDSPAN_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 + mapping(StridedLayoutMapping const& other) noexcept // NOLINT(google-explicit-constructor) +#if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) + : __members{ +#else + : __base_t(__base_t{__member_pair_t( +#endif + other.extents(), __strides_storage_t(__impl::fill_strides(other)) +#if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) + } +#else + )}) +#endif + { + /* + * TODO: check preconditions + * - other.stride(i) > 0 is true for all i in the range [0, rank_ ). + * - other.required_span_size() is a representable value of type index_type ([basic.fundamental]). + * - OFFSET(other) == 0 + */ + } + + //-------------------------------------------------------------------------------- + + MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED + mapping& operator=(mapping const&) noexcept = default; + + MDSPAN_INLINE_FUNCTION constexpr const extents_type& extents() const noexcept { +#if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) + return __members.__first(); +#else + return this->__base_t::__ref().__first(); +#endif + }; + + MDSPAN_INLINE_FUNCTION + constexpr std::array< index_type, extents_type::rank() > strides() const noexcept { + return __impl::return_strides(__strides_storage()); + } + + MDSPAN_INLINE_FUNCTION + constexpr index_type required_span_size() const noexcept { + index_type span_size = 1; + for(unsigned r = 0; r < extents_type::rank(); r++) { + // Return early if any of the extents are zero + if(extents().extent(r)==0) return 0; + span_size += ( static_cast<index_type>(extents().extent(r) - 1 ) * __strides_storage()[r]); + } + return span_size; + } + + + MDSPAN_TEMPLATE_REQUIRES( + class... Indices, + /* requires */ ( + sizeof...(Indices) == Extents::rank() && + (detail::are_valid_indices<index_type, Indices...>()) + ) + ) + MDSPAN_FORCE_INLINE_FUNCTION + constexpr index_type operator()(Indices... idxs) const noexcept { +#if ! defined(NDEBUG) + detail::check_all_indices(this->extents(), idxs...); +#endif // ! NDEBUG + return static_cast<index_type>(__impl::_call_op_impl(*this, static_cast<index_type>(idxs)...)); + } + + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_unique() noexcept { return true; } + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_exhaustive() noexcept { + return false; + } + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_strided() noexcept { return true; } + + MDSPAN_INLINE_FUNCTION static constexpr bool is_unique() noexcept { return true; } + + private: + constexpr bool exhaustive_for_nonzero_span_size() const + { + return required_span_size() == __get_size(extents(), std::make_index_sequence<extents_type::rank()>()); + } + + constexpr bool is_exhaustive_impl(detail::with_rank<0>) const + { + return true; + } + constexpr bool is_exhaustive_impl(detail::with_rank<1>) const + { + if (required_span_size() != static_cast<index_type>(0)) { + return exhaustive_for_nonzero_span_size(); + } + return stride(0) == 1; + } + template <std::size_t N> + constexpr bool is_exhaustive_impl(detail::with_rank<N>) const + { + if (required_span_size() != static_cast<index_type>(0)) { + return exhaustive_for_nonzero_span_size(); + } + + rank_type r_largest = 0; + for (rank_type r = 1; r < extents_type::rank(); r++) { + if (stride(r) > stride(r_largest)) { + r_largest = r; + } + } + for (rank_type r = 0; r < extents_type::rank(); r++) { + if (extents().extent(r) == 0 && r != r_largest) { + return false; + } + } + return true; + } + + public: + MDSPAN_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 bool is_exhaustive() const noexcept { + return is_exhaustive_impl(detail::with_rank<extents_type::rank()>{}); + } + MDSPAN_INLINE_FUNCTION static constexpr bool is_strided() noexcept { return true; } + + + MDSPAN_INLINE_FUNCTION + constexpr index_type stride(rank_type r) const noexcept { + return __strides_storage()[r]; + } + +#if !(defined(_MDSPAN_USE_CONCEPTS) && MDSPAN_HAS_CXX_20) + MDSPAN_TEMPLATE_REQUIRES( + class StridedLayoutMapping, + /* requires */ ( + detail::__is_mapping_of<typename StridedLayoutMapping::layout_type, StridedLayoutMapping> && + (extents_type::rank() == StridedLayoutMapping::extents_type::rank()) && + StridedLayoutMapping::is_always_strided() + ) + ) +#else + template<class StridedLayoutMapping> + requires( + detail::__layout_mapping_alike<StridedLayoutMapping> && + (extents_type::rank() == StridedLayoutMapping::extents_type::rank()) && + StridedLayoutMapping::is_always_strided() + ) +#endif + MDSPAN_INLINE_FUNCTION + friend constexpr bool operator==(const mapping& x, const StridedLayoutMapping& y) noexcept { + return (x.extents() == y.extents()) && + (__impl::__OFFSET(y) == static_cast<typename StridedLayoutMapping::index_type>(0)) && + detail::rankwise_equal(detail::with_rank<extents_type::rank()>{}, x, y, detail::stride); + } + + // This one is not technically part of the proposal. Just here to make implementation a bit more optimal hopefully + MDSPAN_TEMPLATE_REQUIRES( + class OtherExtents, + /* requires */ ( + (extents_type::rank() == OtherExtents::rank()) + ) + ) + MDSPAN_INLINE_FUNCTION + friend constexpr bool operator==(mapping const& lhs, mapping<OtherExtents> const& rhs) noexcept { + return __impl::_eq_impl(lhs, rhs); + } + +#if !MDSPAN_HAS_CXX_20 + MDSPAN_TEMPLATE_REQUIRES( + class StridedLayoutMapping, + /* requires */ ( + detail::__is_mapping_of<typename StridedLayoutMapping::layout_type, StridedLayoutMapping> && + (extents_type::rank() == StridedLayoutMapping::extents_type::rank()) && + StridedLayoutMapping::is_always_strided() + ) + ) + MDSPAN_INLINE_FUNCTION + friend constexpr bool operator!=(const mapping& x, const StridedLayoutMapping& y) noexcept { + return not (x == y); + } + + MDSPAN_TEMPLATE_REQUIRES( + class OtherExtents, + /* requires */ ( + (extents_type::rank() == OtherExtents::rank()) + ) + ) + MDSPAN_INLINE_FUNCTION + friend constexpr bool operator!=(mapping const& lhs, mapping<OtherExtents> const& rhs) noexcept { + return __impl::_not_eq_impl(lhs, rhs); + } +#endif + + // [mdspan.submdspan.mapping], submdspan mapping specialization + template<class... SliceSpecifiers> + MDSPAN_INLINE_FUNCTION + constexpr auto submdspan_mapping_impl( + SliceSpecifiers... slices) const; + + template<class... SliceSpecifiers> + friend constexpr auto submdspan_mapping( + const mapping& src, SliceSpecifiers... slices) { + return src.submdspan_mapping_impl(slices...); + } + }; +}; + +namespace detail { + +template <class Layout, class Extents, class Mapping> +constexpr void validate_strides(with_rank<0>, Layout, const Extents&, const Mapping&) +{} + +template <std::size_t N, class Layout, class Extents, class Mapping> +constexpr void validate_strides(with_rank<N>, Layout, const Extents& ext, const Mapping& other) +{ + static_assert(std::is_same<typename Mapping::layout_type, layout_stride>::value and + (std::is_same<Layout, layout_left>::value or + std::is_same<Layout, layout_right>::value) + , "This function is only intended to validate construction of " + "a layout_left or layout_right mapping from a layout_stride mapping."); + + constexpr auto is_left = std::is_same<Layout, layout_left>::value; + + typename Extents::index_type expected_stride = 1; + + for (std::size_t r = 0; r < N; r++) { + const std::size_t s = is_left ? r : N - 1 - r; + + MDSPAN_IMPL_PRECONDITION(common_integral_compare(expected_stride, other.stride(s)) + and "invalid strides for layout_{left,right}"); + + expected_stride *= ext.extent(s); + } +} + +} // namespace detail +} // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/tests/integration/deps/mdspan/include/experimental/__p0009_bits/macros.hpp b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/macros.hpp new file mode 100644 index 0000000000000000000000000000000000000000..30209a6648b6ef8cc4d32d7bb49411275bc24a8f --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/macros.hpp @@ -0,0 +1,699 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#pragma once + +#include "config.hpp" + +#include <cstdio> +#include <cstdlib> +#include <type_traits> // std::is_void +#if defined(_MDSPAN_HAS_CUDA) || defined(_MDSPAN_HAS_HIP) || defined(_MDSPAN_HAS_SYCL) +#include "assert.h" +#endif + +#ifndef _MDSPAN_HOST_DEVICE +# if defined(_MDSPAN_HAS_CUDA) || defined(_MDSPAN_HAS_HIP) +# define _MDSPAN_HOST_DEVICE __host__ __device__ +# else +# define _MDSPAN_HOST_DEVICE +# endif +#endif + +#ifndef MDSPAN_FORCE_INLINE_FUNCTION +# ifdef _MDSPAN_COMPILER_MSVC // Microsoft compilers +# define MDSPAN_FORCE_INLINE_FUNCTION __forceinline _MDSPAN_HOST_DEVICE +# else +# define MDSPAN_FORCE_INLINE_FUNCTION __attribute__((always_inline)) _MDSPAN_HOST_DEVICE +# endif +#endif + +#ifndef MDSPAN_INLINE_FUNCTION +# define MDSPAN_INLINE_FUNCTION inline _MDSPAN_HOST_DEVICE +#endif + +#ifndef MDSPAN_FUNCTION +# define MDSPAN_FUNCTION _MDSPAN_HOST_DEVICE +#endif + +#ifdef _MDSPAN_HAS_HIP +# define MDSPAN_DEDUCTION_GUIDE _MDSPAN_HOST_DEVICE +#else +# define MDSPAN_DEDUCTION_GUIDE +#endif + +// In CUDA defaulted functions do not need host device markup +#ifndef MDSPAN_INLINE_FUNCTION_DEFAULTED +# define MDSPAN_INLINE_FUNCTION_DEFAULTED +#endif + +//============================================================================== +// <editor-fold desc="Preprocessor helpers"> {{{1 + +#define MDSPAN_PP_COUNT(...) \ + _MDSPAN_PP_INTERNAL_EXPAND_ARGS_PRIVATE( \ + _MDSPAN_PP_INTERNAL_ARGS_AUGMENTER(__VA_ARGS__) \ + ) + +#define _MDSPAN_PP_INTERNAL_ARGS_AUGMENTER(...) unused, __VA_ARGS__ +#define _MDSPAN_PP_INTERNAL_EXPAND(x) x +#define _MDSPAN_PP_INTERNAL_EXPAND_ARGS_PRIVATE(...) \ + _MDSPAN_PP_INTERNAL_EXPAND( \ + _MDSPAN_PP_INTERNAL_COUNT_PRIVATE( \ + __VA_ARGS__, 69, 68, 67, 66, 65, 64, 63, 62, 61, \ + 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, \ + 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, \ + 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, \ + 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, \ + 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 \ + ) \ + ) +# define _MDSPAN_PP_INTERNAL_COUNT_PRIVATE( \ + _1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, _9_, \ + _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, \ + _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, \ + _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, \ + _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, \ + _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, \ + _70, count, ...) count \ + /**/ + +#define MDSPAN_PP_STRINGIFY_IMPL(x) #x +#define MDSPAN_PP_STRINGIFY(x) MDSPAN_PP_STRINGIFY_IMPL(x) + +#define MDSPAN_PP_CAT_IMPL(x, y) x ## y +#define MDSPAN_PP_CAT(x, y) MDSPAN_PP_CAT_IMPL(x, y) + +#define MDSPAN_PP_EVAL(X, ...) X(__VA_ARGS__) + +#define MDSPAN_PP_REMOVE_PARENS_IMPL(...) __VA_ARGS__ +#define MDSPAN_PP_REMOVE_PARENS(...) MDSPAN_PP_REMOVE_PARENS_IMPL __VA_ARGS__ + +#define MDSPAN_IMPL_STANDARD_NAMESPACE_STRING MDSPAN_PP_STRINGIFY(MDSPAN_IMPL_STANDARD_NAMESPACE) +#define MDSPAN_IMPL_PROPOSED_NAMESPACE_STRING MDSPAN_PP_STRINGIFY(MDSPAN_IMPL_STANDARD_NAMESPACE) "::" MDSPAN_PP_STRINGIFY(MDSPAN_IMPL_PROPOSED_NAMESPACE) + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace detail { + +#if defined(_MDSPAN_HAS_CUDA) || defined(_MDSPAN_HAS_HIP) +MDSPAN_FUNCTION inline void default_precondition_violation_handler(const char* cond, const char* file, unsigned line) +{ + printf("%s:%u: precondition failure: `%s`\n", file, line, cond); + assert(0); +} +#elif defined(_MDSPAN_HAS_SYCL) +MDSPAN_FUNCTION inline void default_precondition_violation_handler(const char* cond, const char* file, unsigned line) +{ + sycl::ext::oneapi::experimental::printf("%s:%u: precondition failure: `%s`\n", file, line, cond); + assert(0); +} +#else +MDSPAN_FUNCTION inline void default_precondition_violation_handler(const char* cond, const char* file, unsigned line) +{ + std::fprintf(stderr, "%s:%u: precondition failure: `%s`\n", file, line, cond); + std::abort(); +} +#endif + +} // namespace detail +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE + +#ifndef MDSPAN_IMPL_PRECONDITION_VIOLATION_HANDLER +#define MDSPAN_IMPL_PRECONDITION_VIOLATION_HANDLER(cond, file, line) \ + MDSPAN_IMPL_STANDARD_NAMESPACE::detail::default_precondition_violation_handler(cond, file, line) +#endif + +#ifndef MDSPAN_IMPL_CHECK_PRECONDITION + #ifndef NDEBUG + #define MDSPAN_IMPL_CHECK_PRECONDITION 0 + #else + #define MDSPAN_IMPL_CHECK_PRECONDITION 1 + #endif +#endif + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace detail { + +template <bool check = MDSPAN_IMPL_CHECK_PRECONDITION> +MDSPAN_FUNCTION constexpr void precondition(const char* cond, const char* file, unsigned line) +{ + if (not check) { return; } + // in case the macro doesn't use the arguments for custom macros + (void) cond; + (void) file; + (void) line; + MDSPAN_IMPL_PRECONDITION_VIOLATION_HANDLER(cond, file, line); +} + +} // namespace detail +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE + +#define MDSPAN_IMPL_PRECONDITION(...) \ + do { \ + if (not (__VA_ARGS__)) { \ + MDSPAN_IMPL_STANDARD_NAMESPACE::detail::precondition(#__VA_ARGS__, __FILE__, __LINE__); \ + } \ + } while (0) + +// </editor-fold> end Preprocessor helpers }}}1 +//============================================================================== + +//============================================================================== +// <editor-fold desc="Concept emulation"> {{{1 + +// These compatibility macros don't help with partial ordering, but they should do the trick +// for what we need to do with concepts in mdspan +#ifdef _MDSPAN_USE_CONCEPTS +# define MDSPAN_CLOSE_ANGLE_REQUIRES(REQ) > requires REQ +# define MDSPAN_FUNCTION_REQUIRES(PAREN_PREQUALS, FNAME, PAREN_PARAMS, QUALS, REQ) \ + MDSPAN_PP_REMOVE_PARENS(PAREN_PREQUALS) FNAME PAREN_PARAMS QUALS requires REQ \ + /**/ +#else +# define MDSPAN_CLOSE_ANGLE_REQUIRES(REQ) , typename ::std::enable_if<(REQ), int>::type = 0> +# define MDSPAN_FUNCTION_REQUIRES(PAREN_PREQUALS, FNAME, PAREN_PARAMS, QUALS, REQ) \ + MDSPAN_TEMPLATE_REQUIRES( \ + class __function_requires_ignored=void, \ + (std::is_void<__function_requires_ignored>::value && REQ) \ + ) MDSPAN_PP_REMOVE_PARENS(PAREN_PREQUALS) FNAME PAREN_PARAMS QUALS \ + /**/ +#endif + +#if defined(_MDSPAN_COMPILER_MSVC) && (!defined(_MSVC_TRADITIONAL) || _MSVC_TRADITIONAL) +# define MDSPAN_TEMPLATE_REQUIRES(...) \ + MDSPAN_PP_CAT( \ + MDSPAN_PP_CAT(MDSPAN_TEMPLATE_REQUIRES_, MDSPAN_PP_COUNT(__VA_ARGS__))\ + (__VA_ARGS__), \ + ) \ + /**/ +#else +# define MDSPAN_TEMPLATE_REQUIRES(...) \ + MDSPAN_PP_EVAL( \ + MDSPAN_PP_CAT(MDSPAN_TEMPLATE_REQUIRES_, MDSPAN_PP_COUNT(__VA_ARGS__)), \ + __VA_ARGS__ \ + ) \ + /**/ +#endif + +#define MDSPAN_TEMPLATE_REQUIRES_2(TP1, REQ) \ + template<TP1 \ + MDSPAN_CLOSE_ANGLE_REQUIRES(REQ) \ + /**/ +#define MDSPAN_TEMPLATE_REQUIRES_3(TP1, TP2, REQ) \ + template<TP1, TP2 \ + MDSPAN_CLOSE_ANGLE_REQUIRES(REQ) \ + /**/ +#define MDSPAN_TEMPLATE_REQUIRES_4(TP1, TP2, TP3, REQ) \ + template<TP1, TP2, TP3 \ + MDSPAN_CLOSE_ANGLE_REQUIRES(REQ) \ + /**/ +#define MDSPAN_TEMPLATE_REQUIRES_5(TP1, TP2, TP3, TP4, REQ) \ + template<TP1, TP2, TP3, TP4 \ + MDSPAN_CLOSE_ANGLE_REQUIRES(REQ) \ + /**/ +#define MDSPAN_TEMPLATE_REQUIRES_6(TP1, TP2, TP3, TP4, TP5, REQ) \ + template<TP1, TP2, TP3, TP4, TP5 \ + MDSPAN_CLOSE_ANGLE_REQUIRES(REQ) \ + /**/ +#define MDSPAN_TEMPLATE_REQUIRES_7(TP1, TP2, TP3, TP4, TP5, TP6, REQ) \ + template<TP1, TP2, TP3, TP4, TP5, TP6 \ + MDSPAN_CLOSE_ANGLE_REQUIRES(REQ) \ + /**/ +#define MDSPAN_TEMPLATE_REQUIRES_8(TP1, TP2, TP3, TP4, TP5, TP6, TP7, REQ) \ + template<TP1, TP2, TP3, TP4, TP5, TP6, TP7 \ + MDSPAN_CLOSE_ANGLE_REQUIRES(REQ) \ + /**/ +#define MDSPAN_TEMPLATE_REQUIRES_9(TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8, REQ) \ + template<TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8 \ + MDSPAN_CLOSE_ANGLE_REQUIRES(REQ) \ + /**/ +#define MDSPAN_TEMPLATE_REQUIRES_10(TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8, TP9, REQ) \ + template<TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8, TP9 \ + MDSPAN_CLOSE_ANGLE_REQUIRES(REQ) \ + /**/ +#define MDSPAN_TEMPLATE_REQUIRES_11(TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8, TP9, TP10, REQ) \ + template<TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8, TP9, TP10 \ + MDSPAN_CLOSE_ANGLE_REQUIRES(REQ) \ + /**/ +#define MDSPAN_TEMPLATE_REQUIRES_12(TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8, TP9, TP10, TP11, REQ) \ + template<TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8, TP9, TP10, TP11 \ + MDSPAN_CLOSE_ANGLE_REQUIRES(REQ) \ + /**/ +#define MDSPAN_TEMPLATE_REQUIRES_13(TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8, TP9, TP10, TP11, TP12, REQ) \ + template<TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8, TP9, TP10, TP11, TP12 \ + MDSPAN_CLOSE_ANGLE_REQUIRES(REQ) \ + /**/ +#define MDSPAN_TEMPLATE_REQUIRES_14(TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8, TP9, TP10, TP11, TP12, TP13, REQ) \ + template<TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8, TP9, TP10, TP11, TP12, TP13 \ + MDSPAN_CLOSE_ANGLE_REQUIRES(REQ) \ + /**/ +#define MDSPAN_TEMPLATE_REQUIRES_15(TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8, TP9, TP10, TP11, TP12, TP13, TP14, REQ) \ + template<TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8, TP9, TP10, TP11, TP12, TP13, TP14 \ + MDSPAN_CLOSE_ANGLE_REQUIRES(REQ) \ + /**/ +#define MDSPAN_TEMPLATE_REQUIRES_16(TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8, TP9, TP10, TP11, TP12, TP13, TP14, TP15, REQ) \ + template<TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8, TP9, TP10, TP11, TP12, TP13, TP14, TP15 \ + MDSPAN_CLOSE_ANGLE_REQUIRES(REQ) \ + /**/ +#define MDSPAN_TEMPLATE_REQUIRES_17(TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8, TP9, TP10, TP11, TP12, TP13, TP14, TP15, TP16, REQ) \ + template<TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8, TP9, TP10, TP11, TP12, TP13, TP14, TP15, TP16 \ + MDSPAN_CLOSE_ANGLE_REQUIRES(REQ) \ + /**/ +#define MDSPAN_TEMPLATE_REQUIRES_18(TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8, TP9, TP10, TP11, TP12, TP13, TP14, TP15, TP16, TP17, REQ) \ + template<TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8, TP9, TP10, TP11, TP12, TP13, TP14, TP15, TP16, TP17 \ + MDSPAN_CLOSE_ANGLE_REQUIRES(REQ) \ + /**/ +#define MDSPAN_TEMPLATE_REQUIRES_19(TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8, TP9, TP10, TP11, TP12, TP13, TP14, TP15, TP16, TP17, TP18, REQ) \ + template<TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8, TP9, TP10, TP11, TP12, TP13, TP14, TP15, TP16, TP17, TP18 \ + MDSPAN_CLOSE_ANGLE_REQUIRES(REQ) \ + /**/ +#define MDSPAN_TEMPLATE_REQUIRES_20(TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8, TP9, TP10, TP11, TP12, TP13, TP14, TP15, TP16, TP17, TP18, TP19, REQ) \ + template<TP1, TP2, TP3, TP4, TP5, TP6, TP7, TP8, TP9, TP10, TP11, TP12, TP13, TP14, TP15, TP16, TP17, TP18, TP19 \ + MDSPAN_CLOSE_ANGLE_REQUIRES(REQ) \ + /**/ + +#define MDSPAN_INSTANTIATE_ONLY_IF_USED \ + MDSPAN_TEMPLATE_REQUIRES( \ + class __instantiate_only_if_used_tparam=void, \ + ( _MDSPAN_TRAIT(std::is_void, __instantiate_only_if_used_tparam) ) \ + ) \ + /**/ + +// </editor-fold> end Concept emulation }}}1 +//============================================================================== + +//============================================================================== +// <editor-fold desc="inline variables"> {{{1 + +#ifdef _MDSPAN_USE_INLINE_VARIABLES +# define _MDSPAN_INLINE_VARIABLE inline +#else +# define _MDSPAN_INLINE_VARIABLE +#endif + +// </editor-fold> end inline variables }}}1 +//============================================================================== + +//============================================================================== +// <editor-fold desc="Return type deduction"> {{{1 + +#if _MDSPAN_USE_RETURN_TYPE_DEDUCTION +# define _MDSPAN_DEDUCE_RETURN_TYPE_SINGLE_LINE(SIGNATURE, BODY) \ + auto MDSPAN_PP_REMOVE_PARENS(SIGNATURE) { return MDSPAN_PP_REMOVE_PARENS(BODY); } +# define _MDSPAN_DEDUCE_DECLTYPE_AUTO_RETURN_TYPE_SINGLE_LINE(SIGNATURE, BODY) \ + decltype(auto) MDSPAN_PP_REMOVE_PARENS(SIGNATURE) { return MDSPAN_PP_REMOVE_PARENS(BODY); } +#else +# define _MDSPAN_DEDUCE_RETURN_TYPE_SINGLE_LINE(SIGNATURE, BODY) \ + auto MDSPAN_PP_REMOVE_PARENS(SIGNATURE) \ + -> std::remove_cv_t<std::remove_reference_t<decltype(BODY)>> \ + { return MDSPAN_PP_REMOVE_PARENS(BODY); } +# define _MDSPAN_DEDUCE_DECLTYPE_AUTO_RETURN_TYPE_SINGLE_LINE(SIGNATURE, BODY) \ + auto MDSPAN_PP_REMOVE_PARENS(SIGNATURE) \ + -> decltype(BODY) \ + { return MDSPAN_PP_REMOVE_PARENS(BODY); } + +#endif + +// </editor-fold> end Return type deduction }}}1 +//============================================================================== + +//============================================================================== +// <editor-fold desc="fold expressions"> {{{1 + +struct __mdspan_enable_fold_comma { }; + +#ifdef _MDSPAN_USE_FOLD_EXPRESSIONS +# define _MDSPAN_FOLD_AND(...) ((__VA_ARGS__) && ...) +# define _MDSPAN_FOLD_AND_TEMPLATE(...) ((__VA_ARGS__) && ...) +# define _MDSPAN_FOLD_OR(...) ((__VA_ARGS__) || ...) +# define _MDSPAN_FOLD_ASSIGN_LEFT(INIT, ...) (INIT = ... = (__VA_ARGS__)) +# define _MDSPAN_FOLD_ASSIGN_RIGHT(PACK, ...) (PACK = ... = (__VA_ARGS__)) +# define _MDSPAN_FOLD_TIMES_RIGHT(PACK, ...) (PACK * ... * (__VA_ARGS__)) +# define _MDSPAN_FOLD_PLUS_RIGHT(PACK, ...) (PACK + ... + (__VA_ARGS__)) +# define _MDSPAN_FOLD_COMMA(...) ((__VA_ARGS__), ...) +#else + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { + +namespace __fold_compatibility_impl { + +// We could probably be more clever here, but at the (small) risk of losing some compiler understanding. For the +// few operations we need, it's not worth generalizing over the operation + +#if _MDSPAN_USE_RETURN_TYPE_DEDUCTION + +MDSPAN_FORCE_INLINE_FUNCTION +constexpr decltype(auto) __fold_right_and_impl() { + return true; +} + +template <class Arg, class... Args> +MDSPAN_FORCE_INLINE_FUNCTION +constexpr decltype(auto) __fold_right_and_impl(Arg&& arg, Args&&... args) { + return ((Arg&&)arg) && __fold_compatibility_impl::__fold_right_and_impl((Args&&)args...); +} + +MDSPAN_FORCE_INLINE_FUNCTION +constexpr decltype(auto) __fold_right_or_impl() { + return false; +} + +template <class Arg, class... Args> +MDSPAN_FORCE_INLINE_FUNCTION +constexpr auto __fold_right_or_impl(Arg&& arg, Args&&... args) { + return ((Arg&&)arg) || __fold_compatibility_impl::__fold_right_or_impl((Args&&)args...); +} + +template <class Arg1> +MDSPAN_FORCE_INLINE_FUNCTION +constexpr auto __fold_left_assign_impl(Arg1&& arg1) { + return (Arg1&&)arg1; +} + +template <class Arg1, class Arg2, class... Args> +MDSPAN_FORCE_INLINE_FUNCTION +constexpr auto __fold_left_assign_impl(Arg1&& arg1, Arg2&& arg2, Args&&... args) { + return __fold_compatibility_impl::__fold_left_assign_impl((((Arg1&&)arg1) = ((Arg2&&)arg2)), (Args&&)args...); +} + +template <class Arg1> +MDSPAN_FORCE_INLINE_FUNCTION +constexpr auto __fold_right_assign_impl(Arg1&& arg1) { + return (Arg1&&)arg1; +} + +template <class Arg1, class Arg2, class... Args> +MDSPAN_FORCE_INLINE_FUNCTION +constexpr auto __fold_right_assign_impl(Arg1&& arg1, Arg2&& arg2, Args&&... args) { + return ((Arg1&&)arg1) = __fold_compatibility_impl::__fold_right_assign_impl((Arg2&&)arg2, (Args&&)args...); +} + +template <class Arg1> +MDSPAN_FORCE_INLINE_FUNCTION +constexpr auto __fold_right_plus_impl(Arg1&& arg1) { + return (Arg1&&)arg1; +} + +template <class Arg1, class Arg2, class... Args> +MDSPAN_FORCE_INLINE_FUNCTION +constexpr auto __fold_right_plus_impl(Arg1&& arg1, Arg2&& arg2, Args&&... args) { + return ((Arg1&&)arg1) + __fold_compatibility_impl::__fold_right_plus_impl((Arg2&&)arg2, (Args&&)args...); +} + +template <class Arg1> +MDSPAN_FORCE_INLINE_FUNCTION +constexpr auto __fold_right_times_impl(Arg1&& arg1) { + return (Arg1&&)arg1; +} + +template <class Arg1, class Arg2, class... Args> +MDSPAN_FORCE_INLINE_FUNCTION +constexpr auto __fold_right_times_impl(Arg1&& arg1, Arg2&& arg2, Args&&... args) { + return ((Arg1&&)arg1) * __fold_compatibility_impl::__fold_right_times_impl((Arg2&&)arg2, (Args&&)args...); +} + +#else + +//------------------------------------------------------------------------------ +// <editor-fold desc="right and"> {{{2 + +template <class... Args> +struct __fold_right_and_impl_; +template <> +struct __fold_right_and_impl_<> { + using __rv = bool; + MDSPAN_FORCE_INLINE_FUNCTION + static constexpr __rv + __impl() noexcept { + return true; + } +}; +template <class Arg, class... Args> +struct __fold_right_and_impl_<Arg, Args...> { + using __next_t = __fold_right_and_impl_<Args...>; + using __rv = decltype(std::declval<Arg>() && std::declval<typename __next_t::__rv>()); + MDSPAN_FORCE_INLINE_FUNCTION + static constexpr __rv + __impl(Arg&& arg, Args&&... args) noexcept { + return ((Arg&&)arg) && __next_t::__impl((Args&&)args...); + } +}; + +template <class... Args> +MDSPAN_FORCE_INLINE_FUNCTION +constexpr typename __fold_right_and_impl_<Args...>::__rv +__fold_right_and_impl(Args&&... args) { + return __fold_right_and_impl_<Args...>::__impl((Args&&)args...); +} + +// </editor-fold> end right and }}}2 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// <editor-fold desc="right or"> {{{2 + +template <class... Args> +struct __fold_right_or_impl_; +template <> +struct __fold_right_or_impl_<> { + using __rv = bool; + MDSPAN_FORCE_INLINE_FUNCTION + static constexpr __rv + __impl() noexcept { + return false; + } +}; +template <class Arg, class... Args> +struct __fold_right_or_impl_<Arg, Args...> { + using __next_t = __fold_right_or_impl_<Args...>; + using __rv = decltype(std::declval<Arg>() || std::declval<typename __next_t::__rv>()); + MDSPAN_FORCE_INLINE_FUNCTION + static constexpr __rv + __impl(Arg&& arg, Args&&... args) noexcept { + return ((Arg&&)arg) || __next_t::__impl((Args&&)args...); + } +}; + +template <class... Args> +MDSPAN_FORCE_INLINE_FUNCTION +constexpr typename __fold_right_or_impl_<Args...>::__rv +__fold_right_or_impl(Args&&... args) { + return __fold_right_or_impl_<Args...>::__impl((Args&&)args...); +} + +// </editor-fold> end right or }}}2 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// <editor-fold desc="right plus"> {{{2 + +template <class... Args> +struct __fold_right_plus_impl_; +template <class Arg> +struct __fold_right_plus_impl_<Arg> { + using __rv = Arg&&; + MDSPAN_FORCE_INLINE_FUNCTION + static constexpr __rv + __impl(Arg&& arg) noexcept { + return (Arg&&)arg; + } +}; +template <class Arg1, class Arg2, class... Args> +struct __fold_right_plus_impl_<Arg1, Arg2, Args...> { + using __next_t = __fold_right_plus_impl_<Arg2, Args...>; + using __rv = decltype(std::declval<Arg1>() + std::declval<typename __next_t::__rv>()); + MDSPAN_FORCE_INLINE_FUNCTION + static constexpr __rv + __impl(Arg1&& arg, Arg2&& arg2, Args&&... args) noexcept { + return ((Arg1&&)arg) + __next_t::__impl((Arg2&&)arg2, (Args&&)args...); + } +}; + +template <class... Args> +MDSPAN_FORCE_INLINE_FUNCTION +constexpr typename __fold_right_plus_impl_<Args...>::__rv +__fold_right_plus_impl(Args&&... args) { + return __fold_right_plus_impl_<Args...>::__impl((Args&&)args...); +} + +// </editor-fold> end right plus }}}2 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// <editor-fold desc="right times"> {{{2 + +template <class... Args> +struct __fold_right_times_impl_; +template <class Arg> +struct __fold_right_times_impl_<Arg> { + using __rv = Arg&&; + MDSPAN_FORCE_INLINE_FUNCTION + static constexpr __rv + __impl(Arg&& arg) noexcept { + return (Arg&&)arg; + } +}; +template <class Arg1, class Arg2, class... Args> +struct __fold_right_times_impl_<Arg1, Arg2, Args...> { + using __next_t = __fold_right_times_impl_<Arg2, Args...>; + using __rv = decltype(std::declval<Arg1>() * std::declval<typename __next_t::__rv>()); + MDSPAN_FORCE_INLINE_FUNCTION + static constexpr __rv + __impl(Arg1&& arg, Arg2&& arg2, Args&&... args) noexcept { + return ((Arg1&&)arg) * __next_t::__impl((Arg2&&)arg2, (Args&&)args...); + } +}; + +template <class... Args> +MDSPAN_FORCE_INLINE_FUNCTION +constexpr typename __fold_right_times_impl_<Args...>::__rv +__fold_right_times_impl(Args&&... args) { + return __fold_right_times_impl_<Args...>::__impl((Args&&)args...); +} + +// </editor-fold> end right times }}}2 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// <editor-fold desc="right assign"> {{{2 + +template <class... Args> +struct __fold_right_assign_impl_; +template <class Arg> +struct __fold_right_assign_impl_<Arg> { + using __rv = Arg&&; + MDSPAN_FORCE_INLINE_FUNCTION + static constexpr __rv + __impl(Arg&& arg) noexcept { + return (Arg&&)arg; + } +}; +template <class Arg1, class Arg2, class... Args> +struct __fold_right_assign_impl_<Arg1, Arg2, Args...> { + using __next_t = __fold_right_assign_impl_<Arg2, Args...>; + using __rv = decltype(std::declval<Arg1>() = std::declval<typename __next_t::__rv>()); + MDSPAN_FORCE_INLINE_FUNCTION + static constexpr __rv + __impl(Arg1&& arg, Arg2&& arg2, Args&&... args) noexcept { + return ((Arg1&&)arg) = __next_t::__impl((Arg2&&)arg2, (Args&&)args...); + } +}; + +template <class... Args> +MDSPAN_FORCE_INLINE_FUNCTION +constexpr typename __fold_right_assign_impl_<Args...>::__rv +__fold_right_assign_impl(Args&&... args) { + return __fold_right_assign_impl_<Args...>::__impl((Args&&)args...); +} + +// </editor-fold> end right assign }}}2 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// <editor-fold desc="left assign"> {{{2 + +template <class... Args> +struct __fold_left_assign_impl_; +template <class Arg> +struct __fold_left_assign_impl_<Arg> { + using __rv = Arg&&; + MDSPAN_FORCE_INLINE_FUNCTION + static constexpr __rv + __impl(Arg&& arg) noexcept { + return (Arg&&)arg; + } +}; +template <class Arg1, class Arg2, class... Args> +struct __fold_left_assign_impl_<Arg1, Arg2, Args...> { + using __assign_result_t = decltype(std::declval<Arg1>() = std::declval<Arg2>()); + using __next_t = __fold_left_assign_impl_<__assign_result_t, Args...>; + using __rv = typename __next_t::__rv; + MDSPAN_FORCE_INLINE_FUNCTION + static constexpr __rv + __impl(Arg1&& arg, Arg2&& arg2, Args&&... args) noexcept { + return __next_t::__impl(((Arg1&&)arg) = (Arg2&&)arg2, (Args&&)args...); + } +}; + +template <class... Args> +MDSPAN_FORCE_INLINE_FUNCTION +constexpr typename __fold_left_assign_impl_<Args...>::__rv +__fold_left_assign_impl(Args&&... args) { + return __fold_left_assign_impl_<Args...>::__impl((Args&&)args...); +} + +// </editor-fold> end left assign }}}2 +//------------------------------------------------------------------------------ + +#endif + + +template <class... Args> +constexpr __mdspan_enable_fold_comma __fold_comma_impl(Args&&... args) noexcept { return { }; } + +template <bool... Bs> +struct __bools; + +} // __fold_compatibility_impl + +} // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE + +# define _MDSPAN_FOLD_AND(...) MDSPAN_IMPL_STANDARD_NAMESPACE::__fold_compatibility_impl::__fold_right_and_impl((__VA_ARGS__)...) +# define _MDSPAN_FOLD_OR(...) MDSPAN_IMPL_STANDARD_NAMESPACE::__fold_compatibility_impl::__fold_right_or_impl((__VA_ARGS__)...) +# define _MDSPAN_FOLD_ASSIGN_LEFT(INIT, ...) MDSPAN_IMPL_STANDARD_NAMESPACE::__fold_compatibility_impl::__fold_left_assign_impl(INIT, (__VA_ARGS__)...) +# define _MDSPAN_FOLD_ASSIGN_RIGHT(PACK, ...) MDSPAN_IMPL_STANDARD_NAMESPACE::__fold_compatibility_impl::__fold_right_assign_impl((PACK)..., __VA_ARGS__) +# define _MDSPAN_FOLD_TIMES_RIGHT(PACK, ...) MDSPAN_IMPL_STANDARD_NAMESPACE::__fold_compatibility_impl::__fold_right_times_impl((PACK)..., __VA_ARGS__) +# define _MDSPAN_FOLD_PLUS_RIGHT(PACK, ...) MDSPAN_IMPL_STANDARD_NAMESPACE::__fold_compatibility_impl::__fold_right_plus_impl((PACK)..., __VA_ARGS__) +# define _MDSPAN_FOLD_COMMA(...) MDSPAN_IMPL_STANDARD_NAMESPACE::__fold_compatibility_impl::__fold_comma_impl((__VA_ARGS__)...) + +# define _MDSPAN_FOLD_AND_TEMPLATE(...) \ + _MDSPAN_TRAIT(std::is_same, __fold_compatibility_impl::__bools<(__VA_ARGS__)..., true>, __fold_compatibility_impl::__bools<true, (__VA_ARGS__)...>) + +#endif + +// </editor-fold> end fold expressions }}}1 +//============================================================================== + +//============================================================================== +// <editor-fold desc="Variable template compatibility"> {{{1 + +#if _MDSPAN_USE_VARIABLE_TEMPLATES +# define _MDSPAN_TRAIT(TRAIT, ...) TRAIT##_v<__VA_ARGS__> +#else +# define _MDSPAN_TRAIT(TRAIT, ...) TRAIT<__VA_ARGS__>::value +#endif + +// </editor-fold> end Variable template compatibility }}}1 +//============================================================================== + +//============================================================================== +// <editor-fold desc="Pre-C++14 constexpr"> {{{1 + +#if _MDSPAN_USE_CONSTEXPR_14 +# define _MDSPAN_CONSTEXPR_14 constexpr +// Workaround for a bug (I think?) in EDG frontends +# ifdef __EDG__ +# define _MDSPAN_CONSTEXPR_14_DEFAULTED +# else +# define _MDSPAN_CONSTEXPR_14_DEFAULTED constexpr +# endif +#else +# define _MDSPAN_CONSTEXPR_14 +# define _MDSPAN_CONSTEXPR_14_DEFAULTED +#endif + +// </editor-fold> end Pre-C++14 constexpr }}}1 +//============================================================================== diff --git a/tests/integration/deps/mdspan/include/experimental/__p0009_bits/mdspan.hpp b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/mdspan.hpp new file mode 100644 index 0000000000000000000000000000000000000000..af4848248d64ced5f5ef0d383561db73a3ee7579 --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/mdspan.hpp @@ -0,0 +1,432 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#pragma once + +#include "default_accessor.hpp" +#include "layout_right.hpp" +#include "extents.hpp" +#include "trait_backports.hpp" +#include "compressed_pair.hpp" + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +template < + class ElementType, + class Extents, + class LayoutPolicy = layout_right, + class AccessorPolicy = default_accessor<ElementType> +> +class mdspan +{ +private: + static_assert(detail::__is_extents_v<Extents>, + MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::mdspan's Extents template parameter must be a specialization of " MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::extents."); + static_assert(std::is_same<ElementType, typename AccessorPolicy::element_type>::value, + MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::mdspan's ElementType template parameter must be the same as its AccessorPolicy::element_type."); + + // Workaround for non-deducibility of the index sequence template parameter if it's given at the top level + template <class> + struct __deduction_workaround; + + template <size_t... Idxs> + struct __deduction_workaround<std::index_sequence<Idxs...>> + { + MDSPAN_FORCE_INLINE_FUNCTION static constexpr + size_t __size(mdspan const& __self) noexcept { + return _MDSPAN_FOLD_TIMES_RIGHT((__self.__mapping_ref().extents().extent(Idxs)), /* * ... * */ size_t(1)); + } + MDSPAN_FORCE_INLINE_FUNCTION static constexpr + bool __empty(mdspan const& __self) noexcept { + return (__self.rank()>0) && _MDSPAN_FOLD_OR((__self.__mapping_ref().extents().extent(Idxs)==index_type(0))); + } + template <class ReferenceType, class SizeType, size_t N> + MDSPAN_FORCE_INLINE_FUNCTION static constexpr + ReferenceType __callop(mdspan const& __self, const std::array<SizeType, N>& indices) noexcept { + return __self.__accessor_ref().access(__self.__ptr_ref(), __self.__mapping_ref()(indices[Idxs]...)); + } +#ifdef __cpp_lib_span + template <class ReferenceType, class SizeType, size_t N> + MDSPAN_FORCE_INLINE_FUNCTION static constexpr + ReferenceType __callop(mdspan const& __self, const std::span<SizeType, N>& indices) noexcept { + return __self.__accessor_ref().access(__self.__ptr_ref(), __self.__mapping_ref()(indices[Idxs]...)); + } +#endif + }; + +public: + + //-------------------------------------------------------------------------------- + // Domain and codomain types + + using extents_type = Extents; + using layout_type = LayoutPolicy; + using accessor_type = AccessorPolicy; + using mapping_type = typename layout_type::template mapping<extents_type>; + using element_type = ElementType; + using value_type = std::remove_cv_t<element_type>; + using index_type = typename extents_type::index_type; + using size_type = typename extents_type::size_type; + using rank_type = typename extents_type::rank_type; + using data_handle_type = typename accessor_type::data_handle_type; + using reference = typename accessor_type::reference; + + MDSPAN_INLINE_FUNCTION static constexpr size_t rank() noexcept { return extents_type::rank(); } + MDSPAN_INLINE_FUNCTION static constexpr size_t rank_dynamic() noexcept { return extents_type::rank_dynamic(); } + MDSPAN_INLINE_FUNCTION static constexpr size_t static_extent(size_t r) noexcept { return extents_type::static_extent(r); } + MDSPAN_INLINE_FUNCTION constexpr index_type extent(size_t r) const noexcept { return __mapping_ref().extents().extent(r); }; + +private: + + // Can't use defaulted parameter in the __deduction_workaround template because of a bug in MSVC warning C4348. + using __impl = __deduction_workaround<std::make_index_sequence<extents_type::rank()>>; + + using __map_acc_pair_t = detail::__compressed_pair<mapping_type, accessor_type>; + +public: + + //-------------------------------------------------------------------------------- + // [mdspan.basic.cons], mdspan constructors, assignment, and destructor + +#if !MDSPAN_HAS_CXX_20 + MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mdspan() = default; +#else + MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mdspan() + requires( + // nvhpc has a bug where using just rank_dynamic() here doesn't work ... + (extents_type::rank_dynamic() > 0) && + _MDSPAN_TRAIT(std::is_default_constructible, data_handle_type) && + _MDSPAN_TRAIT(std::is_default_constructible, mapping_type) && + _MDSPAN_TRAIT(std::is_default_constructible, accessor_type) + ) = default; +#endif + MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mdspan(const mdspan&) = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mdspan(mdspan&&) = default; + + MDSPAN_TEMPLATE_REQUIRES( + class... SizeTypes, + /* requires */ ( + ((sizeof...(SizeTypes) == rank()) || (sizeof...(SizeTypes) == rank_dynamic())) && + (detail::are_valid_indices<index_type, SizeTypes...>()) && + _MDSPAN_TRAIT(std::is_constructible, mapping_type, extents_type) && + _MDSPAN_TRAIT(std::is_default_constructible, accessor_type) + ) + ) + MDSPAN_INLINE_FUNCTION + explicit constexpr mdspan(data_handle_type p, SizeTypes... dynamic_extents) + // TODO @proposal-bug shouldn't I be allowed to do `move(p)` here? + : __members(std::move(p), __map_acc_pair_t(mapping_type(extents_type(static_cast<index_type>(std::move(dynamic_extents))...)), accessor_type())) + { } + + MDSPAN_TEMPLATE_REQUIRES( + class SizeType, size_t N, + /* requires */ ( + _MDSPAN_TRAIT(std::is_convertible, const SizeType&, index_type) && + _MDSPAN_TRAIT(std::is_nothrow_constructible, index_type, const SizeType&) && + ((N == rank()) || (N == rank_dynamic())) && + _MDSPAN_TRAIT(std::is_constructible, mapping_type, extents_type) && + _MDSPAN_TRAIT(std::is_default_constructible, accessor_type) + ) + ) + MDSPAN_CONDITIONAL_EXPLICIT(N != rank_dynamic()) + MDSPAN_INLINE_FUNCTION + constexpr mdspan(data_handle_type p, const std::array<SizeType, N>& dynamic_extents) + : __members(std::move(p), __map_acc_pair_t(mapping_type(extents_type(dynamic_extents)), accessor_type())) + { } + +#ifdef __cpp_lib_span + MDSPAN_TEMPLATE_REQUIRES( + class SizeType, size_t N, + /* requires */ ( + _MDSPAN_TRAIT(std::is_convertible, const SizeType&, index_type) && + _MDSPAN_TRAIT(std::is_nothrow_constructible, index_type, const SizeType&) && + ((N == rank()) || (N == rank_dynamic())) && + _MDSPAN_TRAIT(std::is_constructible, mapping_type, extents_type) && + _MDSPAN_TRAIT(std::is_default_constructible, accessor_type) + ) + ) + MDSPAN_CONDITIONAL_EXPLICIT(N != rank_dynamic()) + MDSPAN_INLINE_FUNCTION + constexpr mdspan(data_handle_type p, std::span<SizeType, N> dynamic_extents) + : __members(std::move(p), __map_acc_pair_t(mapping_type(extents_type(as_const(dynamic_extents))), accessor_type())) + { } +#endif + + MDSPAN_FUNCTION_REQUIRES( + (MDSPAN_INLINE_FUNCTION constexpr), + mdspan, (data_handle_type p, const extents_type& exts), , + /* requires */ (_MDSPAN_TRAIT(std::is_default_constructible, accessor_type) && + _MDSPAN_TRAIT(std::is_constructible, mapping_type, const extents_type&)) + ) : __members(std::move(p), __map_acc_pair_t(mapping_type(exts), accessor_type())) + { } + + MDSPAN_FUNCTION_REQUIRES( + (MDSPAN_INLINE_FUNCTION constexpr), + mdspan, (data_handle_type p, const mapping_type& m), , + /* requires */ (_MDSPAN_TRAIT(std::is_default_constructible, accessor_type)) + ) : __members(std::move(p), __map_acc_pair_t(m, accessor_type())) + { } + + MDSPAN_INLINE_FUNCTION + constexpr mdspan(data_handle_type p, const mapping_type& m, const accessor_type& a) + : __members(std::move(p), __map_acc_pair_t(m, a)) + { } + + MDSPAN_TEMPLATE_REQUIRES( + class OtherElementType, class OtherExtents, class OtherLayoutPolicy, class OtherAccessor, + /* requires */ ( + _MDSPAN_TRAIT(std::is_constructible, mapping_type, const typename OtherLayoutPolicy::template mapping<OtherExtents>&) && + _MDSPAN_TRAIT(std::is_constructible, accessor_type, const OtherAccessor&) + ) + ) + MDSPAN_CONDITIONAL_EXPLICIT( + !_MDSPAN_TRAIT(std::is_convertible, const typename OtherLayoutPolicy::template mapping<OtherExtents>&, mapping_type) || + !_MDSPAN_TRAIT(std::is_convertible, const OtherAccessor&, accessor_type) + ) + MDSPAN_INLINE_FUNCTION + constexpr mdspan(const mdspan<OtherElementType, OtherExtents, OtherLayoutPolicy, OtherAccessor>& other) + : __members(other.__ptr_ref(), __map_acc_pair_t(other.__mapping_ref(), other.__accessor_ref())) + { + static_assert(_MDSPAN_TRAIT(std::is_constructible, data_handle_type, typename OtherAccessor::data_handle_type),"Incompatible data_handle_type for mdspan construction"); + static_assert(_MDSPAN_TRAIT(std::is_constructible, extents_type, OtherExtents),"Incompatible extents for mdspan construction"); + /* + * TODO: Check precondition + * For each rank index r of extents_type, static_extent(r) == dynamic_extent || static_extent(r) == other.extent(r) is true. + */ + } + + /* Might need this on NVIDIA? + MDSPAN_INLINE_FUNCTION_DEFAULTED + ~mdspan() = default; + */ + + MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED mdspan& operator=(const mdspan&) = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED mdspan& operator=(mdspan&&) = default; + + + //-------------------------------------------------------------------------------- + // [mdspan.basic.mapping], mdspan mapping domain multidimensional index to access codomain element + + #if MDSPAN_USE_BRACKET_OPERATOR + MDSPAN_TEMPLATE_REQUIRES( + class... SizeTypes, + /* requires */ ( + _MDSPAN_FOLD_AND(_MDSPAN_TRAIT(std::is_convertible, SizeTypes, index_type) /* && ... */) && + _MDSPAN_FOLD_AND(_MDSPAN_TRAIT(std::is_nothrow_constructible, index_type, SizeTypes) /* && ... */) && + (rank() == sizeof...(SizeTypes)) + ) + ) + MDSPAN_FORCE_INLINE_FUNCTION + constexpr reference operator[](SizeTypes... indices) const + { + return __accessor_ref().access(__ptr_ref(), __mapping_ref()(static_cast<index_type>(std::move(indices))...)); + } + #endif + + MDSPAN_TEMPLATE_REQUIRES( + class SizeType, + /* requires */ ( + _MDSPAN_TRAIT(std::is_convertible, const SizeType&, index_type) && + _MDSPAN_TRAIT(std::is_nothrow_constructible, index_type, const SizeType&) + ) + ) + MDSPAN_FORCE_INLINE_FUNCTION + constexpr reference operator[](const std::array< SizeType, rank()>& indices) const + { + return __impl::template __callop<reference>(*this, indices); + } + + #ifdef __cpp_lib_span + MDSPAN_TEMPLATE_REQUIRES( + class SizeType, + /* requires */ ( + _MDSPAN_TRAIT(std::is_convertible, const SizeType&, index_type) && + _MDSPAN_TRAIT(std::is_nothrow_constructible, index_type, const SizeType&) + ) + ) + MDSPAN_FORCE_INLINE_FUNCTION + constexpr reference operator[](std::span<SizeType, rank()> indices) const + { + return __impl::template __callop<reference>(*this, indices); + } + #endif // __cpp_lib_span + + #if !MDSPAN_USE_BRACKET_OPERATOR + MDSPAN_TEMPLATE_REQUIRES( + class Index, + /* requires */ ( + _MDSPAN_TRAIT(std::is_convertible, Index, index_type) && + _MDSPAN_TRAIT(std::is_nothrow_constructible, index_type, Index) && + extents_type::rank() == 1 + ) + ) + MDSPAN_FORCE_INLINE_FUNCTION + constexpr reference operator[](Index idx) const + { + return __accessor_ref().access(__ptr_ref(), __mapping_ref()(static_cast<index_type>(std::move(idx)))); + } + #endif + + #if MDSPAN_USE_PAREN_OPERATOR + MDSPAN_TEMPLATE_REQUIRES( + class... SizeTypes, + /* requires */ ( + extents_type::rank() == sizeof...(SizeTypes) && + (detail::are_valid_indices<index_type, SizeTypes...>()) + ) + ) + MDSPAN_FORCE_INLINE_FUNCTION + constexpr reference operator()(SizeTypes... indices) const + { + return __accessor_ref().access(__ptr_ref(), __mapping_ref()(static_cast<index_type>(std::move(indices))...)); + } + + MDSPAN_TEMPLATE_REQUIRES( + class SizeType, + /* requires */ ( + _MDSPAN_TRAIT(std::is_convertible, const SizeType&, index_type) && + _MDSPAN_TRAIT(std::is_nothrow_constructible, index_type, const SizeType&) + ) + ) + MDSPAN_FORCE_INLINE_FUNCTION + constexpr reference operator()(const std::array<SizeType, rank()>& indices) const + { + return __impl::template __callop<reference>(*this, indices); + } + + #ifdef __cpp_lib_span + MDSPAN_TEMPLATE_REQUIRES( + class SizeType, + /* requires */ ( + _MDSPAN_TRAIT(std::is_convertible, const SizeType&, index_type) && + _MDSPAN_TRAIT(std::is_nothrow_constructible, index_type, const SizeType&) + ) + ) + MDSPAN_FORCE_INLINE_FUNCTION + constexpr reference operator()(std::span<SizeType, rank()> indices) const + { + return __impl::template __callop<reference>(*this, indices); + } + #endif // __cpp_lib_span + #endif // MDSPAN_USE_PAREN_OPERATOR + + MDSPAN_INLINE_FUNCTION constexpr size_type size() const noexcept { + return __impl::__size(*this); + }; + + MDSPAN_INLINE_FUNCTION constexpr bool empty() const noexcept { + return __impl::__empty(*this); + }; + + MDSPAN_INLINE_FUNCTION + friend constexpr void swap(mdspan& x, mdspan& y) noexcept { + // can't call the std::swap inside on HIP + #if !defined(_MDSPAN_HAS_HIP) && !defined(_MDSPAN_HAS_CUDA) + using std::swap; + swap(x.__ptr_ref(), y.__ptr_ref()); + swap(x.__mapping_ref(), y.__mapping_ref()); + swap(x.__accessor_ref(), y.__accessor_ref()); + #else + mdspan tmp = y; + y = x; + x = tmp; + #endif + } + + //-------------------------------------------------------------------------------- + // [mdspan.basic.domobs], mdspan observers of the domain multidimensional index space + + + MDSPAN_INLINE_FUNCTION constexpr const extents_type& extents() const noexcept { return __mapping_ref().extents(); }; + MDSPAN_INLINE_FUNCTION constexpr const data_handle_type& data_handle() const noexcept { return __ptr_ref(); }; + MDSPAN_INLINE_FUNCTION constexpr const mapping_type& mapping() const noexcept { return __mapping_ref(); }; + MDSPAN_INLINE_FUNCTION constexpr const accessor_type& accessor() const noexcept { return __accessor_ref(); }; + + //-------------------------------------------------------------------------------- + // [mdspan.basic.obs], mdspan observers of the mapping + + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_unique() { return mapping_type::is_always_unique(); }; + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_exhaustive() { return mapping_type::is_always_exhaustive(); }; + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_strided() { return mapping_type::is_always_strided(); }; + + MDSPAN_INLINE_FUNCTION constexpr bool is_unique() const { return __mapping_ref().is_unique(); }; + MDSPAN_INLINE_FUNCTION constexpr bool is_exhaustive() const { return __mapping_ref().is_exhaustive(); }; + MDSPAN_INLINE_FUNCTION constexpr bool is_strided() const { return __mapping_ref().is_strided(); }; + MDSPAN_INLINE_FUNCTION constexpr index_type stride(size_t r) const { return __mapping_ref().stride(r); }; + +private: + + detail::__compressed_pair<data_handle_type, __map_acc_pair_t> __members{}; + + MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 data_handle_type& __ptr_ref() noexcept { return __members.__first(); } + MDSPAN_FORCE_INLINE_FUNCTION constexpr data_handle_type const& __ptr_ref() const noexcept { return __members.__first(); } + MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 mapping_type& __mapping_ref() noexcept { return __members.__second().__first(); } + MDSPAN_FORCE_INLINE_FUNCTION constexpr mapping_type const& __mapping_ref() const noexcept { return __members.__second().__first(); } + MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 accessor_type& __accessor_ref() noexcept { return __members.__second().__second(); } + MDSPAN_FORCE_INLINE_FUNCTION constexpr accessor_type const& __accessor_ref() const noexcept { return __members.__second().__second(); } + + template <class, class, class, class> + friend class mdspan; + +}; + +#if defined(_MDSPAN_USE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION) +MDSPAN_TEMPLATE_REQUIRES( + class ElementType, class... SizeTypes, + /* requires */ _MDSPAN_FOLD_AND(_MDSPAN_TRAIT(std::is_convertible, SizeTypes, size_t) /* && ... */) && + (sizeof...(SizeTypes) > 0) +) +MDSPAN_DEDUCTION_GUIDE explicit mdspan(ElementType*, SizeTypes...) + -> mdspan<ElementType, ::MDSPAN_IMPL_STANDARD_NAMESPACE::dextents<size_t, sizeof...(SizeTypes)>>; + +MDSPAN_TEMPLATE_REQUIRES( + class Pointer, + (_MDSPAN_TRAIT(std::is_pointer, std::remove_reference_t<Pointer>)) +) +MDSPAN_DEDUCTION_GUIDE mdspan(Pointer&&) -> mdspan<std::remove_pointer_t<std::remove_reference_t<Pointer>>, extents<size_t>>; + +MDSPAN_TEMPLATE_REQUIRES( + class CArray, + (_MDSPAN_TRAIT(std::is_array, CArray) && (std::rank_v<CArray> == 1)) +) +MDSPAN_DEDUCTION_GUIDE mdspan(CArray&) -> mdspan<std::remove_all_extents_t<CArray>, extents<size_t, ::std::extent_v<CArray,0>>>; + +template <class ElementType, class SizeType, size_t N> +MDSPAN_DEDUCTION_GUIDE mdspan(ElementType*, const ::std::array<SizeType, N>&) + -> mdspan<ElementType, ::MDSPAN_IMPL_STANDARD_NAMESPACE::dextents<size_t, N>>; + +#ifdef __cpp_lib_span +template <class ElementType, class SizeType, size_t N> +MDSPAN_DEDUCTION_GUIDE mdspan(ElementType*, ::std::span<SizeType, N>) + -> mdspan<ElementType, ::MDSPAN_IMPL_STANDARD_NAMESPACE::dextents<size_t, N>>; +#endif + +// This one is necessary because all the constructors take `data_handle_type`s, not +// `ElementType*`s, and `data_handle_type` is taken from `accessor_type::data_handle_type`, which +// seems to throw off automatic deduction guides. +template <class ElementType, class SizeType, size_t... ExtentsPack> +MDSPAN_DEDUCTION_GUIDE mdspan(ElementType*, const extents<SizeType, ExtentsPack...>&) + -> mdspan<ElementType, ::MDSPAN_IMPL_STANDARD_NAMESPACE::extents<SizeType, ExtentsPack...>>; + +template <class ElementType, class MappingType> +MDSPAN_DEDUCTION_GUIDE mdspan(ElementType*, const MappingType&) + -> mdspan<ElementType, typename MappingType::extents_type, typename MappingType::layout_type>; + +template <class MappingType, class AccessorType> +MDSPAN_DEDUCTION_GUIDE mdspan(const typename AccessorType::data_handle_type, const MappingType&, const AccessorType&) + -> mdspan<typename AccessorType::element_type, typename MappingType::extents_type, typename MappingType::layout_type, AccessorType>; +#endif + +} // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/tests/integration/deps/mdspan/include/experimental/__p0009_bits/no_unique_address.hpp b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/no_unique_address.hpp new file mode 100644 index 0000000000000000000000000000000000000000..36e64ee24dbb7166ac5da069bbbfbc4347026993 --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/no_unique_address.hpp @@ -0,0 +1,97 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER +#pragma once + +#include "macros.hpp" +#include "trait_backports.hpp" + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace detail { + +//============================================================================== + +template <class _T, size_t _Disambiguator = 0, class _Enable = void> +struct __no_unique_address_emulation { + using __stored_type = _T; + _T __v; + MDSPAN_FORCE_INLINE_FUNCTION constexpr _T const &__ref() const noexcept { + return __v; + } + MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 _T &__ref() noexcept { + return __v; + } +}; + +// Empty case +// This doesn't work if _T is final, of course, but we're not using anything +// like that currently. That kind of thing could be added pretty easily though +template <class _T, size_t _Disambiguator> +struct __no_unique_address_emulation< + _T, _Disambiguator, + std::enable_if_t<_MDSPAN_TRAIT(std::is_empty, _T) && + // If the type isn't trivially destructible, its destructor + // won't be called at the right time, so don't use this + // specialization + _MDSPAN_TRAIT(std::is_trivially_destructible, _T)>> : +#ifdef _MDSPAN_COMPILER_MSVC + // MSVC doesn't allow you to access public static member functions of a type + // when you *happen* to privately inherit from that type. + protected +#else + // But we still want this to be private if possible so that we don't accidentally + // access members of _T directly rather than calling __ref() first, which wouldn't + // work if _T happens to be stateful and thus we're using the unspecialized definition + // of __no_unique_address_emulation above. + private +#endif + _T { + using __stored_type = _T; + MDSPAN_FORCE_INLINE_FUNCTION constexpr _T const &__ref() const noexcept { + return *static_cast<_T const *>(this); + } + MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 _T &__ref() noexcept { + return *static_cast<_T *>(this); + } + + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr __no_unique_address_emulation() noexcept = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr __no_unique_address_emulation( + __no_unique_address_emulation const &) noexcept = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr __no_unique_address_emulation( + __no_unique_address_emulation &&) noexcept = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + _MDSPAN_CONSTEXPR_14_DEFAULTED __no_unique_address_emulation & + operator=(__no_unique_address_emulation const &) noexcept = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + _MDSPAN_CONSTEXPR_14_DEFAULTED __no_unique_address_emulation & + operator=(__no_unique_address_emulation &&) noexcept = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + ~__no_unique_address_emulation() noexcept = default; + + // Explicitly make this not a reference so that the copy or move + // constructor still gets called. + MDSPAN_INLINE_FUNCTION + explicit constexpr __no_unique_address_emulation(_T const& __v) noexcept : _T(__v) {} + MDSPAN_INLINE_FUNCTION + explicit constexpr __no_unique_address_emulation(_T&& __v) noexcept : _T(::std::move(__v)) {} +}; + +//============================================================================== + +} // end namespace detail +} // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/tests/integration/deps/mdspan/include/experimental/__p0009_bits/trait_backports.hpp b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/trait_backports.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4933dd9934ea0a967ff46b5afc303a1431df6748 --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/trait_backports.hpp @@ -0,0 +1,132 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER +#ifndef MDSPAN_INCLUDE_EXPERIMENTAL_BITS_TRAIT_BACKPORTS_HPP_ +#define MDSPAN_INCLUDE_EXPERIMENTAL_BITS_TRAIT_BACKPORTS_HPP_ + +#include "macros.hpp" +#include "config.hpp" + +#include <type_traits> +#include <utility> // integer_sequence + +//============================================================================== +// <editor-fold desc="Variable template trait backports (e.g., is_void_v)"> {{{1 + +#ifdef _MDSPAN_NEEDS_TRAIT_VARIABLE_TEMPLATE_BACKPORTS + +#if _MDSPAN_USE_VARIABLE_TEMPLATES +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { + +#define _MDSPAN_BACKPORT_TRAIT(TRAIT) \ + template <class... Args> _MDSPAN_INLINE_VARIABLE constexpr auto TRAIT##_v = TRAIT<Args...>::value; + +_MDSPAN_BACKPORT_TRAIT(is_assignable) +_MDSPAN_BACKPORT_TRAIT(is_constructible) +_MDSPAN_BACKPORT_TRAIT(is_convertible) +_MDSPAN_BACKPORT_TRAIT(is_default_constructible) +_MDSPAN_BACKPORT_TRAIT(is_trivially_destructible) +_MDSPAN_BACKPORT_TRAIT(is_same) +_MDSPAN_BACKPORT_TRAIT(is_empty) +_MDSPAN_BACKPORT_TRAIT(is_void) + +#undef _MDSPAN_BACKPORT_TRAIT + +} // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE + +#endif // _MDSPAN_USE_VARIABLE_TEMPLATES + +#endif // _MDSPAN_NEEDS_TRAIT_VARIABLE_TEMPLATE_BACKPORTS + +// </editor-fold> end Variable template trait backports (e.g., is_void_v) }}}1 +//============================================================================== + +//============================================================================== +// <editor-fold desc="integer sequence (ugh...)"> {{{1 + +#if !defined(_MDSPAN_USE_INTEGER_SEQUENCE) || !_MDSPAN_USE_INTEGER_SEQUENCE + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { + +template <class T, T... Vals> +struct integer_sequence { + static constexpr size_t size() noexcept { return sizeof...(Vals); } + using value_type = T; +}; + +template <size_t... Vals> +using index_sequence = std::integer_sequence<size_t, Vals...>; + +namespace __detail { + +template <class T, T N, T I, class Result> +struct __make_int_seq_impl; + +template <class T, T N, T... Vals> +struct __make_int_seq_impl<T, N, N, integer_sequence<T, Vals...>> +{ + using type = integer_sequence<T, Vals...>; +}; + +template <class T, T N, T I, T... Vals> +struct __make_int_seq_impl< + T, N, I, integer_sequence<T, Vals...> +> : __make_int_seq_impl<T, N, I+1, integer_sequence<T, Vals..., I>> +{ }; + +} // end namespace __detail + +template <class T, T N> +using make_integer_sequence = typename __detail::__make_int_seq_impl<T, N, 0, integer_sequence<T>>::type; + +template <size_t N> +using make_index_sequence = typename __detail::__make_int_seq_impl<size_t, N, 0, integer_sequence<size_t>>::type; + +template <class... T> +using index_sequence_for = make_index_sequence<sizeof...(T)>; + +} // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE + +#endif + +// </editor-fold> end integer sequence (ugh...) }}}1 +//============================================================================== + +//============================================================================== +// <editor-fold desc="standard trait aliases"> {{{1 + +#if !defined(_MDSPAN_USE_STANDARD_TRAIT_ALIASES) || !_MDSPAN_USE_STANDARD_TRAIT_ALIASES + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { + +#define _MDSPAN_BACKPORT_TRAIT_ALIAS(TRAIT) \ + template <class... Args> using TRAIT##_t = typename TRAIT<Args...>::type; + +_MDSPAN_BACKPORT_TRAIT_ALIAS(remove_cv) +_MDSPAN_BACKPORT_TRAIT_ALIAS(remove_reference) + +template <bool _B, class _T=void> +using enable_if_t = typename enable_if<_B, _T>::type; + +#undef _MDSPAN_BACKPORT_TRAIT_ALIAS + +} // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE + +#endif + +// </editor-fold> end standard trait aliases }}}1 +//============================================================================== + +#endif //MDSPAN_INCLUDE_EXPERIMENTAL_BITS_TRAIT_BACKPORTS_HPP_ diff --git a/tests/integration/deps/mdspan/include/experimental/__p0009_bits/type_list.hpp b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/type_list.hpp new file mode 100644 index 0000000000000000000000000000000000000000..deca7c15d095857a805fa00cbd1947ce8c434d65 --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/type_list.hpp @@ -0,0 +1,87 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER +#include "macros.hpp" + +#include "trait_backports.hpp" // make_index_sequence + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { + +//============================================================================== + +namespace detail { + +template <class... _Ts> struct __type_list { static constexpr auto __size = sizeof...(_Ts); }; + +// Implementation of type_list at() that's heavily optimized for small typelists +template <size_t, class> struct __type_at; +template <size_t, class _Seq, class=std::make_index_sequence<_Seq::__size>> struct __type_at_large_impl; + +template <size_t _I, size_t _Idx, class _T> +struct __type_at_entry { }; + +template <class _Result> +struct __type_at_assign_op_ignore_rest { + template <class _T> + __type_at_assign_op_ignore_rest<_Result> operator=(_T&&); + using type = _Result; +}; + +struct __type_at_assign_op_impl { + template <size_t _I, size_t _Idx, class _T> + __type_at_assign_op_impl operator=(__type_at_entry<_I, _Idx, _T>&&); + template <size_t _I, class _T> + __type_at_assign_op_ignore_rest<_T> operator=(__type_at_entry<_I, _I, _T>&&); +}; + +template <size_t _I, class... _Ts, size_t... _Idxs> +struct __type_at_large_impl<_I, __type_list<_Ts...>, std::integer_sequence<size_t, _Idxs...>> + : decltype( + _MDSPAN_FOLD_ASSIGN_LEFT(__type_at_assign_op_impl{}, /* = ... = */ __type_at_entry<_I, _Idxs, _Ts>{}) + ) +{ }; + +template <size_t _I, class... _Ts> +struct __type_at<_I, __type_list<_Ts...>> + : __type_at_large_impl<_I, __type_list<_Ts...>> +{ }; + +template <class _T0, class... _Ts> +struct __type_at<0, __type_list<_T0, _Ts...>> { + using type = _T0; +}; + +template <class _T0, class _T1, class... _Ts> +struct __type_at<1, __type_list<_T0, _T1, _Ts...>> { + using type = _T1; +}; + +template <class _T0, class _T1, class _T2, class... _Ts> +struct __type_at<2, __type_list<_T0, _T1, _T2, _Ts...>> { + using type = _T2; +}; + +template <class _T0, class _T1, class _T2, class _T3, class... _Ts> +struct __type_at<3, __type_list<_T0, _T1, _T2, _T3, _Ts...>> { + using type = _T3; +}; + + +} // namespace detail + +//============================================================================== + +} // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE + diff --git a/tests/integration/deps/mdspan/include/experimental/__p0009_bits/utility.hpp b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/utility.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ca821176f875e4e650984a4d674e0a64dede53af --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/__p0009_bits/utility.hpp @@ -0,0 +1,66 @@ +#pragma once + +#include <cstddef> +#include <type_traits> + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace detail { + +// type alias used for rank-based tag dispatch +// +// this is used to enable alternatives to constexpr if when building for C++14 +// +template <std::size_t N> +using with_rank = std::integral_constant<std::size_t, N>; + +template <class I1, class I2> +constexpr bool common_integral_compare(I1 x, I2 y) +{ + static_assert(std::is_integral<I1>::value and + std::is_integral<I2>::value, ""); + + using I = std::common_type_t<I1, I2>; + return static_cast<I>(x) == static_cast<I>(y); +} + +template <class T1, class T2, class F> +constexpr bool rankwise_equal(with_rank<0>, const T1&, const T2&, F) +{ + return true; +} +template <std::size_t N, class T1, class T2, class F> +constexpr bool rankwise_equal(with_rank<N>, const T1& x, const T2& y, F func) +{ + bool match = true; + + for (std::size_t r = 0; r < N; r++) { + match = match && common_integral_compare(func(x, r), func(y, r)); + } + + return match; +} + +constexpr struct +{ + template <class T, class I> + constexpr auto operator()(const T& x, I i) const + { + return x.extent(i); + } +} extent; + +constexpr struct +{ + template <class T, class I> + constexpr auto operator()(const T& x, I i) const + { + return x.stride(i); + } +} stride; + +} // namespace detail + +constexpr struct mdspan_non_standard_tag { +} mdspan_non_standard; + +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/tests/integration/deps/mdspan/include/experimental/__p1684_bits/mdarray.hpp b/tests/integration/deps/mdspan/include/experimental/__p1684_bits/mdarray.hpp new file mode 100644 index 0000000000000000000000000000000000000000..bdc5925f715190b2d7163ec75e0c5956df706df9 --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/__p1684_bits/mdarray.hpp @@ -0,0 +1,460 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#pragma once + +#include "../mdspan" +#include <cassert> +#include <vector> + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace MDSPAN_IMPL_PROPOSED_NAMESPACE { + +namespace { + template<class Extents> + struct size_of_extents; + + template<class IndexType, size_t ... Extents> + struct size_of_extents<extents<IndexType, Extents...>> { + constexpr static size_t value() { + size_t size = 1; + for(size_t r=0; r<extents<IndexType, Extents...>::rank(); r++) + size *= extents<IndexType, Extents...>::static_extent(r); + return size; + } + }; +} + +namespace { + template<class C> + struct container_is_array : std::false_type { + template<class M> + static constexpr C construct(const M& m) { return C(m.required_span_size()); } + }; + template<class T, size_t N> + struct container_is_array<std::array<T,N>> : std::true_type { + template<class M> + static constexpr std::array<T,N> construct(const M&) { return std::array<T,N>(); } + }; +} + +template < + class ElementType, + class Extents, + class LayoutPolicy = layout_right, + class Container = std::vector<ElementType> +> +class mdarray { +private: + static_assert(::MDSPAN_IMPL_STANDARD_NAMESPACE::detail::__is_extents_v<Extents>, + MDSPAN_IMPL_PROPOSED_NAMESPACE_STRING "::mdspan's Extents template parameter must be a specialization of " MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::extents."); + +public: + + //-------------------------------------------------------------------------------- + // Domain and codomain types + + using extents_type = Extents; + using layout_type = LayoutPolicy; + using container_type = Container; + using mapping_type = typename layout_type::template mapping<extents_type>; + using element_type = ElementType; + using mdspan_type = mdspan<element_type, extents_type, layout_type>; + using const_mdspan_type = mdspan<const element_type, extents_type, layout_type>; + using value_type = std::remove_cv_t<element_type>; + using index_type = typename Extents::index_type; + using size_type = typename Extents::size_type; + using rank_type = typename Extents::rank_type; + using pointer = typename container_type::pointer; + using reference = typename container_type::reference; + using const_pointer = typename container_type::const_pointer; + using const_reference = typename container_type::const_reference; + +public: + + //-------------------------------------------------------------------------------- + // [mdspan.basic.cons], mdspan constructors, assignment, and destructor + +#if !(MDSPAN_HAS_CXX_20) + MDSPAN_FUNCTION_REQUIRES( + (MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr), + mdarray, (), , + /* requires */ (extents_type::rank_dynamic()!=0)) {} +#else + MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mdarray() requires(extents_type::rank_dynamic()!=0) = default; +#endif + MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mdarray(const mdarray&) = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mdarray(mdarray&&) = default; + + // Constructors for container types constructible from a size + MDSPAN_TEMPLATE_REQUIRES( + class... SizeTypes, + /* requires */ ( + (::MDSPAN_IMPL_STANDARD_NAMESPACE::detail::are_valid_indices<index_type, SizeTypes...>()) && + _MDSPAN_TRAIT( std::is_constructible, extents_type, SizeTypes...) && + _MDSPAN_TRAIT( std::is_constructible, mapping_type, extents_type) && + (_MDSPAN_TRAIT( std::is_constructible, container_type, size_t) || + container_is_array<container_type>::value) && + (extents_type::rank()>0 || extents_type::rank_dynamic()==0) + ) + ) + MDSPAN_INLINE_FUNCTION + explicit constexpr mdarray(SizeTypes... dynamic_extents) + : map_(extents_type(dynamic_extents...)), ctr_(container_is_array<container_type>::construct(map_)) + { } + + MDSPAN_FUNCTION_REQUIRES( + (MDSPAN_INLINE_FUNCTION constexpr), + mdarray, (const extents_type& exts), , + /* requires */ ((_MDSPAN_TRAIT( std::is_constructible, container_type, size_t) || + container_is_array<container_type>::value) && + _MDSPAN_TRAIT( std::is_constructible, mapping_type, extents_type)) + ) : map_(exts), ctr_(container_is_array<container_type>::construct(map_)) + { } + + MDSPAN_FUNCTION_REQUIRES( + (MDSPAN_INLINE_FUNCTION constexpr), + mdarray, (const mapping_type& m), , + /* requires */ (_MDSPAN_TRAIT( std::is_constructible, container_type, size_t) || + container_is_array<container_type>::value) + ) : map_(m), ctr_(container_is_array<container_type>::construct(map_)) + { } + + MDSPAN_FUNCTION_REQUIRES( + (MDSPAN_INLINE_FUNCTION constexpr), + mdarray, (const extents_type& exts, const container_type& ctr), , + /* requires */ (_MDSPAN_TRAIT( std::is_constructible, mapping_type, extents_type)) + ) : map_(exts), ctr_(ctr) + { assert(ctr.size() >= static_cast<size_t>(map_.required_span_size())); } + + constexpr mdarray(const mapping_type& m, const container_type& ctr) + : map_(m), ctr_(ctr) + { assert(ctr.size() >= static_cast<size_t>(map_.required_span_size())); } + + MDSPAN_FUNCTION_REQUIRES( + (MDSPAN_INLINE_FUNCTION constexpr), + mdarray, (const extents_type& exts, container_type&& ctr), , + /* requires */ (_MDSPAN_TRAIT( std::is_constructible, mapping_type, extents_type)) + ) : map_(exts), ctr_(std::move(ctr)) + { assert(ctr_.size() >= static_cast<size_t>(map_.required_span_size())); } + + constexpr mdarray(const mapping_type& m, container_type&& ctr) + : map_(m), ctr_(std::move(ctr)) + { assert(ctr_.size() >= static_cast<size_t>(map_.required_span_size())); } + + + MDSPAN_TEMPLATE_REQUIRES( + class OtherElementType, class OtherExtents, class OtherLayoutPolicy, class OtherContainer, + /* requires */ ( + _MDSPAN_TRAIT( std::is_constructible, mapping_type, typename OtherLayoutPolicy::template mapping<OtherExtents>) && + _MDSPAN_TRAIT( std::is_constructible, container_type, OtherContainer) + ) + ) + MDSPAN_INLINE_FUNCTION + constexpr mdarray(const mdarray<OtherElementType, OtherExtents, OtherLayoutPolicy, OtherContainer>& other) + : map_(other.mapping()), ctr_(other.container()) + { + static_assert( std::is_constructible<extents_type, OtherExtents>::value, ""); + } + + // Constructors for container types constructible from a size and allocator + MDSPAN_TEMPLATE_REQUIRES( + class Alloc, + /* requires */ (_MDSPAN_TRAIT( std::is_constructible, container_type, size_t, Alloc) && + _MDSPAN_TRAIT( std::is_constructible, mapping_type, extents_type)) + ) + MDSPAN_INLINE_FUNCTION + constexpr mdarray(const extents_type& exts, const Alloc& a) + : map_(exts), ctr_(map_.required_span_size(), a) + { } + + MDSPAN_TEMPLATE_REQUIRES( + class Alloc, + /* requires */ (_MDSPAN_TRAIT( std::is_constructible, container_type, size_t, Alloc)) + ) + MDSPAN_INLINE_FUNCTION + constexpr mdarray(const mapping_type& map, const Alloc& a) + : map_(map), ctr_(map_.required_span_size(), a) + { } + + // Constructors for container types constructible from a container and allocator + MDSPAN_TEMPLATE_REQUIRES( + class Alloc, + /* requires */ (_MDSPAN_TRAIT( std::is_constructible, container_type, container_type, Alloc) && + _MDSPAN_TRAIT( std::is_constructible, mapping_type, extents_type)) + ) + MDSPAN_INLINE_FUNCTION + constexpr mdarray(const extents_type& exts, const container_type& ctr, const Alloc& a) + : map_(exts), ctr_(ctr, a) + { assert(ctr_.size() >= static_cast<size_t>(map_.required_span_size())); } + + MDSPAN_TEMPLATE_REQUIRES( + class Alloc, + /* requires */ (_MDSPAN_TRAIT( std::is_constructible, container_type, size_t, Alloc)) + ) + MDSPAN_INLINE_FUNCTION + constexpr mdarray(const mapping_type& map, const container_type& ctr, const Alloc& a) + : map_(map), ctr_(ctr, a) + { assert(ctr_.size() >= static_cast<size_t>(map_.required_span_size())); } + + MDSPAN_TEMPLATE_REQUIRES( + class Alloc, + /* requires */ (_MDSPAN_TRAIT( std::is_constructible, container_type, container_type, Alloc) && + _MDSPAN_TRAIT( std::is_constructible, mapping_type, extents_type)) + ) + MDSPAN_INLINE_FUNCTION + constexpr mdarray(const extents_type& exts, container_type&& ctr, const Alloc& a) + : map_(exts), ctr_(std::move(ctr), a) + { assert(ctr_.size() >= static_cast<size_t>(map_.required_span_size())); } + + MDSPAN_TEMPLATE_REQUIRES( + class Alloc, + /* requires */ (_MDSPAN_TRAIT( std::is_constructible, container_type, size_t, Alloc)) + ) + MDSPAN_INLINE_FUNCTION + constexpr mdarray(const mapping_type& map, container_type&& ctr, const Alloc& a) + : map_(map), ctr_(std::move(ctr), a) + { assert(ctr_.size() >= map_.required_span_size()); } + + MDSPAN_TEMPLATE_REQUIRES( + class OtherElementType, class OtherExtents, class OtherLayoutPolicy, class OtherContainer, class Alloc, + /* requires */ ( + _MDSPAN_TRAIT( std::is_constructible, mapping_type, typename OtherLayoutPolicy::template mapping<OtherExtents>) && + _MDSPAN_TRAIT( std::is_constructible, container_type, OtherContainer, Alloc) + ) + ) + MDSPAN_INLINE_FUNCTION + constexpr mdarray(const mdarray<OtherElementType, OtherExtents, OtherLayoutPolicy, OtherContainer>& other, const Alloc& a) + : map_(other.mapping()), ctr_(other.container(), a) + { + static_assert( std::is_constructible<extents_type, OtherExtents>::value, ""); + } + + MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mdarray& operator= (const mdarray&) = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mdarray& operator= (mdarray&&) = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED + ~mdarray() = default; + + //-------------------------------------------------------------------------------- + // [mdspan.basic.mapping], mdspan mapping domain multidimensional index to access codomain element + + #if MDSPAN_USE_BRACKET_OPERATOR + MDSPAN_TEMPLATE_REQUIRES( + class... SizeTypes, + /* requires */ ( + _MDSPAN_FOLD_AND(_MDSPAN_TRAIT( std::is_convertible, SizeTypes, index_type) /* && ... */) && + extents_type::rank() == sizeof...(SizeTypes) + ) + ) + MDSPAN_FORCE_INLINE_FUNCTION + constexpr const_reference operator[](SizeTypes... indices) const noexcept + { + return ctr_[map_(static_cast<index_type>(std::move(indices))...)]; + } + + MDSPAN_TEMPLATE_REQUIRES( + class... SizeTypes, + /* requires */ ( + _MDSPAN_FOLD_AND(_MDSPAN_TRAIT( std::is_convertible, SizeTypes, index_type) /* && ... */) && + extents_type::rank() == sizeof...(SizeTypes) + ) + ) + MDSPAN_FORCE_INLINE_FUNCTION + constexpr reference operator[](SizeTypes... indices) noexcept + { + return ctr_[map_(static_cast<index_type>(std::move(indices))...)]; + } + #endif + +#if 0 + MDSPAN_TEMPLATE_REQUIRES( + class SizeType, size_t N, + /* requires */ ( + _MDSPAN_TRAIT( std::is_convertible, SizeType, index_type) && + N == extents_type::rank() + ) + ) + MDSPAN_FORCE_INLINE_FUNCTION + constexpr const_reference operator[](const std::array<SizeType, N>& indices) const noexcept + { + return __impl::template __callop<reference>(*this, indices); + } + + MDSPAN_TEMPLATE_REQUIRES( + class SizeType, size_t N, + /* requires */ ( + _MDSPAN_TRAIT( std::is_convertible, SizeType, index_type) && + N == extents_type::rank() + ) + ) + MDSPAN_FORCE_INLINE_FUNCTION + constexpr reference operator[](const std::array<SizeType, N>& indices) noexcept + { + return __impl::template __callop<reference>(*this, indices); + } +#endif + + + #if MDSPAN_USE_PAREN_OPERATOR + MDSPAN_TEMPLATE_REQUIRES( + class... SizeTypes, + /* requires */ ( + (::MDSPAN_IMPL_STANDARD_NAMESPACE::detail::are_valid_indices<index_type, SizeTypes...>()) && + extents_type::rank() == sizeof...(SizeTypes) + ) + ) + MDSPAN_FORCE_INLINE_FUNCTION + constexpr const_reference operator()(SizeTypes... indices) const noexcept + { + return ctr_[map_(static_cast<index_type>(std::move(indices))...)]; + } + MDSPAN_TEMPLATE_REQUIRES( + class... SizeTypes, + /* requires */ ( + (::MDSPAN_IMPL_STANDARD_NAMESPACE::detail::are_valid_indices<index_type, SizeTypes...>()) && + extents_type::rank() == sizeof...(SizeTypes) + ) + ) + MDSPAN_FORCE_INLINE_FUNCTION + constexpr reference operator()(SizeTypes... indices) noexcept + { + return ctr_[map_(static_cast<index_type>(std::move(indices))...)]; + } + +#if 0 + MDSPAN_TEMPLATE_REQUIRES( + class SizeType, size_t N, + /* requires */ ( + _MDSPAN_TRAIT( std::is_convertible, SizeType, index_type) && + N == extents_type::rank() + ) + ) + MDSPAN_FORCE_INLINE_FUNCTION + constexpr const_reference operator()(const std::array<SizeType, N>& indices) const noexcept + { + return __impl::template __callop<reference>(*this, indices); + } + + MDSPAN_TEMPLATE_REQUIRES( + class SizeType, size_t N, + /* requires */ ( + _MDSPAN_TRAIT( std::is_convertible, SizeType, index_type) && + N == extents_type::rank() + ) + ) + MDSPAN_FORCE_INLINE_FUNCTION + constexpr reference operator()(const std::array<SizeType, N>& indices) noexcept + { + return __impl::template __callop<reference>(*this, indices); + } +#endif + #endif + + MDSPAN_INLINE_FUNCTION constexpr pointer data() noexcept { return ctr_.data(); }; + MDSPAN_INLINE_FUNCTION constexpr const_pointer data() const noexcept { return ctr_.data(); }; + MDSPAN_INLINE_FUNCTION constexpr container_type& container() noexcept { return ctr_; }; + MDSPAN_INLINE_FUNCTION constexpr const container_type& container() const noexcept { return ctr_; }; + + //-------------------------------------------------------------------------------- + // [mdspan.basic.domobs], mdspan observers of the domain multidimensional index space + + MDSPAN_INLINE_FUNCTION static constexpr rank_type rank() noexcept { return extents_type::rank(); } + MDSPAN_INLINE_FUNCTION static constexpr rank_type rank_dynamic() noexcept { return extents_type::rank_dynamic(); } + MDSPAN_INLINE_FUNCTION static constexpr size_t static_extent(size_t r) noexcept { return extents_type::static_extent(r); } + + MDSPAN_INLINE_FUNCTION constexpr const extents_type& extents() const noexcept { return map_.extents(); }; + MDSPAN_INLINE_FUNCTION constexpr index_type extent(size_t r) const noexcept { return map_.extents().extent(r); }; + MDSPAN_INLINE_FUNCTION constexpr index_type size() const noexcept { +// return __impl::__size(*this); + return ctr_.size(); + }; + + + //-------------------------------------------------------------------------------- + // [mdspan.basic.obs], mdspan observers of the mapping + + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_unique() noexcept { return mapping_type::is_always_unique(); }; + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_exhaustive() noexcept { return mapping_type::is_always_exhaustive(); }; + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_strided() noexcept { return mapping_type::is_always_strided(); }; + + MDSPAN_INLINE_FUNCTION constexpr const mapping_type& mapping() const noexcept { return map_; }; + MDSPAN_INLINE_FUNCTION constexpr bool is_unique() const noexcept { return map_.is_unique(); }; + MDSPAN_INLINE_FUNCTION constexpr bool is_exhaustive() const noexcept { return map_.is_exhaustive(); }; + MDSPAN_INLINE_FUNCTION constexpr bool is_strided() const noexcept { return map_.is_strided(); }; + MDSPAN_INLINE_FUNCTION constexpr index_type stride(size_t r) const { return map_.stride(r); }; + + // Converstion to mdspan + MDSPAN_TEMPLATE_REQUIRES( + class OtherElementType, class OtherExtents, + class OtherLayoutType, class OtherAccessorType, + /* requires */ ( + _MDSPAN_TRAIT(std::is_assignable, + mdspan<OtherElementType, OtherExtents, OtherLayoutType, OtherAccessorType>, + mdspan_type) + ) + ) + constexpr operator mdspan<OtherElementType, OtherExtents, OtherLayoutType, OtherAccessorType> () { + return mdspan_type(data(), map_); + } + + MDSPAN_TEMPLATE_REQUIRES( + class OtherElementType, class OtherExtents, + class OtherLayoutType, class OtherAccessorType, + /* requires */ ( + _MDSPAN_TRAIT(std::is_assignable, + mdspan<OtherElementType, OtherExtents, OtherLayoutType, OtherAccessorType>, + const_mdspan_type) + ) + ) + constexpr operator mdspan<OtherElementType, OtherExtents, OtherLayoutType, OtherAccessorType> () const { + return const_mdspan_type(data(), map_); + } + + MDSPAN_TEMPLATE_REQUIRES( + class OtherAccessorType = default_accessor<element_type>, + /* requires */ ( + _MDSPAN_TRAIT(std::is_assignable, mdspan_type, + mdspan<element_type, extents_type, layout_type, OtherAccessorType>) + ) + ) + constexpr mdspan<element_type, extents_type, layout_type, OtherAccessorType> + to_mdspan(const OtherAccessorType& a = default_accessor<element_type>()) { + return mdspan<element_type, extents_type, layout_type, OtherAccessorType>(data(), map_, a); + } + + MDSPAN_TEMPLATE_REQUIRES( + class OtherAccessorType = default_accessor<const element_type>, + /* requires */ ( + _MDSPAN_TRAIT(std::is_assignable, const_mdspan_type, + mdspan<const element_type, extents_type, layout_type, OtherAccessorType>) + ) + ) + constexpr mdspan<const element_type, extents_type, layout_type, OtherAccessorType> + to_mdspan(const OtherAccessorType& a = default_accessor<const element_type>()) const { + return mdspan<const element_type, extents_type, layout_type, OtherAccessorType>(data(), map_, a); + } + +private: + mapping_type map_; + container_type ctr_; + + template <class, class, class, class> + friend class mdarray; +}; + + +} // end namespace MDSPAN_IMPL_PROPOSED_NAMESPACE +} // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/tests/integration/deps/mdspan/include/experimental/__p2389_bits/dims.hpp b/tests/integration/deps/mdspan/include/experimental/__p2389_bits/dims.hpp new file mode 100644 index 0000000000000000000000000000000000000000..00045215c489bfaa04bd0d7967a85b49559fb714 --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/__p2389_bits/dims.hpp @@ -0,0 +1,28 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#pragma once + +// backward compatibility import into experimental +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace MDSPAN_IMPL_PROPOSED_NAMESPACE { + +template< ::std::size_t Rank, class IndexType = std::size_t> +using dims = + :: MDSPAN_IMPL_STANDARD_NAMESPACE :: dextents<IndexType, Rank>; + +} // namespace MDSPAN_IMPL_PROPOSED_NAMESPACE +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/tests/integration/deps/mdspan/include/experimental/__p2630_bits/strided_slice.hpp b/tests/integration/deps/mdspan/include/experimental/__p2630_bits/strided_slice.hpp new file mode 100644 index 0000000000000000000000000000000000000000..89ba8202fb16a090bfa9352a4dcf7041cf53c90f --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/__p2630_bits/strided_slice.hpp @@ -0,0 +1,48 @@ + +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#pragma once + +#include <type_traits> + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { + +namespace { + template<class T> + struct __mdspan_is_integral_constant: std::false_type {}; + + template<class T, T val> + struct __mdspan_is_integral_constant<std::integral_constant<T,val>>: std::true_type {}; +} + +// Slice Specifier allowing for strides and compile time extent +template <class OffsetType, class ExtentType, class StrideType> +struct strided_slice { + using offset_type = OffsetType; + using extent_type = ExtentType; + using stride_type = StrideType; + + _MDSPAN_NO_UNIQUE_ADDRESS OffsetType offset{}; + _MDSPAN_NO_UNIQUE_ADDRESS ExtentType extent{}; + _MDSPAN_NO_UNIQUE_ADDRESS StrideType stride{}; + + static_assert(std::is_integral_v<OffsetType> || __mdspan_is_integral_constant<OffsetType>::value); + static_assert(std::is_integral_v<ExtentType> || __mdspan_is_integral_constant<ExtentType>::value); + static_assert(std::is_integral_v<StrideType> || __mdspan_is_integral_constant<StrideType>::value); +}; + +} // MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/tests/integration/deps/mdspan/include/experimental/__p2630_bits/submdspan.hpp b/tests/integration/deps/mdspan/include/experimental/__p2630_bits/submdspan.hpp new file mode 100644 index 0000000000000000000000000000000000000000..abddd0b59df170f2b16f7e5d301e45378a42bdee --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/__p2630_bits/submdspan.hpp @@ -0,0 +1,40 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#pragma once + +#include "submdspan_extents.hpp" +#include "submdspan_mapping.hpp" + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +template <class ElementType, class Extents, class LayoutPolicy, + class AccessorPolicy, class... SliceSpecifiers> +MDSPAN_INLINE_FUNCTION +constexpr auto +submdspan(const mdspan<ElementType, Extents, LayoutPolicy, AccessorPolicy> &src, + SliceSpecifiers... slices) { + const auto sub_submdspan_mapping_result = submdspan_mapping(src.mapping(), slices...); + // NVCC has a problem with the deduction so lets figure out the type + using sub_mapping_t = std::remove_cv_t<decltype(sub_submdspan_mapping_result.mapping)>; + using sub_extents_t = typename sub_mapping_t::extents_type; + using sub_layout_t = typename sub_mapping_t::layout_type; + using sub_accessor_t = typename AccessorPolicy::offset_policy; + return mdspan<ElementType, sub_extents_t, sub_layout_t, sub_accessor_t>( + src.accessor().offset(src.data_handle(), sub_submdspan_mapping_result.offset), + sub_submdspan_mapping_result.mapping, + sub_accessor_t(src.accessor())); +} +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/tests/integration/deps/mdspan/include/experimental/__p2630_bits/submdspan_extents.hpp b/tests/integration/deps/mdspan/include/experimental/__p2630_bits/submdspan_extents.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c3b2f78fb998cb1a3c77a9cac8738085d81cbbaf --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/__p2630_bits/submdspan_extents.hpp @@ -0,0 +1,321 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#pragma once + +#include <tuple> + +#include "strided_slice.hpp" +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace detail { + +// Mapping from submapping ranks to srcmapping ranks +// InvMapRank is an index_sequence, which we build recursively +// to contain the mapped indices. +// end of recursion specialization containing the final index_sequence +template <size_t Counter, size_t... MapIdxs> +MDSPAN_INLINE_FUNCTION +constexpr auto inv_map_rank(std::integral_constant<size_t, Counter>, std::index_sequence<MapIdxs...>) { + return std::index_sequence<MapIdxs...>(); +} + +// specialization reducing rank by one (i.e., integral slice specifier) +template<size_t Counter, class Slice, class... SliceSpecifiers, size_t... MapIdxs> +MDSPAN_INLINE_FUNCTION +constexpr auto inv_map_rank(std::integral_constant<size_t, Counter>, std::index_sequence<MapIdxs...>, Slice, + SliceSpecifiers... slices) { + using next_idx_seq_t = std::conditional_t<std::is_convertible_v<Slice, size_t>, + std::index_sequence<MapIdxs...>, + std::index_sequence<MapIdxs..., Counter>>; + + return inv_map_rank(std::integral_constant<size_t,Counter + 1>(), next_idx_seq_t(), + slices...); +} + +// Helper for identifying strided_slice +template <class T> struct is_strided_slice : std::false_type {}; + +template <class OffsetType, class ExtentType, class StrideType> +struct is_strided_slice< + strided_slice<OffsetType, ExtentType, StrideType>> : std::true_type {}; + +// first_of(slice): getting begin of slice specifier range +MDSPAN_TEMPLATE_REQUIRES( + class Integral, + /* requires */(std::is_convertible_v<Integral, size_t>) +) +MDSPAN_INLINE_FUNCTION +constexpr Integral first_of(const Integral &i) { + return i; +} + +MDSPAN_INLINE_FUNCTION +constexpr std::integral_constant<size_t, 0> +first_of(const ::MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent_t &) { + return std::integral_constant<size_t, 0>(); +} + +MDSPAN_TEMPLATE_REQUIRES( + class Slice, + /* requires */(std::is_convertible_v<Slice, std::tuple<size_t, size_t>>) +) +MDSPAN_INLINE_FUNCTION +constexpr auto first_of(const Slice &i) { + return std::get<0>(i); +} + +template <class OffsetType, class ExtentType, class StrideType> +MDSPAN_INLINE_FUNCTION +constexpr OffsetType +first_of(const strided_slice<OffsetType, ExtentType, StrideType> &r) { + return r.offset; +} + +// last_of(slice): getting end of slice specifier range +// We need however not just the slice but also the extents +// of the original view and which rank from the extents. +// This is needed in the case of slice being full_extent_t. +MDSPAN_TEMPLATE_REQUIRES( + size_t k, class Extents, class Integral, + /* requires */(std::is_convertible_v<Integral, size_t>) +) +MDSPAN_INLINE_FUNCTION +constexpr Integral + last_of(std::integral_constant<size_t, k>, const Extents &, const Integral &i) { + return i; +} + +MDSPAN_TEMPLATE_REQUIRES( + size_t k, class Extents, class Slice, + /* requires */(std::is_convertible_v<Slice, std::tuple<size_t, size_t>>) +) +MDSPAN_INLINE_FUNCTION +constexpr auto last_of(std::integral_constant<size_t, k>, const Extents &, + const Slice &i) { + return std::get<1>(i); +} + +// Suppress spurious warning with NVCC about no return statement. +// This is a known issue in NVCC and NVC++ +// Depending on the CUDA and GCC version we need both the builtin +// and the diagnostic push. I tried really hard to find something shorter +// but no luck ... +#if defined __NVCC__ + #ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ + #pragma nv_diagnostic push + #pragma nv_diag_suppress = implicit_return_from_non_void_function + #else + #ifdef __CUDA_ARCH__ + #pragma diagnostic push + #pragma diag_suppress implicit_return_from_non_void_function + #endif + #endif +#elif defined __NVCOMPILER + #pragma diagnostic push + #pragma diag_suppress = implicit_return_from_non_void_function +#endif +template <size_t k, class Extents> +MDSPAN_INLINE_FUNCTION +constexpr auto last_of(std::integral_constant<size_t, k>, const Extents &ext, + ::MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent_t) { + if constexpr (Extents::static_extent(k) == dynamic_extent) { + return ext.extent(k); + } else { + return std::integral_constant<size_t, Extents::static_extent(k)>(); + } +#if defined(__NVCC__) && !defined(__CUDA_ARCH__) && defined(__GNUC__) + // Even with CUDA_ARCH protection this thing warns about calling host function + __builtin_unreachable(); +#endif +} +#if defined __NVCC__ + #ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ + #pragma nv_diagnostic pop + #else + #ifdef __CUDA_ARCH__ + #pragma diagnostic pop + #endif + #endif +#elif defined __NVCOMPILER + #pragma diagnostic pop +#endif + +template <size_t k, class Extents, class OffsetType, class ExtentType, + class StrideType> +MDSPAN_INLINE_FUNCTION +constexpr OffsetType +last_of(std::integral_constant<size_t, k>, const Extents &, + const strided_slice<OffsetType, ExtentType, StrideType> &r) { + return r.extent; +} + +// get stride of slices +template <class T> +MDSPAN_INLINE_FUNCTION +constexpr auto stride_of(const T &) { + return std::integral_constant<size_t, 1>(); +} + +template <class OffsetType, class ExtentType, class StrideType> +MDSPAN_INLINE_FUNCTION +constexpr auto +stride_of(const strided_slice<OffsetType, ExtentType, StrideType> &r) { + return r.stride; +} + +// divide which can deal with integral constant preservation +template <class IndexT, class T0, class T1> +MDSPAN_INLINE_FUNCTION +constexpr auto divide(const T0 &v0, const T1 &v1) { + return IndexT(v0) / IndexT(v1); +} + +template <class IndexT, class T0, T0 v0, class T1, T1 v1> +MDSPAN_INLINE_FUNCTION +constexpr auto divide(const std::integral_constant<T0, v0> &, + const std::integral_constant<T1, v1> &) { + // cutting short division by zero + // this is used for strided_slice with zero extent/stride + return std::integral_constant<IndexT, v0 == 0 ? 0 : v0 / v1>(); +} + +// multiply which can deal with integral constant preservation +template <class IndexT, class T0, class T1> +MDSPAN_INLINE_FUNCTION +constexpr auto multiply(const T0 &v0, const T1 &v1) { + return IndexT(v0) * IndexT(v1); +} + +template <class IndexT, class T0, T0 v0, class T1, T1 v1> +MDSPAN_INLINE_FUNCTION +constexpr auto multiply(const std::integral_constant<T0, v0> &, + const std::integral_constant<T1, v1> &) { + return std::integral_constant<IndexT, v0 * v1>(); +} + +// compute new static extent from range, preserving static knowledge +template <class Arg0, class Arg1> struct StaticExtentFromRange { + constexpr static size_t value = dynamic_extent; +}; + +template <class Integral0, Integral0 val0, class Integral1, Integral1 val1> +struct StaticExtentFromRange<std::integral_constant<Integral0, val0>, + std::integral_constant<Integral1, val1>> { + constexpr static size_t value = val1 - val0; +}; + +// compute new static extent from strided_slice, preserving static +// knowledge +template <class Arg0, class Arg1> struct StaticExtentFromStridedRange { + constexpr static size_t value = dynamic_extent; +}; + +template <class Integral0, Integral0 val0, class Integral1, Integral1 val1> +struct StaticExtentFromStridedRange<std::integral_constant<Integral0, val0>, + std::integral_constant<Integral1, val1>> { + constexpr static size_t value = val0 > 0 ? 1 + (val0 - 1) / val1 : 0; +}; + +// creates new extents through recursive calls to next_extent member function +// next_extent has different overloads for different types of stride specifiers +template <size_t K, class Extents, size_t... NewExtents> +struct extents_constructor { + MDSPAN_TEMPLATE_REQUIRES( + class Slice, class... SlicesAndExtents, + /* requires */(!std::is_convertible_v<Slice, size_t> && + !is_strided_slice<Slice>::value) + ) + MDSPAN_INLINE_FUNCTION + constexpr static auto next_extent(const Extents &ext, const Slice &sl, + SlicesAndExtents... slices_and_extents) { + constexpr size_t new_static_extent = StaticExtentFromRange< + decltype(first_of(std::declval<Slice>())), + decltype(last_of(std::integral_constant<size_t, Extents::rank() - K>(), + std::declval<Extents>(), + std::declval<Slice>()))>::value; + + using next_t = + extents_constructor<K - 1, Extents, NewExtents..., new_static_extent>; + using index_t = typename Extents::index_type; + return next_t::next_extent( + ext, slices_and_extents..., + index_t(last_of(std::integral_constant<size_t, Extents::rank() - K>(), ext, + sl)) - + index_t(first_of(sl))); + } + + MDSPAN_TEMPLATE_REQUIRES( + class Slice, class... SlicesAndExtents, + /* requires */ (std::is_convertible_v<Slice, size_t>) + ) + MDSPAN_INLINE_FUNCTION + constexpr static auto next_extent(const Extents &ext, const Slice &, + SlicesAndExtents... slices_and_extents) { + using next_t = extents_constructor<K - 1, Extents, NewExtents...>; + return next_t::next_extent(ext, slices_and_extents...); + } + + template <class OffsetType, class ExtentType, class StrideType, + class... SlicesAndExtents> + MDSPAN_INLINE_FUNCTION + constexpr static auto + next_extent(const Extents &ext, + const strided_slice<OffsetType, ExtentType, StrideType> &r, + SlicesAndExtents... slices_and_extents) { + using index_t = typename Extents::index_type; + using new_static_extent_t = + StaticExtentFromStridedRange<ExtentType, StrideType>; + if constexpr (new_static_extent_t::value == dynamic_extent) { + using next_t = + extents_constructor<K - 1, Extents, NewExtents..., dynamic_extent>; + return next_t::next_extent( + ext, slices_and_extents..., + r.extent > 0 ? 1 + divide<index_t>(r.extent - 1, r.stride) : 0); + } else { + constexpr size_t new_static_extent = new_static_extent_t::value; + using next_t = + extents_constructor<K - 1, Extents, NewExtents..., new_static_extent>; + return next_t::next_extent( + ext, slices_and_extents..., index_t(divide<index_t>(ExtentType(), StrideType()))); + } + } +}; + +template <class Extents, size_t... NewStaticExtents> +struct extents_constructor<0, Extents, NewStaticExtents...> { + + template <class... NewExtents> + MDSPAN_INLINE_FUNCTION + constexpr static auto next_extent(const Extents &, NewExtents... new_exts) { + return extents<typename Extents::index_type, NewStaticExtents...>( + new_exts...); + } +}; + +} // namespace detail + +// submdspan_extents creates new extents given src extents and submdspan slice +// specifiers +template <class IndexType, size_t... Extents, class... SliceSpecifiers> +MDSPAN_INLINE_FUNCTION +constexpr auto submdspan_extents(const extents<IndexType, Extents...> &src_exts, + SliceSpecifiers... slices) { + + using ext_t = extents<IndexType, Extents...>; + return detail::extents_constructor<ext_t::rank(), ext_t>::next_extent( + src_exts, slices...); +} +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/tests/integration/deps/mdspan/include/experimental/__p2630_bits/submdspan_mapping.hpp b/tests/integration/deps/mdspan/include/experimental/__p2630_bits/submdspan_mapping.hpp new file mode 100644 index 0000000000000000000000000000000000000000..cf1bdd1e56f2b1ec356de554fe0cf8685aa76ded --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/__p2630_bits/submdspan_mapping.hpp @@ -0,0 +1,452 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#pragma once + +#include <array> +#include <tuple> +#include <type_traits> +#include <utility> // index_sequence + +// Suppress spurious warning with NVCC about no return statement. +// This is a known issue in NVCC and NVC++ +// Depending on the CUDA and GCC version we need both the builtin +// and the diagnostic push. I tried really hard to find something shorter +// but no luck ... +#if defined __NVCC__ +#ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ +#pragma nv_diagnostic push +#pragma nv_diag_suppress = implicit_return_from_non_void_function +#else +#ifdef __CUDA_ARCH__ +#pragma diagnostic push +#pragma diag_suppress implicit_return_from_non_void_function +#endif +#endif +#elif defined __NVCOMPILER +#pragma diagnostic push +#pragma diag_suppress = implicit_return_from_non_void_function +#endif + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +//****************************************** +// Return type of submdspan_mapping overloads +//****************************************** +template <class LayoutMapping> struct submdspan_mapping_result { + _MDSPAN_NO_UNIQUE_ADDRESS LayoutMapping mapping{}; + size_t offset; +}; + +namespace detail { +// We use const Slice& and not Slice&& because the various +// submdspan_mapping_impl overloads use their slices arguments +// multiple times. This makes perfect forwarding not useful, but we +// still don't want to pass those (possibly of size 64 x 3 bits) +// objects by value. +template <class IndexType, class Slice> +MDSPAN_INLINE_FUNCTION constexpr bool +one_slice_out_of_bounds(const IndexType &ext, const Slice &slice) { + using common_t = + std::common_type_t<decltype(detail::first_of(slice)), IndexType>; + return static_cast<common_t>(detail::first_of(slice)) == + static_cast<common_t>(ext); +} + +template <size_t... RankIndices, class IndexType, size_t... Exts, + class... Slices> +MDSPAN_INLINE_FUNCTION constexpr bool +any_slice_out_of_bounds_helper(std::index_sequence<RankIndices...>, + const extents<IndexType, Exts...> &exts, + const Slices &... slices) { + return _MDSPAN_FOLD_OR( + (one_slice_out_of_bounds(exts.extent(RankIndices), slices))); +} + +template <class IndexType, size_t... Exts, class... Slices> +MDSPAN_INLINE_FUNCTION constexpr bool +any_slice_out_of_bounds(const extents<IndexType, Exts...> &exts, + const Slices &... slices) { + return any_slice_out_of_bounds_helper( + std::make_index_sequence<sizeof...(Slices)>(), exts, slices...); +} + +// constructs sub strides +template <class SrcMapping, class... slice_strides, size_t... InvMapIdxs> +MDSPAN_INLINE_FUNCTION constexpr auto construct_sub_strides( + const SrcMapping &src_mapping, std::index_sequence<InvMapIdxs...>, + const std::tuple<slice_strides...> &slices_stride_factor) { + using index_type = typename SrcMapping::index_type; + return std::array<typename SrcMapping::index_type, sizeof...(InvMapIdxs)>{ + (static_cast<index_type>(src_mapping.stride(InvMapIdxs)) * + static_cast<index_type>(std::get<InvMapIdxs>(slices_stride_factor)))...}; +} + +template<class SliceSpecifier, class IndexType> +struct is_range_slice { + constexpr static bool value = + std::is_same_v<SliceSpecifier, full_extent_t> || + std::is_convertible_v<SliceSpecifier, + std::tuple<IndexType, IndexType>>; +}; + +template<class SliceSpecifier, class IndexType> +constexpr bool is_range_slice_v = is_range_slice<SliceSpecifier, IndexType>::value; + +template<class SliceSpecifier, class IndexType> +struct is_index_slice { + constexpr static bool value = std::is_convertible_v<SliceSpecifier, IndexType>; +}; + +template<class SliceSpecifier, class IndexType> +constexpr bool is_index_slice_v = is_index_slice<SliceSpecifier, IndexType>::value; + +} // namespace detail + +//********************************** +// layout_left submdspan_mapping +//********************************* +namespace detail { + +// Figure out whether to preserve layout_left +template <class IndexType, size_t SubRank, class IndexSequence, + class... SliceSpecifiers> +struct deduce_layout_left_submapping; + +template <class IndexType, size_t SubRank, size_t... Idx, + class... SliceSpecifiers> +struct deduce_layout_left_submapping< + IndexType, SubRank, std::index_sequence<Idx...>, SliceSpecifiers...> { + + using count_range = index_sequence_scan_impl< + 0, (is_index_slice_v<SliceSpecifiers, IndexType> ? 0 : 1)...>; + + constexpr static int gap_len = + (((Idx > 0 && count_range::get(Idx) == 1 && + is_index_slice_v<SliceSpecifiers, IndexType>) + ? 1 + : 0) + + ... + 0); + + MDSPAN_INLINE_FUNCTION + constexpr static bool layout_left_value() { + // Use layout_left for rank 0 + if constexpr (SubRank == 0) { + return true; + // Use layout_left for rank 1 result if leftmost slice specifier is range like + } else if constexpr (SubRank == 1) { + return ((Idx > 0 || is_range_slice_v<SliceSpecifiers, IndexType>)&&...); + } else { + // Preserve if leftmost SubRank-1 slices are full_extent_t and + // the slice at idx Subrank - 1 is a range and + // for idx > SubRank the slice is an index + return ((((Idx < SubRank - 1) && std::is_same_v<SliceSpecifiers, full_extent_t>) || + ((Idx == SubRank - 1) && is_range_slice_v<SliceSpecifiers, IndexType>) || + ((Idx > SubRank - 1) && is_index_slice_v<SliceSpecifiers, IndexType>)) && ...); + } +#if defined(__NVCC__) && !defined(__CUDA_ARCH__) && defined(__GNUC__) + __builtin_unreachable(); +#endif + } + + MDSPAN_INLINE_FUNCTION + constexpr static bool layout_left_padded_value() { + // Technically could also keep layout_left_padded for SubRank==0 + // and SubRank==1 with leftmost slice specifier being a contiguous range + // but we intercept these cases separately + + // In all other cases: + // leftmost slice must be range + // then there can be a gap with index slices + // then SubRank - 2 full_extent slices + // then another range slice + // then more index slices + // e.g. R I I I F F F R I I for obtaining a rank-5 from a rank-10 + return ((((Idx == 0) && is_range_slice_v<SliceSpecifiers, IndexType>) || + ((Idx > 0 && Idx <= gap_len) && is_index_slice_v<SliceSpecifiers, IndexType>) || + ((Idx > gap_len && Idx < gap_len + SubRank - 1) && std::is_same_v<SliceSpecifiers, full_extent_t>) || + ((Idx == gap_len + SubRank - 1) && is_range_slice_v<SliceSpecifiers, IndexType>) || + ((Idx > gap_len + SubRank - 1) && is_index_slice_v<SliceSpecifiers, IndexType>)) && ... ); + } +}; + +} // namespace detail + +// Actual submdspan mapping call +template <class Extents> +template <class... SliceSpecifiers> +MDSPAN_INLINE_FUNCTION constexpr auto +layout_left::mapping<Extents>::submdspan_mapping_impl( + SliceSpecifiers... slices) const { + + // compute sub extents + using src_ext_t = Extents; + auto dst_ext = submdspan_extents(extents(), slices...); + using dst_ext_t = decltype(dst_ext); + + // figure out sub layout type + using deduce_layout = detail::deduce_layout_left_submapping< + typename dst_ext_t::index_type, dst_ext_t::rank(), + std::make_index_sequence<src_ext_t::rank()>, + SliceSpecifiers...>; + + using dst_layout_t = std::conditional_t< + deduce_layout::layout_left_value(), layout_left, + std::conditional_t< + deduce_layout::layout_left_padded_value(), + MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_left_padded<dynamic_extent>, + layout_stride>>; + using dst_mapping_t = typename dst_layout_t::template mapping<dst_ext_t>; + + // Figure out if any slice's lower bound equals the corresponding extent. + // If so, bypass evaluating the layout mapping. This fixes LWG Issue 4060. + const bool out_of_bounds = + detail::any_slice_out_of_bounds(this->extents(), slices...); + auto offset = static_cast<size_t>( + out_of_bounds ? this->required_span_size() + : this->operator()(detail::first_of(slices)...)); + + if constexpr (std::is_same_v<dst_layout_t, layout_left>) { + // layout_left case + return submdspan_mapping_result<dst_mapping_t>{dst_mapping_t(dst_ext), + offset}; + } else if constexpr (std::is_same_v<dst_layout_t, + MDSPAN_IMPL_PROPOSED_NAMESPACE:: + layout_left_padded<dynamic_extent>>) { + return submdspan_mapping_result<dst_mapping_t>{ + dst_mapping_t(dst_ext, stride(1 + deduce_layout::gap_len)), offset}; + } else { + // layout_stride case + auto inv_map = detail::inv_map_rank(std::integral_constant<size_t, 0>(), + std::index_sequence<>(), slices...); + return submdspan_mapping_result<dst_mapping_t> { + dst_mapping_t(dst_ext, + detail::construct_sub_strides( + *this, inv_map, +// HIP needs deduction guides to have markups so we need to be explicit +// NVCC 11.0 has a bug with deduction guide here, tested that 11.2 does not have +// the issue But Clang-CUDA also doesn't accept the use of deduction guide so +// disable it for CUDA altogether +#if defined(_MDSPAN_HAS_HIP) || defined(_MDSPAN_HAS_CUDA) + std::tuple<decltype(detail::stride_of(slices))...>{ + detail::stride_of(slices)...})), +#else + std::tuple{detail::stride_of(slices)...})), +#endif + offset + }; + } +#if defined(__NVCC__) && !defined(__CUDA_ARCH__) && defined(__GNUC__) + __builtin_unreachable(); +#endif +} + +//********************************** +// layout_right submdspan_mapping +//********************************* +namespace detail { + +// Figure out whether to preserve layout_right +template <class IndexType, size_t SubRank, class IndexSequence, + class... SliceSpecifiers> +struct deduce_layout_right_submapping; + +template <class IndexType, size_t SubRank, size_t... Idx, + class... SliceSpecifiers> +struct deduce_layout_right_submapping< + IndexType, SubRank, std::index_sequence<Idx...>, SliceSpecifiers...> { + + static constexpr size_t Rank = sizeof...(Idx); + using count_range = index_sequence_scan_impl< + 0, (std::is_convertible_v<SliceSpecifiers, IndexType> ? 0 : 1)...>; + //__static_partial_sums<!std::is_convertible_v<SliceSpecifiers, + // IndexType>...>; + constexpr static int gap_len = + (((Idx < Rank - 1 && count_range::get(Idx) == SubRank - 1 && + std::is_convertible_v<SliceSpecifiers, IndexType>) + ? 1 + : 0) + + ... + 0); + + MDSPAN_INLINE_FUNCTION + constexpr static bool layout_right_value() { + // Use layout_right for rank 0 + if constexpr (SubRank == 0) { + return true; + // Use layout_right for rank 1 result if rightmost slice specifier is range like + } else if constexpr (SubRank == 1) { + return ((Idx < Rank - 1 || is_range_slice_v<SliceSpecifiers, IndexType>)&&...); + } else { + // Preserve if rightmost SubRank-1 slices are full_extent_t and + // the slice at idx Rank-Subrank is a range and + // for idx < Rank - SubRank the slice is an index + return ((((Idx >= Rank - SubRank) && std::is_same_v<SliceSpecifiers, full_extent_t>) || + ((Idx == Rank - SubRank) && is_range_slice_v<SliceSpecifiers, IndexType>) || + ((Idx < Rank - SubRank) && is_index_slice_v<SliceSpecifiers, IndexType>)) && ...); + } +#if defined(__NVCC__) && !defined(__CUDA_ARCH__) && defined(__GNUC__) + __builtin_unreachable(); +#endif + } + + MDSPAN_INLINE_FUNCTION + constexpr static bool layout_right_padded_value() { + // Technically could also keep layout_right_padded for SubRank==0 + // and SubRank==1 with rightmost slice specifier being a contiguous range + // but we intercept these cases separately + + // In all other cases: + // rightmost slice must be range + // then there can be a gap with index slices + // then SubRank - 2 full_extent slices + // then another range slice + // then more index slices + // e.g. I I R F F F I I I R for obtaining a rank-5 from a rank-10 + return ((((Idx == Rank - 1) && is_range_slice_v<SliceSpecifiers, IndexType>) || + ((Idx >= Rank - gap_len - 1 && Idx < Rank - 1) && is_index_slice_v<SliceSpecifiers, IndexType>) || + ((Idx > Rank - gap_len - SubRank && Idx < Rank - gap_len - 1) && std::is_same_v<SliceSpecifiers, full_extent_t>) || + ((Idx == Rank - gap_len - SubRank) && is_range_slice_v<SliceSpecifiers, IndexType>) || + ((Idx < Rank - gap_len - SubRank) && is_index_slice_v<SliceSpecifiers, IndexType>)) && ... ); + } +}; + +} // namespace detail + +// Actual submdspan mapping call +template <class Extents> +template <class... SliceSpecifiers> +MDSPAN_INLINE_FUNCTION constexpr auto +layout_right::mapping<Extents>::submdspan_mapping_impl( + SliceSpecifiers... slices) const { + + // compute sub extents + using src_ext_t = Extents; + auto dst_ext = submdspan_extents(extents(), slices...); + using dst_ext_t = decltype(dst_ext); + + // figure out sub layout type + using deduce_layout = detail::deduce_layout_right_submapping< + typename dst_ext_t::index_type, dst_ext_t::rank(), + std::make_index_sequence<src_ext_t::rank()>, + SliceSpecifiers...>; + + using dst_layout_t = std::conditional_t< + deduce_layout::layout_right_value(), layout_right, + std::conditional_t< + deduce_layout::layout_right_padded_value(), + MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_right_padded<dynamic_extent>, + layout_stride>>; + using dst_mapping_t = typename dst_layout_t::template mapping<dst_ext_t>; + + // Figure out if any slice's lower bound equals the corresponding extent. + // If so, bypass evaluating the layout mapping. This fixes LWG Issue 4060. + const bool out_of_bounds = + detail::any_slice_out_of_bounds(this->extents(), slices...); + auto offset = static_cast<size_t>( + out_of_bounds ? this->required_span_size() + : this->operator()(detail::first_of(slices)...)); + + if constexpr (std::is_same_v<dst_layout_t, layout_right>) { + // layout_right case + return submdspan_mapping_result<dst_mapping_t>{dst_mapping_t(dst_ext), + offset}; + } else if constexpr (std::is_same_v< + dst_layout_t, + MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_right_padded< + dynamic_extent>>) { + return submdspan_mapping_result<dst_mapping_t>{ + dst_mapping_t(dst_ext, + stride(src_ext_t::rank() - 2 - deduce_layout::gap_len)), + offset}; + } else { + // layout_stride case + auto inv_map = detail::inv_map_rank(std::integral_constant<size_t, 0>(), + std::index_sequence<>(), slices...); + return submdspan_mapping_result<dst_mapping_t> { + dst_mapping_t(dst_ext, + detail::construct_sub_strides( + *this, inv_map, +// HIP needs deduction guides to have markups so we need to be explicit +// NVCC 11.0 has a bug with deduction guide here, tested that 11.2 does not have +// the issue But Clang-CUDA also doesn't accept the use of deduction guide so +// disable it for CUDA altogether +#if defined(_MDSPAN_HAS_HIP) || defined(_MDSPAN_HAS_CUDA) + std::tuple<decltype(detail::stride_of(slices))...>{ + detail::stride_of(slices)...})), +#else + std::tuple{detail::stride_of(slices)...})), +#endif + offset + }; + } +#if defined(__NVCC__) && !defined(__CUDA_ARCH__) && defined(__GNUC__) + __builtin_unreachable(); +#endif +} + +//********************************** +// layout_stride submdspan_mapping +//********************************* +template <class Extents> +template <class... SliceSpecifiers> +MDSPAN_INLINE_FUNCTION constexpr auto +layout_stride::mapping<Extents>::submdspan_mapping_impl( + SliceSpecifiers... slices) const { + auto dst_ext = submdspan_extents(extents(), slices...); + using dst_ext_t = decltype(dst_ext); + auto inv_map = detail::inv_map_rank(std::integral_constant<size_t, 0>(), + std::index_sequence<>(), slices...); + using dst_mapping_t = typename layout_stride::template mapping<dst_ext_t>; + + // Figure out if any slice's lower bound equals the corresponding extent. + // If so, bypass evaluating the layout mapping. This fixes LWG Issue 4060. + const bool out_of_bounds = + detail::any_slice_out_of_bounds(this->extents(), slices...); + auto offset = static_cast<size_t>( + out_of_bounds ? this->required_span_size() + : this->operator()(detail::first_of(slices)...)); + + return submdspan_mapping_result<dst_mapping_t> { + dst_mapping_t(dst_ext, + detail::construct_sub_strides( + *this, inv_map, +// HIP needs deduction guides to have markups so we need to be explicit +// NVCC 11.0 has a bug with deduction guide here, tested that 11.2 does not have +// the issue +#if defined(_MDSPAN_HAS_HIP) || \ + (defined(__NVCC__) && \ + (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__ * 10) < 1120) + std::tuple<decltype(detail::stride_of(slices))...>( + detail::stride_of(slices)...))), +#else + std::tuple(detail::stride_of(slices)...))), +#endif + offset + }; +} + +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE + +#if defined __NVCC__ +#ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ +#pragma nv_diagnostic pop +#else +#ifdef __CUDA_ARCH__ +#pragma diagnostic pop +#endif +#endif +#elif defined __NVCOMPILER +#pragma diagnostic pop +#endif diff --git a/tests/integration/deps/mdspan/include/experimental/__p2642_bits/layout_padded.hpp b/tests/integration/deps/mdspan/include/experimental/__p2642_bits/layout_padded.hpp new file mode 100644 index 0000000000000000000000000000000000000000..99e24fa450ae7973c3ad44e314bbefbc5d82185a --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/__p2642_bits/layout_padded.hpp @@ -0,0 +1,852 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER +#pragma once + +#include <cassert> +#include "layout_padded_fwd.hpp" +#include "../__p0009_bits/dynamic_extent.hpp" +#include "../__p0009_bits/extents.hpp" +#include "../__p0009_bits/mdspan.hpp" +#include "../__p0009_bits/layout_left.hpp" +#include "../__p0009_bits/layout_right.hpp" +#include "../__p0009_bits/layout_stride.hpp" + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace MDSPAN_IMPL_PROPOSED_NAMESPACE { + +namespace detail { +template<class _T> +MDSPAN_INLINE_FUNCTION +constexpr _T +find_next_multiple(_T alignment, _T offset) +{ + if ( alignment == 0 ) { + return _T(0); + } else { + return ( ( offset + alignment - 1 ) / alignment) * alignment; + } +} + +template <class _ExtentsType, size_t _PaddingValue, size_t _ExtentToPadIdx> +MDSPAN_INLINE_FUNCTION constexpr size_t get_actual_static_padding_value() { + constexpr auto rank = _ExtentsType::rank(); + + if constexpr (rank <= typename _ExtentsType::rank_type(1)) { + return 0; + } else if constexpr (_PaddingValue != dynamic_extent && + _ExtentsType::static_extent(_ExtentToPadIdx) != + dynamic_extent) { + static_assert( + (_PaddingValue != 0) || + (_ExtentsType::static_extent(_ExtentToPadIdx) == 0), + "padding stride can be 0 only if " + "extents_type::static_extent(extent-to-pad) is 0 or dynamic_extent"); + return find_next_multiple(_PaddingValue, + _ExtentsType::static_extent(_ExtentToPadIdx)); + } else { + return dynamic_extent; + } + // Missing return statement warning from NVCC +#ifdef __NVCC__ + return 0; +#endif +} + +template <size_t _PaddingValue, typename _Extents, size_t _ExtentToPadIdx, size_t _Rank, typename Enabled = void> +struct static_array_type_for_padded_extent +{ + static constexpr size_t padding_value = _PaddingValue; + using index_type = typename _Extents::index_type; + using extents_type = _Extents; + using type = ::MDSPAN_IMPL_STANDARD_NAMESPACE::detail::maybe_static_array< + index_type, size_t, dynamic_extent, + detail::get_actual_static_padding_value<extents_type, padding_value, + _ExtentToPadIdx>()>; +}; + +template <size_t _PaddingValue, typename _Extents, size_t _ExtentToPadIdx, size_t Rank> +struct static_array_type_for_padded_extent<_PaddingValue, _Extents, + _ExtentToPadIdx, Rank, std::enable_if_t<Rank <= 1>> { + using index_type = typename _Extents::index_type; + using extents_type = _Extents; + using type = + ::MDSPAN_IMPL_STANDARD_NAMESPACE::detail::maybe_static_array< + index_type, size_t, dynamic_extent, 0>; +}; + +template <size_t _PaddingValue, typename _Extents, size_t _ExtentToPadIdx> +struct padded_extent { + static constexpr size_t padding_value = _PaddingValue; + using index_type = typename _Extents::index_type; + using extents_type = _Extents; + using static_array_type = typename static_array_type_for_padded_extent< + padding_value, _Extents, _ExtentToPadIdx, _Extents::rank()>::type; + + static constexpr auto static_value() { return static_array_type::static_value(0); } + + MDSPAN_INLINE_FUNCTION + static constexpr static_array_type + init_padding(const _Extents &exts) { + if constexpr ((_Extents::rank() > 1) && (padding_value == dynamic_extent)) { + return {exts.extent(_ExtentToPadIdx)}; + } else { + return init_padding(exts, padding_value); + } + // Missing return statement warning from NVCC +#ifdef __NVCC__ + return {}; +#endif + } + + MDSPAN_INLINE_FUNCTION static constexpr static_array_type + init_padding([[maybe_unused]] const _Extents &exts, + [[maybe_unused]] index_type pv) { + if constexpr (_Extents::rank() > 1) { + return {find_next_multiple(pv, + exts.extent(_ExtentToPadIdx))}; + } else { + return {}; + } + // Missing return statement warning from NVCC +#ifdef __NVCC__ + return {}; +#endif + } + + template <typename _Mapping, size_t _PaddingStrideIdx> + MDSPAN_INLINE_FUNCTION static constexpr static_array_type + init_padding([[maybe_unused]] const _Mapping &other_mapping, + std::integral_constant<size_t, _PaddingStrideIdx>) { + if constexpr (_Extents::rank() > 1) { + return {other_mapping.stride(_PaddingStrideIdx)}; + } else { + return {}; + } + // Missing return statement warning from NVCC +#ifdef __NVCC__ + return {}; +#endif + } +}; +} // namespace detail + +template <size_t PaddingValue> +template <class Extents> +class layout_left_padded<PaddingValue>::mapping { +public: + static constexpr size_t padding_value = PaddingValue; + + using extents_type = Extents; + using index_type = typename extents_type::index_type; + using size_type = typename extents_type::size_type; + using rank_type = typename extents_type::rank_type; + using layout_type = layout_left_padded<padding_value>; + +#ifndef MDSPAN_INTERNAL_TEST +private: +#endif // MDSPAN_INTERNAL_TEST + + static constexpr rank_type padded_stride_idx = detail::layout_padded_constants<layout_type, extents_type>::padded_stride_idx; + static constexpr rank_type extent_to_pad_idx = detail::layout_padded_constants<layout_type, extents_type>::extent_to_pad_idx; + + static_assert((padding_value != 0) + || (extents_type::static_extent(extent_to_pad_idx) == 0) + || (extents_type::static_extent(extent_to_pad_idx) == dynamic_extent), + "out of bounds access for rank 0"); + + using padded_stride_type = detail::padded_extent< padding_value, extents_type, extent_to_pad_idx >; + + static constexpr size_t static_padding_stride = padded_stride_type::static_value(); + + typename padded_stride_type::static_array_type padded_stride = {}; + extents_type exts = {}; + + MDSPAN_INLINE_FUNCTION constexpr index_type + compute_offset(std::index_sequence<>) const { + return 0; + } + + template <size_t Rank, class IndexOffset> + MDSPAN_INLINE_FUNCTION constexpr index_type + compute_offset(std::index_sequence<Rank>, IndexOffset index_offset) const { + return index_offset; + } + + template <size_t... Ranks, class... IndexOffsets> + MDSPAN_INLINE_FUNCTION constexpr index_type + compute_offset(std::index_sequence<Ranks...>, + IndexOffsets... index_offsets) const { + index_type indices[] = {static_cast<index_type>(index_offsets)...}; + // self-recursive fold trick from + // https://github.com/llvm/llvm-project/blob/96e1914aa2e6d8966acbfbe2f4d184201f1aa318/libcxx/include/mdspan/layout_left.h#L144 + index_type res = 0; + ((res = indices[extents_type::rank() - 1 - Ranks] + + ((extents_type::rank() - 1 - Ranks) == extent_to_pad_idx + ? padded_stride.value(0) + : exts.extent(extents_type::rank() - 1 - Ranks)) * + res), + ...); + return res; + } + +public: +#if !MDSPAN_HAS_CXX_20 + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr mapping() + : mapping(extents_type{}) + {} +#else + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr mapping() + requires(static_padding_stride != dynamic_extent) = default; + + MDSPAN_INLINE_FUNCTION + constexpr mapping() + requires(static_padding_stride == dynamic_extent) + : mapping(extents_type{}) + {} +#endif + + MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping(const mapping&) noexcept = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED mapping& operator=(const mapping&) noexcept = default; + + /** + * Initializes the mapping with the given extents. + * + * \param ext the given extents + */ + MDSPAN_INLINE_FUNCTION + constexpr mapping(const extents_type& ext) + : padded_stride(padded_stride_type::init_padding(ext)), exts(ext) + {} + + /** + * Initializes the mapping with the given extents and the specified padding value. + * + * This overload participates in overload resolution only if `is_convertible_v<Size, index_type>` + * is `true` and `is_nothrow_constructible_v<index_type, Size>` is `true` + * + * \param ext the given extents + * \param padding_value the padding value + */ + MDSPAN_TEMPLATE_REQUIRES( + class _Size, + /* requires */ ( + std::is_convertible_v<_Size, index_type> + && std::is_nothrow_constructible_v<index_type, _Size> + ) + ) + MDSPAN_INLINE_FUNCTION + constexpr mapping(const extents_type &ext, _Size dynamic_padding_value) + : padded_stride(padded_stride_type::init_padding(ext, dynamic_padding_value)), exts(ext) + { + assert((padding_value == dynamic_extent) || (static_cast<index_type>(padding_value) == static_cast<index_type>(dynamic_padding_value))); + } + + /** + * Converting constructor from `layout_left::mapping`. + * + * This overload participates in overload resolution only if + * `is_constructible_v<extents_type, OtherExtents>` is true. If + * `OtherExtents::rank() > 1` then one of `padding_value`, `static_extent(0)`, + * or `OtherExtents::static_extent(0)` must be `dynamic_extent`; otherwise, + * `OtherExtents::static_extent(0)` must be equal to the least multiple of + * `padding_value` greater than or equal to `extents_type::static_extent(0)` + */ + MDSPAN_TEMPLATE_REQUIRES( + class _OtherExtents, + /* requires */ (std::is_constructible_v<extents_type, _OtherExtents>)) + MDSPAN_CONDITIONAL_EXPLICIT( + (!std::is_convertible_v<_OtherExtents, extents_type>)) + MDSPAN_INLINE_FUNCTION + constexpr mapping(const layout_left::mapping<_OtherExtents> &other_mapping) + : padded_stride(padded_stride_type::init_padding( + other_mapping, + std::integral_constant<size_t, padded_stride_idx>{})), + exts(other_mapping.extents()) { + static_assert( + (_OtherExtents::rank() > 1) || + (static_padding_stride != dynamic_extent) || + (_OtherExtents::static_extent(extent_to_pad_idx) != dynamic_extent) || + (static_padding_stride == + _OtherExtents::static_extent(extent_to_pad_idx))); + } + + /** + * Converting constructor from `layout_stride::mapping`. + * + * This overload participates in overload resolution only if + * `is_constructible_v<extents_type, OtherExtents>` is true + */ + MDSPAN_TEMPLATE_REQUIRES( + class _OtherExtents, + /* requires */ (std::is_constructible_v<extents_type, _OtherExtents>)) + MDSPAN_CONDITIONAL_EXPLICIT((extents_type::rank() > 0)) + MDSPAN_INLINE_FUNCTION + constexpr mapping(const layout_stride::mapping<_OtherExtents> &other_mapping) + : padded_stride(padded_stride_type::init_padding( + other_mapping, + std::integral_constant<size_t, padded_stride_idx>{})), + exts(other_mapping.extents()) {} + + /** + * Converting constructor from `layout_left_padded::mapping`. + * + * This overload participates in overload resolution only if + * `is_constructible_v<extents_type, OtherExtents>` is true. Either + * `padding_value` or `OtherPaddingStride` must be `std::dynamic_extent`, or + * `padding_value == OtherPaddingStride`. + */ + MDSPAN_TEMPLATE_REQUIRES( + class _Mapping, + /* requires */ (detail::is_layout_left_padded_mapping<_Mapping>::value + &&std::is_constructible_v< + extents_type, typename _Mapping::extents_type>)) + MDSPAN_CONDITIONAL_EXPLICIT((extents_type::rank() > 1 && + (padding_value == dynamic_extent || + _Mapping::padding_value == dynamic_extent))) + MDSPAN_INLINE_FUNCTION + constexpr mapping(const _Mapping &other_mapping) + : padded_stride(padded_stride_type::init_padding( + other_mapping, + std::integral_constant<size_t, padded_stride_idx>{})), + exts(other_mapping.extents()) { + static_assert(padding_value == dynamic_extent || + _Mapping::padding_value == dynamic_extent || + padding_value == _Mapping::padding_value); + } + + /** + * Converting constructor from `layout_right_padded::mapping`. + * + * This overload participates in overload resolution only if + * `extents_type::rank()` is 0 or 1 and `is_constructible_v<extents_type, + * OtherExtents>` is `true`. + */ + MDSPAN_TEMPLATE_REQUIRES( + class _Mapping, + /* requires */ (detail::is_layout_right_padded_mapping<_Mapping>::value + &&extents_type::rank() <= 1 && + std::is_constructible_v<extents_type, + typename _Mapping::extents_type>)) + MDSPAN_CONDITIONAL_EXPLICIT( + (!std::is_convertible_v<typename _Mapping::extents_type, extents_type>)) + MDSPAN_INLINE_FUNCTION + constexpr mapping(const _Mapping &other_mapping) noexcept + : padded_stride(padded_stride_type::init_padding( + other_mapping.extents(), + other_mapping.extents().extent(extent_to_pad_idx))), + exts(other_mapping.extents()) {} + + MDSPAN_INLINE_FUNCTION constexpr const extents_type & + extents() const noexcept { + return exts; + } + + MDSPAN_INLINE_FUNCTION constexpr std::array<index_type, extents_type::rank()> + strides() const noexcept { + if constexpr (extents_type::rank() == 0) { + return {}; + } else if constexpr (extents_type::rank() == 1) { + return {1}; + } else { + index_type value = 1; + std::array<index_type, extents_type::rank()> s{}; + s[extent_to_pad_idx] = value; + value *= padded_stride.value(0); + for (rank_type r = extent_to_pad_idx + 1; r < extents_type::rank() - 1; + ++r) { + s[r] = value; + value *= exts.extent(r); + } + s[extents_type::rank() - 1] = value; + return s; + } + } + + MDSPAN_INLINE_FUNCTION constexpr index_type + required_span_size() const noexcept { + if constexpr (extents_type::rank() == 0) { + return 1; + } else if constexpr (extents_type::rank() == 1) { + return exts.extent(0); + } else { + index_type value = padded_stride.value(0); + for (rank_type r = 1; r < extents_type::rank(); ++r) { + value *= exts.extent(r); + } + return value; + } + } + + /** + * Return the mapping given the provided indices per rank. + * + * This overload participates in overload resolution only if: + * - `sizeof...(Indices) == extents_type::rank()`, + * - `(is_convertible_v<Indices, index_type> && ...) is true`, and + * - (is_nothrow_constructible_v<index_type, Indices> && ...) is true. + */ + MDSPAN_TEMPLATE_REQUIRES( + class... _Indices, + /* requires */ (sizeof...(_Indices) == extents_type::rank() && + (::MDSPAN_IMPL_STANDARD_NAMESPACE::detail:: + are_valid_indices<index_type, _Indices...>()))) + MDSPAN_INLINE_FUNCTION constexpr size_t + operator()(_Indices... idxs) const noexcept { +#if !defined(NDEBUG) + ::MDSPAN_IMPL_STANDARD_NAMESPACE::detail::check_all_indices(this->extents(), + idxs...); +#endif // ! NDEBUG + return compute_offset(std::index_sequence_for<_Indices...>{}, idxs...); + } + + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_unique() noexcept { + return true; + } + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_exhaustive() noexcept { + return (extents_type::rank() <= rank_type(1)) || + (extents_type::static_extent(extent_to_pad_idx) != dynamic_extent && + extents_type::static_extent(extent_to_pad_idx) == + padded_stride_type::static_value()); + } + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_strided() noexcept { + return true; + } + + MDSPAN_INLINE_FUNCTION static constexpr bool is_unique() noexcept { + return true; + } + MDSPAN_INLINE_FUNCTION constexpr bool is_exhaustive() const noexcept { + return (extents_type::rank() < 2) || + (exts.extent(extent_to_pad_idx) == padded_stride.value(0)); + } + MDSPAN_INLINE_FUNCTION static constexpr bool is_strided() noexcept { + return true; + } + + MDSPAN_INLINE_FUNCTION + constexpr index_type stride(rank_type r) const noexcept { + assert(r < extents_type::rank()); + if (r == 0) + return index_type(1); + + index_type value = padded_stride.value(0); + for (rank_type k = 1; k < r; k++) + value *= exts.extent(k); + + return value; + } + + /** + * Equality operator between `layout_left_padded`s + * + * This overload only participates in overload resolution if + * `OtherExtents::rank() == extents_type::rank()`. + * + * \note There is currently a difference from p2642r2, where this function is + * specified as taking `layout_left_padded< padding_value >::mapping< + * Extents>`. However, this makes `padding_value` non-deducible. + */ + MDSPAN_TEMPLATE_REQUIRES( + class _Mapping, + /* requires */ (detail::is_layout_left_padded_mapping<_Mapping>::value && + (_Mapping::extents_type::rank() == extents_type::rank()))) + MDSPAN_INLINE_FUNCTION friend constexpr bool + operator==(const mapping &left, const _Mapping &right) noexcept { + // Workaround for some compilers not short-circuiting properly with + // compile-time checks i.e. we can't access stride(_padding_stride_idx) of a + // rank 0 mapping + bool strides_equal = true; + if constexpr (extents_type::rank() > rank_type(1)) { + strides_equal = + left.stride(padded_stride_idx) == right.stride(padded_stride_idx); + } + return (left.extents() == right.extents()) && strides_equal; + } + +#if !MDSPAN_HAS_CXX_20 + /** + * Inequality operator between `layout_left_padded`s + * + * This overload only participates in overload resolution if + * `OtherExtents::rank() == extents_type::rank()`. + */ + MDSPAN_TEMPLATE_REQUIRES( + class _Mapping, + /* requires */ (detail::is_layout_left_padded_mapping<_Mapping>::value && + (_Mapping::extents_type::rank() == extents_type::rank()))) + MDSPAN_INLINE_FUNCTION friend constexpr bool + operator!=(const mapping &left, const _Mapping &right) noexcept { + return !(left == right); + } +#endif + + // [mdspan.submdspan.mapping], submdspan mapping specialization + template<class... SliceSpecifiers> + constexpr auto submdspan_mapping_impl( + SliceSpecifiers... slices) const; + + template<class... SliceSpecifiers> + friend constexpr auto submdspan_mapping( + const mapping& src, SliceSpecifiers... slices) { + return src.submdspan_mapping_impl(slices...); + } +}; + +template <size_t PaddingValue> +template <class Extents> +class layout_right_padded<PaddingValue>::mapping { +public: + static constexpr size_t padding_value = PaddingValue; + + using extents_type = Extents; + using index_type = typename extents_type::index_type; + using size_type = typename extents_type::size_type; + using rank_type = typename extents_type::rank_type; + using layout_type = layout_right_padded<padding_value>; + +#ifndef MDSPAN_INTERNAL_TEST + private: +#endif // MDSPAN_INTERNAL_TEST + + static constexpr rank_type padded_stride_idx = detail::layout_padded_constants<layout_type, extents_type>::padded_stride_idx; + static constexpr rank_type extent_to_pad_idx = detail::layout_padded_constants<layout_type, extents_type>::extent_to_pad_idx; + + static_assert((padding_value != 0) + || (extents_type::static_extent(extent_to_pad_idx) == 0) + || (extents_type::static_extent(extent_to_pad_idx) == dynamic_extent), + "if padding stride is 0, static_extent(extent-to-pad-rank) must also be 0 or dynamic_extent"); + + using padded_stride_type = detail::padded_extent< padding_value, extents_type, extent_to_pad_idx >; + static constexpr size_t static_padding_stride = padded_stride_type::static_value(); + + typename padded_stride_type::static_array_type padded_stride = {}; + extents_type exts = {}; + + MDSPAN_INLINE_FUNCTION constexpr index_type + compute_offset(std::index_sequence<>) const { + return 0; + } + + template <size_t Rank, class IndexOffset> + MDSPAN_INLINE_FUNCTION constexpr index_type + compute_offset(std::index_sequence<Rank>, IndexOffset index_offset) const { + return index_offset; + } + + template <size_t... Ranks, class... IndexOffsets> + MDSPAN_INLINE_FUNCTION constexpr index_type + compute_offset(std::index_sequence<Ranks...>, + IndexOffsets... index_offsets) const { + // self-recursive fold trick from + // https://github.com/llvm/llvm-project/blob/4d9771741d40cc9cfcccb6b033f43689d36b705a/libcxx/include/mdspan/layout_right.h#L141 + index_type res = 0; + ((res = static_cast<index_type>(index_offsets) + + (Ranks == extent_to_pad_idx ? padded_stride.value(0) + : exts.extent(Ranks)) * + res), + ...); + return res; + } + +public: +#if !MDSPAN_HAS_CXX_20 + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr mapping() + : mapping(extents_type{}) + {} +#else + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr mapping() + requires(static_padding_stride != dynamic_extent) = default; + + MDSPAN_INLINE_FUNCTION + constexpr mapping() + requires(static_padding_stride == dynamic_extent) + : mapping(extents_type{}) + {} +#endif + + MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping(const mapping&) noexcept = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED mapping& operator=(const mapping&) noexcept = default; + + /** + * Initializes the mapping with the given extents. + * + * \param ext the given extents + */ + MDSPAN_INLINE_FUNCTION + constexpr mapping(const extents_type &ext) + : padded_stride(padded_stride_type::init_padding(ext)), exts(ext) {} + + /** + * Initializes the mapping with the given extents and the specified padding value. + * + * This overload participates in overload resolution only if `is_convertible_v<Size, index_type>` + * is `true` and `is_nothrow_constructible_v<index_type, Size>` is `true` + * + * \param ext the given extents + * \param padding_value the padding value + */ + MDSPAN_TEMPLATE_REQUIRES( + class _Size, + /* requires */ ( + std::is_convertible_v<_Size, index_type> + && std::is_nothrow_constructible_v<index_type, _Size> + ) + ) + MDSPAN_INLINE_FUNCTION + constexpr mapping(const extents_type &ext, _Size dynamic_padding_value) + : padded_stride(padded_stride_type::init_padding(ext, static_cast<index_type>(dynamic_padding_value))), + exts(ext) { + assert((padding_value == dynamic_extent) || + (static_cast<index_type>(padding_value) == static_cast<index_type>(dynamic_padding_value))); + } + + /** + * Converting constructor from `layout_right::mapping`. + * + * This overload participates in overload resolution only if `is_constructible_v<extents_type, OtherExtents>` is true. + * If `OtherExtents::rank() > 1` then one of `padding_value`, `static_extent(0)`, or `OtherExtents::static_extent(0)` must be `dynamic_extent`; + * otherwise, `OtherExtents::static_extent(0)` must be equal to the least multiple of `padding_value` greater than or equal to `extents_type::static_extent(0)` + */ + MDSPAN_TEMPLATE_REQUIRES( + class _OtherExtents, + /* requires */ (std::is_constructible_v<extents_type, _OtherExtents>)) + MDSPAN_CONDITIONAL_EXPLICIT( + (!std::is_convertible_v<_OtherExtents, extents_type>)) + MDSPAN_INLINE_FUNCTION + constexpr mapping(const layout_right::mapping<_OtherExtents> &other_mapping) + : padded_stride(padded_stride_type::init_padding( + other_mapping, + std::integral_constant<size_t, padded_stride_idx>{})), + exts(other_mapping.extents()) { + static_assert( + (_OtherExtents::rank() > 1) || + (padded_stride_type::static_value() != dynamic_extent) || + (_OtherExtents::static_extent(extent_to_pad_idx) != dynamic_extent) || + (padded_stride_type::static_value() == + _OtherExtents::static_extent(extent_to_pad_idx))); + } + + /** + * Converting constructor from `layout_stride::mapping`. + * + * This overload participates in overload resolution only if + * `is_constructible_v<extents_type, OtherExtents>` is true + */ + MDSPAN_TEMPLATE_REQUIRES( + class _OtherExtents, + /* requires */ (std::is_constructible_v<extents_type, _OtherExtents>)) + MDSPAN_CONDITIONAL_EXPLICIT((extents_type::rank() > 0)) + MDSPAN_INLINE_FUNCTION + constexpr mapping(const layout_stride::mapping<_OtherExtents> &other_mapping) + : padded_stride(padded_stride_type::init_padding( + other_mapping, + std::integral_constant<size_t, padded_stride_idx>{})), + exts(other_mapping.extents()) {} + + /** + * Converting constructor from `layout_right_padded::mapping`. + * + * This overload participates in overload resolution only if + * `is_constructible_v<extents_type, OtherExtents>` is true. Either + * `padding_value` or `OtherPaddingStride` must be `std::dynamic_extent`, or + * `padding_value == OtherPaddingStride`. + */ + MDSPAN_TEMPLATE_REQUIRES( + class _Mapping, + /* requires */ (detail::is_layout_right_padded_mapping<_Mapping>::value + &&std::is_constructible_v< + extents_type, typename _Mapping::extents_type>)) + MDSPAN_CONDITIONAL_EXPLICIT((extents_type::rank() > 1 && + (padding_value == dynamic_extent || + _Mapping::padding_value == dynamic_extent))) + MDSPAN_INLINE_FUNCTION + constexpr mapping(const _Mapping &other_mapping) + : padded_stride(padded_stride_type::init_padding( + other_mapping, + std::integral_constant<size_t, padded_stride_idx>{})), + exts(other_mapping.extents()) { + static_assert(padding_value == dynamic_extent || + _Mapping::padding_value == dynamic_extent || + padding_value == _Mapping::padding_value); + } + + /** + * Converting constructor from `layout_left_padded::mapping`. + * + * This overload participates in overload resolution only if + * `extents_type::rank()` is 0 or 1 and `is_constructible_v<extents_type, + * OtherExtents>` is `true`. + */ + MDSPAN_TEMPLATE_REQUIRES( + class _Mapping, + /* requires */ (detail::is_layout_left_padded_mapping<_Mapping>::value + &&extents_type::rank() <= 1 && + std::is_constructible_v<extents_type, + typename _Mapping::extents_type>)) + MDSPAN_CONDITIONAL_EXPLICIT( + (!std::is_convertible_v<typename _Mapping::extents_type, extents_type>)) + MDSPAN_INLINE_FUNCTION + constexpr mapping(const _Mapping &other_mapping) noexcept + : padded_stride(padded_stride_type::init_padding( + other_mapping.extents(), + other_mapping.extents().extent(extent_to_pad_idx))), + exts(other_mapping.extents()) {} + + MDSPAN_INLINE_FUNCTION constexpr const extents_type & + extents() const noexcept { + return exts; + } + + MDSPAN_INLINE_FUNCTION constexpr std::array<index_type, extents_type::rank()> + strides() const noexcept { + if constexpr (extents_type::rank() == 0) { + return {}; + } else if constexpr (extents_type::rank() == 1) { + return {1}; + } else { + index_type value = 1; + std::array<index_type, extents_type::rank()> s{}; + s[extent_to_pad_idx] = value; + value *= padded_stride.value(0); + for (rank_type r = extent_to_pad_idx - 1; r > 0; --r) { + s[r] = value; + value *= exts.extent(r); + } + s[0] = value; + return s; + } + } + + MDSPAN_INLINE_FUNCTION constexpr index_type + required_span_size() const noexcept { + if constexpr (extents_type::rank() == 0) { + return 1; + } else if constexpr (extents_type::rank() == 1) { + return exts.extent(0); + } else { + index_type value = 1; + for (rank_type r = 0; r < extent_to_pad_idx; ++r) { + value *= exts.extent(r); + } + return value * padded_stride.value(0); + } + } + + /** + * Return the mapping given the provided indices per rank. + * + * This overload participates in overload resolution only if: + * - `sizeof...(Indices) == extents_type::rank()`, + * - `(is_convertible_v<Indices, index_type> && ...) is true`, and + * - (is_nothrow_constructible_v<index_type, Indices> && ...) is true. + */ + MDSPAN_TEMPLATE_REQUIRES( + class... _Indices, + /* requires */ (sizeof...(_Indices) == extents_type::rank() && + (::MDSPAN_IMPL_STANDARD_NAMESPACE::detail:: + are_valid_indices<index_type, _Indices...>()))) + MDSPAN_INLINE_FUNCTION constexpr size_t + operator()(_Indices... idxs) const noexcept { + return compute_offset(std::index_sequence_for<_Indices...>{}, idxs...); + } + + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_unique() noexcept { + return true; + } + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_exhaustive() noexcept { + return (extents_type::rank() <= rank_type(1)) || + (extents_type::static_extent(extent_to_pad_idx) != dynamic_extent && + extents_type::static_extent(extent_to_pad_idx) == + padded_stride_type::static_value()); + } + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_strided() noexcept { + return true; + } + + MDSPAN_INLINE_FUNCTION static constexpr bool is_unique() noexcept { + return true; + } + MDSPAN_INLINE_FUNCTION constexpr bool is_exhaustive() const noexcept { + return (extents_type::rank() < 2) || + (exts.extent(extent_to_pad_idx) == padded_stride.value(0)); + } + MDSPAN_INLINE_FUNCTION static constexpr bool is_strided() noexcept { + return true; + } + + MDSPAN_INLINE_FUNCTION constexpr index_type + stride(rank_type r) const noexcept { + assert(r < extents_type::rank()); + if (r == extents_type::rank() - 1) + return index_type(1); + + index_type value = padded_stride.value(0); + for (rank_type k = extents_type::rank() - 2; k > r; k--) + value *= exts.extent(k); + + return value; + } + + /** + * Equality operator between `layout_right_padded`s + * + * This overload only participates in overload resolution if + * `OtherExtents::rank() == extents_type::rank()`. + * + * \note There is currently a difference from p2642r2, where this function is + * specified as taking `layout_right_padded< padding_value >::mapping< + * Extents>`. However, this makes `padding_value` non-deducible. + */ + MDSPAN_TEMPLATE_REQUIRES( + class _Mapping, + /* requires */ (detail::is_layout_right_padded_mapping<_Mapping>::value && + (_Mapping::extents_type::rank() == extents_type::rank()))) + MDSPAN_INLINE_FUNCTION friend constexpr bool + operator==(const mapping &left, const _Mapping &right) noexcept { + // Workaround for some compilers not short-circuiting properly with + // compile-time checks i.e. we can't access stride(_padding_stride_idx) of a + // rank 0 mapping + bool strides_equal = true; + if constexpr (extents_type::rank() > rank_type(1)) { + strides_equal = + left.stride(padded_stride_idx) == right.stride(padded_stride_idx); + } + return (left.extents() == right.extents()) && strides_equal; + } + +#if !MDSPAN_HAS_CXX_20 + /** + * Inequality operator between `layout_right_padded`s + * + * This overload only participates in overload resolution if + * `OtherExtents::rank() == extents_type::rank()`. + */ + MDSPAN_TEMPLATE_REQUIRES( + class _Mapping, + /* requires */ (detail::is_layout_right_padded_mapping<_Mapping>::value && + (_Mapping::extents_type::rank() == extents_type::rank()))) + MDSPAN_INLINE_FUNCTION friend constexpr bool + operator!=(const mapping &left, const _Mapping &right) noexcept { + return !(left == right); + } +#endif +}; +} +} diff --git a/tests/integration/deps/mdspan/include/experimental/__p2642_bits/layout_padded_fwd.hpp b/tests/integration/deps/mdspan/include/experimental/__p2642_bits/layout_padded_fwd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b5eaac952bc8998c9d9710d8c46004f3c5da6b10 --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/__p2642_bits/layout_padded_fwd.hpp @@ -0,0 +1,131 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER +#pragma once + +#include <cassert> +#include "../__p0009_bits/dynamic_extent.hpp" +#include "../__p0009_bits/utility.hpp" + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace MDSPAN_IMPL_PROPOSED_NAMESPACE { + +template <size_t padding_value = dynamic_extent> +struct layout_left_padded { + template <class _Extents> + class mapping; +}; + +template <size_t padding_value = dynamic_extent> +struct layout_right_padded { + template <class _Extents> + class mapping; +}; + +namespace detail { +// The layout_padded_constants structs are only useful if rank > 1, otherwise they may wrap +template <class _Layout, class _ExtentsType> +struct layout_padded_constants; + +template <class _ExtentsType, size_t _PaddingStride> +struct layout_padded_constants<layout_left_padded<_PaddingStride>, _ExtentsType> +{ + using rank_type = typename _ExtentsType::rank_type; + static constexpr rank_type padded_stride_idx = 1; + static constexpr rank_type extent_to_pad_idx = 0; +}; + +template <class _ExtentsType, size_t _PaddingStride> +struct layout_padded_constants<layout_right_padded<_PaddingStride>, _ExtentsType> +{ + using rank_type = typename _ExtentsType::rank_type; + static constexpr rank_type padded_stride_idx = _ExtentsType::rank() - 2; + static constexpr rank_type extent_to_pad_idx = _ExtentsType::rank() - 1; +}; + +template <class _Layout> +struct is_layout_left_padded : std::false_type {}; + +template <size_t _PaddingStride> +struct is_layout_left_padded<layout_left_padded<_PaddingStride>> : std::true_type {}; + +template <class _Mapping, class _Enabled = void> +struct is_layout_left_padded_mapping : std::false_type {}; + +template <class _Mapping> +struct is_layout_left_padded_mapping<_Mapping, + std::enable_if_t<std::is_same<_Mapping, typename layout_left_padded<_Mapping::padding_value>::template mapping<typename _Mapping::extents_type>>::value>> + : std::true_type {}; + +template <class _Layout> +struct is_layout_right_padded : std::false_type {}; + +template <size_t _PaddingStride> +struct is_layout_right_padded<layout_right_padded<_PaddingStride>> : std::true_type {}; + +template <class _Mapping, class _Enabled = void> +struct is_layout_right_padded_mapping : std::false_type {}; + +template <class _Mapping> +struct is_layout_right_padded_mapping<_Mapping, + std::enable_if_t<std::is_same<_Mapping, typename layout_right_padded<_Mapping::padding_value>::template mapping<typename _Mapping::extents_type>>::value>> + : std::true_type {}; + + +template <class _LayoutExtentsType, class _PaddedLayoutMappingType> +constexpr void check_padded_layout_converting_constructor_mandates(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank<0>) {} + +template <class _LayoutExtentsType, class _PaddedLayoutMappingType> +constexpr void check_padded_layout_converting_constructor_mandates(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank<1>) {} + +template <class _LayoutExtentsType, class _PaddedLayoutMappingType, std::size_t N> +constexpr void check_padded_layout_converting_constructor_mandates(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank<N>) +{ + using extents_type = typename _PaddedLayoutMappingType::extents_type; + constexpr auto padding_value = _PaddedLayoutMappingType::padding_value; + constexpr auto idx = layout_padded_constants<typename _PaddedLayoutMappingType::layout_type, _LayoutExtentsType >::extent_to_pad_idx; + + constexpr auto statically_determinable = + (_LayoutExtentsType::static_extent(idx) != dynamic_extent) && + (extents_type::static_extent(idx) != dynamic_extent) && + (padding_value != dynamic_extent); + + static_assert(not statically_determinable or + (padding_value == 0 + ? _LayoutExtentsType::static_extent(idx) == 0 + : _LayoutExtentsType::static_extent(idx) % padding_value == 0), + ""); +} + +template <typename _ExtentsType, typename _OtherMapping> +constexpr void check_padded_layout_converting_constructor_preconditions(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank<0>, + const _OtherMapping&) {} +template <typename _ExtentsType, typename _OtherMapping> +constexpr void check_padded_layout_converting_constructor_preconditions(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank<1>, + const _OtherMapping&) {} +template <typename _ExtentsType, typename _OtherMapping, std::size_t N> +constexpr void check_padded_layout_converting_constructor_preconditions(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank<N>, + const _OtherMapping &other_mapping) { + constexpr auto padded_stride_idx = + layout_padded_constants<typename _OtherMapping::layout_type, + _ExtentsType>::padded_stride_idx; + constexpr auto extent_to_pad_idx = layout_padded_constants<typename _OtherMapping::layout_type, _ExtentsType>::extent_to_pad_idx; + MDSPAN_IMPL_PRECONDITION(other_mapping.stride(padded_stride_idx) == other_mapping.extents().extent(extent_to_pad_idx)); +} + + +} +} +} diff --git a/tests/integration/deps/mdspan/include/experimental/mdarray b/tests/integration/deps/mdspan/include/experimental/mdarray new file mode 100644 index 0000000000000000000000000000000000000000..642d1f5ad9e704ade699bbe89d97344c34e6d5a3 --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/mdarray @@ -0,0 +1,28 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#pragma once + +#ifndef MDSPAN_IMPL_STANDARD_NAMESPACE + #define MDSPAN_IMPL_STANDARD_NAMESPACE std +#endif + +#ifndef MDSPAN_IMPL_PROPOSED_NAMESPACE + #define MDSPAN_IMPL_PROPOSED_NAMESPACE experimental +#endif + +#include "mdspan" +#include "../mdspan/mdarray.hpp" diff --git a/tests/integration/deps/mdspan/include/experimental/mdspan b/tests/integration/deps/mdspan/include/experimental/mdspan new file mode 100644 index 0000000000000000000000000000000000000000..e8ba715ec2fd7afad65627d2eb4b8fcc720b8a3e --- /dev/null +++ b/tests/integration/deps/mdspan/include/experimental/mdspan @@ -0,0 +1,39 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#pragma once + +#ifndef MDSPAN_IMPL_STANDARD_NAMESPACE + #define MDSPAN_IMPL_STANDARD_NAMESPACE std +#endif + +#ifndef MDSPAN_IMPL_PROPOSED_NAMESPACE + #define MDSPAN_IMPL_PROPOSED_NAMESPACE experimental +#endif + +#include "../mdspan/mdspan.hpp" + +// backward compatibility import into experimental +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { + namespace MDSPAN_IMPL_PROPOSED_NAMESPACE { + using ::MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan; + using ::MDSPAN_IMPL_STANDARD_NAMESPACE::extents; + using ::MDSPAN_IMPL_STANDARD_NAMESPACE::layout_left; + using ::MDSPAN_IMPL_STANDARD_NAMESPACE::layout_right; + using ::MDSPAN_IMPL_STANDARD_NAMESPACE::layout_stride; + using ::MDSPAN_IMPL_STANDARD_NAMESPACE::default_accessor; + } +} diff --git a/tests/integration/deps/mdspan/include/mdspan/mdarray.hpp b/tests/integration/deps/mdspan/include/mdspan/mdarray.hpp new file mode 100644 index 0000000000000000000000000000000000000000..fd8f61c52f1b1836aea89f653e263cd33537e218 --- /dev/null +++ b/tests/integration/deps/mdspan/include/mdspan/mdarray.hpp @@ -0,0 +1,31 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#ifndef MDARRAY_HPP_ +#define MDARRAY_HPP_ + +#ifndef MDSPAN_IMPL_STANDARD_NAMESPACE + #define MDSPAN_IMPL_STANDARD_NAMESPACE Kokkos +#endif + +#ifndef MDSPAN_IMPL_PROPOSED_NAMESPACE + #define MDSPAN_IMPL_PROPOSED_NAMESPACE Experimental +#endif + +#include "mdspan.hpp" +#include "../experimental/__p1684_bits/mdarray.hpp" + +#endif // MDARRAY_HPP_ diff --git a/tests/integration/deps/mdspan/include/mdspan/mdspan.hpp b/tests/integration/deps/mdspan/include/mdspan/mdspan.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4a0e354ffd02183e0c738c9c716c1228b3b180bc --- /dev/null +++ b/tests/integration/deps/mdspan/include/mdspan/mdspan.hpp @@ -0,0 +1,43 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#ifndef MDSPAN_HPP_ +#define MDSPAN_HPP_ + +#ifndef MDSPAN_IMPL_STANDARD_NAMESPACE + #define MDSPAN_IMPL_STANDARD_NAMESPACE Kokkos +#endif + +#ifndef MDSPAN_IMPL_PROPOSED_NAMESPACE + #define MDSPAN_IMPL_PROPOSED_NAMESPACE Experimental +#endif + +#include "../experimental/__p0009_bits/default_accessor.hpp" +#include "../experimental/__p0009_bits/full_extent_t.hpp" +#include "../experimental/__p0009_bits/mdspan.hpp" +#include "../experimental/__p0009_bits/dynamic_extent.hpp" +#include "../experimental/__p0009_bits/extents.hpp" +#include "../experimental/__p0009_bits/layout_stride.hpp" +#include "../experimental/__p0009_bits/layout_left.hpp" +#include "../experimental/__p0009_bits/layout_right.hpp" +#include "../experimental/__p0009_bits/macros.hpp" +#if MDSPAN_HAS_CXX_17 +#include "../experimental/__p2642_bits/layout_padded.hpp" +#include "../experimental/__p2630_bits/submdspan.hpp" +#endif +#include "../experimental/__p2389_bits/dims.hpp" + +#endif // MDSPAN_HPP_ diff --git a/tests/integration/expected/SimpleClasses.cpp b/tests/integration/expected/SimpleClasses.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9d27ebeddd488ac23d3b87d126f3d17b312e936a --- /dev/null +++ b/tests/integration/expected/SimpleClasses.cpp @@ -0,0 +1,12 @@ +#include <cstdint> + +class Point { +public: + const int64_t & getX() const { + return this->x; + } +private: + int64_t x; + int64_t y; + int64_t z; +}; diff --git a/tests/integration/scripts/SimpleClasses.py b/tests/integration/scripts/SimpleClasses.py new file mode 100644 index 0000000000000000000000000000000000000000..a729d1f125e69c5ef3d178f2ed8be1d65cd423a8 --- /dev/null +++ b/tests/integration/scripts/SimpleClasses.py @@ -0,0 +1,18 @@ +from pystencilssfg import SourceFileGenerator + +with SourceFileGenerator() as sfg: + + sfg.include("<cstdint>") + + sfg.klass("Point")( + sfg.public( + sfg.method("getX", returns="const int64_t &", const=True)( + "return this->x;" + ) + ), + sfg.private( + sfg.var("x", "int64_t"), + sfg.var("y", "int64_t"), + sfg.var("z", "int64_t") + ) + ) diff --git a/tests/integration/scripts/SimpleJacobi.py b/tests/integration/scripts/SimpleJacobi.py new file mode 100644 index 0000000000000000000000000000000000000000..199419c541fb4aef68d44a1baf7cfc2f28d67e86 --- /dev/null +++ b/tests/integration/scripts/SimpleJacobi.py @@ -0,0 +1,23 @@ +import sympy as sp + +from pystencils import fields, kernel + +from pystencilssfg import SourceFileGenerator +from pystencilssfg.lang.cpp import mdspan_ref + +with SourceFileGenerator() as sfg: + u_src, u_dst, f = fields("u_src, u_dst, f(1) : double[2D]", layout="fzyx") + h = sp.Symbol("h") + + @kernel + def poisson_jacobi(): + u_dst[0,0] @= (h**2 * f[0, 0] + u_src[1, 0] + u_src[-1, 0] + u_src[0, 1] + u_src[0, -1]) / 4 + + poisson_kernel = sfg.kernels.create(poisson_jacobi) + + sfg.function("jacobi_smooth")( + sfg.map_field(u_src, mdspan_ref(u_src)), + sfg.map_field(u_dst, mdspan_ref(u_dst)), + sfg.map_field(f, mdspan_ref(f)), + sfg.call(poisson_kernel) + ) \ No newline at end of file diff --git a/tests/integration/test_generator_scripts.py b/tests/integration/test_generator_scripts.py new file mode 100644 index 0000000000000000000000000000000000000000..33f747820b966bd7a2630e9497cbb00a7937340c --- /dev/null +++ b/tests/integration/test_generator_scripts.py @@ -0,0 +1,81 @@ +import pytest + +from dataclasses import dataclass + +import os +from os import path +import shutil +import subprocess + +THIS_DIR = path.split(__file__)[0] +SCRIPTS_DIR = path.join(THIS_DIR, "scripts") +EXPECTED_DIR = path.join(THIS_DIR, "expected") + + +@dataclass +class ScriptInfo: + script_name: str + expected_outputs: tuple[str, ...] + + compilable_output: str | None = None + compile_cmd: str = f"g++ --std=c++17 -I {THIS_DIR}/deps/mdspan/include" + + +SCRIPTS = [ + ScriptInfo("SimpleJacobi", ("h", "cpp"), compilable_output="cpp"), + ScriptInfo("SimpleClasses", ("h", "cpp")), +] + + +@pytest.mark.parametrize("script_info", SCRIPTS) +def test_generator_script(script_info: ScriptInfo): + script_name = script_info.script_name + script_file = path.join(SCRIPTS_DIR, script_name + ".py") + + output_dir = path.join(THIS_DIR, "out", script_name) + if path.exists(output_dir): + shutil.rmtree(output_dir) + os.makedirs(output_dir, exist_ok=True) + + args = ["python", script_file, "--sfg-output-dir", output_dir] + + result = subprocess.run(args) + + if result.returncode != 0: + raise AssertionError(f"Generator script {script_name} failed.") + + # Check generated files + expected_files = set( + [f"{script_name}.{ext}" for ext in script_info.expected_outputs] + ) + output_files = set(os.listdir(output_dir)) + assert output_files == expected_files + + # Check against expected output + for ofile in output_files: + expected_file = path.join(EXPECTED_DIR, ofile) + actual_file = path.join(output_dir, ofile) + + if not path.exists(expected_file): + continue + + with open(expected_file, "r") as f: + expected = f.read() + + with open(actual_file, "r") as f: + actual = f.read() + + # Strip whitespace + expected = "".join(expected.split()) + actual = "".join(expected.split()) + + assert expected == actual + + # Check if output compiles + if (ext := script_info.compilable_output) is not None: + compilable_file = f"{script_name}.{ext}" + compile_args = script_info.compile_cmd.split() + ["-c", compilable_file] + compile_result = subprocess.run(compile_args, cwd=output_dir) + + if compile_result.returncode != 0: + raise AssertionError("Compilation of generated files failed.") diff --git a/versioneer.py b/versioneer.py deleted file mode 100644 index 1e3753e63fb6d5a204a7a350b65ed69aa7ab66cd..0000000000000000000000000000000000000000 --- a/versioneer.py +++ /dev/null @@ -1,2277 +0,0 @@ - -# Version: 0.29 - -"""The Versioneer - like a rocketeer, but for versions. - -The Versioneer -============== - -* like a rocketeer, but for versions! -* https://github.com/python-versioneer/python-versioneer -* Brian Warner -* License: Public Domain (Unlicense) -* Compatible with: Python 3.7, 3.8, 3.9, 3.10, 3.11 and pypy3 -* [![Latest Version][pypi-image]][pypi-url] -* [![Build Status][travis-image]][travis-url] - -This is a tool for managing a recorded version number in setuptools-based -python projects. The goal is to remove the tedious and error-prone "update -the embedded version string" step from your release process. Making a new -release should be as easy as recording a new tag in your version-control -system, and maybe making new tarballs. - - -## Quick Install - -Versioneer provides two installation modes. The "classic" vendored mode installs -a copy of versioneer into your repository. The experimental build-time dependency mode -is intended to allow you to skip this step and simplify the process of upgrading. - -### Vendored mode - -* `pip install versioneer` to somewhere in your $PATH - * A [conda-forge recipe](https://github.com/conda-forge/versioneer-feedstock) is - available, so you can also use `conda install -c conda-forge versioneer` -* add a `[tool.versioneer]` section to your `pyproject.toml` or a - `[versioneer]` section to your `setup.cfg` (see [Install](INSTALL.md)) - * Note that you will need to add `tomli; python_version < "3.11"` to your - build-time dependencies if you use `pyproject.toml` -* run `versioneer install --vendor` in your source tree, commit the results -* verify version information with `python setup.py version` - -### Build-time dependency mode - -* `pip install versioneer` to somewhere in your $PATH - * A [conda-forge recipe](https://github.com/conda-forge/versioneer-feedstock) is - available, so you can also use `conda install -c conda-forge versioneer` -* add a `[tool.versioneer]` section to your `pyproject.toml` or a - `[versioneer]` section to your `setup.cfg` (see [Install](INSTALL.md)) -* add `versioneer` (with `[toml]` extra, if configuring in `pyproject.toml`) - to the `requires` key of the `build-system` table in `pyproject.toml`: - ```toml - [build-system] - requires = ["setuptools", "versioneer[toml]"] - build-backend = "setuptools.build_meta" - ``` -* run `versioneer install --no-vendor` in your source tree, commit the results -* verify version information with `python setup.py version` - -## Version Identifiers - -Source trees come from a variety of places: - -* a version-control system checkout (mostly used by developers) -* a nightly tarball, produced by build automation -* a snapshot tarball, produced by a web-based VCS browser, like github's - "tarball from tag" feature -* a release tarball, produced by "setup.py sdist", distributed through PyPI - -Within each source tree, the version identifier (either a string or a number, -this tool is format-agnostic) can come from a variety of places: - -* ask the VCS tool itself, e.g. "git describe" (for checkouts), which knows - about recent "tags" and an absolute revision-id -* the name of the directory into which the tarball was unpacked -* an expanded VCS keyword ($Id$, etc) -* a `_version.py` created by some earlier build step - -For released software, the version identifier is closely related to a VCS -tag. Some projects use tag names that include more than just the version -string (e.g. "myproject-1.2" instead of just "1.2"), in which case the tool -needs to strip the tag prefix to extract the version identifier. For -unreleased software (between tags), the version identifier should provide -enough information to help developers recreate the same tree, while also -giving them an idea of roughly how old the tree is (after version 1.2, before -version 1.3). Many VCS systems can report a description that captures this, -for example `git describe --tags --dirty --always` reports things like -"0.7-1-g574ab98-dirty" to indicate that the checkout is one revision past the -0.7 tag, has a unique revision id of "574ab98", and is "dirty" (it has -uncommitted changes). - -The version identifier is used for multiple purposes: - -* to allow the module to self-identify its version: `myproject.__version__` -* to choose a name and prefix for a 'setup.py sdist' tarball - -## Theory of Operation - -Versioneer works by adding a special `_version.py` file into your source -tree, where your `__init__.py` can import it. This `_version.py` knows how to -dynamically ask the VCS tool for version information at import time. - -`_version.py` also contains `$Revision$` markers, and the installation -process marks `_version.py` to have this marker rewritten with a tag name -during the `git archive` command. As a result, generated tarballs will -contain enough information to get the proper version. - -To allow `setup.py` to compute a version too, a `versioneer.py` is added to -the top level of your source tree, next to `setup.py` and the `setup.cfg` -that configures it. This overrides several distutils/setuptools commands to -compute the version when invoked, and changes `setup.py build` and `setup.py -sdist` to replace `_version.py` with a small static file that contains just -the generated version data. - -## Installation - -See [INSTALL.md](./INSTALL.md) for detailed installation instructions. - -## Version-String Flavors - -Code which uses Versioneer can learn about its version string at runtime by -importing `_version` from your main `__init__.py` file and running the -`get_versions()` function. From the "outside" (e.g. in `setup.py`), you can -import the top-level `versioneer.py` and run `get_versions()`. - -Both functions return a dictionary with different flavors of version -information: - -* `['version']`: A condensed version string, rendered using the selected - style. This is the most commonly used value for the project's version - string. The default "pep440" style yields strings like `0.11`, - `0.11+2.g1076c97`, or `0.11+2.g1076c97.dirty`. See the "Styles" section - below for alternative styles. - -* `['full-revisionid']`: detailed revision identifier. For Git, this is the - full SHA1 commit id, e.g. "1076c978a8d3cfc70f408fe5974aa6c092c949ac". - -* `['date']`: Date and time of the latest `HEAD` commit. For Git, it is the - commit date in ISO 8601 format. This will be None if the date is not - available. - -* `['dirty']`: a boolean, True if the tree has uncommitted changes. Note that - this is only accurate if run in a VCS checkout, otherwise it is likely to - be False or None - -* `['error']`: if the version string could not be computed, this will be set - to a string describing the problem, otherwise it will be None. It may be - useful to throw an exception in setup.py if this is set, to avoid e.g. - creating tarballs with a version string of "unknown". - -Some variants are more useful than others. Including `full-revisionid` in a -bug report should allow developers to reconstruct the exact code being tested -(or indicate the presence of local changes that should be shared with the -developers). `version` is suitable for display in an "about" box or a CLI -`--version` output: it can be easily compared against release notes and lists -of bugs fixed in various releases. - -The installer adds the following text to your `__init__.py` to place a basic -version in `YOURPROJECT.__version__`: - - from ._version import get_versions - __version__ = get_versions()['version'] - del get_versions - -## Styles - -The setup.cfg `style=` configuration controls how the VCS information is -rendered into a version string. - -The default style, "pep440", produces a PEP440-compliant string, equal to the -un-prefixed tag name for actual releases, and containing an additional "local -version" section with more detail for in-between builds. For Git, this is -TAG[+DISTANCE.gHEX[.dirty]] , using information from `git describe --tags ---dirty --always`. For example "0.11+2.g1076c97.dirty" indicates that the -tree is like the "1076c97" commit but has uncommitted changes (".dirty"), and -that this commit is two revisions ("+2") beyond the "0.11" tag. For released -software (exactly equal to a known tag), the identifier will only contain the -stripped tag, e.g. "0.11". - -Other styles are available. See [details.md](details.md) in the Versioneer -source tree for descriptions. - -## Debugging - -Versioneer tries to avoid fatal errors: if something goes wrong, it will tend -to return a version of "0+unknown". To investigate the problem, run `setup.py -version`, which will run the version-lookup code in a verbose mode, and will -display the full contents of `get_versions()` (including the `error` string, -which may help identify what went wrong). - -## Known Limitations - -Some situations are known to cause problems for Versioneer. This details the -most significant ones. More can be found on Github -[issues page](https://github.com/python-versioneer/python-versioneer/issues). - -### Subprojects - -Versioneer has limited support for source trees in which `setup.py` is not in -the root directory (e.g. `setup.py` and `.git/` are *not* siblings). The are -two common reasons why `setup.py` might not be in the root: - -* Source trees which contain multiple subprojects, such as - [Buildbot](https://github.com/buildbot/buildbot), which contains both - "master" and "slave" subprojects, each with their own `setup.py`, - `setup.cfg`, and `tox.ini`. Projects like these produce multiple PyPI - distributions (and upload multiple independently-installable tarballs). -* Source trees whose main purpose is to contain a C library, but which also - provide bindings to Python (and perhaps other languages) in subdirectories. - -Versioneer will look for `.git` in parent directories, and most operations -should get the right version string. However `pip` and `setuptools` have bugs -and implementation details which frequently cause `pip install .` from a -subproject directory to fail to find a correct version string (so it usually -defaults to `0+unknown`). - -`pip install --editable .` should work correctly. `setup.py install` might -work too. - -Pip-8.1.1 is known to have this problem, but hopefully it will get fixed in -some later version. - -[Bug #38](https://github.com/python-versioneer/python-versioneer/issues/38) is tracking -this issue. The discussion in -[PR #61](https://github.com/python-versioneer/python-versioneer/pull/61) describes the -issue from the Versioneer side in more detail. -[pip PR#3176](https://github.com/pypa/pip/pull/3176) and -[pip PR#3615](https://github.com/pypa/pip/pull/3615) contain work to improve -pip to let Versioneer work correctly. - -Versioneer-0.16 and earlier only looked for a `.git` directory next to the -`setup.cfg`, so subprojects were completely unsupported with those releases. - -### Editable installs with setuptools <= 18.5 - -`setup.py develop` and `pip install --editable .` allow you to install a -project into a virtualenv once, then continue editing the source code (and -test) without re-installing after every change. - -"Entry-point scripts" (`setup(entry_points={"console_scripts": ..})`) are a -convenient way to specify executable scripts that should be installed along -with the python package. - -These both work as expected when using modern setuptools. When using -setuptools-18.5 or earlier, however, certain operations will cause -`pkg_resources.DistributionNotFound` errors when running the entrypoint -script, which must be resolved by re-installing the package. This happens -when the install happens with one version, then the egg_info data is -regenerated while a different version is checked out. Many setup.py commands -cause egg_info to be rebuilt (including `sdist`, `wheel`, and installing into -a different virtualenv), so this can be surprising. - -[Bug #83](https://github.com/python-versioneer/python-versioneer/issues/83) describes -this one, but upgrading to a newer version of setuptools should probably -resolve it. - - -## Updating Versioneer - -To upgrade your project to a new release of Versioneer, do the following: - -* install the new Versioneer (`pip install -U versioneer` or equivalent) -* edit `setup.cfg` and `pyproject.toml`, if necessary, - to include any new configuration settings indicated by the release notes. - See [UPGRADING](./UPGRADING.md) for details. -* re-run `versioneer install --[no-]vendor` in your source tree, to replace - `SRC/_version.py` -* commit any changed files - -## Future Directions - -This tool is designed to make it easily extended to other version-control -systems: all VCS-specific components are in separate directories like -src/git/ . The top-level `versioneer.py` script is assembled from these -components by running make-versioneer.py . In the future, make-versioneer.py -will take a VCS name as an argument, and will construct a version of -`versioneer.py` that is specific to the given VCS. It might also take the -configuration arguments that are currently provided manually during -installation by editing setup.py . Alternatively, it might go the other -direction and include code from all supported VCS systems, reducing the -number of intermediate scripts. - -## Similar projects - -* [setuptools_scm](https://github.com/pypa/setuptools_scm/) - a non-vendored build-time - dependency -* [minver](https://github.com/jbweston/miniver) - a lightweight reimplementation of - versioneer -* [versioningit](https://github.com/jwodder/versioningit) - a PEP 518-based setuptools - plugin - -## License - -To make Versioneer easier to embed, all its code is dedicated to the public -domain. The `_version.py` that it creates is also in the public domain. -Specifically, both are released under the "Unlicense", as described in -https://unlicense.org/. - -[pypi-image]: https://img.shields.io/pypi/v/versioneer.svg -[pypi-url]: https://pypi.python.org/pypi/versioneer/ -[travis-image]: -https://img.shields.io/travis/com/python-versioneer/python-versioneer.svg -[travis-url]: https://travis-ci.com/github/python-versioneer/python-versioneer - -""" -# pylint:disable=invalid-name,import-outside-toplevel,missing-function-docstring -# pylint:disable=missing-class-docstring,too-many-branches,too-many-statements -# pylint:disable=raise-missing-from,too-many-lines,too-many-locals,import-error -# pylint:disable=too-few-public-methods,redefined-outer-name,consider-using-with -# pylint:disable=attribute-defined-outside-init,too-many-arguments - -import configparser -import errno -import json -import os -import re -import subprocess -import sys -from pathlib import Path -from typing import Any, Callable, cast, Dict, List, Optional, Tuple, Union -from typing import NoReturn -import functools - -have_tomllib = True -if sys.version_info >= (3, 11): - import tomllib -else: - try: - import tomli as tomllib - except ImportError: - have_tomllib = False - - -class VersioneerConfig: - """Container for Versioneer configuration parameters.""" - - VCS: str - style: str - tag_prefix: str - versionfile_source: str - versionfile_build: Optional[str] - parentdir_prefix: Optional[str] - verbose: Optional[bool] - - -def get_root() -> str: - """Get the project root directory. - - We require that all commands are run from the project root, i.e. the - directory that contains setup.py, setup.cfg, and versioneer.py . - """ - root = os.path.realpath(os.path.abspath(os.getcwd())) - setup_py = os.path.join(root, "setup.py") - pyproject_toml = os.path.join(root, "pyproject.toml") - versioneer_py = os.path.join(root, "versioneer.py") - if not ( - os.path.exists(setup_py) - or os.path.exists(pyproject_toml) - or os.path.exists(versioneer_py) - ): - # allow 'python path/to/setup.py COMMAND' - root = os.path.dirname(os.path.realpath(os.path.abspath(sys.argv[0]))) - setup_py = os.path.join(root, "setup.py") - pyproject_toml = os.path.join(root, "pyproject.toml") - versioneer_py = os.path.join(root, "versioneer.py") - if not ( - os.path.exists(setup_py) - or os.path.exists(pyproject_toml) - or os.path.exists(versioneer_py) - ): - err = ("Versioneer was unable to run the project root directory. " - "Versioneer requires setup.py to be executed from " - "its immediate directory (like 'python setup.py COMMAND'), " - "or in a way that lets it use sys.argv[0] to find the root " - "(like 'python path/to/setup.py COMMAND').") - raise VersioneerBadRootError(err) - try: - # Certain runtime workflows (setup.py install/develop in a setuptools - # tree) execute all dependencies in a single python process, so - # "versioneer" may be imported multiple times, and python's shared - # module-import table will cache the first one. So we can't use - # os.path.dirname(__file__), as that will find whichever - # versioneer.py was first imported, even in later projects. - my_path = os.path.realpath(os.path.abspath(__file__)) - me_dir = os.path.normcase(os.path.splitext(my_path)[0]) - vsr_dir = os.path.normcase(os.path.splitext(versioneer_py)[0]) - if me_dir != vsr_dir and "VERSIONEER_PEP518" not in globals(): - print("Warning: build in %s is using versioneer.py from %s" - % (os.path.dirname(my_path), versioneer_py)) - except NameError: - pass - return root - - -def get_config_from_root(root: str) -> VersioneerConfig: - """Read the project setup.cfg file to determine Versioneer config.""" - # This might raise OSError (if setup.cfg is missing), or - # configparser.NoSectionError (if it lacks a [versioneer] section), or - # configparser.NoOptionError (if it lacks "VCS="). See the docstring at - # the top of versioneer.py for instructions on writing your setup.cfg . - root_pth = Path(root) - pyproject_toml = root_pth / "pyproject.toml" - setup_cfg = root_pth / "setup.cfg" - section: Union[Dict[str, Any], configparser.SectionProxy, None] = None - if pyproject_toml.exists() and have_tomllib: - try: - with open(pyproject_toml, 'rb') as fobj: - pp = tomllib.load(fobj) - section = pp['tool']['versioneer'] - except (tomllib.TOMLDecodeError, KeyError) as e: - print(f"Failed to load config from {pyproject_toml}: {e}") - print("Try to load it from setup.cfg") - if not section: - parser = configparser.ConfigParser() - with open(setup_cfg) as cfg_file: - parser.read_file(cfg_file) - parser.get("versioneer", "VCS") # raise error if missing - - section = parser["versioneer"] - - # `cast`` really shouldn't be used, but its simplest for the - # common VersioneerConfig users at the moment. We verify against - # `None` values elsewhere where it matters - - cfg = VersioneerConfig() - cfg.VCS = section['VCS'] - cfg.style = section.get("style", "") - cfg.versionfile_source = cast(str, section.get("versionfile_source")) - cfg.versionfile_build = section.get("versionfile_build") - cfg.tag_prefix = cast(str, section.get("tag_prefix")) - if cfg.tag_prefix in ("''", '""', None): - cfg.tag_prefix = "" - cfg.parentdir_prefix = section.get("parentdir_prefix") - if isinstance(section, configparser.SectionProxy): - # Make sure configparser translates to bool - cfg.verbose = section.getboolean("verbose") - else: - cfg.verbose = section.get("verbose") - - return cfg - - -class NotThisMethod(Exception): - """Exception raised if a method is not valid for the current scenario.""" - - -# these dictionaries contain VCS-specific tools -LONG_VERSION_PY: Dict[str, str] = {} -HANDLERS: Dict[str, Dict[str, Callable]] = {} - - -def register_vcs_handler(vcs: str, method: str) -> Callable: # decorator - """Create decorator to mark a method as the handler of a VCS.""" - def decorate(f: Callable) -> Callable: - """Store f in HANDLERS[vcs][method].""" - HANDLERS.setdefault(vcs, {})[method] = f - return f - return decorate - - -def run_command( - commands: List[str], - args: List[str], - cwd: Optional[str] = None, - verbose: bool = False, - hide_stderr: bool = False, - env: Optional[Dict[str, str]] = None, -) -> Tuple[Optional[str], Optional[int]]: - """Call the given command(s).""" - assert isinstance(commands, list) - process = None - - popen_kwargs: Dict[str, Any] = {} - if sys.platform == "win32": - # This hides the console window if pythonw.exe is used - startupinfo = subprocess.STARTUPINFO() - startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW - popen_kwargs["startupinfo"] = startupinfo - - for command in commands: - try: - dispcmd = str([command] + args) - # remember shell=False, so use git.cmd on windows, not just git - process = subprocess.Popen([command] + args, cwd=cwd, env=env, - stdout=subprocess.PIPE, - stderr=(subprocess.PIPE if hide_stderr - else None), **popen_kwargs) - break - except OSError as e: - if e.errno == errno.ENOENT: - continue - if verbose: - print("unable to run %s" % dispcmd) - print(e) - return None, None - else: - if verbose: - print("unable to find command, tried %s" % (commands,)) - return None, None - stdout = process.communicate()[0].strip().decode() - if process.returncode != 0: - if verbose: - print("unable to run %s (error)" % dispcmd) - print("stdout was %s" % stdout) - return None, process.returncode - return stdout, process.returncode - - -LONG_VERSION_PY['git'] = r''' -# This file helps to compute a version number in source trees obtained from -# git-archive tarball (such as those provided by githubs download-from-tag -# feature). Distribution tarballs (built by setup.py sdist) and build -# directories (produced by setup.py build) will contain a much shorter file -# that just contains the computed version number. - -# This file is released into the public domain. -# Generated by versioneer-0.29 -# https://github.com/python-versioneer/python-versioneer - -"""Git implementation of _version.py.""" - -import errno -import os -import re -import subprocess -import sys -from typing import Any, Callable, Dict, List, Optional, Tuple -import functools - - -def get_keywords() -> Dict[str, str]: - """Get the keywords needed to look up the version information.""" - # these strings will be replaced by git during git-archive. - # setup.py/versioneer.py will grep for the variable names, so they must - # each be defined on a line of their own. _version.py will just call - # get_keywords(). - git_refnames = "%(DOLLAR)sFormat:%%d%(DOLLAR)s" - git_full = "%(DOLLAR)sFormat:%%H%(DOLLAR)s" - git_date = "%(DOLLAR)sFormat:%%ci%(DOLLAR)s" - keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} - return keywords - - -class VersioneerConfig: - """Container for Versioneer configuration parameters.""" - - VCS: str - style: str - tag_prefix: str - parentdir_prefix: str - versionfile_source: str - verbose: bool - - -def get_config() -> VersioneerConfig: - """Create, populate and return the VersioneerConfig() object.""" - # these strings are filled in when 'setup.py versioneer' creates - # _version.py - cfg = VersioneerConfig() - cfg.VCS = "git" - cfg.style = "%(STYLE)s" - cfg.tag_prefix = "%(TAG_PREFIX)s" - cfg.parentdir_prefix = "%(PARENTDIR_PREFIX)s" - cfg.versionfile_source = "%(VERSIONFILE_SOURCE)s" - cfg.verbose = False - return cfg - - -class NotThisMethod(Exception): - """Exception raised if a method is not valid for the current scenario.""" - - -LONG_VERSION_PY: Dict[str, str] = {} -HANDLERS: Dict[str, Dict[str, Callable]] = {} - - -def register_vcs_handler(vcs: str, method: str) -> Callable: # decorator - """Create decorator to mark a method as the handler of a VCS.""" - def decorate(f: Callable) -> Callable: - """Store f in HANDLERS[vcs][method].""" - if vcs not in HANDLERS: - HANDLERS[vcs] = {} - HANDLERS[vcs][method] = f - return f - return decorate - - -def run_command( - commands: List[str], - args: List[str], - cwd: Optional[str] = None, - verbose: bool = False, - hide_stderr: bool = False, - env: Optional[Dict[str, str]] = None, -) -> Tuple[Optional[str], Optional[int]]: - """Call the given command(s).""" - assert isinstance(commands, list) - process = None - - popen_kwargs: Dict[str, Any] = {} - if sys.platform == "win32": - # This hides the console window if pythonw.exe is used - startupinfo = subprocess.STARTUPINFO() - startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW - popen_kwargs["startupinfo"] = startupinfo - - for command in commands: - try: - dispcmd = str([command] + args) - # remember shell=False, so use git.cmd on windows, not just git - process = subprocess.Popen([command] + args, cwd=cwd, env=env, - stdout=subprocess.PIPE, - stderr=(subprocess.PIPE if hide_stderr - else None), **popen_kwargs) - break - except OSError as e: - if e.errno == errno.ENOENT: - continue - if verbose: - print("unable to run %%s" %% dispcmd) - print(e) - return None, None - else: - if verbose: - print("unable to find command, tried %%s" %% (commands,)) - return None, None - stdout = process.communicate()[0].strip().decode() - if process.returncode != 0: - if verbose: - print("unable to run %%s (error)" %% dispcmd) - print("stdout was %%s" %% stdout) - return None, process.returncode - return stdout, process.returncode - - -def versions_from_parentdir( - parentdir_prefix: str, - root: str, - verbose: bool, -) -> Dict[str, Any]: - """Try to determine the version from the parent directory name. - - Source tarballs conventionally unpack into a directory that includes both - the project name and a version string. We will also support searching up - two directory levels for an appropriately named parent directory - """ - rootdirs = [] - - for _ in range(3): - dirname = os.path.basename(root) - if dirname.startswith(parentdir_prefix): - return {"version": dirname[len(parentdir_prefix):], - "full-revisionid": None, - "dirty": False, "error": None, "date": None} - rootdirs.append(root) - root = os.path.dirname(root) # up a level - - if verbose: - print("Tried directories %%s but none started with prefix %%s" %% - (str(rootdirs), parentdir_prefix)) - raise NotThisMethod("rootdir doesn't start with parentdir_prefix") - - -@register_vcs_handler("git", "get_keywords") -def git_get_keywords(versionfile_abs: str) -> Dict[str, str]: - """Extract version information from the given file.""" - # the code embedded in _version.py can just fetch the value of these - # keywords. When used from setup.py, we don't want to import _version.py, - # so we do it with a regexp instead. This function is not used from - # _version.py. - keywords: Dict[str, str] = {} - try: - with open(versionfile_abs, "r") as fobj: - for line in fobj: - if line.strip().startswith("git_refnames ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["refnames"] = mo.group(1) - if line.strip().startswith("git_full ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["full"] = mo.group(1) - if line.strip().startswith("git_date ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["date"] = mo.group(1) - except OSError: - pass - return keywords - - -@register_vcs_handler("git", "keywords") -def git_versions_from_keywords( - keywords: Dict[str, str], - tag_prefix: str, - verbose: bool, -) -> Dict[str, Any]: - """Get version information from git keywords.""" - if "refnames" not in keywords: - raise NotThisMethod("Short version file found") - date = keywords.get("date") - if date is not None: - # Use only the last line. Previous lines may contain GPG signature - # information. - date = date.splitlines()[-1] - - # git-2.2.0 added "%%cI", which expands to an ISO-8601 -compliant - # datestamp. However we prefer "%%ci" (which expands to an "ISO-8601 - # -like" string, which we must then edit to make compliant), because - # it's been around since git-1.5.3, and it's too difficult to - # discover which version we're using, or to work around using an - # older one. - date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - refnames = keywords["refnames"].strip() - if refnames.startswith("$Format"): - if verbose: - print("keywords are unexpanded, not using") - raise NotThisMethod("unexpanded keywords, not a git-archive tarball") - refs = {r.strip() for r in refnames.strip("()").split(",")} - # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of - # just "foo-1.0". If we see a "tag: " prefix, prefer those. - TAG = "tag: " - tags = {r[len(TAG):] for r in refs if r.startswith(TAG)} - if not tags: - # Either we're using git < 1.8.3, or there really are no tags. We use - # a heuristic: assume all version tags have a digit. The old git %%d - # expansion behaves like git log --decorate=short and strips out the - # refs/heads/ and refs/tags/ prefixes that would let us distinguish - # between branches and tags. By ignoring refnames without digits, we - # filter out many common branch names like "release" and - # "stabilization", as well as "HEAD" and "master". - tags = {r for r in refs if re.search(r'\d', r)} - if verbose: - print("discarding '%%s', no digits" %% ",".join(refs - tags)) - if verbose: - print("likely tags: %%s" %% ",".join(sorted(tags))) - for ref in sorted(tags): - # sorting will prefer e.g. "2.0" over "2.0rc1" - if ref.startswith(tag_prefix): - r = ref[len(tag_prefix):] - # Filter out refs that exactly match prefix or that don't start - # with a number once the prefix is stripped (mostly a concern - # when prefix is '') - if not re.match(r'\d', r): - continue - if verbose: - print("picking %%s" %% r) - return {"version": r, - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": None, - "date": date} - # no suitable tags, so version is "0+unknown", but full hex is still there - if verbose: - print("no suitable tags, using unknown + full revision id") - return {"version": "0+unknown", - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": "no suitable tags", "date": None} - - -@register_vcs_handler("git", "pieces_from_vcs") -def git_pieces_from_vcs( - tag_prefix: str, - root: str, - verbose: bool, - runner: Callable = run_command -) -> Dict[str, Any]: - """Get version from 'git describe' in the root of the source tree. - - This only gets called if the git-archive 'subst' keywords were *not* - expanded, and _version.py hasn't already been rewritten with a short - version string, meaning we're inside a checked out source tree. - """ - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] - - # GIT_DIR can interfere with correct operation of Versioneer. - # It may be intended to be passed to the Versioneer-versioned project, - # but that should not change where we get our version from. - env = os.environ.copy() - env.pop("GIT_DIR", None) - runner = functools.partial(runner, env=env) - - _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, - hide_stderr=not verbose) - if rc != 0: - if verbose: - print("Directory %%s not under git control" %% root) - raise NotThisMethod("'git rev-parse --git-dir' returned error") - - # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] - # if there isn't one, this yields HEX[-dirty] (no NUM) - describe_out, rc = runner(GITS, [ - "describe", "--tags", "--dirty", "--always", "--long", - "--match", f"{tag_prefix}[[:digit:]]*" - ], cwd=root) - # --long was added in git-1.5.5 - if describe_out is None: - raise NotThisMethod("'git describe' failed") - describe_out = describe_out.strip() - full_out, rc = runner(GITS, ["rev-parse", "HEAD"], cwd=root) - if full_out is None: - raise NotThisMethod("'git rev-parse' failed") - full_out = full_out.strip() - - pieces: Dict[str, Any] = {} - pieces["long"] = full_out - pieces["short"] = full_out[:7] # maybe improved later - pieces["error"] = None - - branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], - cwd=root) - # --abbrev-ref was added in git-1.6.3 - if rc != 0 or branch_name is None: - raise NotThisMethod("'git rev-parse --abbrev-ref' returned error") - branch_name = branch_name.strip() - - if branch_name == "HEAD": - # If we aren't exactly on a branch, pick a branch which represents - # the current commit. If all else fails, we are on a branchless - # commit. - branches, rc = runner(GITS, ["branch", "--contains"], cwd=root) - # --contains was added in git-1.5.4 - if rc != 0 or branches is None: - raise NotThisMethod("'git branch --contains' returned error") - branches = branches.split("\n") - - # Remove the first line if we're running detached - if "(" in branches[0]: - branches.pop(0) - - # Strip off the leading "* " from the list of branches. - branches = [branch[2:] for branch in branches] - if "master" in branches: - branch_name = "master" - elif not branches: - branch_name = None - else: - # Pick the first branch that is returned. Good or bad. - branch_name = branches[0] - - pieces["branch"] = branch_name - - # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] - # TAG might have hyphens. - git_describe = describe_out - - # look for -dirty suffix - dirty = git_describe.endswith("-dirty") - pieces["dirty"] = dirty - if dirty: - git_describe = git_describe[:git_describe.rindex("-dirty")] - - # now we have TAG-NUM-gHEX or HEX - - if "-" in git_describe: - # TAG-NUM-gHEX - mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) - if not mo: - # unparsable. Maybe git-describe is misbehaving? - pieces["error"] = ("unable to parse git-describe output: '%%s'" - %% describe_out) - return pieces - - # tag - full_tag = mo.group(1) - if not full_tag.startswith(tag_prefix): - if verbose: - fmt = "tag '%%s' doesn't start with prefix '%%s'" - print(fmt %% (full_tag, tag_prefix)) - pieces["error"] = ("tag '%%s' doesn't start with prefix '%%s'" - %% (full_tag, tag_prefix)) - return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix):] - - # distance: number of commits since tag - pieces["distance"] = int(mo.group(2)) - - # commit: short hex revision ID - pieces["short"] = mo.group(3) - - else: - # HEX: no tags - pieces["closest-tag"] = None - out, rc = runner(GITS, ["rev-list", "HEAD", "--left-right"], cwd=root) - pieces["distance"] = len(out.split()) # total number of commits - - # commit date: see ISO-8601 comment in git_versions_from_keywords() - date = runner(GITS, ["show", "-s", "--format=%%ci", "HEAD"], cwd=root)[0].strip() - # Use only the last line. Previous lines may contain GPG signature - # information. - date = date.splitlines()[-1] - pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - - return pieces - - -def plus_or_dot(pieces: Dict[str, Any]) -> str: - """Return a + if we don't already have one, else return a .""" - if "+" in pieces.get("closest-tag", ""): - return "." - return "+" - - -def render_pep440(pieces: Dict[str, Any]) -> str: - """Build up version string, with post-release "local version identifier". - - Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you - get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty - - Exceptions: - 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += plus_or_dot(pieces) - rendered += "%%d.g%%s" %% (pieces["distance"], pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0+untagged.%%d.g%%s" %% (pieces["distance"], - pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def render_pep440_branch(pieces: Dict[str, Any]) -> str: - """TAG[[.dev0]+DISTANCE.gHEX[.dirty]] . - - The ".dev0" means not master branch. Note that .dev0 sorts backwards - (a feature branch will appear "older" than the master branch). - - Exceptions: - 1: no tags. 0[.dev0]+untagged.DISTANCE.gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += "%%d.g%%s" %% (pieces["distance"], pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0" - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += "+untagged.%%d.g%%s" %% (pieces["distance"], - pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def pep440_split_post(ver: str) -> Tuple[str, Optional[int]]: - """Split pep440 version string at the post-release segment. - - Returns the release segments before the post-release and the - post-release version number (or -1 if no post-release segment is present). - """ - vc = str.split(ver, ".post") - return vc[0], int(vc[1] or 0) if len(vc) == 2 else None - - -def render_pep440_pre(pieces: Dict[str, Any]) -> str: - """TAG[.postN.devDISTANCE] -- No -dirty. - - Exceptions: - 1: no tags. 0.post0.devDISTANCE - """ - if pieces["closest-tag"]: - if pieces["distance"]: - # update the post release segment - tag_version, post_version = pep440_split_post(pieces["closest-tag"]) - rendered = tag_version - if post_version is not None: - rendered += ".post%%d.dev%%d" %% (post_version + 1, pieces["distance"]) - else: - rendered += ".post0.dev%%d" %% (pieces["distance"]) - else: - # no commits, use the tag as the version - rendered = pieces["closest-tag"] - else: - # exception #1 - rendered = "0.post0.dev%%d" %% pieces["distance"] - return rendered - - -def render_pep440_post(pieces: Dict[str, Any]) -> str: - """TAG[.postDISTANCE[.dev0]+gHEX] . - - The ".dev0" means dirty. Note that .dev0 sorts backwards - (a dirty tree will appear "older" than the corresponding clean one), - but you shouldn't be releasing software with -dirty anyways. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%%d" %% pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += "g%%s" %% pieces["short"] - else: - # exception #1 - rendered = "0.post%%d" %% pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += "+g%%s" %% pieces["short"] - return rendered - - -def render_pep440_post_branch(pieces: Dict[str, Any]) -> str: - """TAG[.postDISTANCE[.dev0]+gHEX[.dirty]] . - - The ".dev0" means not master branch. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0]+gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%%d" %% pieces["distance"] - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += "g%%s" %% pieces["short"] - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0.post%%d" %% pieces["distance"] - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += "+g%%s" %% pieces["short"] - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def render_pep440_old(pieces: Dict[str, Any]) -> str: - """TAG[.postDISTANCE[.dev0]] . - - The ".dev0" means dirty. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%%d" %% pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - else: - # exception #1 - rendered = "0.post%%d" %% pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - return rendered - - -def render_git_describe(pieces: Dict[str, Any]) -> str: - """TAG[-DISTANCE-gHEX][-dirty]. - - Like 'git describe --tags --dirty --always'. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"]: - rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render_git_describe_long(pieces: Dict[str, Any]) -> str: - """TAG-DISTANCE-gHEX[-dirty]. - - Like 'git describe --tags --dirty --always -long'. - The distance/hash is unconditional. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render(pieces: Dict[str, Any], style: str) -> Dict[str, Any]: - """Render the given version pieces into the requested style.""" - if pieces["error"]: - return {"version": "unknown", - "full-revisionid": pieces.get("long"), - "dirty": None, - "error": pieces["error"], - "date": None} - - if not style or style == "default": - style = "pep440" # the default - - if style == "pep440": - rendered = render_pep440(pieces) - elif style == "pep440-branch": - rendered = render_pep440_branch(pieces) - elif style == "pep440-pre": - rendered = render_pep440_pre(pieces) - elif style == "pep440-post": - rendered = render_pep440_post(pieces) - elif style == "pep440-post-branch": - rendered = render_pep440_post_branch(pieces) - elif style == "pep440-old": - rendered = render_pep440_old(pieces) - elif style == "git-describe": - rendered = render_git_describe(pieces) - elif style == "git-describe-long": - rendered = render_git_describe_long(pieces) - else: - raise ValueError("unknown style '%%s'" %% style) - - return {"version": rendered, "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], "error": None, - "date": pieces.get("date")} - - -def get_versions() -> Dict[str, Any]: - """Get version information or return default if unable to do so.""" - # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have - # __file__, we can work backwards from there to the root. Some - # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which - # case we can only use expanded keywords. - - cfg = get_config() - verbose = cfg.verbose - - try: - return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, - verbose) - except NotThisMethod: - pass - - try: - root = os.path.realpath(__file__) - # versionfile_source is the relative path from the top of the source - # tree (where the .git directory might live) to this file. Invert - # this to find the root from __file__. - for _ in cfg.versionfile_source.split('/'): - root = os.path.dirname(root) - except NameError: - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to find root of source tree", - "date": None} - - try: - pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) - return render(pieces, cfg.style) - except NotThisMethod: - pass - - try: - if cfg.parentdir_prefix: - return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) - except NotThisMethod: - pass - - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to compute version", "date": None} -''' - - -@register_vcs_handler("git", "get_keywords") -def git_get_keywords(versionfile_abs: str) -> Dict[str, str]: - """Extract version information from the given file.""" - # the code embedded in _version.py can just fetch the value of these - # keywords. When used from setup.py, we don't want to import _version.py, - # so we do it with a regexp instead. This function is not used from - # _version.py. - keywords: Dict[str, str] = {} - try: - with open(versionfile_abs, "r") as fobj: - for line in fobj: - if line.strip().startswith("git_refnames ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["refnames"] = mo.group(1) - if line.strip().startswith("git_full ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["full"] = mo.group(1) - if line.strip().startswith("git_date ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["date"] = mo.group(1) - except OSError: - pass - return keywords - - -@register_vcs_handler("git", "keywords") -def git_versions_from_keywords( - keywords: Dict[str, str], - tag_prefix: str, - verbose: bool, -) -> Dict[str, Any]: - """Get version information from git keywords.""" - if "refnames" not in keywords: - raise NotThisMethod("Short version file found") - date = keywords.get("date") - if date is not None: - # Use only the last line. Previous lines may contain GPG signature - # information. - date = date.splitlines()[-1] - - # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant - # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 - # -like" string, which we must then edit to make compliant), because - # it's been around since git-1.5.3, and it's too difficult to - # discover which version we're using, or to work around using an - # older one. - date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - refnames = keywords["refnames"].strip() - if refnames.startswith("$Format"): - if verbose: - print("keywords are unexpanded, not using") - raise NotThisMethod("unexpanded keywords, not a git-archive tarball") - refs = {r.strip() for r in refnames.strip("()").split(",")} - # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of - # just "foo-1.0". If we see a "tag: " prefix, prefer those. - TAG = "tag: " - tags = {r[len(TAG):] for r in refs if r.startswith(TAG)} - if not tags: - # Either we're using git < 1.8.3, or there really are no tags. We use - # a heuristic: assume all version tags have a digit. The old git %d - # expansion behaves like git log --decorate=short and strips out the - # refs/heads/ and refs/tags/ prefixes that would let us distinguish - # between branches and tags. By ignoring refnames without digits, we - # filter out many common branch names like "release" and - # "stabilization", as well as "HEAD" and "master". - tags = {r for r in refs if re.search(r'\d', r)} - if verbose: - print("discarding '%s', no digits" % ",".join(refs - tags)) - if verbose: - print("likely tags: %s" % ",".join(sorted(tags))) - for ref in sorted(tags): - # sorting will prefer e.g. "2.0" over "2.0rc1" - if ref.startswith(tag_prefix): - r = ref[len(tag_prefix):] - # Filter out refs that exactly match prefix or that don't start - # with a number once the prefix is stripped (mostly a concern - # when prefix is '') - if not re.match(r'\d', r): - continue - if verbose: - print("picking %s" % r) - return {"version": r, - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": None, - "date": date} - # no suitable tags, so version is "0+unknown", but full hex is still there - if verbose: - print("no suitable tags, using unknown + full revision id") - return {"version": "0+unknown", - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": "no suitable tags", "date": None} - - -@register_vcs_handler("git", "pieces_from_vcs") -def git_pieces_from_vcs( - tag_prefix: str, - root: str, - verbose: bool, - runner: Callable = run_command -) -> Dict[str, Any]: - """Get version from 'git describe' in the root of the source tree. - - This only gets called if the git-archive 'subst' keywords were *not* - expanded, and _version.py hasn't already been rewritten with a short - version string, meaning we're inside a checked out source tree. - """ - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] - - # GIT_DIR can interfere with correct operation of Versioneer. - # It may be intended to be passed to the Versioneer-versioned project, - # but that should not change where we get our version from. - env = os.environ.copy() - env.pop("GIT_DIR", None) - runner = functools.partial(runner, env=env) - - _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, - hide_stderr=not verbose) - if rc != 0: - if verbose: - print("Directory %s not under git control" % root) - raise NotThisMethod("'git rev-parse --git-dir' returned error") - - # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] - # if there isn't one, this yields HEX[-dirty] (no NUM) - describe_out, rc = runner(GITS, [ - "describe", "--tags", "--dirty", "--always", "--long", - "--match", f"{tag_prefix}[[:digit:]]*" - ], cwd=root) - # --long was added in git-1.5.5 - if describe_out is None: - raise NotThisMethod("'git describe' failed") - describe_out = describe_out.strip() - full_out, rc = runner(GITS, ["rev-parse", "HEAD"], cwd=root) - if full_out is None: - raise NotThisMethod("'git rev-parse' failed") - full_out = full_out.strip() - - pieces: Dict[str, Any] = {} - pieces["long"] = full_out - pieces["short"] = full_out[:7] # maybe improved later - pieces["error"] = None - - branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], - cwd=root) - # --abbrev-ref was added in git-1.6.3 - if rc != 0 or branch_name is None: - raise NotThisMethod("'git rev-parse --abbrev-ref' returned error") - branch_name = branch_name.strip() - - if branch_name == "HEAD": - # If we aren't exactly on a branch, pick a branch which represents - # the current commit. If all else fails, we are on a branchless - # commit. - branches, rc = runner(GITS, ["branch", "--contains"], cwd=root) - # --contains was added in git-1.5.4 - if rc != 0 or branches is None: - raise NotThisMethod("'git branch --contains' returned error") - branches = branches.split("\n") - - # Remove the first line if we're running detached - if "(" in branches[0]: - branches.pop(0) - - # Strip off the leading "* " from the list of branches. - branches = [branch[2:] for branch in branches] - if "master" in branches: - branch_name = "master" - elif not branches: - branch_name = None - else: - # Pick the first branch that is returned. Good or bad. - branch_name = branches[0] - - pieces["branch"] = branch_name - - # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] - # TAG might have hyphens. - git_describe = describe_out - - # look for -dirty suffix - dirty = git_describe.endswith("-dirty") - pieces["dirty"] = dirty - if dirty: - git_describe = git_describe[:git_describe.rindex("-dirty")] - - # now we have TAG-NUM-gHEX or HEX - - if "-" in git_describe: - # TAG-NUM-gHEX - mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) - if not mo: - # unparsable. Maybe git-describe is misbehaving? - pieces["error"] = ("unable to parse git-describe output: '%s'" - % describe_out) - return pieces - - # tag - full_tag = mo.group(1) - if not full_tag.startswith(tag_prefix): - if verbose: - fmt = "tag '%s' doesn't start with prefix '%s'" - print(fmt % (full_tag, tag_prefix)) - pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" - % (full_tag, tag_prefix)) - return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix):] - - # distance: number of commits since tag - pieces["distance"] = int(mo.group(2)) - - # commit: short hex revision ID - pieces["short"] = mo.group(3) - - else: - # HEX: no tags - pieces["closest-tag"] = None - out, rc = runner(GITS, ["rev-list", "HEAD", "--left-right"], cwd=root) - pieces["distance"] = len(out.split()) # total number of commits - - # commit date: see ISO-8601 comment in git_versions_from_keywords() - date = runner(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip() - # Use only the last line. Previous lines may contain GPG signature - # information. - date = date.splitlines()[-1] - pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - - return pieces - - -def do_vcs_install(versionfile_source: str, ipy: Optional[str]) -> None: - """Git-specific installation logic for Versioneer. - - For Git, this means creating/changing .gitattributes to mark _version.py - for export-subst keyword substitution. - """ - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] - files = [versionfile_source] - if ipy: - files.append(ipy) - if "VERSIONEER_PEP518" not in globals(): - try: - my_path = __file__ - if my_path.endswith((".pyc", ".pyo")): - my_path = os.path.splitext(my_path)[0] + ".py" - versioneer_file = os.path.relpath(my_path) - except NameError: - versioneer_file = "versioneer.py" - files.append(versioneer_file) - present = False - try: - with open(".gitattributes", "r") as fobj: - for line in fobj: - if line.strip().startswith(versionfile_source): - if "export-subst" in line.strip().split()[1:]: - present = True - break - except OSError: - pass - if not present: - with open(".gitattributes", "a+") as fobj: - fobj.write(f"{versionfile_source} export-subst\n") - files.append(".gitattributes") - run_command(GITS, ["add", "--"] + files) - - -def versions_from_parentdir( - parentdir_prefix: str, - root: str, - verbose: bool, -) -> Dict[str, Any]: - """Try to determine the version from the parent directory name. - - Source tarballs conventionally unpack into a directory that includes both - the project name and a version string. We will also support searching up - two directory levels for an appropriately named parent directory - """ - rootdirs = [] - - for _ in range(3): - dirname = os.path.basename(root) - if dirname.startswith(parentdir_prefix): - return {"version": dirname[len(parentdir_prefix):], - "full-revisionid": None, - "dirty": False, "error": None, "date": None} - rootdirs.append(root) - root = os.path.dirname(root) # up a level - - if verbose: - print("Tried directories %s but none started with prefix %s" % - (str(rootdirs), parentdir_prefix)) - raise NotThisMethod("rootdir doesn't start with parentdir_prefix") - - -SHORT_VERSION_PY = """ -# This file was generated by 'versioneer.py' (0.29) from -# revision-control system data, or from the parent directory name of an -# unpacked source archive. Distribution tarballs contain a pre-generated copy -# of this file. - -import json - -version_json = ''' -%s -''' # END VERSION_JSON - - -def get_versions(): - return json.loads(version_json) -""" - - -def versions_from_file(filename: str) -> Dict[str, Any]: - """Try to determine the version from _version.py if present.""" - try: - with open(filename) as f: - contents = f.read() - except OSError: - raise NotThisMethod("unable to read _version.py") - mo = re.search(r"version_json = '''\n(.*)''' # END VERSION_JSON", - contents, re.M | re.S) - if not mo: - mo = re.search(r"version_json = '''\r\n(.*)''' # END VERSION_JSON", - contents, re.M | re.S) - if not mo: - raise NotThisMethod("no version_json in _version.py") - return json.loads(mo.group(1)) - - -def write_to_version_file(filename: str, versions: Dict[str, Any]) -> None: - """Write the given version number to the given _version.py file.""" - contents = json.dumps(versions, sort_keys=True, - indent=1, separators=(",", ": ")) - with open(filename, "w") as f: - f.write(SHORT_VERSION_PY % contents) - - print("set %s to '%s'" % (filename, versions["version"])) - - -def plus_or_dot(pieces: Dict[str, Any]) -> str: - """Return a + if we don't already have one, else return a .""" - if "+" in pieces.get("closest-tag", ""): - return "." - return "+" - - -def render_pep440(pieces: Dict[str, Any]) -> str: - """Build up version string, with post-release "local version identifier". - - Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you - get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty - - Exceptions: - 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += plus_or_dot(pieces) - rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0+untagged.%d.g%s" % (pieces["distance"], - pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def render_pep440_branch(pieces: Dict[str, Any]) -> str: - """TAG[[.dev0]+DISTANCE.gHEX[.dirty]] . - - The ".dev0" means not master branch. Note that .dev0 sorts backwards - (a feature branch will appear "older" than the master branch). - - Exceptions: - 1: no tags. 0[.dev0]+untagged.DISTANCE.gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0" - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += "+untagged.%d.g%s" % (pieces["distance"], - pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def pep440_split_post(ver: str) -> Tuple[str, Optional[int]]: - """Split pep440 version string at the post-release segment. - - Returns the release segments before the post-release and the - post-release version number (or -1 if no post-release segment is present). - """ - vc = str.split(ver, ".post") - return vc[0], int(vc[1] or 0) if len(vc) == 2 else None - - -def render_pep440_pre(pieces: Dict[str, Any]) -> str: - """TAG[.postN.devDISTANCE] -- No -dirty. - - Exceptions: - 1: no tags. 0.post0.devDISTANCE - """ - if pieces["closest-tag"]: - if pieces["distance"]: - # update the post release segment - tag_version, post_version = pep440_split_post(pieces["closest-tag"]) - rendered = tag_version - if post_version is not None: - rendered += ".post%d.dev%d" % (post_version + 1, pieces["distance"]) - else: - rendered += ".post0.dev%d" % (pieces["distance"]) - else: - # no commits, use the tag as the version - rendered = pieces["closest-tag"] - else: - # exception #1 - rendered = "0.post0.dev%d" % pieces["distance"] - return rendered - - -def render_pep440_post(pieces: Dict[str, Any]) -> str: - """TAG[.postDISTANCE[.dev0]+gHEX] . - - The ".dev0" means dirty. Note that .dev0 sorts backwards - (a dirty tree will appear "older" than the corresponding clean one), - but you shouldn't be releasing software with -dirty anyways. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += "g%s" % pieces["short"] - else: - # exception #1 - rendered = "0.post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += "+g%s" % pieces["short"] - return rendered - - -def render_pep440_post_branch(pieces: Dict[str, Any]) -> str: - """TAG[.postDISTANCE[.dev0]+gHEX[.dirty]] . - - The ".dev0" means not master branch. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0]+gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%d" % pieces["distance"] - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += "g%s" % pieces["short"] - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0.post%d" % pieces["distance"] - if pieces["branch"] != "master": - rendered += ".dev0" - rendered += "+g%s" % pieces["short"] - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def render_pep440_old(pieces: Dict[str, Any]) -> str: - """TAG[.postDISTANCE[.dev0]] . - - The ".dev0" means dirty. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - else: - # exception #1 - rendered = "0.post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - return rendered - - -def render_git_describe(pieces: Dict[str, Any]) -> str: - """TAG[-DISTANCE-gHEX][-dirty]. - - Like 'git describe --tags --dirty --always'. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"]: - rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render_git_describe_long(pieces: Dict[str, Any]) -> str: - """TAG-DISTANCE-gHEX[-dirty]. - - Like 'git describe --tags --dirty --always -long'. - The distance/hash is unconditional. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render(pieces: Dict[str, Any], style: str) -> Dict[str, Any]: - """Render the given version pieces into the requested style.""" - if pieces["error"]: - return {"version": "unknown", - "full-revisionid": pieces.get("long"), - "dirty": None, - "error": pieces["error"], - "date": None} - - if not style or style == "default": - style = "pep440" # the default - - if style == "pep440": - rendered = render_pep440(pieces) - elif style == "pep440-branch": - rendered = render_pep440_branch(pieces) - elif style == "pep440-pre": - rendered = render_pep440_pre(pieces) - elif style == "pep440-post": - rendered = render_pep440_post(pieces) - elif style == "pep440-post-branch": - rendered = render_pep440_post_branch(pieces) - elif style == "pep440-old": - rendered = render_pep440_old(pieces) - elif style == "git-describe": - rendered = render_git_describe(pieces) - elif style == "git-describe-long": - rendered = render_git_describe_long(pieces) - else: - raise ValueError("unknown style '%s'" % style) - - return {"version": rendered, "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], "error": None, - "date": pieces.get("date")} - - -class VersioneerBadRootError(Exception): - """The project root directory is unknown or missing key files.""" - - -def get_versions(verbose: bool = False) -> Dict[str, Any]: - """Get the project version from whatever source is available. - - Returns dict with two keys: 'version' and 'full'. - """ - if "versioneer" in sys.modules: - # see the discussion in cmdclass.py:get_cmdclass() - del sys.modules["versioneer"] - - root = get_root() - cfg = get_config_from_root(root) - - assert cfg.VCS is not None, "please set [versioneer]VCS= in setup.cfg" - handlers = HANDLERS.get(cfg.VCS) - assert handlers, "unrecognized VCS '%s'" % cfg.VCS - verbose = verbose or bool(cfg.verbose) # `bool()` used to avoid `None` - assert cfg.versionfile_source is not None, \ - "please set versioneer.versionfile_source" - assert cfg.tag_prefix is not None, "please set versioneer.tag_prefix" - - versionfile_abs = os.path.join(root, cfg.versionfile_source) - - # extract version from first of: _version.py, VCS command (e.g. 'git - # describe'), parentdir. This is meant to work for developers using a - # source checkout, for users of a tarball created by 'setup.py sdist', - # and for users of a tarball/zipball created by 'git archive' or github's - # download-from-tag feature or the equivalent in other VCSes. - - get_keywords_f = handlers.get("get_keywords") - from_keywords_f = handlers.get("keywords") - if get_keywords_f and from_keywords_f: - try: - keywords = get_keywords_f(versionfile_abs) - ver = from_keywords_f(keywords, cfg.tag_prefix, verbose) - if verbose: - print("got version from expanded keyword %s" % ver) - return ver - except NotThisMethod: - pass - - try: - ver = versions_from_file(versionfile_abs) - if verbose: - print("got version from file %s %s" % (versionfile_abs, ver)) - return ver - except NotThisMethod: - pass - - from_vcs_f = handlers.get("pieces_from_vcs") - if from_vcs_f: - try: - pieces = from_vcs_f(cfg.tag_prefix, root, verbose) - ver = render(pieces, cfg.style) - if verbose: - print("got version from VCS %s" % ver) - return ver - except NotThisMethod: - pass - - try: - if cfg.parentdir_prefix: - ver = versions_from_parentdir(cfg.parentdir_prefix, root, verbose) - if verbose: - print("got version from parentdir %s" % ver) - return ver - except NotThisMethod: - pass - - if verbose: - print("unable to compute version") - - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, "error": "unable to compute version", - "date": None} - - -def get_version() -> str: - """Get the short version string for this project.""" - return get_versions()["version"] - - -def get_cmdclass(cmdclass: Optional[Dict[str, Any]] = None): - """Get the custom setuptools subclasses used by Versioneer. - - If the package uses a different cmdclass (e.g. one from numpy), it - should be provide as an argument. - """ - if "versioneer" in sys.modules: - del sys.modules["versioneer"] - # this fixes the "python setup.py develop" case (also 'install' and - # 'easy_install .'), in which subdependencies of the main project are - # built (using setup.py bdist_egg) in the same python process. Assume - # a main project A and a dependency B, which use different versions - # of Versioneer. A's setup.py imports A's Versioneer, leaving it in - # sys.modules by the time B's setup.py is executed, causing B to run - # with the wrong versioneer. Setuptools wraps the sub-dep builds in a - # sandbox that restores sys.modules to it's pre-build state, so the - # parent is protected against the child's "import versioneer". By - # removing ourselves from sys.modules here, before the child build - # happens, we protect the child from the parent's versioneer too. - # Also see https://github.com/python-versioneer/python-versioneer/issues/52 - - cmds = {} if cmdclass is None else cmdclass.copy() - - # we add "version" to setuptools - from setuptools import Command - - class cmd_version(Command): - description = "report generated version string" - user_options: List[Tuple[str, str, str]] = [] - boolean_options: List[str] = [] - - def initialize_options(self) -> None: - pass - - def finalize_options(self) -> None: - pass - - def run(self) -> None: - vers = get_versions(verbose=True) - print("Version: %s" % vers["version"]) - print(" full-revisionid: %s" % vers.get("full-revisionid")) - print(" dirty: %s" % vers.get("dirty")) - print(" date: %s" % vers.get("date")) - if vers["error"]: - print(" error: %s" % vers["error"]) - cmds["version"] = cmd_version - - # we override "build_py" in setuptools - # - # most invocation pathways end up running build_py: - # distutils/build -> build_py - # distutils/install -> distutils/build ->.. - # setuptools/bdist_wheel -> distutils/install ->.. - # setuptools/bdist_egg -> distutils/install_lib -> build_py - # setuptools/install -> bdist_egg ->.. - # setuptools/develop -> ? - # pip install: - # copies source tree to a tempdir before running egg_info/etc - # if .git isn't copied too, 'git describe' will fail - # then does setup.py bdist_wheel, or sometimes setup.py install - # setup.py egg_info -> ? - - # pip install -e . and setuptool/editable_wheel will invoke build_py - # but the build_py command is not expected to copy any files. - - # we override different "build_py" commands for both environments - if 'build_py' in cmds: - _build_py: Any = cmds['build_py'] - else: - from setuptools.command.build_py import build_py as _build_py - - class cmd_build_py(_build_py): - def run(self) -> None: - root = get_root() - cfg = get_config_from_root(root) - versions = get_versions() - _build_py.run(self) - if getattr(self, "editable_mode", False): - # During editable installs `.py` and data files are - # not copied to build_lib - return - # now locate _version.py in the new build/ directory and replace - # it with an updated value - if cfg.versionfile_build: - target_versionfile = os.path.join(self.build_lib, - cfg.versionfile_build) - print("UPDATING %s" % target_versionfile) - write_to_version_file(target_versionfile, versions) - cmds["build_py"] = cmd_build_py - - if 'build_ext' in cmds: - _build_ext: Any = cmds['build_ext'] - else: - from setuptools.command.build_ext import build_ext as _build_ext - - class cmd_build_ext(_build_ext): - def run(self) -> None: - root = get_root() - cfg = get_config_from_root(root) - versions = get_versions() - _build_ext.run(self) - if self.inplace: - # build_ext --inplace will only build extensions in - # build/lib<..> dir with no _version.py to write to. - # As in place builds will already have a _version.py - # in the module dir, we do not need to write one. - return - # now locate _version.py in the new build/ directory and replace - # it with an updated value - if not cfg.versionfile_build: - return - target_versionfile = os.path.join(self.build_lib, - cfg.versionfile_build) - if not os.path.exists(target_versionfile): - print(f"Warning: {target_versionfile} does not exist, skipping " - "version update. This can happen if you are running build_ext " - "without first running build_py.") - return - print("UPDATING %s" % target_versionfile) - write_to_version_file(target_versionfile, versions) - cmds["build_ext"] = cmd_build_ext - - if "cx_Freeze" in sys.modules: # cx_freeze enabled? - from cx_Freeze.dist import build_exe as _build_exe # type: ignore - # nczeczulin reports that py2exe won't like the pep440-style string - # as FILEVERSION, but it can be used for PRODUCTVERSION, e.g. - # setup(console=[{ - # "version": versioneer.get_version().split("+", 1)[0], # FILEVERSION - # "product_version": versioneer.get_version(), - # ... - - class cmd_build_exe(_build_exe): - def run(self) -> None: - root = get_root() - cfg = get_config_from_root(root) - versions = get_versions() - target_versionfile = cfg.versionfile_source - print("UPDATING %s" % target_versionfile) - write_to_version_file(target_versionfile, versions) - - _build_exe.run(self) - os.unlink(target_versionfile) - with open(cfg.versionfile_source, "w") as f: - LONG = LONG_VERSION_PY[cfg.VCS] - f.write(LONG % - {"DOLLAR": "$", - "STYLE": cfg.style, - "TAG_PREFIX": cfg.tag_prefix, - "PARENTDIR_PREFIX": cfg.parentdir_prefix, - "VERSIONFILE_SOURCE": cfg.versionfile_source, - }) - cmds["build_exe"] = cmd_build_exe - del cmds["build_py"] - - if 'py2exe' in sys.modules: # py2exe enabled? - try: - from py2exe.setuptools_buildexe import py2exe as _py2exe # type: ignore - except ImportError: - from py2exe.distutils_buildexe import py2exe as _py2exe # type: ignore - - class cmd_py2exe(_py2exe): - def run(self) -> None: - root = get_root() - cfg = get_config_from_root(root) - versions = get_versions() - target_versionfile = cfg.versionfile_source - print("UPDATING %s" % target_versionfile) - write_to_version_file(target_versionfile, versions) - - _py2exe.run(self) - os.unlink(target_versionfile) - with open(cfg.versionfile_source, "w") as f: - LONG = LONG_VERSION_PY[cfg.VCS] - f.write(LONG % - {"DOLLAR": "$", - "STYLE": cfg.style, - "TAG_PREFIX": cfg.tag_prefix, - "PARENTDIR_PREFIX": cfg.parentdir_prefix, - "VERSIONFILE_SOURCE": cfg.versionfile_source, - }) - cmds["py2exe"] = cmd_py2exe - - # sdist farms its file list building out to egg_info - if 'egg_info' in cmds: - _egg_info: Any = cmds['egg_info'] - else: - from setuptools.command.egg_info import egg_info as _egg_info - - class cmd_egg_info(_egg_info): - def find_sources(self) -> None: - # egg_info.find_sources builds the manifest list and writes it - # in one shot - super().find_sources() - - # Modify the filelist and normalize it - root = get_root() - cfg = get_config_from_root(root) - self.filelist.append('versioneer.py') - if cfg.versionfile_source: - # There are rare cases where versionfile_source might not be - # included by default, so we must be explicit - self.filelist.append(cfg.versionfile_source) - self.filelist.sort() - self.filelist.remove_duplicates() - - # The write method is hidden in the manifest_maker instance that - # generated the filelist and was thrown away - # We will instead replicate their final normalization (to unicode, - # and POSIX-style paths) - from setuptools import unicode_utils - normalized = [unicode_utils.filesys_decode(f).replace(os.sep, '/') - for f in self.filelist.files] - - manifest_filename = os.path.join(self.egg_info, 'SOURCES.txt') - with open(manifest_filename, 'w') as fobj: - fobj.write('\n'.join(normalized)) - - cmds['egg_info'] = cmd_egg_info - - # we override different "sdist" commands for both environments - if 'sdist' in cmds: - _sdist: Any = cmds['sdist'] - else: - from setuptools.command.sdist import sdist as _sdist - - class cmd_sdist(_sdist): - def run(self) -> None: - versions = get_versions() - self._versioneer_generated_versions = versions - # unless we update this, the command will keep using the old - # version - self.distribution.metadata.version = versions["version"] - return _sdist.run(self) - - def make_release_tree(self, base_dir: str, files: List[str]) -> None: - root = get_root() - cfg = get_config_from_root(root) - _sdist.make_release_tree(self, base_dir, files) - # now locate _version.py in the new base_dir directory - # (remembering that it may be a hardlink) and replace it with an - # updated value - target_versionfile = os.path.join(base_dir, cfg.versionfile_source) - print("UPDATING %s" % target_versionfile) - write_to_version_file(target_versionfile, - self._versioneer_generated_versions) - cmds["sdist"] = cmd_sdist - - return cmds - - -CONFIG_ERROR = """ -setup.cfg is missing the necessary Versioneer configuration. You need -a section like: - - [versioneer] - VCS = git - style = pep440 - versionfile_source = src/myproject/_version.py - versionfile_build = myproject/_version.py - tag_prefix = - parentdir_prefix = myproject- - -You will also need to edit your setup.py to use the results: - - import versioneer - setup(version=versioneer.get_version(), - cmdclass=versioneer.get_cmdclass(), ...) - -Please read the docstring in ./versioneer.py for configuration instructions, -edit setup.cfg, and re-run the installer or 'python versioneer.py setup'. -""" - -SAMPLE_CONFIG = """ -# See the docstring in versioneer.py for instructions. Note that you must -# re-run 'versioneer.py setup' after changing this section, and commit the -# resulting files. - -[versioneer] -#VCS = git -#style = pep440 -#versionfile_source = -#versionfile_build = -#tag_prefix = -#parentdir_prefix = - -""" - -OLD_SNIPPET = """ -from ._version import get_versions -__version__ = get_versions()['version'] -del get_versions -""" - -INIT_PY_SNIPPET = """ -from . import {0} -__version__ = {0}.get_versions()['version'] -""" - - -def do_setup() -> int: - """Do main VCS-independent setup function for installing Versioneer.""" - root = get_root() - try: - cfg = get_config_from_root(root) - except (OSError, configparser.NoSectionError, - configparser.NoOptionError) as e: - if isinstance(e, (OSError, configparser.NoSectionError)): - print("Adding sample versioneer config to setup.cfg", - file=sys.stderr) - with open(os.path.join(root, "setup.cfg"), "a") as f: - f.write(SAMPLE_CONFIG) - print(CONFIG_ERROR, file=sys.stderr) - return 1 - - print(" creating %s" % cfg.versionfile_source) - with open(cfg.versionfile_source, "w") as f: - LONG = LONG_VERSION_PY[cfg.VCS] - f.write(LONG % {"DOLLAR": "$", - "STYLE": cfg.style, - "TAG_PREFIX": cfg.tag_prefix, - "PARENTDIR_PREFIX": cfg.parentdir_prefix, - "VERSIONFILE_SOURCE": cfg.versionfile_source, - }) - - ipy = os.path.join(os.path.dirname(cfg.versionfile_source), - "__init__.py") - maybe_ipy: Optional[str] = ipy - if os.path.exists(ipy): - try: - with open(ipy, "r") as f: - old = f.read() - except OSError: - old = "" - module = os.path.splitext(os.path.basename(cfg.versionfile_source))[0] - snippet = INIT_PY_SNIPPET.format(module) - if OLD_SNIPPET in old: - print(" replacing boilerplate in %s" % ipy) - with open(ipy, "w") as f: - f.write(old.replace(OLD_SNIPPET, snippet)) - elif snippet not in old: - print(" appending to %s" % ipy) - with open(ipy, "a") as f: - f.write(snippet) - else: - print(" %s unmodified" % ipy) - else: - print(" %s doesn't exist, ok" % ipy) - maybe_ipy = None - - # Make VCS-specific changes. For git, this means creating/changing - # .gitattributes to mark _version.py for export-subst keyword - # substitution. - do_vcs_install(cfg.versionfile_source, maybe_ipy) - return 0 - - -def scan_setup_py() -> int: - """Validate the contents of setup.py against Versioneer's expectations.""" - found = set() - setters = False - errors = 0 - with open("setup.py", "r") as f: - for line in f.readlines(): - if "import versioneer" in line: - found.add("import") - if "versioneer.get_cmdclass()" in line: - found.add("cmdclass") - if "versioneer.get_version()" in line: - found.add("get_version") - if "versioneer.VCS" in line: - setters = True - if "versioneer.versionfile_source" in line: - setters = True - if len(found) != 3: - print("") - print("Your setup.py appears to be missing some important items") - print("(but I might be wrong). Please make sure it has something") - print("roughly like the following:") - print("") - print(" import versioneer") - print(" setup( version=versioneer.get_version(),") - print(" cmdclass=versioneer.get_cmdclass(), ...)") - print("") - errors += 1 - if setters: - print("You should remove lines like 'versioneer.VCS = ' and") - print("'versioneer.versionfile_source = ' . This configuration") - print("now lives in setup.cfg, and should be removed from setup.py") - print("") - errors += 1 - return errors - - -def setup_command() -> NoReturn: - """Set up Versioneer and exit with appropriate error code.""" - errors = do_setup() - errors += scan_setup_py() - sys.exit(1 if errors else 0) - - -if __name__ == "__main__": - cmd = sys.argv[1] - if cmd == "setup": - setup_command()